View Python: DescIDs and Animation
The trickiest part of dealing with animation tracks in Python is the concept of DescIDs.
DescIDs are actually used throughout CINEMA 4D, but they’re especially important when working with animation tracks. A DescID is a multi-level ID structure for individual description elements. The DescID can define multiple levels of data, like with UserData, where the first element of the DescID is always ID_USERDATA and the second element is the index for each userdata element. The multiple levels of a DescID are also used for subchannels, like the individual vector elements of a Position, Scale, Rotation or Color element.
Each element of a DescID is a DescLevel. The DescLevel also has three elements - in this case, they are the id itself, the data type and the creator.
Let’s look at some examples:
Creating tracks for simple description elements
If a description element doesn’t have sub-channels, the DescID is simply the ID of the description element.
# Track for Object Enabled Boolean enabled = c4d.CTrack(op, c4d.DescID(c4d.ID_BASEOBJECT_GENERATOR_FLAG)) op.InsertTrackSorted(enabled) # Track for Light Brightness Real track = c4d.CTrack(op,c4d.DescID(c4d.LIGHT_BRIGHTNESS)) op.InsertTrackSorted(track)
Creating Position X, Y, Z tracks
Note that each vector element has its own track, just like in C4D’s timeline. The DescID for each contains two levels. Level 1 in each case is the Position track itself, which has a VECTOR type. This is the parent Position element you see in the timeline. Level 2 is the vector element for this specific track, and has a REAL type.
trackX = c4d.CTrack(op, c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION,c4d.DTYPE_VECTOR,0), c4d.DescLevel(c4d.VECTOR_X,c4d.DTYPE_REAL,0))) trackY = c4d.CTrack(op, c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION,c4d.DTYPE_VECTOR,0), c4d.DescLevel(c4d.VECTOR_Y,c4d.DTYPE_REAL,0))) trackZ = c4d.CTrack(op, c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION,c4d.DTYPE_VECTOR,0), c4d.DescLevel(c4d.VECTOR_Z,c4d.DTYPE_REAL,0))) op.InsertTrackSorted(trackX) op.InsertTrackSorted(trackY) op.InsertTrackSorted(trackZ)
Creating tracks for userdata
When you create userdata or iterate through the userdata container, the ID you get is the DescID. However, for elements with subchannels like vectors or color, you still have to add individual tracks for each subchannel. This code sample elaborates on DescIDs and DescLevels through some print statements and also contains a custom function that creates the appropriate tracks for certain userdata types. Not all possible types are considered - it’s just a start.
import c4d def CreateUDTrack(op,id): tracks = [] # element0 is always UD group # element1 is the UD itself ud = id[1] dtype = ud.dtype if dtype == c4d.DTYPE_VECTOR or dtype == c4d.DTYPE_COLOR: # get datatypes with subchannels and add multiple tracks for v in xrange(c4d.VECTOR_X, c4d.VECTOR_Z+1): descID = c4d.DescID(id[0],id[1],c4d.DescLevel(v,c4d.DTYPE_REAL)) tracks.append(c4d.CTrack(op,descID)) else: # just add the single track tracks.append(c4d.CTrack(op,id)) return tracks def main(): for id, bc in op.GetUserDataContainer(): # id is the DescID, bc is the container print bc[c4d.DESC_NAME], id # look at each DescLevel # this isn't necessary, just instructive for level in xrange(id.GetDepth()): print "Level ", level, ": ", \ id[level].id, ",", \ id[level].dtype, ",", \ id[level].creator # Create tracks using custom function tracks = CreateUDTrack(op,id) # Loop through returned tracks and insert for track in tracks: op.InsertTrackSorted(track) if __name__=='__main__': main()
Finding a Track
In Release 13 and greater, you can use FindCTrack to find an existing animation track for a particular DescID. It’s a good idea to do this if you’re not sure if the track exists. Otherwise, C4D will just create additional tracks for the same parameter.
# Track for Light Brightness Real dBrightness = c4d.DescID(c4d.LIGHT_BRIGHTNESS) #assign the DescID to a var for convenience tBrightness = op.FindCTrack(dBrightness) #find the track if not tBrightness: #if track isn't found, create it tBrightness = c4d.CTrack(op,dBrightness) op.InsertTrackSorted(tBrightness)
Adding Keys
Once you have the necessary tracks, adding keys is relatively simple. All the keys are applied to an F-Curve, which is a CCurve object in Python. So first you have to get the CCurve, and then you can use AddKey or InsertKey to add keys to the curve. Note that you can use the CTrack method FillKey to fill a key with the default values for the track, setting the appropriate interpolation. Also, you should use SetValue when setting a float value, and SetGeData when setting any other data type.
#Get the curve curve = track.GetCurve() #Add keys using AddKey #Creates the key at the proper time #Then you modify the key keyDict = curve.AddKey(c4d.BaseTime(0)) #keyDict is a dict with key=CKey and int=index myKey = keyDict["key"] #Use FillKey to fill the key with default values (interpolation) trk.FillKey(doc,op,myKey) #Use SetValue or SetGeData to set the key value myKey.SetValue(curve,1) #myKey.SetGeData(curve,1) #Add keys using InsertKey #Define the key first key = c4d.CKey() #Use FillKey to fill the key with default values (interpolation) trk.FillKey(doc,op,key) key.SetTime(curve,c4d.BaseTime(1)) key.SetValue(curve,.5) #key.SetGeData(curve,1) #Then insert it curve.InsertKey(key)
Setting the Keyframe Selection
A similar task also involving DescIDs is setting the Keyframe Selection for an Object. You can use this in conjunction with the record button to create tracks, but the direct method specified above is more robust. You might however wish to enable keyframe selection on specific description IDs if you’re generating a rig or other object in Python which the user will be animating.
# Put the "Position" desclevel in a variable for convenience pos = c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DA_VECTOR) # Set Keyframe selection on each vector X,Y,Z op.SetKeyframeSelection(c4d.DescID(pos,c4d.DescLevel(c4d.VECTOR_X, c4d.DA_REAL, c4d.DA_VECTOR)), True) op.SetKeyframeSelection(c4d.DescID(pos,c4d.DescLevel(c4d.VECTOR_Y, c4d.DA_REAL, c4d.DA_VECTOR)), True) op.SetKeyframeSelection(c4d.DescID(pos,c4d.DescLevel(c4d.VECTOR_Z, c4d.DA_REAL, c4d.DA_VECTOR)), True) # Update the C4D Interface c4d.EventAdd()