• Set View Transform in Picture Viewer

    Cinema 4D SDK c++ 2026
    1
    0 Votes
    1 Posts
    25 Views
    No one has replied
  • 0 Votes
    1 Posts
    40 Views
    No one has replied
  • Access Node Material Path Redshift 2026

    Cinema 4D SDK 2026 python windows
    2
    0 Votes
    2 Posts
    55 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
    120 Views
    ferdinandF
    Good to hear that things worked out for you!
  • 0 Votes
    4 Posts
    138 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
    70 Views
    B
    Thank you @Dunhou ! That solved it!
  • 0 Votes
    2 Posts
    79 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
    153 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
    118 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
    320 Views
    ferdinandF
    Good to hear!
  • How to get edge 'island' selection

    Cinema 4D SDK python 2026
    6
    1
    0 Votes
    6 Posts
    328 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
    154 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
    237 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
    833 Views
    ferdinandF
    FYI, there has been a verified fix for the Booleans crash, it will be included in an upcoming release of Cinema 4D.
  • Several Asset Browser Issues

    Cinema 4D SDK 2026 2025 python
    7
    0 Votes
    7 Posts
    569 Views
    B
    Hi @ferdinand Thank you for the reply and I totally understand your point. I think I will try to find another solution and will adjust my rigs to maybe get rid of the python tag thats causing the issue. I wasn't aware that this is considered "dark magic" python code^^ haha Thank you once again for your efforts! Cheers, Ben
  • Setting Preferences via script

    Cinema 4D SDK python 2025 2026
    5
    0 Votes
    5 Posts
    285 Views
    ferdinandF
    Hello @CJtheTiger, I am not quite sure how your question is meant, and generally new questions should constitute new topics. When you are asking, if when there is an ID_FOO: int = 12345 in V1, if we then just silently switch out the numeric value to ID_FOO: int = 54321 in V2, then the answer is sort of yesn't. We try to keep identifiers persistent. And for things like plugin IDs this is true without ifs and buts. I.e., once Ocube: int = 5159 has been defined, it will stay like this. But parameter values, e.g., PRIM_CUBE_LEN: int = 1100, can technically change. The goal is also to keep them persistent but in ABI breaking releases we sometimes have to modify descriptions. That is why we recommend that you always use symbols and not numbers. Cheers, Ferdinand
  • 1 Votes
    4 Posts
    491 Views
    ferdinandF
    Hey Jacob, Thank you for the added data. First of all, I have invited you to the forum to end this middle man communication, which is a bit odd. The pyz file is part of python ... I am aware of what pyz is, I just pointed this out because I of course looked inside your package and found all the py_armor obfuscated code and the injected binaries in there. So, I pointed out that this is bit more than just "packaged in a pyz file for ease of distribution [...]" as Lasse/you put it, the goal is here clearly obfuscation. Which is also relevant for support, as it limits what you and I can see (without getting hacky). My finding with the 10mb file freeze comes from my trial and error ... mean[t] when you run a script from Extensions -> User Scripts. Your code also freezes when you load it as a Script Manager script. That is what I did with the last package from Lasse, and now also yours. The code in your script is again wrong, which is why it won't freeze until you fix it. This is the code I found: [image: 1760086817339-2bd4290e-78b2-43d4-936d-1e2a7eaf366b-image.png] And I fixed it then to this. When I ran it then, Cinema 4D froze for two minutes or so, after that it opened a myriad of dialogs to then terminate into two crash dialogs (it was pure luck that I let it run for so long, Lasses previous version might have acted similar, but there I killed the C4D process, as soon as I saw the 'beach ball of death' cursor on MacOS). [image: 1760086755748-69fcb5da-ac49-477e-8f70-9daeb1daa1aa-image.png] Please read my answer below carefully, as I already pointed out most of this in my previous posting. I would STRONGLY suggest debugging this without obfuscation. Maxon also cannot debug larger sections of code or test further packages for you. I understand that obfuscation might not be your choice, but it will make your life harder in debugging this, as you always fly blind. We of course still will provide support, but you have to provide more than "it does not work/crashes/freezes, please help us", especially when this is not code tied to our APIs. Attach a debugger from the Script Manager and see why your code crashes/freezes (see link in last posting when unsure how to do this). But you need an un-obfuscated code base for this to make any sense. Defer your loading to a later point, e.g., C4DPL_PROGRAM_STARTED, when you have issues in the direct plugin registration context. In that case you would always register your plugin, but then only execute it when the your own license check succeeded. But you absolutely cannot ship a plugin which freezes Cinema 4D for multiple minutes on startup or when invoking your plugin because your licensing takes so long. When we see this in the wild, we will have to blacklist your plugin IDs, as this damages the brand Cinema 4D. Please use threading then to not block the main thread with your long running code. What I did not notice before is that you apparently try to open multiple dialogs (for me it opened multiple dialogs when I ran the script). The GUI and many other systems are not yet available when Cinema 4D is still booting, e.g., in the splash screen. You can expect all systems to be up and running when C4DPL_STARTACTIVITY is emitted, but it is better to wait for C4DPL_PROGRAM_STARTED for long running tasks (i.e., the two events I tested in my previous posting). Please also keep in mind that Cinema 4D has its own anti-piracy measures. Python plugins are sort of special in that they work slightly different than native C++ plugin modules (the Python C++ module shipped by Maxon sort of acts as a surrogate for Python plugins in the module registration phase). But Cinema 4D won't allow plugin threads to start their own processes at this point (which you might be trying to do with your injected binaries), and threading should also be avoided at this point, as the job system of Cinema 4D might be still booting. What you are meant to do in PluginStart (or the __main__ context of a pyp file), is register your plugins. You can run some quick logic there, but you are certainly not meant to start communicating with servers and opening GUIs there. You can read here a bit more about this from a C++ system perspective. I would recommend to do your license check in the background in its own thread once C4DPL_PROGRAM_STARTED has been emitted (so that you can also open dialogs to signal errors). An alternative would be to do it when the user clicks the button of your command. But you should also here put it into its own thread, so that it does not block everything else. Cheers, Ferdinand
  • 0 Votes
    3 Posts
    259 Views
    gheyretG
    Thank you for the C++ code—it's been very helpful! Cheers!