ObjectData plugin, parent change in OM
-
On 27/01/2016 at 09:49, xxxxxxxx wrote:
Hi,
could somebody explain what a GUID is? I looked it up in the docs and it seems to be a unique ID working also for cache objects. Fair enough, but I would assume then it is always the same ID (for a normal object) no?
If not (and it changes) on what is this change based?Also the docs say that if there is no marker (for 'real' objects) it won't return a GUID but it doesn't say what is returned instead.
Would definetly like to know more about this. Haven't heard of it before.
Thanks in advance.Concerning the topic, have you tried using GetHDirty() instead of GetDirty()? I remember it handles more cases somehow (though I don't really know what the exact difference is I kind of created the rule that I use GetHDirty() if GetDirty() doesn't work).
-
On 28/01/2016 at 00:45, xxxxxxxx wrote:
EDIT: hit post by accident without typing anything.
I just tested GetHDirty and HDIRTY_OBJECT_HIERARCHY only increments if that object's children is changed. It does nothing when its parents change, which I needed.
As for GUID, I'm not 100% sure myself how it differs with the other means of identifying objects in Cinema (GUID, UniqueIP, Link, GeMarker, etc.) There's a lot of info in the documents, but I haven't found examples that make sense to me.
-
On 28/01/2016 at 02:08, xxxxxxxx wrote:
Hello,
the GUID returned by GetGUID() is a checksum based on several properties. In case of a object in a generator's cache it is based on the UniqueIP set by the generator. In case of non-cache objects it is based on the marker returned by GetMarker(). For polygon and point objects the polygon and point count is added to the calculation of the checksum.
Best wishes,
Sebastian -
On 28/01/2016 at 04:50, xxxxxxxx wrote:
Thanks Sebastian. So I assume if no Marker is available on a non-cache object it will return -1 (NOTOK)?
-
On 28/01/2016 at 04:52, xxxxxxxx wrote:
Originally posted by xxxxxxxx
I just tested GetHDirty and HDIRTY_OBJECT_HIERARCHY only increments if that object's children is changed. It does nothing when its parents change, which I needed.
Ah ok, thanks for the info. By change you mean the position in the OM? Or the data/matrix of parent objects?
-
On 28/01/2016 at 05:15, xxxxxxxx wrote:
The OM position.
There are some modes where I don't need to check the parent's matrix/data, e.g. I just need to generate geo in local space, so I don't need to update my cache when the parent matrix changes. -
On 28/01/2016 at 08:55, xxxxxxxx wrote:
Hello,
as said, the GUID is a checksum. If no marker is presented, the marker simply does not influence the checksum calculation but other factors may. So if no marker is present the return value does not need to be -1.
best wishes,
Sebastian -
On 28/01/2016 at 09:45, xxxxxxxx wrote:
Sorry but I am still confused because the docs say that "if an object has no marker it will not return a GUID". So no GUID is still a checksum? Simply confuses me (this sentence in the docs. I do know now that it's a checksum but the question still arises when reading that sentence because it says the id is based on the marker and so I would logically assume that the id is NOT "filled" validly if there is no marker = no GUID returned).
-
On 28/01/2016 at 09:49, xxxxxxxx wrote:
Originally posted by xxxxxxxx
The OM position.
There are some modes where I don't need to check the parent's matrix/data, e.g. I just need to generate geo in local space, so I don't need to update my cache when the parent matrix changes.Ah I see. Okay, thanks. Good to have this in mind if I ever come across this necessity.
-
On 29/01/2016 at 01:38, xxxxxxxx wrote:
Hello,
I think what the documentation is trying to say is that without a marker the returned number may not be unique. But the function will still return some number.
Best wishes,
Sebastian -
On 29/01/2016 at 04:55, xxxxxxxx wrote:
Ok, it would be great if the documentation would indeed say that. An update request this is then.
Cheers
-
On 13/02/2016 at 04:58, xxxxxxxx wrote:
Alrighty, here's the current code I have for handling my plugin's dirty checksum. It's much longer than I expected as there's some weird quirks with where and when the checksums actually increment.
- Deformers and Effectors do not affect the dirty flags on an object; they affect the dirty checksums of a cached instance of the object.
I need to get the modified objects via GetCache() or GetDeformCache() and check that returned object's dirty checksum. If the object creates a virtual hierarchy (Cloner, SubD, etc.) I have to do a recursive function to check the dirty checksums of each element.
The VirtualDirtySum() function below does this.- Switching an object's parent in the OM does not increment dirty checksums. Moving an object's parent's matrix does not increment it either; DIRTY_MATRIX only increments changes with the local matrix.
To check for changes in the parent's hierarchy + matrixes I need to do a recursion upwards to get their dirty matrix checksums, as well as the GUID of their parents.- My plugin can also read thinking particles from TP_PGroup. The whole TP system increments with every frame change, so to reduce dirty calls I only check for the particle system's dirty checksum if the group contains particles. If the group is empty, I skip the check.
- Right now I'm using a BaseArray to store the checksums. I thought of just adding everything into one UInt64 variable, but I fear there might be some rare cases where I couldn't detect changes (e.g. checksums of the inputs increment but a generator object has fewer objects in the next frame, so the total checksum is the same.)
I'll do a summation instead of an array if I can do enough tests to show it's safe.- Render Instances and the Sphere object's Render Perfect mode do not return any cached geo, so
several of my checks are invalid for those cases. I fear that if I override those objects to generate geo during render time, it could cause unexpected changes in how the scene looks. For now I'll just ignore them as special cases; if a user wants to use those objects as input for my plugin, they'll have to turn off instancing.- There might still be some scenarios that I missed. I'll post updates if I find any more problems.
#include "c4d.h" #include "c4d_particles.h" _// TP system_ #include "customgui_inexclude.h" _// in/exclude methods_ ** _//---------------------------------------------------------------------------------------- ///Gets TP Master System for the document. /// @param[in] doc : BaseDocument the object is in /// @return : TP_MasterSystem of the document /// @note : Only one instance should exist for a document //----------------------------------------------------------------------------------------_ TP_MasterSystem* GetTpMasterSystem ( const BaseDocument* doc )** { if ( !doc ) { return nullptr; } BaseSceneHook* hook = doc->FindSceneHook( ID_THINKINGPARTICLES ); if ( !hook || hook->GetType() != ID_THINKINGPARTICLES ) { return nullptr; } return static_cast< TP_MasterSystem* >( hook ); } ** _//---------------------------------------------------------------------------------------- ///Loops through input object's virtual hierarchy, gets total dirty checksum of its elements /// @param[out] dirtySum : dirty checksum. make sure it's initialized before calling /// @param[in] obj : input object to get virtual hierarchy from /// @param[in] INEX_FLAGS : inex flags tied to object /// @param[in] ISTIP : flag if object is tip of virtual hierarchy's branch /// @param[in] ISCACHE : flag if recursion occurs /// @return : none //----------------------------------------------------------------------------------------_ void VirtualDirtySum( UInt64& dirtySum, BaseObject* obj, const Int32& INEX_FLAGS, Bool ISTIP = false, const Bool ISCACHE = false )** { BaseObject* cachedObj = obj->GetDeformCache(); //------------------------------------------------------------------------------------------------------------------------------------------------------------ if ( !ISCACHE ) { _// item passes first time, pre-emptively check if it has virtual children_ BaseObject *testParent = cachedObj; if ( !testParent ) { _// no deform cache? check for regular cache_ testParent = obj->GetCache(); } if ( testParent ) { _// deform cache or regular cache? check if cache has children_ ISTIP = ( testParent->GetDown() == nullptr ); } else { _// no cache at all? object force tip status ISTIP = true;_ } } //------------------------------------------------------------------------------------------------------------------------------------------------------------ if ( cachedObj ) { _// deformed cache exists? apply recursion_ VirtualDirtySum( dirtySum, cachedObj, INEX_FLAGS, ISTIP, true ); } else { cachedObj = obj->GetCache(); if ( cachedObj ) { VirtualDirtySum( dirtySum, cachedObj, INEX_FLAGS, ISTIP, true ); _// regular cache exists? apply recursion_ } else { _// not cache anymore? get dirty sum_ if ( ISTIP || INEX_FLAGS & UT_READ_FULL_CHAIN ) { _// if flagged, check if object is tip of its hierarchy. skip if it's not._ if ( !obj->GetBit( BIT_CONTROLOBJECT ) ) { _// if under generator, check if non-input. skip if used._ dirtySum += obj->GetDirty( DIRTYFLAGS_ALL ); } } } } //------------------------------------------------------------------------------------------------------------------------------------------------------------ if ( ISCACHE ) { _// object is in virtual hierarchy? apply recursion_ for ( cachedObj = obj->GetDown(); cachedObj; cachedObj = cachedObj->GetNext() ) { _// get next object in virtual hierarchy_ ISTIP = ( cachedObj->GetDown() == nullptr ); VirtualDirtySum( dirtySum, cachedObj, INEX_FLAGS, ISTIP, true ); } } } ** _//---------------------------------------------------------------------------------------- ///Iterates through inexclude list of input objects. ///First entry of output array is the plugin object. ///Successive array entries are from the input inexclude list. /// @param[out] inexDirtySum_old : array containing dirty checksums of input objects. Global variable from class /// @param[in] op : object generated by plugin /// @param[in] doc : document the plugin is in /// @return : true if checksum changed. false if remains the same //----------------------------------------------------------------------------------------_ Bool IsInputDirty ( maxon::BaseArray< UInt64 >& inexDirtySum_old, BaseObject* op, BaseDocument* doc )** { Bool isInputDirty = false; TP_MasterSystem* tpMasterSystem = GetTpMasterSystem( doc ); const InExcludeData* inex = ( InExcludeData* )op->GetDataInstance()->GetCustomDataType( INEX_INPUT, CUSTOMDATATYPE_INEXCLUDE_LIST ); const Int32 inexCt = inex->GetObjectCount(); BaseList2D* inexObj; maxon::BaseArray< UInt64 > inexDirtySum; inexDirtySum.Reset(); UInt64 dirtySumTemp = 0, parentId = 0; BaseObject* parent, * child; //------------------------------------------------------------------------------------------------------------------------------------------------------------ if ( op->GetDataInstance()->GetBool( USE_GLOBAL, false ) ) { _// if plugin object set to global mode, matrixes are checked_ parent = op->GetUp(); if ( parent ) { parentId = parent->GetGUID(); } inexDirtySum.Append( op->GetDirty( DIRTYFLAGS_MATRIX ) + parentId ); _// plugin object's dirty matrix checksum plus parent's GUID_ for ( child = op->GetUp(); child; child = child->GetUp() ) { _// checksums of object's parent chain_ parent = child->GetUp(); if ( parent ) { parentId = parent->GetGUID(); } else { parentId = 0; } inexDirtySum.Append( child->GetDirty( DIRTYFLAGS_MATRIX ) + parentId ); _// for each parent up the chain, get dirty matrix flags_ } } else { _// if plugin set to local mode, only parent switching is checked_ parent = op->GetUp(); while ( parent ) { _// recurse for parent's GUID_ inexDirtySum.Append( parent->GetGUID() ); parent = parent->GetUp(); } } //------------------------------------------------------------------------------------------------------------------------------------------------------------ for ( Int32 i = 0; i < inexCt; ++i ) { inexObj = inex->ObjectFromIndex( doc, i ); if ( inexObj ) { if ( inexObj->GetType() == ID_TP_PGROUP ) { _//input object is tp_pGroup_ if ( tpMasterSystem ) { if ( tpMasterSystem->GetGroupParticleCount( static_cast< TP_PGroup* >( inexObj ), 0 ) ) { _// tp_pGroup has particles? read tpMasterSystem's dirty checksum_ inexDirtySum.Append( tpMasterSystem->GetDirty() + inexObj->GetDirty( DIRTYFLAGS_ALL ) ); } else { inexDirtySum.Append( inexObj->GetDirty( DIRTYFLAGS_ALL ) ); _// no particles? just get flags from tp_pGroup_ } } } else { BaseObject* obj = static_cast< BaseObject* >( inexObj ); _// input object is baseobject_ BaseObject* parent, * child; UInt64 parentId = 0; UInt64 dirtySumTemp = 0; parent = obj->GetUp(); if ( parent ) { parentId = parent->GetGUID(); } VirtualDirtySum( dirtySumTemp, obj, inex->GetFlags( i ) ); _// dirty checksum of object's virtual hierarchy_ inexDirtySum.Append( obj->GetDirty( DIRTYFLAGS_ALL ) + dirtySumTemp + parentId ); _// add parent's GUID, in case parent is changed_ for ( child = obj->GetUp(); child; child = child->GetUp() ) { _// checksums of object's parent chain_ parent = child->GetUp(); if ( parent ) { parentId = parent->GetGUID(); } else { parentId = 0; } inexDirtySum.Append( child->GetDirty( DIRTYFLAGS_MATRIX ) + parentId ); _// for each parent up the chain, get dirty matrix flags_ } } } } //------------------------------------------------------------------------------------------------------------------------------------------------------------ if ( inexDirtySum.GetCount() == inexDirtySum_old.GetCount() ) { _// item count the same, check for checksum mismatches_ for ( Int32 i = 0; i < inexDirtySum.GetCount(); ++i ) { if ( inexDirtySum[i] != inexDirtySum_old[i] ) { isInputDirty = true; break; } } } else { _// item count different, automatically assume dirty_ isInputDirty = true; } //------------------------------------------------------------------------------------------------------------------------------------------------------------ if ( isInputDirty ) { _// transfer array_ inexDirtySum_old.Reset(); inexDirtySum_old.CopyFrom( inexDirtySum ); } inexDirtySum.Reset(); //------------------------------------------------------------------------------------------------------------------------------------------------------------ _// colorizes object in viewport when dirty checksum changes. remove after all testing is okay._ ObjectColorProperties prop; op->GetColorProperties( &prop ); if ( isInputDirty ) { prop.color = Vector( 0.9, 0.2, 0.1 ); } else { prop.color = Vector( 1.0 ); } if ( prop.usecolor != ID_BASEOBJECT_USECOLOR_ALWAYS ) { prop.usecolor = ID_BASEOBJECT_USECOLOR_ALWAYS; } op->SetColorProperties( &prop ); //------------------------------------------------------------------------------------------------------------------------------------------------------------ return isInputDirty; } ** _//---------------------------------------------------------------------------------------- ///virtual Member from ObjectData class /// @param[in] op : object generated by plugin /// @param[in] doc : document the plugin is in /// @return : none /// @notes : _inexDirtySum_old is a private global variable. stores array of dirty checksums //----------------------------------------------------------------------------------------_ void UberTracer02::CheckDirty ( BaseObject* op, BaseDocument* doc )** { if ( IsInputDirty( _inexDirtySum_old, op, doc ) ) { _// check input inex list for dirty checksum, plus plugin object itself_ op->SetDirty( DIRTYFLAGS_DATA ); } }