Detecting a Change in Position/Rotation Keyframes from Tag
-
Hello,
How can I create a function in a tag that only executes when there has been a change to its object's position or rotation? Is there an event I can listen for?This is what I'm trying now and it doesn't work.
def Message(self, tag, type, data): obj = tag.GetObject() if obj: if obj.IsDirty(c4d.DIRTYFLAGS_MATRIX): # code goes here return True
Thank you.
-
Hello @blastframe,
thank you for reaching out to us. No, there is no specific message for an object being moved. The closest message is
EVMSG_CHANGE
, but that is a core message which is not being broadcasted to theNodeData
message stream, .e.g., themessage()
function of a scripting object. One could also (ab)use some node messages in this scenario of yours, but it would not be my first choice.I would simply store the data in the script module or use Cinema's dirty flags. For details see example below. I didn't bother with hitting your extremely specific requirements of reacting to translation and rotation transforms, but not scaling transforms, and instead just compare against the plain matrix. If you wanted to do this, you would have to compare against the normalized frame components of the matrix (v1, v2, v3) and its offset.
Edit: Eh, mistook this for a scripting object question. The answer remains the same, it is only that you then probably want to attach
MATRIX_CACHE
to yourNodeData
implementation and not some module. In aNodeData
environment you would also have to make sure thatMATRIX_CACHE
is flushed with frame zero if you want it to be flushed and as it can be done by a Python Programming Tag.Cheers,
FerdinandThe file: move_cube.c4d
The result:
The code:"""Example for storing data in the module of a scripting object. This approach can be sometimes not be sufficient, then one can do more or less the same by storing data in the BaseContainer of an object. For that one will need a Plugin ID to store that data collision free. One could also take a more message oriented approach, but it seems a bit over the top here. The strategy would be there to check for IsDirty(DIRTYFLAGS_MATRIX). As discussed in: https://developers.maxon.net/forum/topic/13509/ """ import c4d MATRIX_CACHE = None def main(): """ """ # Make the module attribute storing the last seen matrix locally visible, # get the object attached to the tag and the global matrix of the that # object. global MATRIX_CACHE obj = op.GetObject() mg = obj.GetMg() # Compare them and if they are not equal, then do stuff. if mg != MATRIX_CACHE: MATRIX_CACHE = mg print (f"Global matrix of {obj.GetName()} has changed to: {mg}") # We can also more or less do the same with DIRTYFLAGS_MATRIX. The problem # with this approach is, that dirty flags can be consumed before one had # a chance to react to them. It is more safe to use dirty counts, but # there one will need some form of cache/look-up value again, since the # dirty count only stores the total number of changes, not their purpose. if obj.IsDirty(c4d.DIRTYFLAGS_MATRIX): print (f"{obj.GetName()} is DIRTYFLAGS_MATRIX dirty.")
-
@ferdinand Thank you for the reply. I mentioned in the title, but (my apologies), I didn't specify in the body of my topic or code example: Is it possible to detect when there a new keyframe has been added for transformation from a tag plugin?
-
Hello @blastframe,
that is only possible through caching it yourself, like I have shown it more generally above. There is
c4d.C4DAtom.GetHDirty(c4d.HDIRTYFLAGS_ANIMATION)
, but it won't tell you what increased that dirty count. But if I would implement this, I still might use this to throttle a bit the overhead of always building and checking my cache.Noteworthy seems also, that you cannot just reference the track or curve of the position or scale animation of your object in question, because both
CTrack
andCCurve
(and alsoCKey
) are mutable. You must make sure to copy the data, by for example usingC4DAtom.GetClone()
. Comparing then between your cache and some otherCCurve
orCTrack
, will then of course be a bit more manual since both classes can only compare by identity and not by equality. Please also make sure that you keep in mind thatc4dpy
has sometimes the weird behavior that it wraps (undocumented) equality comparisons for some types but then factually carries out identity comparisons. So, whilemyTrackCache == someOtherTrack
might run without an error (haven't tried myself), it will likely not do what one would expect it do, i.e., compare for equality, but instead compare of identity, what one would usually express asmyTrackCache is someOtherTrack
in Python.Cheers,
Ferdinand -
@ferdinand Thank you. I'll try to cache myself.