• Advice on implementing undo/redo in a Tool

    Cinema 4D SDK c++ 2026
    3
    0 Votes
    3 Posts
    24 Views
    SteveHill3DS
    Thank you for the pointers. I will digest the examples.
  • Tile rendering with Cinema 4D

    Cinema 4D SDK python 2026
    5
    0 Votes
    5 Posts
    80 Views
    K
    Thanks @ferdinand . I was about to add to this forum post that the rendered images are bit darker than actual. Thanks for the code link. Here's my updated script with changes for OCIO and I was able to get it rendering correctly at least locally. Anything to avoid or missed here? import c4d import os doc: c4d.documents.BaseDocument op: c4d.BaseObject | None def EnsureIsOcioDocument(doc: c4d.documents.BaseDocument) -> None: if doc[c4d.DOCUMENT_COLOR_MANAGEMENT] is not c4d.DOCUMENT_COLOR_MANAGEMENT_OCIO: doc[c4d.DOCUMENT_COLOR_MANAGEMENT] = c4d.DOCUMENT_COLOR_MANAGEMENT_OCIO doc.UpdateOcioColorSpaces() if c4d.threading.GeIsMainThreadAndNoDrawThread(): c4d.EventAdd() def main() -> None: """Renders the scene as a grid of tiles, then reassembles into the final image.""" # --- Configure these --- tiles_x = 2 tiles_y = 2 output_dir = os.path.join(os.path.expanduser("~"), "Desktop", "tiles") final_path = os.path.join(output_dir, "final_assembled.png") # ------------------------ os.makedirs(output_dir, exist_ok=True) EnsureIsOcioDocument(doc) if not doc.GetDocumentPath(): c4d.gui.MessageDialog("Please save the document first.") return # Work directly on the document's render data, matching the reference pattern. renderData: c4d.documents.RenderData = doc.GetActiveRenderData() data: c4d.BaseContainer = renderData.GetDataInstance() requiresBaking: bool = data[c4d.RDATA_FORMATDEPTH] is c4d.RDATA_FORMATDEPTH_8 xRes: int = int(data[c4d.RDATA_XRES_VIRTUAL] or data[c4d.RDATA_XRES]) yRes: int = int(data[c4d.RDATA_YRES_VIRTUAL] or data[c4d.RDATA_YRES]) tile_w = xRes // tiles_x tile_h = yRes // tiles_y # Determine save bit flags based on format depth if data[c4d.RDATA_FORMATDEPTH] is c4d.RDATA_FORMATDEPTH_16: save_bits = c4d.SAVEBIT_16BITCHANNELS elif data[c4d.RDATA_FORMATDEPTH] is c4d.RDATA_FORMATDEPTH_32: save_bits = c4d.SAVEBIT_32BITCHANNELS else: save_bits = c4d.SAVEBIT_NONE # Save original render region state to restore later orig_region = data[c4d.RDATA_RENDERREGION] orig_region_left = data[c4d.RDATA_RENDERREGION_LEFT] orig_region_top = data[c4d.RDATA_RENDERREGION_TOP] orig_region_right = data[c4d.RDATA_RENDERREGION_RIGHT] orig_region_bottom = data[c4d.RDATA_RENDERREGION_BOTTOM] orig_bake_flag = data.GetBool(c4d.RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER) if requiresBaking: data[c4d.RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER] = False tile_bmps = {} for ty in range(tiles_y): for tx in range(tiles_x): left = tx * tile_w top_ = ty * tile_h right = left + tile_w bottom = top_ + tile_h data[c4d.RDATA_RENDERREGION] = True data[c4d.RDATA_RENDERREGION_LEFT] = left data[c4d.RDATA_RENDERREGION_TOP] = top_ data[c4d.RDATA_RENDERREGION_RIGHT] = right data[c4d.RDATA_RENDERREGION_BOTTOM] = bottom # Always render as 32-bit float bmp = c4d.bitmaps.MultipassBitmap(xRes, yRes, c4d.COLORMODE_RGBf) bmp.AddChannel(True, True) print(f"Rendering tile ({tx}, {ty}) region=({left},{top_})-({right},{bottom})") result = c4d.documents.RenderDocument( doc, data, bmp, c4d.RENDERFLAGS_EXTERNAL, ) if result != c4d.RENDERRESULT_OK: print(f"Tile ({tx}, {ty}) failed with code: {result}") # Restore original settings before returning data[c4d.RDATA_RENDERREGION] = orig_region data[c4d.RDATA_RENDERREGION_LEFT] = orig_region_left data[c4d.RDATA_RENDERREGION_TOP] = orig_region_top data[c4d.RDATA_RENDERREGION_RIGHT] = orig_region_right data[c4d.RDATA_RENDERREGION_BOTTOM] = orig_region_bottom data[c4d.RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER] = orig_bake_flag return # Bake OCIO and null profiles — exactly as reference if requiresBaking: bmp = c4d.documents.BakeOcioViewToBitmap(bmp, data, c4d.SAVEBIT_NONE) or bmp bmp.SetColorProfile(c4d.bitmaps.ColorProfile(), c4d.COLORPROFILE_INDEX_DISPLAYSPACE) bmp.SetColorProfile(c4d.bitmaps.ColorProfile(), c4d.COLORPROFILE_INDEX_VIEW_TRANSFORM) # Crop tile preserving bit depth tile_bmp = bmp.GetClonePart(left, top_, tile_w, tile_h) if tile_bmp is None: print(f"Tile ({tx}, {ty}) crop failed") continue tile_path = os.path.join(output_dir, f"tile_{tx}_{ty}.png") tile_bmp.Save(tile_path, c4d.FILTER_PNG, c4d.BaseContainer(), save_bits) print(f"Saved {tile_path}") tile_bmps[(tx, ty)] = tile_bmp # Restore original render data settings data[c4d.RDATA_RENDERREGION] = orig_region data[c4d.RDATA_RENDERREGION_LEFT] = orig_region_left data[c4d.RDATA_RENDERREGION_TOP] = orig_region_top data[c4d.RDATA_RENDERREGION_RIGHT] = orig_region_right data[c4d.RDATA_RENDERREGION_BOTTOM] = orig_region_bottom data[c4d.RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER] = orig_bake_flag # --- Reassemble using GetPixelCnt/SetPixelCnt to preserve full bit depth --- print("Assembling final image...") # Get bit depth from the first tile first_tile = tile_bmps.get((0, 0)) if first_tile is None: print("No tiles to assemble") return tile_bpp = first_tile.GetBt() bpc = tile_bpp // 3 if bpc == 32: color_mode = c4d.COLORMODE_RGBf inc = 12 elif bpc == 16: color_mode = c4d.COLORMODE_RGBw inc = 6 else: color_mode = c4d.COLORMODE_RGB inc = 3 final_bmp = c4d.bitmaps.BaseBitmap() final_bmp.Init(xRes, yRes, depth=tile_bpp) row_buffer = bytearray(tile_w * inc) row_view = memoryview(row_buffer) for ty in range(tiles_y): for tx in range(tiles_x): if (tx, ty) not in tile_bmps: continue tile_bmp = tile_bmps[(tx, ty)] dst_x = tx * tile_w dst_y = ty * tile_h for py in range(tile_h): tile_bmp.GetPixelCnt( 0, py, tile_w, row_view, inc, color_mode, c4d.PIXELCNT_0 ) final_bmp.SetPixelCnt( dst_x, dst_y + py, tile_w, row_view, inc, color_mode, c4d.PIXELCNT_0 ) final_bmp.Save(final_path, c4d.FILTER_PNG, c4d.BaseContainer(), save_bits) print(f"Saved assembled image to {final_path}") c4d.bitmaps.ShowBitmap(final_bmp) c4d.gui.MessageDialog(f"Done! {tiles_x * tiles_y} tiles rendered and assembled.\n{final_path}") if __name__ == "__main__": main()
  • 0 Votes
    2 Posts
    75 Views
    ferdinandF
    Hey @aturtur, Thank you for reaching out to us. EventAdd will never really work in script manager scripts in the sense you mean it, unless you use hacks like dangling async dialogs (which as I always point out are a really bad idea). The reason is that Script Manager scripts are blocking, i.e., all scene and GUI execution is being halted until the script finishes. You can hack yourself around this with a dangling async dialog, i.e., a dialog that lives beyond the life time of its script. But that is not a good idea, you should implement some form of plugin to host your asnyc dialog, as you otherwise risk crashes. A modal dialog is just an extension of this. It is right in the name, it is modal, i.e., synchronous. All scene and GUI execution is being halted while this dialog is open and only resumes once it closes. When you want updates while your dialog is open, you need an async dialog (and a plugin which hosts it). Cheers, Ferdinand Since you also might misunderstand the nature of EventAdd() I am also putting here the C++ docs I updated a few weeks ago, to better reflect the nature of it (not yet live): /// @brief Enqueues an update event for the active document. /// @details Only must be called when modifying the active document and is without meaning for other documents. The typical example of using `EventAdd` is after adding or removing elements from the active document; and wanting these changes to be reflected in the UI. The function itself is technically thread-safe, but the vast majority of operations that require calling `EventAdd` are not thread-safe and must be called from the main thread (and therefore calling this function is usually main thread bound). The function also does not enqueue a dedicated event item, but rather sets a flag that is checked when the next update event is processed. Therefore, calling `EventAdd` multiple times in one function scope is unnecessary overhead which must be avoided. Because such multiple event flags cannot be consumed while a function on the main thread is still running, and instead the event will only be consumed after that function returns. /// @code /// Result<void> AddCubes() /// { /// CheckState(maxon::ThreadInterface::IsMainThread(), "AddCubes must be called from the main thread."_s); /// /// // EventAdd(); // We could also technically call it here with the same effect. The event /// // will only happen after this function returns. /// /// BaseDocument* doc = GetActiveDocument(); /// for (int i = 0; i < 10; ++i) /// { /// BaseObject* cube = BaseObject::Alloc(Ocube); /// if (!cube) /// return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION, "Failed to allocate cube object."_s); /// /// doc->InsertObject(cube); /// /// // Calling EventAdd here would have no extra effect, since this event cannot be consumed while /// // our main thread function is still running. And such extra calls on a large scale can cause /// // considerable overhead. /// } /// /// // Notify C4D that the active document has changed. The very end of a function or scope is the /// // canonical place to call EventAdd(). /// EventAdd(); /// } /// @endcode /// @see The article @link page_manual_coremessages Core Messages@endlink for more information. /// @param[in] eventflag The event to add: @enumerateEnum{EVENT}
  • Xref Material reference

    Moved General Talk windows 2026 2025 c++
    2
    0 Votes
    2 Posts
    58 Views
    ferdinandF
    Hello @Jespersather, thank you for reaching out to us. This is a developer forum, not an end user support forum. We cannot help you here with your end user issues. Please use our Support Center to get end user support for Cinema 4D. I have moved your topic into General Talk. Cheers, Ferdinand
  • C++ SDK Matrix object style distribution

    Cinema 4D SDK 2026 c++
    3
    0 Votes
    3 Posts
    105 Views
    dexD
    Thank you so much Ferdinand! I have a way forward now for my plugin using the EffectorData ModifyPoints() method. Kind regards Fredrik
  • 0 Votes
    3 Posts
    82 Views
    pislicesP
    Hi @ferdinand, I appreciate the reply! I didn't have a chance to update this post until now, but over the weekend I also found the Graph Descriptions Manual you've linked. The Scalar Ramp example in there was enough to help me figure out how to implement it with the Ramp node. Thank you for your response though, I'm sure it will make things easier if anyone else comes across this subject!
  • Set View Transform in Picture Viewer

    Cinema 4D SDK c++ 2026
    2
    0 Votes
    2 Posts
    74 Views
    ferdinandF
    Hey @ECHekman, Thank you for reaching out to us. The answer to your question is sort of yesn't. You can set the embedded view transform of a bitmap, which by default will be used by the Picture Viewer. The Picture Viewer like most managers in Cinema 4D is sealed, and we do not want to change that. So, you cannot change what view transform override is used by the Picture Viewer (when the user chooses to ignore the view transform embedded into an image). See Manage Bitmap OCIO Color Profiles for details. Cheers, Ferdinand
  • Debugging in VS Code does not pause at breakpoints

    Moved Bugs 2026 python macos
    3
    0 Votes
    3 Posts
    101 Views
    idealflawI
    Thank you @ferdinand for the warm welcome And thank you for having a look and for the detailed reply! I made sure to read through the posts you linked above and will keep them in mind for the future. I'm really looking forward to being a member of this amazing community 🤩
  • Access Node Material Path Redshift 2026

    Cinema 4D SDK 2026 python windows
    2
    0 Votes
    2 Posts
    112 Views
    R
    hi there, I actually did sort this out in a very round about way with some "vibe coding". this was for a mapp creation project I am working where I am displaying various eras of map onto 20km grids (so as not to kill the viewport functionality). have a look at the below and let me know if this is a solid approach or if there was a better way: import c4d import maxon def main(): era = "INSERT_YOUR_ERA_HERE" basePath = "INSERT_YOUR_PATH_HERE" prefix = f"map_{era}_tile_20k_" extension = ".tif" doc = c4d.documents.GetActiveDocument() if doc is None: return nodeSpaceId = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace") textureNodeId = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler") for mat_index in range(1, 9): mat_name = f"column_{mat_index:02d}" mat = doc.SearchMaterial(mat_name) if not mat: print(f"Material {mat_name} not found.") continue nodeMat = mat.GetNodeMaterialReference() if nodeMat is None: print(f"{mat_name} is not a node material.") continue graph = nodeMat.GetGraph(nodeSpaceId) if graph.IsNullValue(): print(f"No Redshift graph for {mat_name}.") continue textureNodes = [] maxon.GraphModelHelper.FindNodesByAssetId(graph, textureNodeId, False, textureNodes) with graph.BeginTransaction() as transaction: for node in textureNodes: node_name = node.GetValue(maxon.NODE.BASE.NAME) if not node_name: print(f"Unnamed node in {mat_name}, skipping.") continue node_name = str(node_name) try: local_index = int(node_name) except: print(f"Non-numeric node name '{node_name}' in {mat_name}, skipping.") continue global_index = (mat_index - 1) * 11 + local_index filename = f"{prefix}{global_index:03d}{extension}" full_path = basePath + filename tex0 = node.GetInputs().FindChild( "com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0" ) if tex0.IsNullValue(): print(f"No tex0 on node '{node_name}' in {mat_name}") continue pathPort = tex0.FindChild("path") if pathPort.IsNullValue(): print(f"No path port on node '{node_name}' in {mat_name}") continue pathPort.SetDefaultValue(maxon.Url(full_path)) print(f"{mat_name} → Node '{node_name}' set to {full_path}") transaction.Commit() c4d.EventAdd() if __name__ == "__main__": main()
  • Creating custom asset nodes via Python API

    Cinema 4D SDK 2026 python windows
    4
    0 Votes
    4 Posts
    177 Views
    ferdinandF
    Good to hear that things worked out for you!
  • 0 Votes
    4 Posts
    233 Views
    ferdinandF
    Hey @vaishhg, As I tried to explain there are no nested dependencies. *.rs is a full blown scene file format which can express geometry, curves, simulation data, materials and more. When you save a Cinema 4D scene as *.rs al data in it is exported to that format, including the "nested" case where a *.c4d scene is referencing a *.c4d scene. So when you start out with this *.c4d scene: Scene.c4d +-- Objects +-- Cloner ( creates 5 instances) +-- Cube Generator +-- Cache +-- Polygon Object +-- Tags +-- Material Tag (references 'Red Material') +-- Materials +-- Red Material And then export it to Scene.rs, you get this (this is not an actual depiction of the file format, just a visualization of what happens, rs is not an open format). Scene.rs +-- Objects +-- Cube.0 [Red Material] +-- Cube.1 [Red Material] +-- Cube.2 [Red Material] +-- Cube.3 [Red Material] +-- Cube.4 [Red Material] +-- Materials +-- Red Material (contains Red Material definition) If you load that file back into Cinema 4D you get this. All data - that these are 5 separate cubes with a red material each - resides in the Redshift core only, we only see a proxy in Cinema 4D, hence the name "RS Proxy Object". It is the Redshift Core which will resolve the data in the RS file at render time. ReferencingScene.c4d +-- Objects +-- RS Proxy Object.0 (loads Scene.rs) +-- Cache (will be empty by default, there is literally no data in the c4d core, only when we set 'Preview' to 'Mesh' there will be a cache so that the viewport can display something) +-- Polygon Object (one blob representing all 5 cubes and no material information) +-- RS Proxy Object.1 (loads Scene.rs) +-- Cache +-- Polygon Object When we now export ReferencingScene.c4d to ReferencingScene.rs we get this. Because when the exporter runs, it will encounter the two RS Proxy Objects when flattening the c4d scene and do what you cannot do, grab the rs scene data from the referenced Scene.rs files and inline that into the new ReferencingScene.rs file. So we end up with 10 cubes in total, each with the red material assigned. ReferencingScene.rs +-- Objects +-- Cube.0 [Red Material] (from RS Proxy Object.0) +-- Cube.1 [Red Material] ... +-- Cube.2 [Red Material] ... +-- Cube.3 [Red Material] ... +-- Cube.4 [Red Material] ... +-- Cube.0 [Red Material] (from RS Proxy Object.1) +-- Cube.1 [Red Material] ... +-- Cube.2 [Red Material] ... +-- Cube.3 [Red Material] ... +-- Cube.4 [Red Material] ... +-- Materials +-- Red Material (contains Red Material definition) And when we load that back into Cinema 4D we get this: SecondGeneration.c4d +-- Objects +-- RS Proxy Object.0 (loads ReferencingScene.rs) +-- Cache +-- Polygon Object (one blob representing all 10 cubes and no material information) The TLDR is that the Redshift Core can read *.rs files and the Cinema API cannot, it can only write them or load them via an RS Proxy Object. And there is no 'resolving [...] the full proxy chain' as you put it. An *.rs scene file is just a discrete scene representation that contains does not know concepts such as generators or assets known to the Cinema API/Core. When export a *.c4d scene that references *.rs files all data is just flattened into a single *.rs file (again, what I showed under the *.rs formats above was just a visualization, not the actual file format). There is currently no way to do what you want to do, even if you would request access to the Redshift Core C++ SDK. Because the RS file format is a GPU scene file format and very deeply integrated into the core. Even the RS Core SDK does not expose functionalities to read RS files to CPU memory structures. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    115 Views
    B
    Thank you @Dunhou ! That solved it!
  • 0 Votes
    2 Posts
    119 Views
    ferdinandF
    Hey @lasselauch, Thank you for reaching out to us. I am not 100% sure that I am understanding you correctly. You basically want to hook into this menu entry, right? [image: 1768249550647-650df545-8d6c-4444-a525-a4fe70bee750-image.png] That is not possible at the moment. Because what this thing does, is gather information from the description of the selected entity or the active dialog and with that data calls cinema::OpenHelpBrowser (at least the backend version of that function). This is not even a dedicated command, just a switch case within the abstracted dialog menu handling. So, this is custom built for help.maxon.net. It would not be impossible to isolate this so that there could be either a dedicated plugin hook for this or it somehow reusing the existing RegisterPluginHelpDelegate (the C++ variant of the Python hook you used). But that would be quite a bit of work, and you would also have to answer if that justifies the overhead of calling all hooks each time a user presses that button/menu entry (but you could also argue that the overhead of RegisterPluginHelpDelegate is even worse). I can see the allure of "Show Help" working for third parties, but I doubt many people would use it and the current system is very Maxon centric which are not good arguments for going for this. On top of this, in theory, it would have to support both NodeData entities and dialogs (because the menu entry works for both). We could only support nodes, but there I would just recommend the proven and tested workflow of including a base description at the end of your nodes, which places there a bitmap icon branding that is clickable or just a button. I talked a bit in the all new Licensing Manual videos and code about this workflow. edit: An alternative could be to offer a hook into OpenHelpBrowser but there you probably then run into problems with dialogs as the back end function splits into two signatures (which do not exist in the frontend). Also solvable but again extra work that can hardly be justified but the few users this will have. I am not strictly against adding such hook, but I currently do not see a good cost/effect ratio unless this thread is flooded with third party developers stating otherwise. Cheers, Ferdinand
  • 0 Votes
    7 Posts
    217 Views
    M
    Hi Ferdinant, Great! Thank you for your support - I really appreciate it! with... asset: maxon.AssetDescription = repo.FindLatestAsset( maxon.AssetTypes.File(), aid, maxon.Id(), maxon.ASSET_FIND_MODE.LATEST) version_string: str = maxon.AssetInterface.GetVersionString(asset) version_string = version_string[:-19].strip() #deleting the timestamp I could filter out the Version String. Thank you again for your help. Cheers, MPB
  • 0 Votes
    3 Posts
    147 Views
    K
    Hi @ferdinand , Thank you for your detailed answer. I will look into the details and try to understand the concepts. For now the issue is solved. Thank you as always.
  • ObjectData handles - Unexpected position jitter

    Moved Bugs python 2026
    8
    1 Votes
    8 Posts
    376 Views
    ferdinandF
    Good to hear!
  • How to get edge 'island' selection

    Cinema 4D SDK python 2026
    6
    1
    0 Votes
    6 Posts
    393 Views
    ferdinandF
    Hey @BretBays, Let me answer a few things, I although I still have the feeling we are not at the bottom of things yet. So my idea(based off a maya tool used at work) is to be able to select those loops myself with edges, and have it run the interpolation on all the loops at once(well, at once as far as clicking apply once and it does each loop for you). You can of course implement a point, edge, or polygon loop or ring selection yourself. But my advice would be to use the builtin tool programmatically unless you really have to implement your own tool. Because while a simple loop selection is relatively trivial, a production level loop tool is then quite a bit of work, due to all the edge cases you have to handle. The issues I am running into is that it seems like working with edge selections is very cumbersome in Cinema. [...] I don't know I just am having a hard time wrapping my head around these concepts in Cinema. Is it possible to pass in an edge ID and work with edge ID's or do you have to go through the polygon info and all of that to get them? Cinema 4D does not store edges explicitly, as this would unnecessarily increase the size of a scene. One can sufficiently describe polygonal geometry as a set of points for the vertices, and a set of ordered quadruples of point indices for each polygon. This is common practice in 3D applications and called 'indexed face set'. You always have to go through the polygons and points to work with edges. Edges are effectively just a smoke and mirrors convenience feature for end users. One could argue how much front-end wrappers for edges an API requires to be easy to use, but I would say Cinema 4D is there at least okay. You can find helper functions for edges on PolygonObject and SendModelingCommand supports edge selections directly. In short, for each perceived user edge E_p, exist n 'real' or 'raw' edges for the indexed face set, where n is either 1 or 2 when the is mesh manifold (or larger when non-manifold). If n=1, then E_p is a boundary edge, otherwise it is an internal edge shared by two polygons. This is due to these two polygons having two have opposite winding orders for that shared edge when the polygons are meant to face into the same direction. The following diagram illustrates this (arrows indicate the winding order of the polygons): a- → -b b- → -e | | | | ↑ P ↓ ↑ Q ↓ | | | | d- ← -c c- ← -f Fig. 1: Two polygons P and Q sharing the user perceived edge E_p defined by the points b and c. The lower case labels denote unique point identifiers in the indexed face set, not a point order within the polygon. The polygon P is defined as (a, b, c, d) and the polygon Q as (b, e, f, c), i.e., a and b are the first vertex of each polygon respectively. The arrows describe the winding order of the polygons. The global raw edge index is defined as rawEdgeIndex = polygonIndex * 4 + localEdgeIndex. E.g., when P would have the polygon index 2 and Q the polygon index 6, then the user perceived edge E_p would correspond to the two raw edges indices p_bc = 2 * 4 + 1 = 8 (edge bc in P which is the second edge, i.e. local index 1) and q_cb = 6 * 4 + 3 = 27 (edge cb in Q which is the fourth edge, i.e. local index 3). Here are some code examples and forum posts about working with edges in Cinema 4D's Python API: geometry_polgyon_edges_2024: This is the official example script showing how to work with polygon edges in Cinema 4D 2024. It explains how to access and identify edges in a polygon object. Select Edges by Length: An example that shows how to select edges based on their length. Select Polygons Facing into the Same Direction: Not directly related to edges, but I talk here about the fundamental concept of a winding order, which is important when working with polygon edges. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    195 Views
    ymoonY
    @ferdinand Thank you. It works well. --> tag.Message(c4d.MSG_EDIT)
  • set GvNode value via python

    Cinema 4D SDK windows python 2026
    4
    1
    0 Votes
    4 Posts
    279 Views
    DunhouD
    Thanks for @ferdinand awesome answer! I found DescLevel and DescId always confused to me, but them do can access more than simple set item, time to dive deeper to the DescLevel part Cheers~ DunHou
  • 0 Votes
    14 Posts
    913 Views
    ferdinandF
    FYI, there has been a verified fix for the Booleans crash, it will be included in an upcoming release of Cinema 4D.