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

    Ctr+drag copy vs internal copy in CopyTo function in C++

    General Talk
    plugin-information
    2
    5
    39
    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.
    • aghiad322A
      aghiad322
      last edited by aghiad322

      hello,
      i want to distinguish between c4d's internal use of "CopyTo" and when a user drags the object (in this case it's a tag), also is there a message specifically called when user drag copy an object?

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

        Hey @aghiad322,

        Thank you for reaching out to us. Your question is very hard to answer like this. Please familiarize yourself with out support procedures and what makes a good technical question. I have effectively no idea what you are doing.

        When you are talking about a NodeData::CopyTo call, its context is expressed in the flags, including if this was a clipboard event. I would assume that a drag and drop operation, especially for tags, does not entail the tag being copied, but actually rather be moved (i.e., being removed and then re-inserted). If it is being copied, or at least being copied in your case, I do not see how this should then be distinguished from an 'internal' call, since they are all 'internal'.

        I would just look at the flags and see if they change in a manner that is helpful for your task. Although what you are trying to do does not sound like a good idea, but I more or less know nothing about what you are doing and am guessing mostly.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        aghiad322A 1 Reply Last reply Reply Quote 0
        • aghiad322A
          aghiad322 @ferdinand
          last edited by aghiad322

          Hi @ferdinand , thanks for the reply, my main problem is that "init" is not being called on copying, what im trying to do is that i have a scenehook and it has an array member variable pointing to all tags in the scene with this tag type, and with same parameter (grouping tags based on id parameter), and i need to keep this array in sync with the scene tags.

          Thanks and regards

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

            Hey @aghiad322,

            Thank you for your reply. NodeData::Init is always being called when a node is initialized, explicitly also when a node is being copied (that is what the argument isCloneInit is for). But scene hooks only very rarely get copied since they are effectively singletons.

            All I can tell you from your description, is that what you are doing in general - "have a scenehook and it has an array member variable pointing to all tags in the scene with this tag type", sounds like a bad idea. It sounds like you have a BaseArray<BaseTag*> - or even worse, a std::vector<BaseTag*> - on your scene hook as a field; and this is just asking for crashes. You must use either smart pointers (BaseArray<WeakRawPointer<const BaseTag>>) or BaseLink's. But even then this is not such a great idea, your scene hook should gather its inputs each time it needs them. And when you end up constantly scanning the scene, you are doing something wrong design-wise. There are niche scenarios where this cannot be avoided, but I cannot really evaluate that if I have absolutely no idea what you are doing.

            As already lined out, we cannot help you if you do not show us what you are doing. You are likely talking about your tags being copied, but you do not show us if you implement the tag yourself, what your code is, and what you are trying to achieve in general. How are we supposed to help you, when you do not tell us/show us what you are doing?

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            aghiad322A 1 Reply Last reply Reply Quote 0
            • aghiad322A
              aghiad322 @ferdinand
              last edited by ferdinand

              Hi @ferdinand,

              Thanks for your reply!

              Currently, I can’t use isCloneInit because I’m still developing on the Cinema 4D 2026 SDK. I do plan to migrate to the 2026 version later.

              For context — my plugin is essentially a grouping system based on an ID parameter, similar in concept to how:

              The Simulation system tracks objects containing simulation tags, or

              The Arnold tag groups objects into “trace sets” based on a string ID.

              I’m not scanning the scene constantly for updates. Instead, each tag updates itself through a SceneHook when needed, using the following function:

              void NexusRegistry::UpdateGroupFromTag(BaseDocument* doc, BaseTag* tag)
              {
                  if (!tag) return;
                  BaseObject* obj = tag->GetObject();
                  if (!obj) return;
                  BaseContainer* tagContainer = tag->GetDataInstance();
              
                  // Read new values from tag
                  String id = tagContainer->GetString(ID);
                  Int32 hashedID = HashID(id);
                  Bool includeChildren = tagContainer->GetBool(CHILDREN);
              
                  // Handle old ID if changed
                  Int32 oldIdHash = tagContainer->GetInt32(NEXUS_TAG_PREV_ID, NOTOK);
                  if (oldIdHash != NOTOK && oldIdHash != hashedID)
                  {
                      auto it = groups.find(oldIdHash);
                      if (it != groups.end())
                      {
                          auto& members = it->second.members;
                          members.erase(std::remove_if(members.begin(), members.end(),
                              [obj](const NexusMember& m) { return m.object == obj; }),
                              members.end());
              
                          it->second.dirtyLevel++;
                          if ((Int32)members.size() == 0)
                              groups.erase(it);
                      }
                  }
              
                  // Update new group
                  NexusGroup& group = groups[hashedID];
              
                  // Remove duplicates of this object first
                  group.members.erase(std::remove_if(group.members.begin(), group.members.end(),
                      [obj](const NexusMember& m) { return m.object == obj; }),
                      group.members.end());
              
                  group.members.push_back({ obj, includeChildren });
              
                  ((Nexus*)tag)->UpdateInfo(doc, tag);
              
                  // Store current ID for next update
                  tagContainer->SetInt32(NEXUS_TAG_PREV_ID, hashedID);
              }
              

              To circumvent the tag copy issue, I’m currently deferring registration:

              During CopyTo, tags are added to a pending registration list.

              Then, in the SceneHook Execute() method, I check for tags that have been attached to a document and process them there.

              Here’s that part:

              void NexusRegistry::ProcessPendingTags(BaseDocument* doc)
              {
                  if (pendingRegistration.IsEmpty())
                      return;
              
                  Int32 i = 0;
                  while (i < pendingRegistration.GetCount())
                  {
                      BaseTag* tag = pendingRegistration[i];
                      BaseObject* op = tag->GetObject();
              
                      if (op)
                      {
                          UpdateGroupFromTag(doc, tag);
                          maxon::ResultRef eraseresult = pendingRegistration.Erase(i, 1);
                      }
                      else
                      {
                          i++;
                      }
                  }
              }
              
              EXECUTIONRESULT NexusRegistry::Execute(BaseSceneHook* node, BaseDocument* doc,
                                                     BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags)
              {
                  ProcessPendingTags(doc);
                  return EXECUTIONRESULT::OK;
              }
              

              Here’s the SceneHook class structure for reference:

              struct NexusMember
              {
                  BaseObject* object = nullptr; // weak ref, don’t own it
                  Bool isChild = false;         // "include children" flag
              };
              
              struct NexusGroup
              {
                  Vector color;
                  std::vector<String> links;
                  std::vector<NexusMember> members;
                  Int32 dirtyLevel = 0;
              };
              
              class NexusRegistry : public SceneHookData
              {
              public:
                  static NexusRegistry* Get(BaseDocument* doc);
                  static NodeData* Alloc();
                  virtual Bool Message(GeListNode* node, Int32 type, void* data);
                  virtual EXECUTIONRESULT Execute(BaseSceneHook* node, BaseDocument* doc,
                                                  BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags);
              
                  std::unordered_map<Int32, NexusGroup> groups;
                  maxon::BaseArray<BaseTag*> pendingRegistration;
              
                  void ProcessPendingTags(BaseDocument* doc);
                  void RebuildRegistry(BaseDocument* doc);
                  void UpdateGroupFromTag(BaseDocument* doc, BaseTag* tag);
                  void RemoveObjectFromGroups(BaseObject* obj);
                  void RemoveTagFromGroup(BaseTag* tag);
                  const NexusGroup* GetGroup(Int32 hash) const;
              };
              

              The main purpose of this system is to allow other plugins (e.g., object plugins) to:

              Reference tags that share the same ID,

              Access their data via the registry, and

              Rebuild themselves when the related group’s dirtyLevel changes.

              Thanks for your time, and sorry if this looks a bit rough, I just wanted to show the current design.
              If you have any design suggestions or best practices for handling deferred tag registration or object cloning across documents, I’d really appreciate your insights!

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