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,205
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: Tile rendering with Cinema 4D

      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

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to select multiple files using "c4d.storage.LoadDialog()"?

      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.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Is it okay to release a free plugin that overlaps with paid alternatives?

      Hey @Anlv,

      to make it short: You can release whatever you want, there is no etiquette that prevents you from releasing your software because it rains on the parade of someone else. That is just part of software development that you always outshine someone else in some shape or form.

      But the Cinema community is very polite, I would be surprised if someone would take offsense in any shape or form.

      Cheers,
      Ferdinand

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: How to select multiple files using "c4d.storage.LoadDialog()"?

      Yes, that is a very low level way to do it, I assume you had help from an AI in writing it?

      I personally meant more to use a GUI kit such as tkinter or QT. We deliberately do not ship our Python with tkinter (the builtin GUI lib of Python), but you could just install it yourself. The advantage of tkinter or a similar lib is that things like filedialog.askopenfilenames will work on multiple OS's and versions of it, the disadvantge is that you have to install the dependency. Doing it manually yourself means that you have to support each OS yourself and also have to track versions of the API. But at least GetOpenFileNameW is probably so stable that you never have to care about the latter on WIndows.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Effector Objects written in Python

      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

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Reading Immutable Selection Tags

      Hello @Dimitris_Derm. ,

      Thank you for reaching out to us. Please follow our Support Procedures, especially regarding providing code and scene data alongside your questions. Because otherwise things can become a guessing game for us. It also often the case that things are more complicated than it seems, as the case here.

      About your Question

      When you talk about 'selection tags', I assume from the context that you actually mean selection proxy tags (denoted by the little link overlay icon). E.g., as here generated by the extrude object.
      13749476-8f97-400c-af06-411186aaad78-image.png

      Proxy selection tags exist because selection tags need a point object to work, when a selection tag sits on a generator, it becomes ambiguous to which point object inside the cache of the generator they belong. Proxies solve this by pointing to one or many actual selection tags in a cache (it is often the case that a proxy tag does not resolve to a singular discrete tag in the cache but to multiple tags).

      The proxy tag type class/interface is private as declared in the C++ API, i.e., one cannot interact directly with them (as a third party). But one can send MSG_GETREALTAGDATA to find the discrete tag(s) it points to.

      ⚠ The data returned by MSG_GETREALTAGDATA is by design data in caches. You can read data in caches, but you should never attempt to write data in caches, as caches are static by design, and violating this rule can lead to crashes.

      ⚠ What makes this more complicated in this case is that you use Neutron (Scene Nodes) which adds another layer of complexity. We would need here your existing code and a concrete scene to give an answer, as the approach shown below will likely not work there.

      Cheers,
      Ferdinand

      Result

      An extrude object which holds a proxy selection tag. In the console we can see the partial scene graph for the object. I have highlighted the line which is the actual selection tag inside the cache of the extrude object the proxy points to.

      ec1e1feb-d525-49c5-a800-4668aeb9abd4-image.png

      My scene: extrude.c4d

      Code

      """Demonstrates how to find the discrete selection tags which are pointed to by a selection proxy tag 
      on a generator.
      """
      
      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`.
      
      # The types of proxy tags which we want to find on the selected object.
      PROXY_TAG_TYPES: tuple[int, ...] = (
          c4d.Tcacheproxytagpointselection,
          c4d.Tcacheproxytagedgeselection,
          c4d.Tcacheproxytagpolyselection
      )
      
      def main() -> None:
          """Called by Cinema 4D when the script is being executed.
          """
          if op is None:
              return c4d.gui.MessageDialog('Please select an object.')
          
          # The proxy tags of #op. Proxy tags exist because selections (and some caches) only work directly
          # on a point object. A selection tag on a generator can be ambiguous, because it might not be 
          # clear which of the multiple point objects in the cache of the generator it should be applied 
          # to. Proxy tags solve this problem, as they point to one or multiple discrete selection tags 
          # in the cache of a generator, which should be used in place of the proxy.
          proxyTags: list[c4d.BaseTag] = [t for t in op.GetTags() if t.GetType() in PROXY_TAG_TYPES]
          if not proxyTags:
              return c4d.gui.MessageDialog('The selected object has no proxy tags.')
          
          # Print the partial scene graph of #op, so that we can see what is going on in the cache tree 
          # of it.We should see there the discrete selection tag(s) which are pointed to by the proxy tags 
          # of #op.
          print(mxutils.GetSceneGraphString(op))
          print("\n", "-" * 80, "\n")
      
          # Iterate over all proxy tags and find the discrete selection tags they point to, using the 
          # message c4d.MSG_GETREALTAGDATA. The result of this message is a dictionary, which contains 
          # references to the discrete selection tags in the cache of the generator, which are pointed 
          # to by the proxy tag.
          for proxyTag in proxyTags:
              res: dict = {}
              proxyTag.Message(c4d.MSG_GETREALTAGDATA, res)
              tagSymbol: str = mxutils.g_c4d_symbol_translation_cache.Get(proxyTag.GetType())[0]
              print(f"{proxyTag.GetName()}({tagSymbol}) points to: {res}")
      
          # NEVER modify the returned tags, as caches are by design static (but that is not physically 
          # enforced by Cinema 4D). Changing the content of caches can very easily lead to crashes, unless
          # you know exactly what you are doing. Reading data from caches is perfectly fine, though.
      
      if __name__ == '__main__':
          main()
      
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: How to select multiple files using "c4d.storage.LoadDialog()"?

      Hello @Anlv,

      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

      What you are asking for is not possible in this form at the moment. The Cinema (C++) API has two functions for opening the native OS file and folder dialogs: FileSelect and FileSelectMultiple. The Python API makes this more complicated that it has to be with c4d.storage.LoadDialog and c4d.storage.SaveDialog but both just wrap FileSelect.

      With FileSelectMultiple you can do what you want, select multiple files in one go, but the Python API currently does not wrap this function. You must either open multiple dialogs or use a third party GUI API which can open such native OS dialog for you.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Can I have access to the Loop and Ring Selection algorithm ?

      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.

      c122abc5-8cfc-40da-90be-534532600b4a-image.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.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Can I have access to the Loop and Ring Selection algorithm ?

      Hello @Dimitris_Derm.,

      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 am not quite sure how your question is meant, but the loop and ring selection tools are accessible in the API. When you are literally asking for the algorithm, we cannot share that, as declared in our Support Procedures:

      We will not reveal non-public functionalities of our APIs.

      When you just want to use the tools programmatically, SendModelingCommand is the way to go. We also have a dedicated section in our examples for them.

      Cheers,
      Ferdinand

      Result

      af91631a-5119-4df2-86ac-d9539f3e80ed-image.png

      Code

      """Demonstrates how to use the loop selection command in the Python API. 
      
      The script creates a cylinder, makes it editable, and then performs a loop selection based on two 
      vertices and a polygon index. Finally, it inserts the modified object into the active document and 
      updates the scene.
      """
      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.
          """
          # Instantiate a cylinder and insert it into a temporary working document.
          cylinder: c4d.BaseObject = mxutils.CheckType(c4d.BaseObject(c4d.Ocylinder))
          temp: c4d.documents.BaseDocument = mxutils.CheckType(c4d.documents.BaseDocument())
          temp.InsertObject(cylinder)
      
          # Make the object editable. We could also build the caches and get the cache, but since the 
          # subject is here SMC, we can also use SendModelingCommand and MODELINGCOMMAND_MAKEEDITABLE.
          res: list[c4d.BaseObject] | bool = c4d.utils.SendModelingCommand(
              c4d.MCOMMAND_MAKEEDITABLE, list=[cylinder], mode=c4d.MODELINGCOMMANDMODE_ALL, doc=temp)
          if not res:
              raise RuntimeError("Failed to make object editable.")
          
          # The cylinder is now editable.
          cylinder: c4d.PolygonObject = res[0]
      
          # Now we are going to do the loop selection. These are the IDs defined in toolloopselection.h:
      
          #     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
      
          settings: c4d.BaseContainer = c4d.BaseContainer()
      
          # Define the major input, two vertices and a polygon index. You need all three, regardless of the output mode 
          # you choose in SMC.
          settings[c4d.MDATA_LOOP_FIRST_VERTEX] = 2
          settings[c4d.MDATA_LOOP_SECOND_VERTEX] = 3
          settings[c4d.MDATA_LOOP_POLYGON_INDEX] = 1
      
          # Define the selection mode, this is more cosmetic, as SELECTION_NEW is the default.
          settings[c4d.MDATA_LOOP_SELECTION_TYPE] = c4d.SELECTION_NEW
      
          # Carry out the loop selection, targeting edge mode.
          res: list[c4d.BaseObject] | bool = c4d.utils.SendModelingCommand(
              c4d.ID_MODELING_LOOP_TOOL, list=[cylinder], mode=c4d.MODELINGCOMMANDMODE_EDGESELECTION, bc=settings, doc=temp)
          if not res:
              raise RuntimeError("Failed to perform loop selection.")
      
          # Scene elements can only be inserted once, the Python API removes nodes automatically when you call InsertObject, but 
          # C++ does not and there is no guarantee that the Python API babysits you in all cases where you can insert something, 
          # so it is good practice to ensure scene integrity yourself. (#cylinder is currently part of #temp).
          cylinder.Remove()
      
          # Insert the object into our scene, enable edge mode, make the object selected, and push an update event.
          doc.InsertObject(cylinder)
          doc.SetMode(c4d.Medges)
          doc.SetActiveObject(cylinder)
          c4d.EventAdd()
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • 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