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
    • Login

    VolumeBuilder Cache and AddDependence()

    Cinema 4D SDK
    2024 c++
    2
    9
    1.5k
    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
      d_schmidt
      last edited by ferdinand

      Hello, I'm trying to create and ObjectPlugin that is taking a VolumeBuilder in as a child object.

      In general I'm able to get the information from the VolumeBuilder that I need, but I haven't be able to mark it as used and have it hidden in the viewport.

      When I run this code it works as desired, but if I enable the NewDependenceList() through TouchDependenceList() then the VolumeBuilder is hidden as expected, but I can no longer retrieve the VolumeBuilder's cache.

      This doesn't seem to be a problem with other objects in the same situation, but the VolumeBuilder doesn't seem to work here. Is there something I'm missing here?

      BaseObject* ForumTest::GetVirtualObjects(BaseObject* op, const HierarchyHelp* hh)
      {
          
          
          BaseObject* down = op->GetDown();
          BaseObject* object = down;
          
          /*
          op->NewDependenceList();
          Bool dirtyBool;
          while (down != nullptr)
          {
              
              op->GetHierarchyClone(hh, down, HIERARCHYCLONEFLAGS::ASVOLUME , &dirtyBool, nullptr);
              op->AddDependence(down);
              down = down->GetNext();
              
          }
          op->TouchDependenceList();
          */
          
          if (object == nullptr)
          {
              ApplicationOutput("Child object is nullptr");
              return BaseObject::Alloc(Osphere);;
          }
          
          if (object->IsInstanceOf(Ovolumebuilder) == false)
          {
              ApplicationOutput("Not a volumebuilder");
              return BaseObject::Alloc(Osphere);;
          }
        
          VolumeBuilder* const volumeBuilder = static_cast<VolumeBuilder*>(object);
         
          
          //if AddDependence() is used than the volumeBuilder is always a nullptr
          BaseObject* const cache = volumeBuilder->GetCache();
          if (cache == nullptr)
          {
              ApplicationOutput("No cache, happens when marking Dependence");
              return BaseObject::Alloc(Osphere);;
          }
        
          if(cache->IsInstanceOf(Ovolume) == false)
          {
              return BaseObject::Alloc(Osphere);;
          }
          
          const VolumeObject* const volumeObject = static_cast<VolumeObject*>(cache);
         
          maxon::Volume volume = volumeObject->GetVolume();
         
          if(volume == nullptr)
          {
           
          }
          else
          {
            
          }
          
          ApplicationOutput("Successfully reached the end");
          return BaseObject::Alloc(Osphere);
          
          
          
          
      }
      
      1 Reply Last reply Reply Quote 0
      • i_mazlovI
        i_mazlov
        last edited by i_mazlov

        Hi @d_schmidt ,

        Sorry for the delayed answer.

        The way you use Dependence-functions the observed result is expected, because this is how the TouchDependenceList() function works.

        I assume you would like to process the Volume yourself in order to infer some geometry. If that's the case, I would recommend you to check if your dependence list is dirty, and return the cache (without expensive calculations) if that's the case. Otherwise, you can go ahead and perform your calculations.

        Please check the attached script snippet for the GetVirtualObjects() function, which generates the cube that represents the volume bounding box.

        Cheers,
        Ilia

        9ae4c36a-bbac-403b-b7b5-7d8efdc99571-image.png

        Updated on 16/11/23:

        • final TouchDependenceList() call
        • improved isDirty flag initialization (check op cache and check is op is dirty too)

        Updated on 10/01/24:

        • fixed dirtiness check by removing the AddDependence() function call
        • DIRTYFLAGS::DATA should be used instead of DIRTYFLAGS::CACHE
        BaseObject* RoundedTube::GetVirtualObjects(BaseObject* op, const HierarchyHelp* hh)
        {
        	BaseObject* opResult = nullptr;
        	iferr_scope_handler
        	{
        		BaseObject::Free(opResult);
        		return BaseObject::Alloc(Osphere);
        	};
        
        	BaseObject* down = op->GetDown();
        	CheckState(down, "Child object is nullptr");
        	CheckState(down->IsInstanceOf(Ovolumebuilder), "Not a volumebuilder");
        
        	BaseObject* opVB = down; // VolumeBuilder object
        
        	Bool isDirty = op->CheckCache(hh) || op->IsDirty(DIRTYFLAGS::DATA);
        
        	op->NewDependenceList();
        	while (down != nullptr)
        	{
        		op->GetHierarchyClone(hh, down, HIERARCHYCLONEFLAGS::ASVOLUME , &isDirty, nullptr);
        		down = down->GetNext();
        	}
        	isDirty |= !op->CompareDependenceList();
        
        	if (!isDirty)
        	{
        		op->TouchDependenceList();
        		DiagnosticOutput("Returning CACHE");
        		return op->GetCache();
        	}
        
        	DiagnosticOutput("Calculating NEW_OBJECT");
        
        	VolumeBuilder* const volumeBuilder = static_cast<VolumeBuilder*>(opVB);
        	CheckState(volumeBuilder, "Couldn't retrieve volumeBuilder");
        	BaseObject* const cache = volumeBuilder->GetCache();
        	CheckState(cache && cache->IsInstanceOf(Ovolume), "Couldn't retrieve cache volume");
        
        	VolumeObject* const volumeObject = static_cast<VolumeObject*>(cache);
        	CheckState(volumeObject, "Couldn't retrieve volumeObject");
        	maxon::Volume volume = volumeObject->GetVolume();
        	CheckState(volume, "Couldn't retrieve volume");
        
        	maxon::Range<Vector> bb = volume.GetWorldBoundingBox();
        	maxon::Range<Float> minMax = volume.GetMinMaxValues();
        	Vector size = bb.GetDimension() / 2 * minMax.GetDimension() / 2;
        
        	opResult = BaseObject::Alloc(Ocube);
        	CheckState(opResult, "Out of memory!");
        
        	opResult->SetParameter(CreateDescID(PRIM_CUBE_LEN), size, DESCFLAGS_SET::NONE);
        
        	op->TouchDependenceList();
        	return opResult;
        }
        

        MAXON SDK Specialist
        developers.maxon.net

        D 1 Reply Last reply Reply Quote 0
        • D
          d_schmidt @i_mazlov
          last edited by d_schmidt

          Hello @i_mazlov Thanks for the reply!

          This is the output I'm getting with your code:

          Screenshot 2023-11-14 at 4.07.19 PM.png

          It seems like the code is working in but the Volume Builder is still visible in the viewport.

          Should op->TouchDependenceList(); also be called outside of this if statement?

          if (!isDirty)
          	{
          		DiagnosticOutput("Returning CACHE");
          		op->TouchDependenceList(); // hide the original children
          		return op->GetCache();		 // return the previous hierarchy
          	}
          

          Dan

          1 Reply Last reply Reply Quote 0
          • i_mazlovI
            i_mazlov
            last edited by i_mazlov

            Hi Dan,

            Yes, you're right. The Volume Builder is still visible in the viewport because the final TouchDependenceList() function call after calculating geometry cache is missing. This function call makes Volume Builder to loose its cache, hence we need to first calculate our stuff and only after that touch volume builder.

            Please, refer to the updated script in my previous posting.

            Cheers,
            Ilia

            MAXON SDK Specialist
            developers.maxon.net

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

              This post is deleted!
              1 Reply Last reply Reply Quote 0
              • D
                d_schmidt @i_mazlov
                last edited by

                Hi @i_mazlov
                I recently returned to your code, and reading what you just wrote:

                This function call makes Volume Builder to loose its cache, hence we need to first calculate our stuff and only after that touch volume builder.

                This means that even when checking if the Volume Builder is dirty or not, it's being rebuilt?

                Wouldn't that be slow to perform? Especially if the idea with checking if its dirty or not is to skip unneeded building.

                Dan

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

                  Hello again!

                  I recently began working from scratch again. In @i_mazlov 's code above here: https://developers.maxon.net/forum/post/73005 I'm always getting a dirty result and this seems to be incorrect.

                  My GVO Is purely the provided code and all I have in the scene is my test plugin, and Volume Builder as a child, and then a Cube a a child of the Volume Builder.

                  Even when nothing is changed in the scene the plugin is returning that's it's dirty and being rebuilt.

                  Dan

                  i_mazlovI 1 Reply Last reply Reply Quote 0
                  • i_mazlovI
                    i_mazlov @d_schmidt
                    last edited by i_mazlov

                    Hi Dan,

                    Yes, you're right, the cache part doesn't work right in the code snippet above. I had a look there and it looks that function GetHierarchyClone adds a dependency deep under the hood of its callstack, which in turn makes CompareDependenceList always set dirty to true. For the fix you can simply remove the op->AddDependence(down); line and it would do what you need. (I've updated the code above once again)

                    However, if you actually don't really need using the dependency list here, then I can suggest you using the following part for recalculation check:

                    Bool isDirty = op->CheckCache(hh) || op->IsDirty(DIRTYFLAGS::DATA);
                    op->GetAndCheckHierarchyClone(hh, firstChild, HIERARCHYCLONEFLAGS::ASVOLUME, &isDirty, nullptr, true);
                    op->TouchDependenceList();
                    if (!isDirty)
                    {
                      	DiagnosticOutput("Returning CACHE");
                      	return op->GetCache();
                    }
                    

                    Here, I'm using the GetAndCheckHierarchyClone() function, which actually automatically does everything as we did above but in a more convenient way (and with some more edge cases check).

                    Please note, that I also changed the dirty flags in IsDirty() function call from CACHE to DATA, as it was misused.

                    I'm sorry the answer to this thread took so much time.

                    Cheers,
                    Ilia

                    MAXON SDK Specialist
                    developers.maxon.net

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

                      Hi Illia,

                      I haven't been able to properly stress test the new code, but by the first look it seems to be working. Thanks!

                      Dan

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