Dirty Handling

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);  

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.  
   * The last dirtycount for the specified dirtybit.  
  LONG count;  
  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;