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 17
    • Topics 58
    • Posts 3,301
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: Change render Space (Color Profile) - how?

      You likely have to call UpdateOcioColorSpaces, just follow the example. There is btw no guarantee at all that DOCUMENT_COLOR_MANAGEMENT entails a render space of ACEScg. That is just the default, the user could have changed that.

      I am also not sure what all that RDATA_IMAGECOLORPROFILE code is meant to do. It depends a bit on what your plugin does and what self.IsForceLinear is meant to express. Only so much: An OCIO document does not necessarily entail a bitmap with a non-linear color profile, and OCIO also does not mean that the bitmap has to be 32bit. You are however missing UNDOTYPE_PRIVATE_DOCUMENTDATA undo management for setting the color management.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Change render Space (Color Profile) - how?

      I do not quite understand your question. A node (e.g., an object) will always hold its color parameters in render space. The only exception is NodeData.Init and it is explained in the example.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Change render Space (Color Profile) - how?

      Yes, OCIO having become the standard color management entailed some changes. In case your plugin is some kind of NodeData, you should also look at ocio_node_2025.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Change render Space (Color Profile) - how?

      Hey @mogh,

      you cannot do that, because we do not provide access to GUIs as always, only to the data structures that stand behind them. When you see this dropdown in a document, it means it is in OCIO mode. Which in turns means all colors are in render space; which is the main idea of OCIO that all computations happen in render space. All color read and write operations happen in render space, i.e., ACEScg by default. You can use a color converter to convert a color from for example sRGB to render space. But there is no color space setting for a color, that is just UI fluff. See GetSetColorValuesInSceneElements.

      I would recommend to read:

      • open_color_io_2025_2.py
      • C++ SDK: Ocio Manual

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Change render Space (Color Profile) - how?

      Hey @mogh,

      the answer can be found in the OCIO examples, specifically here. In the example I do the exact opposite from what you want to do, I convert a scene from legacy/basic mode to OCIO. Its inverse would look somewhat like what I show below. I hope this helps.

      Cheers,
      Ferdinand

      """Demonstrates how to convert a document from OCIO color management to basic color management.
      """
      import c4d
      
      
      op: c4d.BaseObject | None # The primary selected object in the scene, can be None.
      doc: c4d.documents.BaseDocument # The currently active document.
      
      def main() -> None:
          """Runs the example.
          """
          # The document is already in basic mode, so no conversion is needed.
          if doc[c4d.DOCUMENT_COLOR_MANAGEMENT] == c4d.DOCUMENT_COLOR_MANAGEMENT_BASIC:
              return c4d.gui.MessageDialog("The document is already in basic mode. No conversion is needed.")
      
          # Get the converter, and the active render space, which by default will be ACEScg.
          converter: c4d.modules.render.SceneColorConverter = c4d.modules.render.SceneColorConverter()
          renderSpace: str = doc.GetOcioRenderingColorSpaceNames()[0]
      
          # Initialize the converter with:
          #              doc, from-low   , from-high   , to
          converter.Init(doc, renderSpace, renderSpace, "scene-linear Rec.709-sRGB")
      
          # Convert everything in the document (we pass the document itself as the second argument). The
          # undo management is only needed so that our DOCUMENT_COLOR_MANAGEMENT below is reversible. The
          # ConvertObject call creates undo steps on its own with the default flag we pass. 
          doc.StartUndo()
          doc.AddUndo(c4d.UNDOTYPE_PRIVATE_DOCUMENTDATA, doc)
          if not converter.ConvertObject(doc, doc):
              print(f"Failed to color convert document '{doc}'.")
      
          # Finally, set the color management mode to basic/legacy. It is absolutely important that we do
          # things in this order, otherwise SceneColorConverter will not work correctly.
          doc[c4d.DOCUMENT_COLOR_MANAGEMENT] = c4d.DOCUMENT_COLOR_MANAGEMENT_BASIC
          doc.EndUndo()
          c4d.EventAdd()
          
      if __name__ == "__main__":
          main()
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using SetWeightMap() multiple times can lead to inexplicable changes in weight values

      Hey @chuanzhen,

      thanks for the update. I can reproduce the issue.

      The cause is that some frontend systems can work in a higher precision than the weighting backend can. We use there for some reason UInt16 in the character animation backend to store weights, while the smooth tool for example operates in Float64. Python operates always in 64bit with float. The error is caused by UInt16 normalization (make the value fit into one of the 65535 value bins there are in 16 bit for the range [0, 1]).

      I did not trace down why this happens not at once but in multiple steps, but the bottom line is that sooner or later weights are forced into a 16 bit format. You as a user cannot do anything about it, but it also is not really an issue. This is not a loss of data, the character weights internally use 16bit. It is more that some of the newer tools built around them provide more precise data than the old character animation core can handle.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: MergeDocument() crash C4D

      (As an aside, I found this issue in one of my plugins (which uses MergeDocument). This plugin was developed in C4D 2024, and the same code worked in older versions but crashes in 2026. Even with the same script code and merge same file, it works normally in 2024 but crashes in 2026)

      things can change in the backend of Cinema, without concrete code (the plugin) and example files, I cannot help you much. It could very well be that we added some corner case bug.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: MergeDocument() crash C4D

      @chuanzhen said in MergeDocument() crash C4D:

      But I still have a question. If it's a plugin issue ( CopyTo Read Write function is not implemented), then why can the Merge command in C4D merge files normally?

      What I meant was this: Imagine you have an ObjectData plugin Foo which has a class instance attribute called _data. Many of Foo's methods rely on _data, as for example GetVirtualObjects, GetDDescription, etc. When you now copy an instance of Foo, Cinema will copy its data container but not _data when you have not implemented CopyTo. On the copied instance of Foo, the _data attribute will either not exist at all or be in uninitialized state. Cinema can then for example crash or freeze when you raise an AttributeError (because you try to access a non-existing _data; or any other error) in a GetDDescription call in some corner cases. _data being misaligned with the data container of a node could also lead to all sorts of bugs when your code assumes that they are somehow aligned.

      In short, Cinema can always read, write, and copy nodes, no matter what you do. But with CopyTo you can make sure that data outside of the data container of the node (BaseList2D.GetDataInstance) is also correctly copied. The lesson here is that when you have fields such as self._my_data on a node, and that field cannot be reestablished on the fly, you must implement Read, Write, and CopyTo, so that _my_data can be written, read, and copied from/to/between scene files. You therefore also usually implement all three of these methods and not just one of them when you need them.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using SetWeightMap() multiple times can lead to inexplicable changes in weight values

      Hey,

      so I gave this a shot but I cannot reproduce it. Please provide a scene file as asked for when this is still a problem. I did not fully understand your instructions, so I created a simple scene where a cube is being rigged.

      When I print out the weights of the rig while running your script, I cannot see any change of data. Your video shows some change of values, but overlays and GUI values in general can be unreliable, and I am also not quite sure where the "cube_joint" overlay draws its data from.

      Cheers,
      Ferdinand

      Result

      my scene: joints.c4d
      3349d252-8642-4041-a6f3-1b69cddb7e8c-image.png

      import c4d
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
      
      def main() -> None:
          tag = op.GetTag(c4d.Tweights)
          if not tag:
              return
          j_cnt = tag.GetJointCount()
          p_cnt = op.GetPointCount()
          print("="*200)
          for i in range(10):
              weights = [tag.GetWeightMap(j_id,p_cnt) for j_id in range(j_cnt)]
              print(weights)
              for j_id in range(j_cnt):
                  tag.SetWeightMap(j_id,weights[j_id])
              tag.WeightDirty()
          
          
          c4d.EventAdd()
      
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: MergeDocument() crash C4D

      Hey @chuanzhen,

      we will need both the scene you are merging and merging into to make here any sensible statement. We will also need more than this one sole line of code.

      And while a crash always indicates that we could do something better, my hunch would be that that fault lies mostly with some plugin. Because Cinema itself uses heavily MergeDocument and if there would be a principal bug with the very common flags c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS, we would have found and fixed it before.

      The first thing that comes to mind with your line of code is that you are off-main thread and then run into problems because you try to let the code run on the MT by passing None for thread. But off-main-thread, merging documents would be a bit unusual (and also illegal if one of the participating documents is a loaded document). You can use c4d.threading.GeGetCurrentThread() to get the current thread and pass it for thread.

      Another cause could be some NodeData plugin which holds data outside of its data container and which does not correctly serialize that extra data (does not implement NodeData::Read, ::Write, and ::CopyTo) which can then lead to crashes when Cinema tries to run the merged scene with only partially copied node data (assuming the plugin in questions also fails to do sanity/existence checks on its internal extra data).

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: List of selected edges

      Hey @Kantronin,

      you might want to look at geometry_polgyon_edges_2024.0.py, it should answer all your questions.

      The TLDR is: full-edge maps as you hint at in your posting do exist in Cinema via helper functions, but they are often also not very useful. Internally, Cinema is operating as any other DCC application with the concept of what is often called a "half-edge" data structure. So, when you have the two polygons A and B:

        a---b---e
        | A | B |
        d---c---f
      

      Which share an edge over the vertices b and c, this is actually two edges and not one, due to how normals work. For both polygons to have a normal facing in the same direction, they must have opposite winding orders, which means that the edge b-c is actually two edges: b->c and c->b. So, A could for example index the edge as bc, and B as cb. The example goes into more detail with this.

      Cheers,
      Ferdinand

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: Disable all animation in document

      Hello @ceen,

      Yes, this is possible. @Anlv is right that ID_CTRACK_ANIMOFF is the toggle you are looking for (thank you for helping out!). You can then either combine it with manual scene traversal code, or use mxutils.RecurseGraph which is for most users probably the simpler option when they do not have intimate knowledge of the Cinema scene graph.

      Cheers,
      Ferdinand

      """Loops over all tracks in the scene and toggles their animation state.
      
      I.e., running this once will disable all tracks, running it again will enable all tracks, and so on.
      """
      
      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.
          """
          # We walk the document using RecurseGraph, finding all tracks in the scene, no matter where they
          # are hiding. This is a relatively inefficient call as we really walk everything, and experts
          # could fine tune this. But for a simple script like this or any code that is not performance 
          # critical, this is just fine.
          for track in mxutils.RecurseGraph(doc, yieldBranches=True, yieldHierarchy=True, nodeFilter=[c4d.CTbase]):
              track[c4d.ID_CTRACK_ANIMOFF] = not track[c4d.ID_CTRACK_ANIMOFF]
      
          c4d.EventAdd()
      
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using SetWeightMap() multiple times can lead to inexplicable changes in weight values

      Thanks I will have a look, but it might take a few days before I find the time.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Using SetWeightMap() multiple times can lead to inexplicable changes in weight values

      Hey @chuanzhen,

      thank you for reaching out to us. Please provide a code example and scene file to reproduce the issue, without it is hard to give a concrete answer.

      @Anlv and @ThomasB are correct in bringing forward that floating point precision could be an issue in principle. But IEEE 754 corresponds to roughly 17 significant digits in the interval [0, 1] without loss of precision for the data type Float64, a change in the third significant digit as you report is somewhat unlikely in the interval [0, 1] (but not impossible).

      And more importantly, all integer values, e.g., 1.0, are exactly representable up to 2^53 in IEEE 754-64, so you should never see a loss for the value 1.0. More over, round trips should mean no accumulated loss when you do not do additional computations with the value. So, you first write the value X which might or might not be representable as IEEE 754 -64 because you entered it as some kind of literal in your code or via a GUI. Once written X will be X', i.e., the closest value of X which is representable in IEEE 754-64. Further read and write events will not change that value, unless you do some arithmetic operations with it.

      Overall this sounds all bit like the weight tag is somehow post processing the passed data, and you therefore encounter some error creep. But I can only say anything concrete with code and a scene.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: After calling BaseContainer.Sort(), the stored values are lost

      Hey @chuanzhen,

      Thank you for reporting this. Yes, you are misunderstanding this function a bit. But I have to admit that I did too first, because the function is very weird in what it does. But when you look at the C++ docs, there is a warning.

      846d8773-a0bf-4ac4-b6b0-6207ca995bf6-image.png

      I have just updated the Python docs for the next release to also contain the warning. So, this function is effectively only useable for containers holding strings. As you can see by my example below, it does not only remove all values but turns them into strings (and then leaves them empty when the source was not a string). The function is badly named.

      Find also a little example below to do what you want: sort a container by values.

      Cheers,
      Ferdinand

      Result

      Root (None , id = -1):
      ├── 4 (DTYPE_LONG): 4
      ├── 5 (DTYPE_LONG): 5
      ├── 6 (DTYPE_LONG): 6
      ├── 7 (DTYPE_LONG): 7
      ├── 8 (DTYPE_LONG): 8
      ├── 9 (DTYPE_LONG): 9
      ├── 10 (DTYPE_LONG): 10
      ├── 11 (DTYPE_LONG): 11
      ├── 0 (DTYPE_LONG): 0
      ├── 1 (DTYPE_LONG): 1
      └── 2 (DTYPE_LONG): 2
      --------
      Root (None , id = -1):
      ├── 1 (DTYPE_STRING):
      ├── 4 (DTYPE_STRING):
      ├── 5 (DTYPE_STRING):
      ├── 6 (DTYPE_STRING):
      ├── 7 (DTYPE_STRING):
      ├── 8 (DTYPE_STRING):
      ├── 9 (DTYPE_STRING):
      ├── 10 (DTYPE_STRING):
      ├── 11 (DTYPE_STRING):
      ├── 0 (DTYPE_STRING):
      └── 2 (DTYPE_STRING):
      

      Code

      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:
          bc = c4d.BaseContainer()
          for i in range(4,12):
              bc.SetInt32(i,i)
          for i in range(3):
              bc.SetInt32(i,i)
          print(mxutils.GetContainerTreeString(bc))
          print("--------")
          bc.Sort()
          print(mxutils.GetContainerTreeString(bc))
          
          
      if __name__ == '__main__':
          main()
      

      Custom Sorting Function

      Result

      Root (None , id = -1):
      ├── 0 (DTYPE_LONG): 0
      ├── 1 (DTYPE_LONG): 1
      ├── 2 (DTYPE_LONG): 2
      ├── 3 (DTYPE_LONG): 3
      ├── 5 (DTYPE_LONG): 5
      ├── 6 (DTYPE_LONG): 6
      ├── 7 (DTYPE_LONG): 7
      ├── 8 (DTYPE_LONG): 8
      ├── 9 (DTYPE_LONG): 9
      ├── 10 (DTYPE_LONG): 10
      ├── 11 (DTYPE_LONG): 11
      ├── 12 (DTYPE_LONG): 12
      ├── 13 (DTYPE_LONG): 13
      ├── 14 (DTYPE_LONG): 14
      ├── 15 (DTYPE_LONG): 15
      ├── 16 (DTYPE_LONG): 16
      ├── 17 (DTYPE_LONG): 17
      ├── 18 (DTYPE_LONG): 18
      ├── 19 (DTYPE_LONG): 19
      └── 4 (DTYPE_SUBCONTAINER , id = -1):
          ├── 0 (DTYPE_LONG): 0
          ├── 1 (DTYPE_LONG): 1
          ├── 2 (DTYPE_LONG): 2
          ├── 3 (DTYPE_LONG): 3
          ├── 4 (DTYPE_LONG): 4
          ├── 5 (DTYPE_LONG): 5
          ├── 6 (DTYPE_LONG): 6
          ├── 7 (DTYPE_LONG): 7
          ├── 8 (DTYPE_LONG): 8
          ├── 9 (DTYPE_LONG): 9
          ├── 10 (DTYPE_LONG): 10
          ├── 11 (DTYPE_LONG): 11
          ├── 12 (DTYPE_LONG): 12
          ├── 13 (DTYPE_LONG): 13
          ├── 14 (DTYPE_LONG): 14
          ├── 15 (DTYPE_LONG): 15
          ├── 16 (DTYPE_LONG): 16
          ├── 17 (DTYPE_LONG): 17
          ├── 18 (DTYPE_LONG): 18
          └── 19 (DTYPE_LONG): 19
      

      Code

      import c4d
      import mxutils
      
      import random
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
      
      def SortContainer(bc: c4d.BaseContainer, mode: str = "value") -> None:
          """Sorts the given container by ID or value.
      
          This function reallocates sub-containers but modifies the passed in container in place. It would also be
          possible to modify the sub-container in place, but more work to implement and also more complex to run.
          Due to the fact that we have to copy container data, this is also not the cheapest function.
          """
          def copy(bc: c4d.BaseContainer) -> list[tuple[int, any]]:
              """Copies the contents of the given BaseContainer to a list of (ID, value) tuples.
              """
              result = []
              for i, v in bc:
                  if isinstance(v, c4d.BaseContainer):
                      v = copy(v)
                  result.append((i, v))
              return result
      
          def build(bc: c4d.BaseContainer, items: list[tuple[int, any]]) -> None:
              """Builds a BaseContainer from the given list of (ID, value) tuples.
              """
              bc.FlushAll()
              for i, v in items:
                  if isinstance(v, list):
                      sub = c4d.BaseContainer()
                      build(sub, v)
                      v = sub
                  bc[i] = v
      
          def sort_items(items: list[tuple[int, any]], mode: str) -> list[tuple[int, any]]:
              """Sorts the given list of (ID, value) tuples by ID or value. Nested containers are sorted recursively.
              """
              def is_nested(item: tuple[int, any]) -> bool:
                  return isinstance(item[1], (list, tuple))
      
              def sort_key(item: tuple[int, any]) -> tuple[bool, any]:
                  item_id, value = item
      
                  # The first key puts nested values after scalar values.
                  # The second key is only compared within the same group.
                  if is_nested(item):
                      return True, item_id if mode == "id" else 0
      
                  return False, item_id if mode == "id" else value
      
              # Sort nested contents recursively before sorting this level.
              for index, (item_id, value) in enumerate(items):
                  if is_nested((item_id, value)):
                      items[index] = (item_id, sort_items(value, mode))
      
              return sorted(items, key=sort_key)
      
          items: list[tuple[int, any]] = copy(bc)
          return build(bc, sort_items(items, mode))
      
      def main() -> None:
          """
          """
          bc: c4d.BaseContainer = c4d.BaseContainer()
          data: list[int] = list(range(20))
          random.shuffle(data)
          for v in data:
              bc[v] = v
      
          copy: c4d.BaseContainer = bc.GetClone(0)
          bc[4] = copy
      
          print(mxutils.GetContainerTreeString(bc))
          print("--------")
          SortContainer(bc)
          print(mxutils.GetContainerTreeString(bc))
          
          
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Redshift Maya Extension SDK Access Request (Custom Hair Translator)

      There seems to be some solution, please stay tuned until the RS team has sorted out things. I will then update this topic.

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: Alembic Export to Unreal - RootUVs?

      What I meant was to use the Cineware Unreal bindings, but I am not sure if they support it. Going the export route is a big backwards when Cinema has specific bindings.

      https://www.maxon.net/en/cineware

      I will give the Unreal dev a bump when I see him again.

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand
    • RE: Redshift Maya Extension SDK Access Request (Custom Hair Translator)

      Hey @shiryuta,

      thank you for reaching out to us. There is no public Redshift for Maya SDK. There is a semi-public Redshift Core SDK, but I doubt that this will help you much in this case, as you communicate directly with the Redshift Core in the GPU (and the access is also fairly limited). The Core SDK is also semi-public, i.e., we do not give access to everyone.

      I have pinged the Redshift team about your case. Please have some patience, as some key people are there currently on vacation and will only return in early August. What I can already tell you now, is that we will not meet your request in all points. We will for example never publish internal headers, and we also never write code example for non-public APIs.

      But that does not necessarily mean that there will not be a solution for your problem, only the Redshift for Maya team can clear up the details.

      Cheers,
      Ferdinand

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: [Free Plugin]Plugin Debug Manager v1.3.0— Python Plugin Reloading for Cinema 4D

      I have pinned this as extra tooling is always welcome 🙂 Thanks for sharing it !

      posted in General Talk
      ferdinandF
      ferdinand
    • RE: Programmatically created Redshift Lights in an Object Plugin converts lights into Null-Objekts without an function after making Objekt-Plugin editable

      This is not common knowledge, otherwise I would have spotted that earlier.

      So, you are not really expected to know this as a third party dev. You can look at the geometry_caches_s26.py example and there at this line, under [2] I mention that the cache of that object is muted. But that is of course only very indirect information and not enough to deduce this. I think we also talked about it a couple of times on the forum.

      The flag BIT_CONTROLOBJECT is set on input objects of generators, e.g., the to be extruded spline of an Extrude object, the profile and rail spine of a Loft object, the to be cloned cube and sphere objects of a MoGraph cloner, etc. As shown in the caching example, Cinema will mute the caches of input objects. To be very formal, they are actually built but then again deleted. BIT_CONTROLOBJECT marks such input objects.

      Your light in that scene should not have that flag, as it is not the input for some generator. But it somehow got it anyway, there is probably somewhere a bug. For normal scene evaluation that bug is also not really a problem (because as you saw from your own test, the light still converted correctly when you inserted it on its own outside of caches). The problems start when that irregularly as BIT_CONTROLOBJECT marked object is part of a cache. 'Make Editable' then incorrectly collapses the cache. Not only does it not stop at nested generators which are incorrectly marked like this, it also seems to apply the cache muting logic for input objects (resulting in the empty null object instead of the content of the cache of the collapsed generator).

      I already told the relevant devs that I consider this borderline buggy but they do not seem very inclined to do anything about this. What is effectively missing somewhere in cache resolvement is the evaluation if the flag BIT_CONTROLOBJECT 'makes sense on an object'. That is of not so easy to do, as generator - input object relations can be quite complex in Cinema. Which is probably also why the modelling team does not want to touch this.

      Cheers,
      Ferdinand

      posted in Cinema 4D SDK
      ferdinandF
      ferdinand