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

    Dirty Handling

    SDK Help
    0
    2
    256
    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.
    • H
      Helper
      last edited by

      On 03/03/2013 at 12:08, xxxxxxxx wrote:

      User Information:
      Cinema 4D Version:   RXX 
      Platform:   Windows  ;   Mac OSX  ; 
      Language(s) :     C++  ;

      ---------
      After getting fed up trying to understand checking, getting, and setting dirty flags and counts, I decided to look at one of the SDK examples.  It had a curious array to store each flag and dirty count for each flag.  Now it makes perfect sense.  This might not be news to some of you but I figured that I would put this code and the ideas out here for other developers who might struggle with dirty states.

      So, basically, you have a set of bit flags to set or get the dirty state of something.  The return value of GetDirty() is a running count that increments every time the dirty state changes - it is NOT the flags that are 'set'.  You query on the state changes that you need to check using the DIRTYFLAGS_xxxx bitmask.

      What I've found is that it is best to have Bool class members to track whether a change of interest occurred and ULONG class members to store the last dirty counts.  The ULONG variables are set to 0 in Init().  If you need to do different things on, say, change of an object's data then when a change of the matrix occurs, you need to split it up and track each one separately.  Nothing special code-wise but this is a good example of the 'micro-managing' that might be required when you only want to allocate or initialize data structures on certain specific conditions.

      Here, I am showing an example of CheckDirty() for a deformer object plugin.  For the linked object, its Bool dirty checks are set to TRUE when the object is dropped into the link to kickstart initialization of data structures.  For both objects, the Bool dirty checks are set to TRUE on MSG_MULTI_DOCUMENTIMPORTED.

      // ObjectData.CheckDirty  
      //*---------------------------------------------------------------------------*  
      void CollisionDeformerObj::CheckDirty(BaseObject* op, BaseDocument* doc)  
      //*---------------------------------------------------------------------------*  
      {  
        // Assume that nothing has changed  
        deformedDirtyMatrix =            FALSE;  
        deformedDirtyData =                FALSE;  
        colliderDirtyMatrix =            FALSE;  
        colliderDirtyData =                FALSE;  
        compositeDirty =                FALSE;  
        
        if (!doc)                        return;  
        if (!op)                        return;  
        
        // Check for Collider object  
        BaseObject*        dop =            op->GetUp();  
        // - No object being deformed, get out!  
        if (!dop)                        return;  
        // - Get Data Container  
        BaseContainer*    bc =            op->GetDataInstance();  
        if (!bc)                        return;  
        // - Get linked object  
        BaseObject*        lop =            (BaseObject* )bc->GetLink(COLLISIONDEFORMER_OBJECT, doc, Obase);  
        // - No need to check dirty flags if no collider  
        if (!lop)                        return;  
        
        // Check Deformed object  
        ULONG            dirty =            dop->GetDirty(DIRTYFLAGS_MATRIX);  
        if (dirty != dmDirty)  
        {  
            dmDirty =                    dirty;  
            // For collision bounds checks  
            deformedDirtyMatrix =        TRUE;  
        }  
        dirty =                            dop->GetDirty(DIRTYFLAGS_DATA);  
        if (dirty != ddDirty)  
        {  
            ddDirty =                    dirty;  
            // A data change constitutes a need to update deformedOp  
            deformedDirtyData =            TRUE;  
            // A data change constitutes a need to recreate Mass-Spring System  
            needInitMassSpring =        TRUE;  
        }  
        
        // Check Collider object  
        dirty =                            lop->GetDirty(DIRTYFLAGS_MATRIX);  
        if (dirty != cmDirty)  
        {  
            cmDirty =                    dirty;  
            // For collision bounds checks  
            colliderDirtyMatrix =        TRUE;  
        }  
        dirty =                            lop->GetDirty(DIRTYFLAGS_DATA);  
        if (dirty != cdDirty)  
        {  
            cdDirty =                    dirty;  
            // A data change constitutes a need to update colliderOp  
            colliderDirtyData =            TRUE;  
        }  
        
        // For faster bounds-check requirement determination  
        compositeDirty =                (colliderDirtyData || deformedDirtyData || colliderDirtyMatrix || deformedDirtyMatrix);  
        // Data or matrix changes constitute a need to ModifyObject()  
        if (compositeDirty)                op->SetDirty(DIRTYFLAGS_DATA);  
      }  
      
      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 03/03/2013 at 13:09, xxxxxxxx wrote:

        Hi Robert,

        thanks for sharing. Here's a class similar to what I often use:

        /**  
         * This class keeps track of an object and its dirtycount for a specific  
         * bit. The {@check();} method returns TRUE when either the passed  
         * reference has changed since the last call or the dirtycount for the  
         * specified dirtybit.  
         */  
        class DirtyCounter {  
          
          /**  
           * The address of the atom that was passed in the last call to  
           * {@check();}. It is stored passively, no methods are called on  
           * this object, it is therefore safe to deallocate it even when  
           * an object of this class still has a reference to it.  
           */  
          C4DAtom* atom;  
          
          /**  
           * The dirtybit to check.  
           */  
          DIRTYFLAGS bit;  
          
          /**  
           * The last dirtycount for the specified dirtybit.  
           */  
          LONG count;  
          
        public:  
          
          DirtyCounter() : atom(NULL), bit(DIRTYFLAGS_DATA), count(0) {  
          }  
          
          DirtyCounter(DIRTYFLAGS bit) : atom(atom), bit(bit), count(0) {  
          }  
          
          /**  
           * Returns TRUE when either the passed atom is different to the atom  
           * passed to the previous call to {@check();} or when the dirtycount  
           * for the specified dirtybit has increased.  
           */  
          Bool Check(C4DAtom* newAtom) {  
              if (!newAtom) {  
                  if (atom) {  
                      atom = newAtom;  
                      return TRUE;  
                  }  
              }  
              else if (newAtom != atom) {  
                  count = newAtom->GetDirty(bit);  
                  atom = newAtom;  
                  return TRUE;  
              }  
              else {  
                  LONG newCount = newAtom->GetDirty(bit);  
                  if (newCount > count) {  
                      count = newCount;  
                      return TRUE;  
                  }  
              }  
              return FALSE;  
          }  
          
          /**  
           * Change the dirtybit to the passed value. The stored dirtycount  
           * is reset.  
           */  
          void SetBit(DIRTYFLAGS newBit) {  
              count = 0;  
              bit = newBit;  
          }  
          
        };
        

        Best,
        -Niklas

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