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
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Register to object change

    Cinema 4D SDK
    sdk c++
    2
    5
    905
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • CJtheTigerC
      CJtheTiger
      last edited by

      Can an ObjectData plugin react to the change of an object it created? I'm working in C++ on 2023.2.0.

      Situation

      My ObjectData plugin creates a null and an IK-Spline tag among many other objects. The tag is not attached to the null in question.

      Requirement

      Whenever the user changes the size of the null I need to update the Handle Depth of one of the handles in the IK-Spline tag. Is it possible to register to the change of an object?

      What I've tried

      The Message method of my ObjectData does not receive a relevant message upon changing the size of the null.

      I inspected the BaseObject instance of my null and found the AddEventNofication function. However this function is only documented as "Private" in the online documentation.

      Disclaimer

      Please note that I've read up on creating additional objects in an ObjectData plugin and I'm already aware of the fallpits and risks.

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @CJtheTiger
        last edited by ferdinand

        Hello @CJtheTiger,

        thank you for reaching out to us.

        Is it possible to register to the change of an object? [...] The Message method of my ObjectData does not receive a relevant message upon changing the size of the null.

        NodeData::Message only receives messages sent to that node, as it is just the counter part to C4DAtom::Message. So, if you implement a Foo object, you will receive messages sent to Foo instances. There is an exception to that rule, C4DAtom::MultiMessage. When a caller decides to use it instead of ::Message, also the children and branches of the node upon which ::MultiMessage is being invoked will be informed. Multi message events are rare and nothing one can rely on as a principle.

        Node messages, i.e., C4DAtom::Message/NodeData::Message, are intrinsically bound to a singular node. Broad scene or system wide messages are the so called core messages. It is however not possible to receive core messages in a node.

        [...] and found the AddEventNofication function. However this function is only documented as "Private" in the online documentation.

        There are some examples on the forum here, but you first of all must register for each node yourself. So, you cannot say 'broadcast all Onull data container changes to me', and there is also no data container event (see NOTIFY_EVENT for details).

        What can I do?

        You are running here into the well known problem that the classic API has no meaningful dependency graph/information, and as a root cause for that also only a very coarse scene update event system.

        • Simple Solution: Use the built-in dependency mechanism for object plugins, the flag OBJECT_INPUT. When you flag your plugin as such a direct child of your plugin node which has become data dirty will also cause your plugin's GetVirtualObjects (and similar methods) to be invoked. But OBJECT_INPUT is indeed limited to direct children, everything else will be ignored. So, it depends on where your null objects live.
        • Tag Solution : Another pattern to solve problems such as this are tags. The idea is to mark the thing you want to be notified about with a custom tag of yours. Because a tag qualifies for ::MultiMessage messages sent to its hosting object, and because tags are executed on each scene pass, they can be a clever solution to the problem. The tags are then usually hidden and carry a base link to the node they should inform about changes of their host object. Either in their ::Message or ::Execute method they then carry out the information propagation. Problems can be here threading restrictions and feedback or delay effects.
        • Complex Solution: You can solve all these problems with a MessageData plugin. With it you can hook in the core message stream, and on EVMSG_CHANGE events traverse/analyze a scene graph to decide if an event X has happened. Then you can inform specific nodes of that event you have detected. To inform your nodes, you should then use C4DAtom::Message with the message ID MSG_BASECONTAINER, sending the relevant data in a BaseContainer. Efficiently doing all of this, especially gracefully traversing and analyzing the scene graph, can be tricky.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        CJtheTigerC 1 Reply Last reply Reply Quote 1
        • CJtheTigerC
          CJtheTiger @ferdinand
          last edited by

          Hey @ferdinand,

          thank you for your response! And a very involved one at that, as expected from you. Thanks!

          Simple solution using OBJECT_INPUT

          I tried the solution using OBJECT_INPUT. To verify whether it works I created a breakpoint in GetVirtualObjects and tried it both with and without the OBJECT_INPUT flag. What I found was that the flag does not make a difference, the function will still be called oh so many times. Also you mentioned "direct children". I tested this both with children directly under my generator as well as with nested children of the generator. All of them trigger GVO. And since I don't get information about which children exactly has changed and what property I'd have to re-evaluate all of them which doesn't sit quite right with me. They're only nulls but still.

          Complex solution using MessageData

          As for the complex solution (and this is very theoretical so feel free to skip it) I'd have to register a MessageData plugin and hook into the messaging pipeline, just to ignore all messages except this very specific one. And then I'd also have to identify whether the issuing object is indeed one of my desired nulls so I'd have to mark those in some way. Which then makes me think "mark it with a tag". And at that point I can just use a custom tag anyways.

          Tag Solution

          Performance-wise this sounds the cleanest to me. It doesn't bloat the messaging pipeline, it won't require any fancy evaluation logic to see whether this is my desired null, it's very explicit in its definition. So this is the one I'll use.

          But feel free to correct me in my theories about performance.

          1 Reply Last reply Reply Quote 0
          • CJtheTigerC
            CJtheTiger
            last edited by

            I would like to add the solution I chose to close this topic.

            The Tag Solution did indeed work very well. Execute got called plenty of times and I was easily able to filter for the change of the object I was looking for.

            But what I actually did was create a whole new class deriving from ObjectData and listen to the message MSG_DESCRIPTION_POSTSETPARAMETER to perform my actions right then. In the first post I said that my plugin creates a null. I replaced all those nulls with my new ObjectData class. So my object hierarchy is similiar to this:

            • MainPluginObject : ObjectData -> Needs to be accessed by HelperObject.
              • HelperObject : ObjectData -> Accesses MainPluginObject in its Message function.

            Also note that thread awareness is required.

            The reason I chose this structure this is the following:

            I would like the user to Current State to Object my MainPluginObject. Before that the HelperObject might not be visible in the object manager depending on the values of some properties in MainPluginObject. But once CSTO is called I want all objects to be shown in the object manager. If I had chosen the Tag Solution the user might delete the tag by accident and break everything. Now of course the user can still break things by deleting or moving the HelperObject out of the hierarchy of MainPluginObject but this I already have to validate anyways. Having a similiar check in a tag just adds another point of error which I'd like to avoid.

            So I'll mark this as my answer after taking everything @ferdinand said into consideration.

            Again, thank you very much for your assistance! You never let us down! ❤

            ferdinandF 1 Reply Last reply Reply Quote 1
            • ferdinandF
              ferdinand @CJtheTiger
              last edited by

              Hey @CJtheTiger,

              sorry for my radio silence here and thank you for updating the thread. In short: you drew all the right conclusions for the general case.

              A MessageData solution can be quite performant when you know exactly what you are doing and when you must monitor either every object (in the sense of geometry) in the scene graph or at least many of them. For something like monitoring materials, shaders, or tags, the solution is mandatory anyways.

              The tag solution on the other hand can become a bottle neck when the information flow becomes too expensive. Imagine such dependency propagation tag called DataTag, and you have 1000's of them in a scene. Each tag not only informs then one but multiple other nodes about changes of its host. Due to that there is also overlap, i.e., both DataTag DA and DataTag DB inform a target object T about changes of their host objects HA and HB. But for T this might be the "same" event and it then might be doing work twice. So, when stuff is getting complicated, you. are often better off with a MessageData solution, as you can consolidate things there.

              Cheers,
              Ferdinand

              MAXON SDK Specialist
              developers.maxon.net

              1 Reply Last reply Reply Quote 1
              • First post
                Last post