Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Recent
    • Tags
    • Users
    • Register
    • Login
    1. Maxon Developers Forum
    2. ferdinand
    3. Posts
    Offline
    • Profile
    • Following 0
    • Followers 16
    • Topics 55
    • Posts 3,196
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: GetEffectiveRenderData does not return per-take overrides for RDATA_FRAMEFROM / RDATA_FRAMETO

      Hello @vaishhg,

      Thank you for reaching out to us and your comprehensive yet sparse problem description, this really makes things easier for us.

      The problem is a bit that you somewhat forgot a crucial part: The scene data :). You provided this:

      Create a Cinema 4D scene with a Main take (frame range 0-6)
      Create child takes (V, E, R, A) and override the render data's frame range on each (e.g., V: 0-9, E: 0-23, R: 0-13, A: 0-17)

      But I cannot recreate a scene with these instructions, unless you meant the second step in a rather metaphorical way (switching the render data itself via takes). I also talked with some of our support specialists, and the consensus seems to be that what you claim to be the case - a scene with take overwritten FROM/TO values - is not possible. What you can do, is create multiple render settings and then activate them via takes.

      But Takes are a tricky subject, and I would not rule out that you somehow found a way to create take overrides for parameters of a render data instance. If so, please share the scene. Find also a scene attached which demonstrates what I discussed above: A scene that switches the active render data based on takes. It will print this with your script:

      Take 'Main': FRAMEFROM=0, FRAMETO=90
      Take '0_45': FRAMEFROM=0, FRAMETO=45
      Take '0_90': FRAMEFROM=0, FRAMETO=90
      

      take_renderdata.c4d

      So, to formally answer your main question: When my assumptions above are true, your scene is printing the same values because each take does have the same values, as these two parameters are not directly overridable.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: NETWORK_CONNECTTIMEOUT with SSL

      Timeouts should not happen unless the endpoint is indeed timing out. But as I said, I cannot help you unless you give me compilable code (i.e., a little self contained function) with which I can reproduce your claims. If possible, please also share your endpoint as it obviously plays a major role in this problem.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: NETWORK_CONNECTTIMEOUT with SSL

      Hey @Rox,

      Thank you for reaching out to us. Please provide a concrete executable code example as to what exactly does not work with SSL. I understand that you have put effort into make yourself understood, but in the current form it is very ambiguous what it not working for you.

      HTTPS methods surely work, we use them heavily internally, in all variants such as GET, POST, DELETE, etc. I looked at our code base and did not find an exact match for POSTing towards an HTTPs endpoint with a custom timeout, but found one for DELETEing.

      You can connect to an https URL without any extra steps. I am not quite sure what you would consider "setting the flags" in your code. Depending on the endpoint you want to reach, you might have to configure your headers though. UrlInterface is not a web framework which would automate these things for you.

      Here is some slightly obfuscated production code for posting towards an (https) endpoint:

      Bool DoRequestPost(
      	const maxon::String& endpoint, const maxon::CString& postData, Bool isFile = false, 
      	maxon::DataDictionary* responseAsDict = nullptr, 
      	maxon::BaseArray<maxon::DataDictionary>* responseAsArray = nullptr)
      {			
      	if (isFile)
      	{
      		additionalHeader.Set("Content-Type"_s, FormatString("multipart/form-data; boundary=@"_s, boundary)) iferr_cannot_fail("Cannot set content type"_s);
      		additionalHeader.Set("Some-Token"_s, "no-check"_s) iferr_cannot_fail("Cannot set Some-Token"_s);
      	}
      	else
      	{
      		additionalHeader.Set("Content-Type"_s, "application/json"_s) iferr_cannot_fail("Cannot set content-type"_s);
      		additionalHeader.Set("Accept"_s, "application/json"_s) iferr_cannot_fail("Cannot set accept datatype"_s);
      	}
      	url = maxon::Url();
      	url.Set(maxon::URLFLAGS::HTTP_ADDITIONAL_REQUEST_HEADER, additionalHeader) iferr_cannot_fail("Cannot set additional headers"_s);
      	url.Set(maxon::URLFLAGS::HTTP_POSTMETHOD, maxon::HTTPMETHOD::POST) iferr_cannot_fail("Cannot set httpmethod"_s);
      	url.Set(maxon::URLFLAGS::HTTP_POSTDATA, postData) iferr_cannot_fail("Cannot set post data"_s);
      	url.Set(maxon::URLFLAGS::HTTP_NO_PROXY_CACHING, true) iferr_cannot_fail("Cannot set NOPROXY");
      
      	return DoRequest(endpoint, responseAsDict, responseAsArray);
      }
      

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Command to create a Nodes Modifier

      Hey @ImperfectLink,
      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 posting you linked to was a legacy posting, I have deleted it. You can find all relevant information in this posting or in the Support entry of the site nav. Your question is a bit ambiguous but I assume you want to instantiate a nodes modifier? Or what do you mean by "the command"? I would say it works like this:

      import c4d
      import maxon
      
      from mxutils import CheckType
      
      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.
          """
          node: c4d.BaseObject = CheckType(c4d.BaseObject(maxon.neutron.DEFORMER_ID))
          doc.InsertObject(node)
          c4d.EventAdd()
      
      if __name__ == '__main__':
          main()
      

      You can find all the Neutron (the code name for scene nodes) IDs here. What stands out that this instantiates a scene node deformer and not a 'modifier', which is indeed a bit puzzling. But both share the same numeric value for their ID:

      c377ba54-581c-445d-bf0a-3b72111390fa-image.png

      In the API there is maxon.neutron.DEFORMER_ID but no modifier ID. And in the UI there is only a modifier. But since they share the type ID and also type name (Scene Nodes Deformer) they must be the same thing and the node editor just renames the entity when it is being inserted for some reason.

      Cheers,
      Ferdinand

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: Create Motion Clip Source with Python API

      Hello @jaroslavnovotny,

      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

      BaseException: the plugin 'c4d.BaseList2D' (ID 465003004) is missing. Could not allocate instance.

      Just means that Cinema 4D cannot instantiate a node with such ID as it does find its implementation. In some VERY rare cases nodes are sealed and cannot be instantiated manually from the outside but that is very rare and not a difference between Python or C++ thing. If it fails in one API, it does so in the other.

      The other issue is you hard coded the ID. Where did you get this 465003004 for "motion source" from? Maxime did unfortunately the same in his old thread. You should never hardcode IDs when you can avoid it, i.e., when there is a symbol. With modern tools it also easier than ever to discover the correct symbols. Here I look with mxutils.GetSceneGraphString at the partial scene graph that is attached to my cube in the scene (op because my cube is selected).

      ed53288e-04da-4c83-ac4c-2ce7218c1547-image.png

      If you dig a little deeper (by passing the document, instead of just the cube) you can find out that there is also data in the motion branch of the document:

      79762613-bb37-44b3-a89e-c39dcf8cd597-image.png

      Which is why we keep seeing the motion source in my screen even though I deleted the cube. I do not know where you have digged out that ID MT_SOURCE = 465003004, because I could not find it when reverse searching for it. But assuming it is correct, it is probably just the ID for a sealed node to be instantiated by the timeline to realize its little display thingy of motion sources to the left.

      When you want to create an NLA animation on an object, you must create the motion system on it. And when you want to create one of the motion system 'templates' stored in the document, you just dump some entity (in my case the cube) into the motion branch of the document.

      The higher level problem is that I have no idea what you are tyring to achieve. You want to create motion system 'templates' (aka motion sources) inside a document without actually adding content to the document?

      Cheers,
      Ferdinand

      edit:
      Here is a little proof of concept, although I am not quite sure what one is supposed to do with such dangling motion sources?

      import c4d
      import mxutils
      
      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.
          """
          # Create a cube and add a little 3 second animation to its X position (I was lazy here and just
          # assumed the document would have 30 FPS instead of doing it more formally).
          cube: c4d.BaseObject = mxutils.CheckType(c4d.BaseList2D(c4d.Ocube))
          
          did: c4d.DescID = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DTYPE_VECTOR, 0),
                                       c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))
          track: c4d.CTrack = c4d.CTrack(cube, did)
          cube.InsertTrackSorted(track)
      
          curve: c4d.CCurve = track.GetCurve()
      
          curve.AddKey(c4d.BaseTime(0, 30))
          curve.AddKey(c4d.BaseTime(90, 30))
      
          curve.GetKey(0).SetValue(curve, 0.0)
          curve.GetKey(1).SetValue(curve, 1000.0)
      
          # Now insert the cube into the motion branch of the document. For that we have to first find
          # the motion branch (what we discovered before with mxutils.GetSceneGraphString).
          info: dict | None = next((b for b in doc.GetBranchInfo(c4d.GETBRANCHINFO_NONE) 
                                    if b['id'] == c4d.NLAbase), None)
          if not info or not info['head']:
              raise RuntimeError('Could not find valid motion branch in document')
          
          head: c4d.GeListHead = info['head']
          cnt: int = len(head.GetChildren())
          cube.SetName(f'Cube Motion Source {cnt}')
          cube.InsertUnderLast(head)
          c4d.EventAdd()
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Best way to hide a child and get best perfomance

      I do not quite understand your problem and more importantly what your goal is. Where does 'my path is no longer processed as GetRealSpline' happen? I assume with 'processed' you mean that you call BaseObject.GetRealSpline on something?

      1. Using CSTO as a stand-in for GetRealSpline sounds like a bad idea.
      2. It is better to use caches when possible, because there you do not have to copy whole documents to ensure that all dependencies are included. But you still have to copy something (the cache) but that is less expensive. When the whole thing happens for splines, the issue is that splines return LineObject and not SplineObject as their cache. See here for details. You can sort of just ignore the issue and treat the LineObject instances as if they were SplineObject instances, but that might cause issues here and there. Some tools/objects such as the Extrude Object also accept LineObject inputs even though a user can never manually create them. But there is no guarantee that everything that can deal with SplineObject instances as inputs also supports LineObject.
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Best way to hide a child and get best perfomance

      Regrading @ThomasB snippets, in the context of a GVO call I would curate my C4DAtom.GetClone more, e.g., with COPYFLAGS_NO_HIERARCHY, COPYFLAGS_NO_ANIMATION, or even COPYFLAGS_NO_BRANCHES. Copying a full node can otherwise be quite costly because it is not just "a node" but a whole subset of the scene graph with all the things which are attached to it.

      And that is exactly the problem I talk about in the SMC example I linked to above: Copying can be expensive. And in the broadest scenario you will always need everything, because dependencies can be complex. And even go beyond a pure "down"-dependency (with for example base link fields) where this copying approach then fails.

      • COPYFLAGS_NO_HIERARCHY: Means no children or deeper hierarchical descendants are copied.
      • COPYFLAGS_NO_ANIMATION: Means that no animation tracks or keyframes are copied (which are a form of branches).
      • COPYFLAGS_NO_BRANCHES: Means that no branches are copied. Children are not branches but pretty much everything else is. tracks, curves, tags, special data, etc., they will be all lost. Editable polygon- and spline objects store for example their point, tangent, and polygon data as tags, so you would loose all that. But it can make sense to use this on generator objects (e.g., "Cube Object" or "Circle Spline") to strip them of everything, as they are self contained capsules.

      For @ThomasB case it is probably more performant, to just build the caches for the inputs, i.e., the splines. Which will include all the crazy MoGraph fields and deformer dependencies with which splines could be modified, and then just use that cache as the input for the sweep. The caches of SplineObject instances will be LineObject instances, which is a little bit wrong to put them under a sweep. So, to make it perfect, you would just grab all the points and segments of the line object and re-express them as a new linear spline object.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Best way to hide a child and get best perfomance

      Hey @Tpaxep,

      Thank you for reaching out to us. There are few things to unpack here.

      Hiding Input Objects

      Input objects are hidden automatically when you implement an ObjectData plugin that uses the flag OBJECT_INPUT and 'touches' its input objects, by for example calling GetAndCheckHierarchyClone on them as you do. This only works for direct children of the object, not grandchildren or even deeper descendants.

      Under the hood, this causes the input objects to have the flag BIT_CONTROLOBJECT to be set and then being touched which among other things will delete their cache, resulting in them becoming invisible. Technically you can do parts of this yourself (to hide also objects that are not direct children of your object) but:

      • Cinema 4D evaluates its scene graph hierarchically top down. This is also why you need GetAndCheckHierarchyClone so that you can build the cache of your descendants which has not been build yet when you are being built because fundamentally your child objects come after you in a hierarchy. I cannot unpack this in all detail, but trying to sidestep the cache building logic of Cinema 4D is not recommended and not trivial. Your inputs should always be direct children of yourself, otherwise you will land in a world of hurt sooner or later.
      • 'Hiding' inputs is actually deleting them. BaseObject.Touch literally marks the cache of an object for deletion and BIT_CONTROLOBJECT then later prevents the object from rebuilding it when it is its turn to naturally build the cache. No cache means there is nothing for the object to draw into the viewport. You can technically also hide objects with things like NBIT_OHIDE, but there you run into threading issues (see the section below) and also update issues. When your plugin does miss some case, the user ends up with a permanently hidden object in his or her scene, destroying his or her work. So, using NBIT_OHIDE in this context is not a good idea.

      Threading Restrictions

      Your code violates the threading restrictions of Cinema 4D. You run a modelling command in the GVO of your object plugin directly on an element attached to a loaded (in this case active) scene and with that risk crashes and loss of data. The subject is also explicitly being discussed for modelling commands in this code example (also read the file doc string as it explains some things).

      In general, you cannot change the state of scene elements from any threaded function such as ObjectData.GetVirtualObjects as this will sooner later lead to crashes. Functions such as GetVirtualObjects or ObjectData.GetContour will always only be called in parallel. Functions such as NodeData.Message might be called from the main thread (you can check with c4d.threading.GeIsMainThread) and therefore you can then manipulate the scene from there. E.g., implement a button in an object or tag plugin UI that inserts another object, deletes something, etc.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Batching Slider messages

      Hey @SteveHill3D,

      it's okay, the start is always a bit chaotic. That snippet alone would have told me some things.

      Yes, sorry, MSG_DESCRIPTION_POSTSETPARAMETER is fired within the drag event (the parameter has still to be set). The final description event is MSG_DESCRIPTION_USERINTERACTION_END. But I still do not really understand what you are doing. What is triggering the updates? Just dragging one of the sliders will not update the tool in our edge cutter tool example (because it does not really implement the interactive mode that comes with description tools).

      So, the tool should either fire when you have keyboard or viewport inputs and implemented MouseInput or KeyboardInput. When you click Apply, it should call DoCommand. You can of course hook into Message to react to all sorts of things (and that is indeed how you implement the interactive mode), but you message implies that your tool fires on your own. Which it should not.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Advice on implementing undo/redo in a Tool

      Hey @SteveHill3D,

      Storing a tool state in a tag is possible, but a bit unusual. But generally you are on the right track. Tags are also internally often used as hidden data containers. When you write a modelling tool, you probably already found out that points and polygons are are actually stored in hidden tags on objects.

      I am not sure though if using tags in tools is a good advice, as injecting all that data all the time into the scene graph is probably not the fastest. It depends a bit on what you are doing, but in general the solution is a bit unusual.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Batching Slider messages

      Hey @SteveHill3D,

      Thank you for reaching out to us. It depends a bit on what is your 'tool'. When it is a 'classic' tool, i.e., implemented as a ToolData with a GeDialog, then BFM_DRAGEND would be the way to sort out BFM_ACTION events for a slider that are the final action of a drag event. When you search for the message symbol BFM_DRAGEND and its sibling symbols BFM_DRAGSTART and BFM_ACTION_INDRAG here on the forum, you will find multiple code examples around the topic of sliders. I think there are also some examples in the C++ SDK.

      When your tool is a DescriptionToolData, i.e., a node in disguise, then you cannot handle drag events yourself. Your tool should fire on its own only after MSG_DESCRIPTION_POSTSETPARAMETER has fired for your node, i.e., after the final value of a drag event for some slider has been set.

      Please have a look at our Support Procedures, we require users to post code for pretty much all questions, as it minimizes the guess work on our side. As for example me here writing about two cases.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Tile rendering with Cinema 4D

      @karthikbp

      As far as I can see this at a glance, this seems to be correct. You already did the most important thing by using RDATA_RENDERREGION and not some camera cropping tricks, so it renders with the full scene data for each tile.

      There is however one issue, and it depends on the render engine how it is handled. At verbatim, kernels applied to your rendering (e.g., a box filter) will be slightly wrong, because for the the seams of a tile, there is data missing (the seam pixels of the neighboring tile) which would exist in a full rendering. E.g., when you have this,

      --------------- ----------------
                    x y
          Tile A    x y    Tile B
                    x y
      --------------- ----------------
      

      a rendering split into two tiles Tile A and Tile B, where x are the 'seam pixels' of Tile A and y are the seam pixels of Tile B. If you would have rendered the whole image in one go, processing one of the xs with a kernel would have included neighboring pixels, i.e., also what is now the seam pixels y of the other tile. So, at the borders of the tile, any filter kernels you will apply will not be quite the same.

      To prevent that issue, Redshift does render region tiles with an extra border of the maximum size of the kernels used for that rendering. But the standard renderer does not do that. Other render engines which might support our render region setting might not do that either. In such cases, you would have to either live with the small error or render with an extra border yourself, depending on the kernels that will be used., and then crop the final result back.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: 2025 SDKs missing files

      Hello @atg,

      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

      I can see why would think that, but you mixed things there a bit up. The CMake SDK build system was introduced with 2025.2.0 and became the standard with 2026.0.0. Between 2025.2 and 2025.3 we supported both build systems as a grace period for developers to get accustomed.

      Chances are very good, that you can just copy the CMake setup, i.e., the cmake folder and files such as CMakeLists.txt, CMakePresets.json, and sdk_modules.txt to a 2025.0 folder and it will generate a correct build system for you.

      But the supported range is only 2025.2+. For older projects you would have to use the old project tool based build system. Since I know what you are trying to do, I would recommend trying copying before you get into the old build system of ours.

      • 2025.2 Release Notes
      • 2025.2 Build System Docs (which covered both the old Project Tool and CMake)

      Cheers,
      Ferdinand

      edit: You will only find the old project tool tooling in old extended SDKs which supported it, such as 2025.2 or 2025.0.1

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: CUSTOMGUI_QUICKTAB trigger twice when click

      hey @Gene,

      So, you were just trying to add information for future readers? That is of course very welcome and explains my inability to extract a question from your posting.

      I would not have been that generous with the word bug either. Since this has never been fixed, chances are high that we came to the same conclusion internally.

      In general, you should not expect Command calling patterns to make perfect sense in every case. There are multiple cases where Command is triggered more often than one would think at first glance. I would also not try to interpret some deeper sense into all that.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: CUSTOMGUI_QUICKTAB trigger twice when click

      Hey @Gene,

      Thread-necromancy is often not so good, because it almost always derails the original topic and often also lacks in clarity. I also here do not understand what your concrete question is?

      When I click on the Quicktab, the first event returns True, while the second returns False.

      What do you mean by "the event returns true"? It is you, the developer, who returns the result for an event (or you do not handle it at all). Are you talking about something like BFM_ACTION_VALUE?

      If I drag over two entries on the Quicktab gadget (click on one entry, then drag to the next entry and let go of the mouse,) Command triggers three times, msg[c4d.BFM_ACTION_INDRAG] returns True the first two times and False for the last one. Also, the first entry returns True for its IsSelected() check on mouse down. After dragging, the the second entry's IsSelected() is True. Second entry's IsSelected() is also true when I let go of the mouse.

      I do not see any question here, so what is the goal?

      Regarding Command triggering twice, is it because of the mouse events?

      GeDialog.Command is just a convenience wrapper for GeDialog.Message events (most from the BFM_ACTION family). If you want the full picture, use Message. I struggle to see a tangible question which I could answer, what do you want to achieve?

      Yes, some controls fire multiple times in Command, it is impossible (and also somewhat futile) to reconstruct the reason for each of them. Someone thought at some point it would be convenient to forward this kind of event to Command for some feature in Cinema 4D.

      I am not familiar with the exact issue discussed in this topic, but at a glance, what Maxime wrote then looks a bit overkill with the hashing, I would just store the last selected element and be done with it. Alternatively you could design your code so that you do not tie any heavy logic to a tab being activated (which is IMHO not a great idea in general), so that you do not mind when such code runs multiple times.

      Cheers,
      Ferdinand

      class MyDialog (c4d.gui.GeDialog):
          ID_TAB_1: int = 10000
          ID_TAB_2: int = 10001
      
          IDS_TABS: tuple[int, ...] = (ID_TAB_1, ID_TAB_2)
      
          def __init__(self) -> None:
              self._activeTab: int = MyDialog.ID_TAB_1
      
          def Command(self, cid: int, msg: c4d.BaseContainer) -> bool:
              """
              """
              if cid == self._activeTab:
                  return True
              elif cid in MyDialog.IDS_TABS:
                  self._activeTab = cid
      
              if cid is MyDialog.ID_TAB_1:
                  foo()
              elif cid is MyDialog.ID_TAB_2:
                  bar()
      
              return True
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to detect Global Illumination state via Python API?

      Hey @mfersaoui,

      Thank you for reaching out to us. The question is a bit ambiguous. What it entails when 'Global Illumination is enabled' (or not), differs from render engine to render engine. There is neither an abstracted switch to toggle GI in all render engines, nor a flag which would signal of GI is enabled or not.

      So, the first thing you would have to do, is read RDATA_RENDERENGINE and then branch of based on that. For the standard renderer, you would then check if the GI post effect is loaded and enabled. For Redshift you would get the RS post effect and then check there if has GI enabled.

      You can find the post effect symbols here: VideoPost Types

      Cheers,
      Ferdinand

      Some example code for searching the post effects in some render data.

      # Get the active render data of the document.
      renderData: c4d.documents.RenderData = doc.GetActiveRenderData()
      
      # Set the active render engine to Redshift.
      renderData[c4d.RDATA_RENDERENGINE] = c4d.VPrsrenderer
      
      # Iterate over all video post effects and remove any effect not compatible with the Redshift 
      # render engine and finally make sure the Redshift video post effect itself is present. We do 
      # not remove the effects in place in the loop, as that would mess up the iteration because nodes
      # effectively work as a linked list.
      removeEffects: list[c4d.documents.BaseVideoPost] = []
      foundRedshiftEffect: bool = False
      for effect in mxutils.IterateTree(renderData.GetFirstVideoPost(), True):
          # This is the Redshift video post effect.
          if effect.GetType() == c4d.VPrsrenderer:
              foundRedshiftEffect = True
              continue
          # This is some effect not compatible with Redshift.
          elif not effect.RenderEngineCheck(c4d.VPrsrenderer):
              removeEffects.append(effect)
      
      # Remove all the video post effects we marked for removal.
      for effect in removeEffects:
          effect.Remove()
      
      # And add the Redshift video post effect if it was not found yet. Note that there is no hard
      # guarantee that a render engine uses the same ID for its video post effect as the render 
      # engine ID (VPrsrenderer in this case). But it is a very common convention, check with your
      # render engine vendor for details.
      if not foundRedshiftEffect:
          redshiftEffect: c4d.documents.BaseVideoPost = mxutils.CheckType(
              c4d.documents.BaseVideoPost(c4d.VPrsrenderer))
          renderData.InsertVideoPost(redshiftEffect)
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Advice on implementing undo/redo in a Tool

      Hey @SteveHill3D,

      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

      You could look at our edgecuttool.cpp C++ SDK example, it demonstrates both the usage of undos and cinema::InteractiveModeling_Restart, i.e., rolling updates. In some cases InteractiveModeling_Restart will not cut it you have to un-roll your undos yourself or even not use undos at all for rolling updates and instead work with a tool finalization logic.

      By chance I just wrote an example for the latter for the upcoming Python SDK. You will not be able to run it, as the customer release version is missing the Python API to deal with SDS weights, but you can have a look at its code. The idea is simple: Establish a ground truth and operate based on that. But for modelling tools and especially tools that add or remove geometry using undos and InteractiveModeling_Restart should be preferred when possible, as it will be usually faster, as the cinema core can pull some tricks you cannot as a third party.

      Cheers,
      Ferdinand

      Please understand that this example is a preview and might be subject to change.
      py-cmd_tool_finalization_2026_2.zip

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Tile rendering with Cinema 4D

      Hey @karthikbp,

      Thank you for reaching out to us. And sorry, I somehow overlooked your topic. Yes, that is how I would have done it too, via RDATA_RENDERREGION_LEFT, etc. The 2026.2 Python SDK will contain new rendering examples, that could be something I could add, as tile rendering is something that comes up from time to time.

      As a fair warning: Your code will not respect OCIO, i.e., the images will have the wrong colors. Since you marked this as 2026, where OCIO is the color management standard, this would apply to all documents. See open_color_io_2025_2.py for an example how to do it correctly. The next release will streamline things there quite a bit.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: EventAdd doesn't work with a modal dialog in C4D 2026

      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}
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Render Token Questions

      @tommyf said in Render Token Questions:

      The frame padding I was referring to are in regards to the frame numbering that is written out when selecting a render pattern in the output settings that you would find under Render Settings > Save > Name where the one we typically use is Name.0000.TIF.

      Yes, that was already my hunch, as described above, and you cannot alter this numbering with render tokens, you can only add your own. When you want to change this numbering, you would have to write yourself a post processor as lined out above. Technically, you could also just run this outside of Cinema 4D, e.g.,

      commandline.exe -someScene.c4d ...
      c4dpy.exe renameScript.py -someScene.c4d
      # Or just even just a CPython instance (or any other scripting language), the problem here is
      # that you could not access the the render settings of the scene and would have to rely on conventions.
      python  renameScript.py -someScene.c4d
      

      Based on how you answered my questions it sounds like as long as I load the plugin script that contains the registration code that I could create custom tokens that way. I can also therefore create my own $frame token with my own frame padding (python str.zfill method) for frame numbers however there is no way to override the 4 digit frame padding that comes from the render settings name field.

      Yes. But you could not name it $frame, because that would collide with the builtin token of the same name.

      When the pattern my_render_frame-000001_001.tif works for you, I would definitely go for the render token. When you absolutely have to change the postfix set by Cinema 4D, that is possible too, but much more work. The work lies there not in actually renaming the files, that is somewhat trivial. The work lies in supporting all the ways a rendering can happen. When you are locked into using the commandline on your farm only, that would simplify things and drive down the costs of doing this (i.e., you would not support picture viewer, teams, editor, etc. renderings).

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand