Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware 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
    • Register
    • Login

    Overriding `NodeData::GetAccessedObjects(..)` prevents cache generation of cloned hierarchy

    Cinema 4D SDK
    2024 c++
    2
    6
    700
    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.
    • D
      Deyan
      last edited by

      Hi, I'm currently trying to implement the new NodeData::GetAccessedObjects(..) as described in this post. But something is not right, because after derived for one particular object, the objects in the cache are left without their caches.

      To give a bit more details - I have an object generator implementation, which is effectively cloning its child hierarchy and putting it under a Connector object. But after adding an override of NodeData::GetAccessedObjects(..) for this generator object, the cache is missing. A simplified version of my code below:

      maxon::Result<Bool> MyGenerator::GetAccessedObjects(const BaseList2D *node, METHOD_ID method, AccessedObjectsCallback &access) const {
      	yield_scope;
      	switch (method) {
      		case METHOD_ID::GET_VIRTUAL_OBJECTS: {
      			node->GetAccessedObjectsOfFirstChildHierarchy(
      				ACCESSED_OBJECTS_MASK::ALL | ACCESSED_OBJECTS_MASK::GLOBAL_MATRIX,
      				ACCESSED_OBJECTS_MASK::CACHE, 
      				METHOD_ID::GET_VIRTUAL_OBJECTS_AND_MODIFY_OBJECT,
      				access
      			) yield_return;
      			return access.MayAccess(
      				node,
      				ACCESSED_OBJECTS_MASK::DATA | ACCESSED_OBJECTS_MASK::GLOBAL_MATRIX,
      				ACCESSED_OBJECTS_MASK::CACHE
      			);
      		}
      	}
      	return ObjectBase::GetAccessedObjects(node, method, access);
      }
      
      BaseObject* MyGenerator::GetVirtualObjects(BaseObject *object, const HierarchyHelp *hierarchyHelp) {
      	BaseObject *firstChild=object->GetDown();
      	if (!firstChild) {
      		return nullptr;
      	}
      	bool dirtyInput=false;
      	BaseObject *input=object->GetAndCheckHierarchyClone(hierarchyHelp, firstChild, HIERARCHYCLONEFLAGS::ASIS, &dirtyInput, nullptr, true);
      	if (input) {
      		if (!dirtyInput) {
      			return object->GetCache();
      		}
      		BaseObject *cacheRoot=BaseObject::Alloc(Oconnector);
      		if (!cacheRoot) {
      			return cacheRoot;
      		}
      		input->InsertUnder(cacheRoot);
      		return cacheRoot;
      	}
      	return nullptr;
      }
      

      I tried several different flags to be used in the GetAccessedObjects(..) implementation, but none seemed to work. Only using acess.MayAccessAnything() in the implementation results in the previous behavior, but that is not the point of actually using the function.

      Am I missing something obvious or there might be a problem with my approach as a whole?

      1 Reply Last reply Reply Quote 0
      • O
        o_kniemeyer
        last edited by o_kniemeyer

        Hi Deyan,

        your code for GetAccessedObjects is correct. The problem is that you use the connect object, and this object doesn't implement GetAccessedObjects yet. There are a lot of objects in the C4D code base, and we at Maxon haven't had the time yet to implement GetAccessedObjects for all of them.

        A GetAccessedObjects implementation has to tell about all access which GetVirtualObjects will make to the scene, and also about all access which the objects created by GetVirtualObjects will make to the scene when GetVirtualObjects is called on them. Also all objects created by GetVirtualObjects have to support GetAccessedObjects. The connect object doesn't, so your code fails.

        The connect object will support GetAccessedObjects in 2024.4. So you have to wait a bit, I'm sorry.

        1 Reply Last reply Reply Quote 0
        • O
          o_kniemeyer
          last edited by

          There's no way for you to know if an object implements GetAccessedObjects or not. And even if there was a way, that wouldn't help in general because that implementation could still decide to call MayAccessAnything(), e.g. based on the object's settings. You need the knowledge that the connect object supports GetAccessedObjects AND won't call MayAccessAnything() in the configuration which you create in your code.

          The only clean way would be to call GetAccessedObjects on the connect object within MyGenerator::GetAccessedObjects. That's what node->GetAccessedObjectsOfFirstChildHierarchy() does with the object's children. But you don't have the connect object yet in GetAccessedObjects, so that's not feasible.

          1 Reply Last reply Reply Quote 0
          • D
            Deyan
            last edited by Deyan

            Thank you for you answer @o_kniemeyer . Just to be clear about the proper implementation once GetAccessedObjects(..) is implemented for the Connector - what are the minimal read and write flags for the hierarchy call, if GetVirtualObjects(..) calls GetAndCheckHierarchyClone(hierarchyHelp, firstChild, HIERARCHYCLONEFLAGS::ASIS, &dirtyInput, nullptr, true);. I'm asking because in my example I tried with ACCESSED_OBJECTS_MASK::ALL | ACCESSED_OBJECTS_MASK::GLOBAL_MATRIX for read and ACCESSED_OBJECTS_MASK::CACHE for write, but I have a suspicion this may not be optimal (the write flags even feel wrong).

            1 Reply Last reply Reply Quote 0
            • O
              o_kniemeyer
              last edited by o_kniemeyer

              The function GetAndCheckHierarchyClone creates clones of all children, therefore it has to read everything from the children and you need ACCESSED_OBJECTS_MASK::ALL in the read part. It'll also mark the children as being consumed by your generator so that they don't create virtual objects on their own (only their clones in the cache of your generator will create virtual objects), and for this mark you need ACCESSED_OBJECTS_MASK::CACHE in the write part.

              GetAndCheckHierarchyClone doesn't read global matrices, so the GLOBAL_MATRIX flag isn't needed.

              The flags in the other call can be reduced to access.MayAccess(node, ACCESSED_OBJECTS_MASK::NONE, ACCESSED_OBJECTS_MASK::CACHE). That's because your GetVirtualObjects doesn't read anything from the generator itself (NONE), it'll only set up its virtual objects (CACHE). As soon as you access the BaseContainer of your generator, you have to add DATA. For the matrix MATRIX or even GLOBAL_MATRIX etc.

              1 Reply Last reply Reply Quote 0
              • D
                Deyan
                last edited by

                Thanks a lot for the clarification! It would be nice to include more usecase examples of GetAccessedObjects(..) implementation in the SDK examples in the future. I will try your suggestion once 2024.4 becomes available.

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