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

    Store a keyframe as a variable?

    Cinema 4D SDK
    r21 python
    3
    10
    1.2k
    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.
    • B
      bentraje
      last edited by

      Hi,

      Is there a way to store a keyframe as a variable for future usage?

      Here's my problem:
      I have an animation and two cameras. The second camera cuts at Frame 10.
      However, if I changed the duration of the animation to Frame 11. The second camera will be out of sync. I want the second camera to "anchor" to the original Frame 10, which is now Frame 11.

      I was thinking of something this logic. Don't mind the variables as I don't know how yet to key.

      cut_key = timeline.getkey(frameNumber10)
      # Change animation 
      current_frame = cut_key.fetchCurrentFrame()
      camera_cut.set_key(current_frame) 
      
      #
      Or something like that
      

      Is this possible? Or are there any other alternatives to this?

      Thank you for looking at my problem.

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        Hi,

        1. The fundamental types for animation in c4d are c4d.CTrack, .CCurve, and .CKey you should have a look at them. You can access all animated properties of an object, i.e. their CTracks, via BaseList2D.GetCTracks().
        2. I don't really understand your question, but note that animations are interpolations, so when you change your animation midway (you talked about current_frame), the results might not what you expect them to be.

        Here is a short snippet, that shows you in principle how to deal with animations. This is sort of pseudo code, but it should show everything you need:

        # the postion parameter description id, i.e. the animation type we
        # are going to look for in our source object.
        target_description = c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, 
                                           c4d.DTYPE_VECTOR, 0)
        
        # get the position animation of object a
        source_ctrack = my_baselist2d_a.FindCTrack(target_description)
        # No position track found
        if source_ctrack is None:
            return
            
        # get the ccurve of our source ctrack
        source_curve = source_ctrack.GetCurve()
        # get the index of the last keyframe
        lid = source_curve.GetKeyCount() - 1
        # no keyframes
        if lid < 0:
            return
        # get the last CKey 
        source_last_key = source_curve.GetKey(lid)
            
        # get all animations of object b
        target_ctracks = my_baselist2d_b.GetCtracks()
        
        # loop over all tracks in our target
        for ctrack in target_ctracks:
            # same thing as above, just shorter
            lid = ctrack.GetCurve().GetKeyCount() - 1
            if lid < 0:
                continue
            last_key = ctrack.GetCurve().GetKey(lid)
            # set the basetime of the last keyframe in the one of the tracks in
            # our target object to the basetime of the last keyframe in our source
            # objects position animation.
            last_key.SetTime(curve, source_last_key.GetTime())
        

        Cheers
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 2
        • r_giganteR
          r_gigante
          last edited by

          Hi bentraje thanks for reaching out us.

          With regard to your request, first of all I'd like to point you to the different manuals related to the topics @zipit has mentioned:

          • CKey Manual
          • CTrack Manual
          • CCurve Manual

          On top of this, maybe it's me being dumb, can you elaborate a little bit more on:

          • why you state you have two cameras? what the first camera does?
          • what do you mean by "out of sync"?
          • what do you mean by "anchor to original frame 10"?

          Best, Riccardo

          1 Reply Last reply Reply Quote 1
          • B
            bentraje
            last edited by

            Hi @zipit

            Thanks for the pointing out the relevant API
            I just revise some lines to work:

            target_description = c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0) )

            target_ctracks = objB.GetCTracks()

            Unfortunately, the following API does not let me "anchor" the keyframe to another. Correct me if I'm wrong

            =======================================================================================

            @r_gigante

            RE: why you state you have two cameras? what the first camera does?

            Camera 1 is for CharA and Camera 2 is for CharB
            When CharB talks, I need the Camera 2 to be activated. Hence, I want to anchor it to the first keyframe when CharB talks

            RE: what do you mean by "out of sync"?

            When I extend CharA scene from frame 10 to frame 11 I need the Camera 2 to move from frame 10 to frame 11, hence "anchor" it (no manual intervention

            RE: what do you mean by "anchor to original frame 10"?
            See answer above.

            Is there a way around this? Thank you

            ferdinandF 1 Reply Last reply Reply Quote 0
            • ferdinandF
              ferdinand @bentraje
              last edited by ferdinand

              @bentraje

              Hi,

              like I hinted at in my first posting and @r_gigante also seems to struggle with, your explanation of what you are trying to do is not unambiguous enough to tell what you are exactly trying to do.

              Especially the part about anchoring remains ambiguous to me even after your additional explanations. If you mean by that, that you want to set time of the start or end of an animation (i.e. a keyframe in that animation) to a given point in time (for example the time of another keyframe), my example shows you exactly how to do that (it sets BaseTime of the last keyframe of every animation in the target object to the BaseTime of the last keyframe of the position animation of the source object). Noteworthy in that context is also that Cinema does not handle animations in keyframes internally (since they are interpolation dependent) but in BaseTime.

              To get further help you should describe more precisely what you mean by anchoring and give us some code on how far you got, as this will make it easier to give you concrete code advise.

              Cheers
              zipit

              MAXON SDK Specialist
              developers.maxon.net

              1 Reply Last reply Reply Quote 1
              • B
                bentraje
                last edited by

                Hi @zipit. Apologies for the confusion.

                Here's a better illustration (although in this case instead of extending the frame range. I reduced it). Just think of the images as a keyframe or keyframe range.
                https://www.dropbox.com/s/pfekan0ll1na2qc/c4d144_anchor_ripple_edit.mp4?dl=0

                As you might see in the above workflow, it will allow me to have a faster iteration
                Let me know if you need further details.

                1 Reply Last reply Reply Quote 0
                • ferdinandF
                  ferdinand
                  last edited by ferdinand

                  Hi,

                  no need to apologize, it is hard to describe problems unambiguously. Judging by your video I did understand your question initially correctly. Here is a modified version of the above example that works as a Python Scripting tag. You will find instructions on how to use this in the code below. You will have to extend that code to what you are trying to. This example will only "anchor" the postion animation of one object to another.

                  On a side note: I am certainly no expert for Cinema's out of the box functions anymore, but isn't there already some kind of constraint that does this?

                  import c4d
                  
                  '''
                  This little scripts assumes you to have two objects in your scene. We will call
                  them source object and target object. Both objects are assumed to have a 
                  position animation, where in your terms the animation of target "anchors" to
                  the animation of source, i. e. the animation of our target starts when the 
                  animation of source ends. This script will ensure that this relation remains.
                  
                  You will need:
                      - the two objects with their animations
                      - a Python Scripting Tag where this code goes into, sitting on your target
                       object.
                      - a User Data element in that scripting tag of the type Link with the ID 1,
                       you have to link your source object here
                  '''
                  
                  
                  def main():
                      # Get the target object (op is predefined as the Python Scripting Tag)
                      target = op.GetObject()
                      # Get the source object
                      source = op[c4d.ID_USERDATA, 1]
                      
                      # Abort if any of the objects can not be found
                      if not target or not source:
                          return
                  
                      # the type of animation we want to "anchor"
                      position_desc = c4d.ID_BASEOBJECT_POSITION
                  
                      target_ctrack = target.FindCTrack(position_desc)
                      source_ctrack = source.FindCTrack(position_desc)
                  
                      # Abort if any of the objects has no position animation
                      if target_ctrack is None or source_ctrack is None:
                          return
                  
                      # Get the curves for the ctracks
                      target_ccurve = target_ctrack.GetCurve()
                      source_ccurve = source_ctrack.GetCurve()
                  
                      # get the last key of the source curve
                      lid = source_ccurve.GetKeyCount() - 1
                      source_last_key = source_ccurve.GetKey(lid)
                      # Get the BaseTime of the last key, i.e. our "anchor"
                      source_time = source_last_key.GetTime()
                      # Prints out the frame of our anchor
                      # print source_time.GetFrame(doc.GetFps())
                  
                      # The delta between the last key frame of our source and the first key
                      # frame of our target. We will populate that value in the loop below.
                      dt = None
                  
                      # loop over all keys in our target object
                      for i in range(target_ccurve.GetKeyCount()):
                          # get the current key and its BaseTime
                          current_key = target_ccurve.GetKey(i)
                          current_time = current_key.GetTime()
                          # Prints the frame of the current key
                          # print current_time.GetFrame(doc.GetFps())
                  
                          # Set the delta on the first key
                          if i == 0:
                              # BaseTime has arithmetic operations implemented, this is basically
                              # the same as saying dt = frame target - frame source
                              dt = c4d.BaseTime((current_time - source_time).Get())
                              # When the delta is zero the animations are already "anchored"
                              if dt.Get() == 0.:
                                  # print "Tracks are already aligned."
                                  return
                  
                          # set the shifted time with our delta 
                          current_key.SetTime(target_ccurve, current_time - dt)
                  

                  Cheers
                  zipit

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 1
                  • B
                    bentraje
                    last edited by

                    @zipit

                    I haven't explored it deeper but it works as expected.

                    I just changed a line from
                    op[c4d.ID_USERDATA, 1] to target[c4d.ID_USERDATA, 1]
                    because it was causing an error

                    Here is the result of your script:
                    https://www.dropbox.com/s/6lm7x7jv8tv73b4/c4d144_anchor_ripple_edit_fix.mp4?dl=0

                    Here is the file if anyone wants to test it out
                    https://www.dropbox.com/s/82eamn24x8no63p/c4d144_anchor_ripple_edit_illustration_file.c4d?dl=0

                    Thanks again. Have a great day ahead! 🙂

                    1 Reply Last reply Reply Quote 0
                    • ferdinandF
                      ferdinand
                      last edited by ferdinand

                      @bentraje said in Store a keyframe as a variable?:

                      I just changed a line from
                      op[c4d.ID_USERDATA, 1] to target[c4d.ID_USERDATA, 1]

                      Hi,

                      the reason for that is probably that you have created the user data on your target object and not the python tag on the target object. It does not really matter where you place the user data, but I thought it would be more convenient on the tag, since you then could just copy and paste it around. You could skip setting up he user data interface manually by adding this code (which is kind of hacky, as it uses a message ID against its purpose):

                      def message(mid, data):
                          """
                          """
                          if mid == c4d.MSG_GETREALCAMERADATA and not op.GetUserDataContainer():
                              # Generates the user data source object link on the tag
                              bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
                              bc[c4d.DESC_NAME] = "Source Object"
                              eid = op.AddUserData(bc)
                              c4d.EventAdd()
                      

                      You do not have to use a link either, you could determine the source by positional relation in the scene graph (the next object for example) or by some naming convention. I am also just seeing it just now, but you probably should add the last line of that snippet to handle properly empty tracks in your source:

                      # get the last key of the source curve
                      lid = source_ccurve.GetKeyCount() - 1
                      if lid < 0: return
                      

                      Cheers
                      zipit

                      MAXON SDK Specialist
                      developers.maxon.net

                      1 Reply Last reply Reply Quote 1
                      • B
                        bentraje
                        last edited by

                        Interesting. Thanks for the further clarification!

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