Notifications
Clear all

[Closed] Copy keys of all tracks from one frame to another?

I need to know how to copy the keys on ALL tracks on a particular frame to another frame. Not only the position/rotation/scale, but things like weights on position constraints, values of animated custom attributes, etc. (anything keyable). And I need it to do this for an array of objects.

What is the best approach for this?

4 Replies

…anyone?

what do you mean by copy keys? store values of animatable controllers at some frame?
check the LoadSaveAnimation Interface. using it smart can give you all that you want.

Well, I’m coming at it from a different angle but maybe you (or someone) can help me. This pretty well sums up what I am trying to do, how I am trying to do it, and what I am still having trouble with:

-- Whenever a key of an object in array "c" is copied from one frame to another,
  -- the corresponding key of all other objects in the array are also copied
  
  -- Does not seem to work with:
  --  IK Chain Object enabled states
  --  Target weights in position or rotation constraints
  --  Possibly others
  
  global copyKeyFrame
  fn copyArrayObjKeys c f1 f2 =
  (
  fn copyKeyFrame t f1 f2 =
  (
  if isController t.controller do
  for k = 1 to t.controller.keys.count do
  (
  if t.controller.keys[k] != undefined do
  if t.controller.keys[k].time == f1
  then t.controller.keys[k].selected = true
  else t.controller.keys[k].selected = false
  
  copyPasteKeys t (fn copyKeys f x = x) f2 #replaceKeys
  )
  for i = 1 to t.numSubs do copyKeyFrame t[i] f1 f2
  )
  
  for m in c do copyKeyFrame m f1 f2
  )

I’m guessing you trying to loop through all the controllers in an object and see if they have keyframes. Then copy them to another object.

I seem to remember this being a tricky problem. I have some old code that might help.

It allows you to browse all the controllers in an object using a simple dialog. You could modify it to suit your needs.



-- subAnimbrowser

