• 0 Votes
    8 Posts
    311 Views
    ferdinandF
    Hey @ThomasB, so, I had a look and figured it out. When you diff the two objects from my file, there are quite few differences (as I was not 100% faithful in copying all values), but the culprit is this: [image: 1783349248850-cef8fb22-88d1-4073-8b29-1e2958c0db14-image.png] The RS Light uses a standard light to accomplish its VP representation. When you disable the 'Illumination' checkmark, the RS Light GetVirtualObjects will simply return a null object, i.e., the behavior you experienced. I filed a ticket for this (ITEM#651586 Inconsistent Redshift Light 'Make Editable' behavior), as I would consider this so unintuitive that it borders on a bug. But this is outside of the SDK/Cinema 4D domain, so I cannot just fix it myself. In Python, there is currently no way to fix this on your side. The message MSG_CURRENTSTATE_END is not wrapped for Python (with it you can manipulate the output of a "Make Editable" action before it is inserted). The other route, the build flags on HierarchyHelp are also not possible in Python either, because HierarchyHelp is also not wrapped for Python. But I just changed the latter route, and in the next major update you will be able to react to build flags. There will also be a new code example named py-objectdata_buildflags_2027 which showcases this feature. You can then react to your object being made editable, built for rendering, built for export, etc. and react by changing its output. In your case you would then probably not disable the illumination when your object is being made editable. So, for now, you either have to live with the behavior, or turn on "preview > illumination". Cheers, Ferdinand Result The output, i.e., diff, with the offending line highlighted. [image: 1783418649677-d0a8e14c-d913-4990-9ec5-29e4a5150cf3-image-resized.png] Code This assumes my scene from above with everything deleted but the two objects. import c4d import mxutils import difflib doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. def main() -> None: """Called by Cinema 4D when the script is being executed. """ data: list[str] = [ mxutils.GetParameterTreeString(node) for node in mxutils.IterateTree(doc.GetFirstObject(), True) ] # Diff the first two strings in the list and print the result to the console. if len(data) < 2: return c4d.gui.MessageDialog("Not enough data to perform diff.") a: str = data[0] b: str = data[1] diff: list[str] = list(difflib.unified_diff(a.splitlines(), b.splitlines(), lineterm='')) if not diff: print("No differences found.") print("\n".join(diff)) print("\n\n" + "=" * 80 + "\n\n") print(a) print("\n" + "-" * 80 + "\n") print(b) c4d.EventAdd() if __name__ == '__main__': main()
  • Sub materials links and undo

    Cinema 4D SDK c++ windows s26
    5
    0 Votes
    5 Posts
    278 Views
    ferdinandF
    @DronKozy Yeah, I got the general direction, that you are probably implementing a material mixer type of material/shader. When you implement a full render engine binding, you should go the event notification route, as I am sure you can then easily handle the (slight) complexity that comes with them. Regarding the architecture, in principle you can do with whatever you want. As Maxon employee I would of course recommend to use our Nodes API (what you call 'native'). The SDK contains both code examples and documentation about this. But this biggest flaw of the Nodes API is probably that it is not entirely non-trivial. But implementing a full node editor also requires a relatively high level of expertise about our APIs, although in different areas. Currently Redshift, Vray, Arnold, and CentiLeo use the Nodes API and all other render engines (Corona, Octane, Cycles, etc.) use either a completely custom system or the old Xpresso Nodes API. Cheers, Ferdinand PS: When you are developing a render engine binding, you might want to consider our Maxon Registered Developer Program.
  • Support for Material X?

    Moved General Talk c++ 2024
    3
    0 Votes
    3 Posts
    980 Views
    M
    Material X has been part of the Redshift Trello for years now with no movement. It feels like it is not a priority at the moment
  • GetWorkplaneMatrix is broken in Cinema 4D 2026.3

    Moved Bugs c++ 2026
    5
    0 Votes
    5 Posts
    428 Views
    ferdinandF
    This will be fixed in the next upcoming hotfix.
  • UV Peeler - accessible?

    Cinema 4D SDK python
    7
    0 Votes
    7 Posts
    1k Views
    ferdinandF
    I don't know what your script does and if it needs fixing for 2026.3. What I said is that we usually put a lot of effort into keeping our ABIs and APIs stable, as exemplified with the 2026.3.1 hotfix where we fixed an issue with the UV tag interface that made it incompatible with old code. 2026.3 contained the UV manager overhaul as its main feature. We tried not to break any glass there but I cannot rule it out. when you find a regression in our APIs, you can also always report it. when the break was unintentional we usually provide a fix (in critical cases even a hotfix) or at least a workaround. We also usually do not remove systems. For example, the old content system and its "Content Browser" is still in our APIs and Cinema, although it long has been replaced by the Asset Browser and API. Can I guarantee that the UVPeeler will not be removed soon? No, we sometimes have to remove things. But it is quite rare.
  • 0 Votes
    2 Posts
    297 Views
    K
    Hello, Following my request for SubTool folder management, I would like to suggest two additional features to improve pipeline automation: Masking Data Access: Please provide read/write access to SubTool masking data. Layer Manipulation: Please add API support for managing SubTool layers. Thank you for your consideration.
  • 0 Votes
    17 Posts
    2k Views
    ferdinandF
    @ThomasB That is not a misunderstanding. I am aware that MRDs can only see their own tickets. But we still needed that crash reported, so I did it. I would have done the same if you would not have been an MRD. I usually mention ticket IDs here so that I can come back and close bugs. As you might have noticed, I have moved this into the Bugs forum. You do not have to do anything regarding that ticket, it it inaccessible for non-Maxonians. But we are still very much interested in a version of your plugin or a workflow which caused the crash. Otherwise we will not be able to reproduce and fix the issue. When you cannot provide it, this is also not a biggie (please do not waste time on this), then we have at least documented that there is somewhere a problem with audio tracks and how they handle memory while post-processing audio; in case we run into the same issue in the future in another context. Cheers, Ferdinand To share plugins with us, you can either use a chat on backstage or send us a mail to sdk_support(at)maxon(dot)net. There is a good chance that when you use a binary data attachment in a mail, that our mail system will filter out that attachment. When you use backstage (and upload the plugin there in a chat) that data would be permanently in the assets of that forum. When you just give us a link, we will delete your data once we do not need them anymore for debugging.
  • TimeLine DopeSheet not update

    Moved Bugs 2026 python
    4
    0 Votes
    4 Posts
    457 Views
    chuanzhenC
    @ferdinand Thanks for help
  • 0 Votes
    1 Posts
    175 Views
    No one has replied
  • 0 Votes
    2 Posts
    333 Views
    ferdinandF
    Hey @ECHekman, well, when a node displays an embedded description (what you exemplify at the case of a BaseLink), it uses a DescriptionCustomGui too. Other parameter types aside from a BaseLink which can do this are for example field lists. I am quite frankly a bit surprised that this even bubbles up in the form you report it, that you get a DescID which is selected inside a DescriptionCustomGui that is shown by the node in the DescriptionCustomGui you opened yourself. Attribute Manger selection states (and by extension DescriptionCustomGui) are not a public API feature, as we usually keep our GUIs sealed, i.e., inaccessible to third parties. DescriptionCustomGui is here a bit in a grey area, but I am afraid there is no way to do what you want to do. To do what you want to do, you would need access to internal systems. And other than in some other cases, where we do not always had the time to expose something (and are open to the idea of changing something), this is for GUIs not the case; they are intentionally sealed as we do not want third parties to change basic UX concepts. My general advice would also be to not to try to overcome this boundary (that a plugin reacts to which parameter in a node is being highlighted by the user), as you break UX conventions of Cinema 4D with that. Cheers, Ferdinand You can do this, but it is very hacky: Get the description of the node(s) you are currently displaying in the your DescriptionCustomGui. Check if desc is a member via CheckDescID (I think GetParameter would also work and return an empty container when the ID does not exist). When the ID does not exist, start browsing the container for parameters that are of a DTYPE that implies an underlying node, such as DTYPE_BASELINK. When you find such parameter, get the node node, and check there. Rinse and repeat recursively. Issues: You cannot distinguish the case where an object A has two BaseLink parameters which both hold an object of type T, but not the same instances. You will run into issues with DescID translations which some nodes might do. This is a really big point, which is probably already overlooked in your current code. Nested node relations can get REALLY complicated. BaseLink is trivial, but stuff like field lists, volume lists, or some special shader stuff can get really complicated. You would probably only have to worry about base links and field lists in your case, as other renderers are irrelevant for you, but field list alone are already complex enough that I would strongly advise against 'just implementing it'.
  • How to export icons of asset

    Cinema 4D SDK python 2026 2024
    8
    0 Votes
    8 Posts
    801 Views
    DunhouD
    Hey @ferdinand , Sorry I forgot click Submit button well, that is the info I need, I use same tech to get icons for CMD, and now waiting for svg part in the future. Cheers~ DunHou
  • 0 Votes
    1 Posts
    5k Views
    No one has replied
  • Reverse direction of multi-segment splines

    Cinema 4D SDK windows 2024 python
    6
    0 Votes
    6 Posts
    713 Views
    ferdinandF
    Okay, I think I was a bit overly cautious in my answer here. You gave my a very broad question, or to be precise, you gave me a video and a scene file, and did not really ask a precise question. I understand that asking good questions can be hard, especially with a language barrier. But when I have nothing to go by, I of course assume the worst case possible. The scene you have there is a trivial case, even the best case possible. You have there spline segments which lie in a perfect plane and which have no self intersections. You can treat them just like polygons (in the CG sense, not in the mathematical sense) and simply compute their normal over the first three vertices of each segment. Due to the fundamental property of a polygon - reversing the order of the vertices reverses the normal - you can then easily determine if two segments have opposite or equal winding. But all this starts to fall apart, as soon as you cannot make these assumptions. And I cannot help you to write the code for this, as this is then more than just a few lines. Hope this helps, and that I my answer was now less 'overly cautious'. Cheers, Ferdinand Result [image: 1780950951310-85f415e7-7f45-4c00-9651-b4091ee543e2-image.png] The code correctly identifies that in this six segment spline are four segments of one winding direction (clockwise in this case) and two of the other winding direction. winding_direction.c4d Code """Treats spline segments as polygons and compares their plane normals to find segments with reversed winding order. """ import c4d op: c4d.SplineObject # The currently active object in the scene. def main() -> None: """Called by Cinema 4D when the script is being executed. """ if not isinstance(op, c4d.SplineObject): return c4d.gui.MessageDialog("Please select a spline object.") # Get all points in the spline and organize them into their segments. points: list[c4d.Vector] = op.GetAllPoints() segments: list[list[c4d.Vector]] = [] j: int = 0 for i in [op.GetSegment(i)["cnt"] for i in range(op.GetSegmentCount())]: segments.append(points[j:j+i]) j += i if len(segments) < 2: return c4d.gui.MessageDialog("Please select a spline object with at least 2 segments.") # Now build normal data for the spline segments. This assumes: # # - A spline where all points lie in a single plane, a '2D' spline in 3D space. # - No self intersections in the spline. # - A piecewise linear spline, i.e., what Cinema 4D calls a 'Linear' spline. When we have a # 'Cubic' or 'Bezier' spline, we would have make it linear with 'Current State to Object' # first. # # We build the normal for each segment over its three first vertices. The reason why we are doing # this is because of the fundamental identity of a polygon (in a computer graphics sense), # reversing the order of the vertices of a polygon will reverse the normal. So if we have a # spline with two segments with reversed winding order, they will have antiparallel normals (normals # pointing in opposite directions). segmentNormals: list[c4d.Vector] = [] for segment in segments: if len(segment) < 3: print("Segment has less than 3 points, skipping normal calculation.") continue a, b, c = segment[0], segment[1], segment[2] edge1: c4d.Vector = b - a edge2: c4d.Vector = c - b normal: c4d.Vector = edge1.Cross(edge2).GetNormalized() segmentNormals.append(normal) # Now we just declare one segment as 'ground truth' and check if the other segments have normals # that are parallel or antiparallel to it. When we found a segment with an antiparallel normal, we # know we found a segment with reversed winding order. To check if two normals are parallel or # antiparallel, we just compute their dot product (i.e., spanned angle). When the dot product is # negative, the normals are antiparallel. print(f"Establishing the first segment normal {segmentNormals[0]} as ground truth.") baseNormal: c4d.Vector = segmentNormals[0] for i, normal in enumerate(segmentNormals[1:], start=1): isAntiparallel: bool = baseNormal.Dot(normal) < 0 print(f"Segment {i} normal: {normal} is {'antiparallel' if isAntiparallel else 'parallel'} " f"to the base normal {baseNormal}.") if __name__ == '__main__': main()
  • Copy res folder without shortcut

    Cinema 4D SDK c++ windows
    4
    0 Votes
    4 Posts
    491 Views
    ferdinandF
    That is of course also a valid option, just pick the scripting language you are most comfortable with.
  • How to draw svg to bitmaps?

    Cinema 4D SDK windows python 2026
    4
    0 Votes
    4 Posts
    536 Views
    ferdinandF
    I started to work last Friday on a BaseBitmap.InitWithVectorImage. It will for sure not make it into the next release of Cinema 4D, as we are too close to that, and I cannot make any promises when or if it will arrive. But I see value in this especially since I realized that you cannot even really use this in C++, as VectorImageInterface requires access to some internal components to be rasterized.
  • 0 Votes
    3 Posts
    449 Views
    rndm_cgR
    @ferdinand thanks so much, problem solved!
  • 0 Votes
    2 Posts
    416 Views
    ferdinandF
    Hey @Anlv, Thank you for reaching out to us and reporting this. I just changed it so that the path is always a raw string. This will be shipped with an upcoming (but not the next) public release. Cheers, Ferdinand I.e., your script will then be as follows where there is an r in front of the string, which should fix the issue on Windows. plugins.ReloadPythonPlugin( path = r"C:\Users\<user>\AppData\Roaming\Maxon\Maxon Cinema 4D 2026_XXXXXXXX\plugins\PluginName\PluginName.pyp", reloadDocumentAfterReload=False, reloadOnlyActiveDocument=False )
  • 0 Votes
    6 Posts
    808 Views
    AnlvA
    Another small tip: you can simply use a single <br> for line breaks — there’s no need to use <b> or </b>. Also, just like in the script example, scripts can support localized language strings as well: """ Name-US:Example Name-CN:示例 Description-US:Example line 1.<br>Example line 2.<br>Example line 3. Description-CN:示例第 1 行。<br>示例第 2 行。<br>示例第 3 行。 """
  • Can we draw alpha image in viewport

    Cinema 4D SDK windows python 2026
    4
    0 Votes
    4 Posts
    510 Views
    ferdinandF
    I do not think that DRAW_ALPHA_FROM_IMAGE should not work. I just picked DRAW_ALPHA_NORMAL because it is the "most default one" out of the DRAW_ALPHA flags. I will have a look later. But you can for now probably just use DRAW_ALPHA_NORMAL.
  • How to add tabs to tool plugins.

    Cinema 4D SDK windows python 2026
    6
    0 Votes
    6 Posts
    682 Views
    ferdinandF
    You very likely have not unpacked your resources. The resource folder found in an installation only contains a part of the application resources (we are doing this since release 2023 if I remember correctly). A good portion of the resources sits inside resource.zip for performance reasons and is unpacked on demand. You can just unpack the resource.zip into you resource folder without an performance or stability issues. [image: 1779175199591-b8e8db94-ae20-42b4-9a5d-e92ac928e89d-image.png] You as an MRD can also look at the Bugslife client, as it is probably the better example, as it uses more features of the quick tab GUI. But my answer above was targeted at a general audience who can see the resources for Bugslife but not Bugslife itself, and it therefore is not a good example for them. Just grep the resource folder as I did in my screen for files that match the path dialogs/*.res (i.e., are res files for dialogs) and contain the word QUICKTAB. Cheers, Ferdinand