• 0 Votes
    1 Posts
    11 Views
    No one has replied
  • Executing a Redshift texture bake from Python

    Cinema 4D SDK python 2026
    2
    0 Votes
    2 Posts
    41 Views
    ferdinandF
    Hello @mplec1, Welcome to the Maxon developers forum and its community, it is great to have you with us! Getting Started Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules. Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment. Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support. Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads. It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions. About your First Question The bake tag and by extension cinema::BakeTexture (and its Python equivalent c4d.utils.BakeTexture) unfortunately do not support Redshift, even for the older Xpresso based materials. Redshift uses the tools found in Redshift/Tools/Texture Baking for baking. And while you can programmatically create a bake set and then programmatically click the 'Bake' button in it (which both would also work in a headless version of Cinema 4D, such as c4dpy), the following dialog which opens to set baking details and actually start the rendering is sealed, i.e., you cannot interact with it from the public API. And opening such dialog would also fail in a headless Cinema 4D instance. So, I am afraid there is currently no solution for your problem. You can technically export the whole scene to a format such FBX or USD, and use the builtin baking output (which would also work in a headless environment, as long as you do NOT pass SAVEDOCUMENTFLAGS_DIALOGSALLOWED to the save/export operation). But the output of that automated baking is often of poor quality compared to manually baking object(s) via bake sets. Cheers, Ferdinand
  • 0 Votes
    6 Posts
    89 Views
    ferdinandF
    The second ID is the sub ID. You can have a command (FOO = 100) which supports the sub-commands (BAR=1, and BAZ=2). So, you can then call CallCommand(100, 1) or CallCommand(100, 2) to invoke the two different things FOO can do. But as you said yourself, this is a rather unusual thing, and it is even more unusual that I manged to randomly click five things in a row that all have sub IDs What should also be said is that my little "extrude" log there does not really reproduce the modelling operations I carried out. It just runs the tools in the order they are invoked. And because when you run this right after your created the log, the extrude tool will for example still have set the same extrude depth from the last operation. But when you manually extrude something, and then run the script again, it will use these new current extrude tool settings, i.e., and not the ones from when your 'recorded' the log. The script log can be useful, but it is not the auto-script-recording feature users often whish it was.
  • Using the Bridge Tool

    Cinema 4D SDK python
    9
    0 Votes
    9 Posts
    242 Views
    ferdinandF
    Hello @Dimitris_Derm. No, with islands I do not mean different objects. With islands are polygon (selection) islands meant. And that is just the term that is used for these things. The "Plane" object below has two polygon islands, the left and right rectangle shown in the viewport, each composed out of 4 * 5 polygons. They are islands because they are topological disjunct from each other - you cannot 'get' from one island to another without jumping over a gap. [image: 1777019034509-a88ac530-c105-430c-bd11-f922f5688881-image.png] The same can be applied to selections, as shown below. Now the left polygon island in the mesh has two polygonal selection islands. You cannot get from one selection island to another without jumping over a gap (they are topologically disjunct). [image: 1777019065284-bf41be6a-3993-4eb1-b603-438df9aa1bb3-image.png] This is a requirement because as my code example demonstrates, when bridging in island mode, you just specify a polygon and a point (and set the flags), and the tool then 'grows out' the to be bridged patch from the given polygon, based on the active polygon selection. And this growing will stop at topological boundaries. So, when you would bridge from the selection in the lower left corner of the left rectangle (to some unspecified target in another mesh), it would only grow into these four polygons. The selection in the top right corner would never be part of the operation. Cheers, Ferdinand
  • 2 Votes
    1 Posts
    3k Views
    No one has replied
  • Effector Objects written in Python

    Cinema 4D SDK python
    3
    0 Votes
    3 Posts
    134 Views
    ferdinandF
    Hey @Dimitris_Derm. , no there is currently no dedicated plugin class for MoGraph effectors and fields in Python, only the scripting objects exist at the moment. Cheers, Ferdinand
  • Reading Immutable Selection Tags

    Cinema 4D SDK python
    3
    0 Votes
    3 Posts
    137 Views
    D
    Ah ! They are called Proxy Tags and I can see the actual tag with the MSG_GETREALTAGDATA ! Thank you ️
  • 0 Votes
    6 Posts
    185 Views
    ferdinandF
    Your approach is not necessarily worse, one could even argue that it is better. I personally would always avoid manually binding to an OS DLL via ctypes, but that is more a personal preference.
  • 0 Votes
    5 Posts
    196 Views
    ferdinandF
    Hey, well, "intended" would be the wrong word, but I am aware. The whole resource parsing situation is a bit of a mess at the moment, both in C++ and the Python API. Why is it like this? A description is defined by a res file (more or less its GUI declaration) and an h file which declares symbols for parameter IDs. The resource for the loop tool looks like this: CONTAINER ToolLoopSelection { NAME ToolLoopSelection; INCLUDE ToolBase; GROUP MDATA_MAINGROUP { BOOL MDATA_LOOP_SEL_STOP_AT_SELECTIONS { } BOOL MDATA_LOOP_SEL_STOP_AT_NON_QUADS { } BOOL MDATA_LOOP_SEL_STOP_AT_POLES { } } HIDE MDATA_COMMANDGROUP; } I.e., it indeed only defines three parameters. But the header file looks like this: #ifndef TOOLLOOPSELECTION_H__ #define TOOLLOOPSELECTION_H__ enum { MDATA_LOOP_SEL_STOP_AT_SELECTIONS = 1100, // BOOL MDATA_LOOP_SEL_STOP_AT_NON_QUADS = 1110, // BOOL MDATA_LOOP_SEL_STOP_AT_POLES = 1120, // BOOL MDATA_LOOP_FIRST_VERTEX = 1130, // LONG MDATA_LOOP_SECOND_VERTEX = 1131, // LONG MDATA_LOOP_POLYGON_INDEX = 1132, // LONG MDATA_LOOP_BOTH_SIDES = 1133, // BOOL MDATA_LOOP_SWAP_SIDES = 1134, // BOOL MDATA_LOOP_SELECTION_TYPE = 1140, // LONG (must be SELECTION_NEW, SELECTION_ADD or SELECTION_SUB) MDATA_LOOP_SEL_POLYGON_OBJECT = 1150, // LINK }; #endif // TOOLLOOPSELECTION_H__ I.e., it not only defines these three parameters, but also all the others. Because there are all these "hidden" parameters which are written into the data container of the tool, but never show up in the GUI. What collides here is (a) the a bit irregular (but not illegal) behavior of a resource to define more parameters in its header file than are used in the resource and (b) the questionable decision of our resource parser to ignore hidden parameters. Our resource parsing is automated, i.e., I cannot just go into a file and just add these parameters for a docs build. I could touch the resource parsers for Python and C++, but I do not have the time for that right now as they has been written in a very opaque manner. My advice is simply what the docs suggest: Search in the header files directly. Go to your Cinema folder, make sure that the resource.zip is either unpacked to the local folder (the folder existing does not mean necessarily that it has been fully unpacked) or an external folder of your choice. And then simply search in that folder with an editor of your choice. [image: 1775557617243-c122abc5-8cfc-40da-90be-534532600b4a-image-resized.png] At some point I will replace the resource and symbols parsing for Python and C++, because we made there quite a mess in the past with questionable parsers and manually curated lists. But for now this cannot be changed and using the files directly is the way to go when you want to look at descriptions. Cheers, Ferdinand PS: The C++ docs are NOT inherently better in that regard. The parser shares there the same flaws. The reason why you find some symbols there is because developers have duplicated them from the resources into the frameworks.
  • Create Motion Clip Source with Python API

    Cinema 4D SDK python windows 2026
    3
    0 Votes
    3 Posts
    182 Views
    J
    Hi @ferdinand, and thank you! Your proof-of-concept and pointing me toward mxutils.GetSceneGraphString() was exactly what I needed to solve this. By using the scene graph dumper on a native UI-generated Motion Source from a rigged character, I realized it's just a standard Ojoint hierarchy with normal CTrack objects. I used GetClone(c4d.COPYFLAGS_NO_HIERARCHY) to perfectly replicate the Ojoint skeleton and injected the time variables into the container, and it maps and plays back perfectly. Thanks again.
  • 0 Votes
    7 Posts
    363 Views
    T
    Here's my prototype, if you are interested in my goal Right now, everything works as expected, but only with these lines of code. profile = GetCloneSpline(profile_orig) path = GetCloneSpline(path_orig) I'm happy with the current result. So, I'd love to get some additional advice on correctness and optimization. I think your previous answer was comprehensive enough, so I'll try to integrate some of it. But I'd also be very grateful if you could take a look at my plugin and perhaps give me some more specific optimization tips. If that's not too much trouble, of course! @ferdinand @ThomasB Anyway, thanks for your replies and advices!
  • 0 Votes
    3 Posts
    227 Views
    mfersaouiM
    @ferdinand Hey @ferdinand, Thank you again for the clarification — checking the Video Post list was indeed the right direction. Just to share what worked on my side (Standard/Physical renderer), I check the presence of the GI Video Post and use its BIT_VPDISABLED state to determine whether it’s enabled. Here’s the small helper function I’m using: def is_gi_active(doc): rd = doc.GetActiveRenderData() if not rd: return False # GI Video Post IDs (verified on C4D 2024) GI_IDS = [ 1021096, # Global Illumination Video Post (C4D 2024) 300001038, # VPglobalillumination (older versions) ] vp = rd.GetFirstVideoPost() while vp: if vp.GetType() in GI_IDS: return not vp.GetBit(c4d.BIT_VPDISABLED) vp = vp.GetNext() return False Thanks again for your help. Best regards, Mustapha
  • Tile rendering with Cinema 4D

    Cinema 4D SDK python 2026
    7
    0 Votes
    7 Posts
    516 Views
    ferdinandF
    Hey, just as an FYI, I added this as an example to the 2026.2 rendering examples. You will be able to find it under \scripts\04_3d_concepts\rendering\render_document_tiles_2026_2.py. Since the script will use some 2026.2 features, it does not make much sense to post a preview here, as you will not be able to run it right now. The example also does the kernel border thing we discussed here. Cheers, Ferdinand
  • 0 Votes
    2 Posts
    273 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}
  • 0 Votes
    3 Posts
    313 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!
  • Debugging in VS Code does not pause at breakpoints

    Moved Bugs 2026 python macos
    3
    0 Votes
    3 Posts
    349 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
    345 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()
  • 0 Votes
    2 Posts
    277 Views
    ferdinandF
    Hey @Dunhou, Thank you for reaching out to us. We agree that this would be desirable. These methods are actually already wrapped but we hide them for now in the Python SDK. Find the reasons below. ExecuteJavascript: I just did remove the bindings of the Python API for that method in the current beta and also switched the C++ method to internal. The reason for that decision was is that we have security concerns about attackers being able to execute arbitrary JS in a web browser opened in Cinema 4D. SetWebMessageCallback: This is intended solution, i.e., the JS you want to execute must be already embedded into the HTML which is running in the HtmlView. On Windows/WebView2 it uses web messages, on MacOS/WebKit a custom solution emulating them. And SetURLCallback is then the way to get data back from the JS VM. For 2026.1 I already wrote examples for these methods, but on the last meters we discovered that something not only broke the Python bindings but the whole "execute JS" in the WebView2/WebKit bindings. My last info is that something broke there due to a project update, and that the two devs involved in it will have a look. I'll give them another bump and report here if there are any updates. Cheers, Ferdinand
  • Creating custom asset nodes via Python API

    Cinema 4D SDK 2026 python windows
    4
    0 Votes
    4 Posts
    431 Views
    ferdinandF
    Good to hear that things worked out for you!
  • how to detect obj selected in InExcludeData()?

    Cinema 4D SDK 2025 python
    7
    0 Votes
    7 Posts
    473 Views
    chuanzhenC
    @ferdinand Thank you. hope this can be updated in the document