Difficulties to dedect the right Message Data
-
Hi ,
I have a small Object Plugin which is reading data from a Measure&Contruct Object.
So a small example:
The plugin has a Baselist Link where you can insert a Measure&Construct-Object.
And it is reading the positions of point1, point2 and point3 from this object.Then internally it creates splines and objects depending on this positions and so forth.
I set the cache optimization to True with self.SetOptimizeCache(True) in my init() method.
When the user now changes the measurement and distances of the Measure Object. The plugin instance does not update. Obvious because it is not part of the description.Question:
How can I dedect or catch the right Message Data from the Measure Object to force my plugin to react on changes and to update?So first I tried to catch a global message from the viewport which i readed out with print() and wrote the last points-position to a member-variable and if the user changed the distance of the Measure-Object he catched a message and compares the actual positions with the previos positions and if they differ I sent a node.Message(c4d.MSG_CHANGE). And wright the new position to the variable.
But this just worked for origin point of the Measure Object but not for point2 and point3.Otherwise the user has to press "A" Key on the Keyboard to refresh the Editor View or I implement a Refresh Button.
But it's tiresome for users to keep hitting refresh or A all the time.Thanks and best regards
Thomas -
Hey @ThomasB,
Thank you for reaching out to us. I have split your question into two sections:
Detecting Change Counts
One part of your question is how to determine how often a scene element has changed. Change counters, or how Cinema 4D calls them, dirty states, are built into the base class of all scene elements,
c4d.C4DAtom
. So, when you have a nodemyNode
(aBaseObject
,BaseShader
, etc.), you would retrieve its current data container change count withcnt: int = myNode.GetDirty(c4d. DIRTYFLAGS_DATA)
.cnt
would denote how often the data container ofmyNode
has changed since it has been instantiated, i.e., or how often the parameter values ofmyNode
changed. There are also other categories of dirtiness, I would recommend having a look at the docs.Detecting Change Events
The other part of your question is to detect when a change event occurred. And while core messages can be a solution (by manually making objects dirty which should update), we want to avoid using such an overly complex solution when we can avoid that [1].
First, you should turn off
SetOptimizeCache
as it is just Python-exclusive convenance method which is targeted at beginners. Cinema 4D will now call yourGetVirtualObjects
quite often, and your plugin "will work" but you are also calculating the cache of your plugin more often than necessary. Therounded_tube
example highlights the basic implementation of determining the dirtiness of your node yourself:def GetVirtualObjects(self, op: c4d.BaseObject, hh: "HierarchyHelp") -> c4d.BaseObject: """ """ # Determine if #op, the node that is representing this plugin hook instance, is dirty, # i.e., has changed and therefore must be rebuilt. dirty: bool = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_DATA) if not dirty: return op.GetCache(hh) # The node is not dirty we simply return its cache. # Compute and return a new cache. return c4d.BaseObject(c4d.Ocube)
Usually, doing this will be enough, as Cinema 4D will call
GetVirtualObjects
quite often, but you can get event starved. For example, Cinema 4D will not try to update the cache of your object when the user pans with the camera around it. Modifying aBasaeList2D
which has been linked in the description of your object will invoke a cache building attempt out of the box.Your Concrete Case
You did not provide any code here, so I can only speculate, but in essence, it will look something like this:
import c4d class MyPlugin (c4d.plugins.ObjectData): def __init__(self) -> None: """ """ # We initialize two fields to track the linked node via its UUID and the last known (data) # dirty count of that linked node. We could also overwrite SetDParameter to track changes # to our link parameter, then we would not have to track the UUID. self._linkDirtyCountCache: int = c4d.NOTOK self._linkUuidCache: bytes | None = None super().__init__() def GetVirtualObjects(self, op: c4d.BaseObject, hh: "HierarchyHelp") -> c4d.BaseObject: """ """ # Try to get the linked object or bail when there is None. link: c4d.BaseList2D | None = op.GetParameter(c4d.DescID(c4d.ID_MYPLUGIN_LINK), c4d.DESCFLAGS_GET_NONE) if link is None: return c4d.BaseObject(c4d.Onull) # Determine if the node itself is dirty. This will probably already suffice alone, because a # link field should make its host data dirty once the linked node changed. selfDirty: bool = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_DATA) # Or we compute the dirtiness of #link ourselves. There are two ways how we can do this, for # once also with IsDirty (which can fail when there are also other interacting entities) linkDirty: bool = link.IsDirty(c4d.DIRTYFLAGS_DATA) # ... or by manually managing the dirty counter of the linked object. dirtyCnt: int = link.GetDirty(c4d.DIRTYFLAGS_DATA) uuid: bytes = bytes(link.FindUniqueID(c4d.MAXON_CREATOR_ID)) # First check if the linked object is still the same as the last time we built the cache. if uuid != self._linkUuidCache: linkDirty = True self._linkUuidCache = uuid self._linkDirtyCache = dirtyCnt # Now compare the dirty count ... elif dirtyCnt > self._linkDirtyCountCache: linkDirty = True self._linkDirtyCache = dirtyCnt # Make use of the computed dirty states. if not any((selfDirty, linkDirty)): return op.GetCache(hh) # Compute and return a new cache. return c4d.BaseObject(c4d.Ocube)
Cheers,
Ferdinand[1] The node message stream you were trying to use is useless in this case because node messages are sealed, i.e., you cannot peek into the node message stream of your measure object.
-
@ferdinand
Many thanks for this detailed example, I'll try it outP.S.
Oh my gosh, it's working perfectly. I decided to go the easiest way, just to check the dirtyness of the linked object.linkDirty: bool = link.IsDirty(c4d.DIRTYFLAGS_DATA)
Many many thanks
Thomas
-
-