struct subAnimFinderStruct
(
	subAnimPath = #(),
	ctrl = undefined,
	validControllerList = #(bezier_float,Linear_Float,TCB_float,Expose_Float_Control,Float_Expression,Float_list,Float_motion_capture,Float_reactor,Float_script),
	
	fn TrimSpaces txt = trimleft ( trimRight txt ),
	fn mySubAnim = 
	(
		c = subAnimPath.Count 
		if c >0 then subAnimPath[c] else undefined
	),
	fn setValidControllerList type =
	(
		validControllerList = #(bezier_float,Linear_Float,TCB_float)
		if type = #input then validControllerList += #(Expose_Float_Control,Float_Expression,Float_list,Float_motion_capture,Float_reactor,Float_script)
	),
	fn setCtrl =
	(
		ctrlSubAnim = mySubAnim()
		isCtrlOk = ctrlSubAnim != undefined and isProperty ctrlSubAnim "controller"
		ctrl = if isCtrlOk then ctrlSubAnim.controller else Undefined
		isCtrlOk
	),
	fn setSubAnimPath newSubAnimPath =
	(
		subAnimPath = newSubAnimPath
		setCtrl()
	),
	fn findSubAnimOf theProperty byName: =
	(
		--format  "numsubs: % 
" (theProperty.numSubs)
			
		SubItemList = for i = 1 to theProperty.numSubs collect ( getSubAnim theProperty i)
		caList = for i = 1 to ( custAttributes.count theProperty ) collect ( custAttributes.get theProperty i )

		childPropertyList = SubItemList + caList
		childPropertyNamesList =  for n in childPropertyList collect (n.name as string)
		--print childPropertyNamesList
		i = findItem childPropertyNamesList byName
		if i > 0 then childPropertyList[i] else undefined
	),
	fn SetSubAnimFromCommaDelimitedString theString =
	(
		--print theString
		nameList = filterString theString ",\"#"
		trimName = ""
		setSubAnimPath #()
		nameList = for txt  in nameList where (( trimName = ( trimSpaces txt ) ) != "" ) collect trimName
		if nameList.count > 0 then
		(
			theNode = getNodeByName (nameList [1])
			
			if theNode != undefined then
			(
				theProperty = theNode
				deleteItem nameList 1
				
				newsubAnimPath = for n in nameList while theProperty != undefined collect
				(
					theProperty = findSubAnimOf theProperty byName:n
				)
				setSubAnimPath ( #(theNode) + newsubAnimPath )
				true
			)  else false
		) else false
	),
	fn getSubAnimPathAsString =
	(
		txt = ""
		for n in subAnimPath do
		(
			txt +=(  ( n.name as string ) + ",")
		)
		txt
	),
	fn mySubAnimNameAsString = 
	(
		theSubAnim = mySubAnim()
		if theSubAnim != undefined then ( theSubAnim.name as string ) else ""
	),
	fn writeMe f =
	(
		txt = getSubAnimPathAsString()
		format "%
" txt to:f
	),
	fn readMe fStream =
	(
		SetSubAnimFromCommaDelimitedString ( fStream.ReadToDelimitChar "
" )
	)
)
try ( destroyDialog subAnimRoll) catch ()

rollout subAnimRoll "subanim"
(
	local subAnimPath = #()
	local currentProperty = undefined
	local childPropertyList = #()
	local validControllerList = #(bezier_float,Linear_Float,TCB_float,Expose_Float_Control,Float_Expression,Float_list,Float_motion_capture,Float_reactor,Float_script)
	
	-- Set these values in the calling function
	local returnIndex = 0
	local returnFnPtr = undefined
	
	Listbox LbxCurrentSubAnims "  "
	button btnUp "back" Across:3
	pickButton pbtnPickObject "pick object"
	
	button btnUse "use Contoller" enabled:false
	
	label lbl1 "Pick an object in the scene using the 'pick object' button." align:#center offset:[0,10]
	label lbl2 "Click an item in the list to browse it's properties." align:#center
	label lbl3 "Go back up the tree by pressing the 'back' button" align:#center
	
	fn setListCaption =
	(
		txt = ""
		for n in subAnimPath do
		(
			txt +=(  ( n.name as string ) + " > ")
		)
		LbxCurrentSubAnims.caption = txt
	)
	
	fn isValidController theSubAnim =
	(
		--validControllerList = #(bezier_float,Linear_Float,TCB_float,Expose_Float_Control,Float_Expression,Float_list,Float_motion_capture,Float_reactor,Float_script)
		( isProperty theSubAnim "controller" ) and ( ( findItem validControllerList (classof theSubAnim.controller) ) >0 )
	)
	
	fn FillSubAnimList =
	(
		if subAnimPath.Count  > 0 then 
		(
			currentProperty = subAnimPath[subAnimPath.Count ]
			--format  "numsubs: % 
" (currentProperty.numSubs)
			SubItemList = for i = 1 to currentProperty.numSubs collect ( getSubAnim currentProperty i)
			caList = for i = 1 to ( custAttributes.count currentProperty ) collect ( custAttributes.get currentProperty i )
			childPropertyList = SubItemList + caList

			LbxCurrentSubAnims.items =  for n in childPropertyList collect (n.name as string)
		) else LbxCurrentSubAnims.item = #()
		setListCaption()
		btnUse.enabled =  subAnimPath.count > 0 and ( isValidController subAnimPath[subAnimPath.count] )
	)
	
	fn  setChildPropertyIndex childPropertyIndex =
	(
		currentProperty = subAnimPath[subAnimPath.Count ]
		newProperty = childPropertyList[ childPropertyIndex ]
	--	format  "newProperty: % " newProperty
	--	format  "set current prop: % " childPropertyIndex
		if newProperty != undefined then
		(
			append subAnimPath newProperty
			FillSubAnimList()
		)
	)
	
	on pbtnPickObject picked obj do 
	(
		subAnimPath = if obj !=undefined then #(obj) else #()	
		FillSubAnimList()
	)
	
	on LbxCurrentSubAnims selected i do ( setChildPropertyIndex i )
	
	on btnUp Pressed do
	(
		if subAnimPath.count > 1 then deleteitem subAnimPath (subAnimPath.count)
		FillSubAnimList()
	)
	
	on btnUse pressed do
	(
		if returnIndex > 0 and returnFnPtr != undefined then
		(
			newSubAnim = subAnimFinderStruct()
			newSubAnim.setSubAnimPath subAnimPath
			returnFnPtr newSubAnim index: returnIndex         --- a pointer to a function in faceLinkControl 
			--LaunchFaceLinkControlGuiRollouts()
		) else
		(
			--these values should have been set by the calling function
			messagebox "oops returnIndex or returnFnPtr have not been defined "
		)
		
		destroyDialog subAnimRoll
	)
)

createDialog subAnimRoll 400 300

I guess the main thing to note is that you can use this bit of code to collect all the custom attributes.

caList = for i = 1 to ( custAttributes.count theProperty ) collect 
( 
          custAttributes.get theProperty i 
)