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

    Detecting a Change in Position/Rotation Keyframes from Tag

    Cinema 4D SDK
    windows python s24
    2
    5
    619
    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.
    • ?
      A Former User
      last edited by A Former User

      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.

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @A Former User
        last edited by ferdinand

        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 the NodeData message stream, .e.g., the message() 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 your NodeData implementation and not some module. In a NodeData environment you would also have to make sure that MATRIX_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,
        Ferdinand

        The file: move_cube.c4d
        The result: cubeMove.gif
        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.")
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • ?
          A Former User
          last edited by

          @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?

          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @A Former User
            last edited by ferdinand

            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 and CCurve (and also CKey) are mutable. You must make sure to copy the data, by for example using C4DAtom.GetClone(). Comparing then between your cache and some other CCurve or CTrack, 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 that c4dpy has sometimes the weird behavior that it wraps (undocumented) equality comparisons for some types but then factually carries out identity comparisons. So, while myTrackCache == 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 as myTrackCache is someOtherTrack in Python.

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • ?
              A Former User
              last edited by

              @ferdinand Thank you. I'll try to cache myself.

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