Store a keyframe as a variable?
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.
- The fundamental types for animation in c4d are
.CKeyyou should have a look at them. You can access all animated properties of an object, i.e. their
- 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())
- The fundamental types for animation in c4d are
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:
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"?
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
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
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
BaseTimeof the last keyframe of every animation in the target object to the
BaseTimeof 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
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.
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.
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.
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)
I haven't explored it deeper but it works as expected.
I just changed a line from
because it was causing an error
Here is the result of your script:
Here is the file if anyone wants to test it out
Thanks again. Have a great day ahead! :)
I just changed a line from
op[c4d.ID_USERDATA, 1] to target[c4d.ID_USERDATA, 1]
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
Interesting. Thanks for the further clarification!