Euler Filter for Cinema 4D


#21

Could you share or point me out to such file? I downloaded a couple of free mocap files for some testing but they behave all fine.

Thanks in advance,

bunk


#22

I’ve also added it to my wiki for ease of access in future and to the scripts thread in the scripting subforum here : http://www.peranders.com/wiki/Euler_Filter


#23

Thanks Per but unfortunately it doesn’t work correctly, as I mentioned on the previous page it needs to be able to detect a flip when it’s not caused by a full 360 in one axis.

Quick example of where it fails.

http://www.graphite9.com/MiscClips/EullersDayOff/EullersDayOff.html

Cheers,
Brian


#24

Edit, actually dont’ worry i was able to rectrate, though your capture was so small i had to peer hard on my monitor to see what values you had. Sure that should be fixable.


#25

Sorry silly new version of Camtasia makes them small like that by default. You can click the full-screen button in the time bar to see it a little better.

To clarify the values of each key are 1: 0,0,0 then 2: 180,180,180 then 3: 360,360,360 and finally back to 0,0,0 for the fourth key. I created the same values in both examples. The Maya Euler Filter version sees the 2nd key (180,180,180) as a flip but the Python version only sees the difference between the last two keys as a flip so it makes the last key 360,360,360 too but leaves the 2nd key (180,180,180) as is so the flip still occurs. With the Maya one all four keys end up being 0,0,0.

Cheers,
Brian


#26

Yup, found that out, try this :

I wont necessarily be able to resolve correctly if you only allow it a single key on a single track rather than all hpb tracks as you're locking it out from changing the other keys then, and if a key only exists on one track it will generate new keys on the other tracks in order to force generate a decent rotation.
import c4d
  import math
  from c4d import gui
  
  #Cinema 4D Euler Fiter Command
  #Select an object, it's tracks, or keys and run to apply the filter
  #Have no selection to apply the filter to the entire scene!
  #Per-Anders Edwards 2013
  #Happy Thanksgiving!
  
  #check for selection, limited to just the first timeline in order to keep thigns simple
  #it could be possible to extend this to cover all four timelines
  #but determining the active f-curve/key window between all four isn't possible in python currently
  def checkSelect(obj):
  	#Check selection in timeline
  	if c4d.IsCommandChecked(465001191) is True:
  		if obj.GetNBit(c4d.NBIT_TL1_SELECT):
  			return True
  		
  		if obj.GetNBit(c4d.NBIT_TL1_SELECT2):
  			return True
  		
  	#Check selection in fcurve manager
  	if c4d.IsCommandChecked(465001190) is True:
  		if obj.GetNBit(c4d.NBIT_TL1_FCSELECT):
  			return True
  
  	
  	#Key specific check
  	if obj.GetNBit(c4d.NBIT_CKEY_ACTIVE):
  		return True
  	
  	#No selection
  	return False
  
  def shiftKey(doc, key, keyIndex, curve, newValue):
  	keyCount = curve.GetKeyCount()
  	kval = key.GetValue()
  	delta = newValue - kval
  	
  	if keyIndex > 0:
  		#left tangent
  		leftKey = curve.GetKey(keyIndex - 1)
  		leftKVal = leftKey.GetValue()			 
  		dif = kval - leftKVal
  		if dif != 0.0:
  			doc.AddUndo(c4d.UNDO_CHANGE, leftKey)
  			kLeftVal = key.GetValueLeft()
  			leftKRightVal = leftKey.GetValueRight()
  			
  			proportion = (delta + dif) / dif
  		
  			kLeftVal = kLeftVal * proportion
  			leftKRightVal = leftKRightVal * proportion
  			leftKey.SetValueRight(curve, leftKRightVal)
  			key.SetValueLeft(curve, kLeftVal)
  		
  	if keyIndex < keyCount - 1:
  		#right tangent
  		rightKey = curve.GetKey(keyIndex + 1)
  		rightKVal = rightKey.GetValue()			 
  		dif = kval - rightKVal
  		if dif != 0.0:
  			doc.AddUndo(c4d.UNDO_CHANGE, rightKey)
  			kRightVal = key.GetValueRight()
  			rightKLeftVal = rightKey.GetValueLeft()
  			
  			proportion = (delta + dif) / dif
  		
  			kRightVal = kRightVal * proportion
  			rightKLeftVal = rightKLeftVal * proportion
  			rightKey.SetValueLeft(curve, rightKLeftVal)
  			key.SetValueRight(curve, kRightVal)								   
  	
  	#finally set the key value itself
  	key.SetValue(curve, newValue)
  
  #main euler filter function
  def main():
  	arr = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
  	if len(arr) == 0:
  		op = doc.GetFirstObject()
  		while op:
  			arr.append(op)
  			if op.GetDown():
  				op = op.GetDown()
  			else:
  				while (not op.GetNext()) and op.GetUp():
  					op = op.GetUp()
  				op = op.GetNext()
  				
  	hasSelectedTracks = False
  	hasSelectedKeys = False
  	
  	#find out if there's a selection to use
  	for op in arr:
  		track = op.GetFirstCTrack()
  		
  		while track is not None and not hasSelectedTracks and not hasSelectedKeys:
  			if track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_REL_ROTATION or track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_ABS_ROTATION:
  					
  				if checkSelect(track):
  					hasSelectedTracks = True
  					break
  				
  				curve = track.GetCurve()
  				while curve is not None and not hasSelectedKeys:
  					keyCount = curve.GetKeyCount()
  					for i in range(0, keyCount):
  						key = curve.GetKey(i)
  						if checkSelect(key):
  							hasSelectedKeys = True
  							break
  						
  					curve = curve.GetNext()
  			
  			track = track.GetNext()
  		
  	#Filter the keys to be within 180 degrees of their previous neighbors
  	for op in arr:
  		trackH = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_X])
  		trackP = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Y])
  		trackB = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Z])
  		
  		if hasSelectedTracks and not (checkSelect(trackH) or checkSelect(trackP) or checkSelect(trackB)):
  			continue
     
  		curveH = trackH.GetCurve() if trackH is not None else None
  		curveP = trackP.GetCurve() if trackP is not None else None
  		curveB = trackB.GetCurve() if trackB is not None else None
  		
  		keyTimeList = []
  		
  		for curve in [curveH, curveP, curveB]:
  			if curve is None:
  				continue
  			keyCount = curve.GetKeyCount()
  			for i in range(0, keyCount):
  				key = curve.GetKey(i)
  				if not hasSelectedKeys or checkSelect(key):
  					keyTimeList.append(key.GetTime())
  		
  		keyTimeList.sort()
  		lastKey = keyTimeList[-1]
  		for i in range(len(keyTimeList) - 2, -1, -1):
  			if lastKey == keyTimeList[i]:
  				del(keyTimeList[i])
  			else:
  				lastKey = keyTimeList[i]
  		
  		trackList = [trackH, trackP, trackB] 
  		
  		for keyTime in keyTimeList:
  			valH = trackH.GetValue(doc, keyTime, doc.GetFps()) if trackH is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_X]
  			valP = trackP.GetValue(doc, keyTime, doc.GetFps()) if trackP is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Y]
  			valB = trackB.GetValue(doc, keyTime, doc.GetFps()) if trackB is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Z]
  			
  			rotationMatrix = c4d.utils.HPBToMatrix(c4d.Vector(valH, valP, valB), op[c4d.ID_BASEOBJECT_ROTATION_ORDER])
  			hpb = c4d.utils.MatrixToHPB(rotationMatrix, op[c4d.ID_BASEOBJECT_ROTATION_ORDER])
  			
  			for curve, val in [[curveH, hpb.x], [curveP, hpb.y], [curveB, hpb.z]]:
  				if curve is None:
  					continue
  				
  				key, index = curve.FindKey(keyTime).values()
  				
  				if key is None:
  					key, index = curve.addKey(keyTime)
  					doc.AddUndo(c4d.UNDO_NEW, key)
  					
  				if hasSelectedKeys and not checkSelect(key):
  					continue 
  				
  				doc.AddUndo(c4d.UNDO_CHANGE, key)	
  				
  				lastValue = curve.GetKey(index - 1).GetValue() if index > 0 else key.GetValue()
  				
  				offset = math.fmod((val - lastValue), math.pi * 2.0) + lastValue
  				
  				shiftKey(doc, key, index, curve, lastValue)
  	
  	c4d.EventAdd()
  
  if __name__=='__main__':
  	main()
     

Also here : http://www.peranders.com/wiki/Euler_Filter_2

edit - Forgot to add the attachment of the command in a neat little package for those that want it :


#27

Well i set up a test for the first one you did and it performed admirably. It took care of about 90-95% of the problems. I was rendering out a version to show you, but now i’ll try the second version before rendering out an animation.

Needless to say you are a superstar for doing this Per…

Here is a still from the test while i try the new script…


#28

Trouble in paradise.

In the image below the lower graph shows where my problems stem from, You can see the values bounce 360 up & down (even though its about the same value).

With FerrisEuler 1 the lower graph was fixed.

With FerrisEuler 2 it was too, but the upper graph was flattened?!

Maybe i should post an example mo-cap file for testing purposes?


#29

Please do post any files you have problems with, I’m sure they can be resolved. The real question is whether flattened or not was the motion correct?

As of right now the second version requires all three tracks in order to solve correctly, however it should be possible to change this.


#30

Here, try this version, this changes the behavior in that it will try to force setting the keys on the other tracks if they exist provided one key is selected, which is a bit cheeky, if that’s undesirable it’s easy to change :

import c4d
 import math
 from c4d import gui
 
 #Cinema 4D Euler Fiter Command
 #Select an object, it's tracks, or keys and run to apply the filter
 #Have no selection to apply the filter to the entire scene!
 #Per-Anders Edwards 2013
 #Happy Thanksgiving!
 
 #check for selection, limited to just the first timeline in order to keep thigns simple
 #it could be possible to extend this to cover all four timelines
 #but determining the active f-curve/key window between all four isn't possible in python currently
 def checkSelect(obj):
 	#Check selection in timeline
 	if c4d.IsCommandChecked(465001191) is True:
 		if obj.GetNBit(c4d.NBIT_TL1_SELECT):
 			return True
 		
 		if obj.GetNBit(c4d.NBIT_TL1_SELECT2):
 			return True
 		
 	#Check selection in fcurve manager
 	if c4d.IsCommandChecked(465001190) is True:
 		if obj.GetNBit(c4d.NBIT_TL1_FCSELECT):
 			return True
 
 	
 	#Key specific check
 	if obj.GetNBit(c4d.NBIT_CKEY_ACTIVE):
 		return True
 	
 	#No selection
 	return False
 
 def shiftKey(doc, key, keyIndex, curve, newValue):
 	keyCount = curve.GetKeyCount()
 	kval = key.GetValue()
 	
 	if kval == newValue:
 		return
 	
 	delta = newValue - kval
 	
 	if keyIndex > 0:
 		#left tangent
 		leftKey = curve.GetKey(keyIndex - 1)
 		leftKVal = leftKey.GetValue()			 
 		dif = kval - leftKVal
 		if dif != 0.0:
 			doc.AddUndo(c4d.UNDO_CHANGE, leftKey)
 			kLeftVal = key.GetValueLeft()
 			leftKRightVal = leftKey.GetValueRight()
 			
 			proportion = (delta + dif) / dif
 		
 			kLeftVal = kLeftVal * proportion
 			leftKRightVal = leftKRightVal * proportion
 			leftKey.SetValueRight(curve, leftKRightVal)
 			key.SetValueLeft(curve, kLeftVal)
 		
 	if keyIndex < keyCount - 1:
 		#right tangent
 		rightKey = curve.GetKey(keyIndex + 1)
 		rightKVal = rightKey.GetValue()			 
 		dif = kval - rightKVal
 		if dif != 0.0:
 			doc.AddUndo(c4d.UNDO_CHANGE, rightKey)
 			kRightVal = key.GetValueRight()
 			rightKLeftVal = rightKey.GetValueLeft()
 			
 			proportion = (delta + dif) / dif
 		
 			kRightVal = kRightVal * proportion
 			rightKLeftVal = rightKLeftVal * proportion
 			rightKey.SetValueLeft(curve, rightKLeftVal)
 			key.SetValueRight(curve, kRightVal)								   
 	
 	#finally set the key value itself
 	key.SetValue(curve, newValue)
 
 #main euler filter function
 def main():
 	arr = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
 	if len(arr) == 0:
 		op = doc.GetFirstObject()
 		while op:
 			arr.append(op)
 			if op.GetDown():
 				op = op.GetDown()
 			else:
 				while (not op.GetNext()) and op.GetUp():
 					op = op.GetUp()
 				op = op.GetNext()
 				
 	hasSelectedTracks = False
 	hasSelectedKeys = False
 	
 	#find out if there's a selection to use
 	for op in arr:
 		track = op.GetFirstCTrack()
 		
 		while track is not None and not hasSelectedTracks and not hasSelectedKeys:
 			if track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_REL_ROTATION or track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_ABS_ROTATION:
 					
 				if checkSelect(track):
 					hasSelectedTracks = True
 					break
 				
 				curve = track.GetCurve()
 				while curve is not None and not hasSelectedKeys:
 					keyCount = curve.GetKeyCount()
 					for i in range(0, keyCount):
 						key = curve.GetKey(i)
 						if checkSelect(key):
 							hasSelectedKeys = True
 							break
 						
 					curve = curve.GetNext()
 			
 			track = track.GetNext()
 		
 	#Filter the keys to be within 180 degrees of their previous neighbors
 	for op in arr:
 		trackH = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_X])
 		trackP = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Y])
 		trackB = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Z])
 		
 		if hasSelectedTracks and not (checkSelect(trackH) or checkSelect(trackP) or checkSelect(trackB)):
 			continue
    
 		curveH = trackH.GetCurve() if trackH is not None else None
 		curveP = trackP.GetCurve() if trackP is not None else None
 		curveB = trackB.GetCurve() if trackB is not None else None
 		
 		keyTimeList = []
 		
 		for curve in [curveH, curveP, curveB]:
 			if curve is None:
 				continue
 			keyCount = curve.GetKeyCount()
 			for i in range(0, keyCount):
 				key = curve.GetKey(i)
 				if not hasSelectedKeys or checkSelect(key):
 					doc.AddUndo(c4d.UNDO_CHANGE, key)
 					keyTimeList.append([key.GetTime().Get(), key.GetTime()])
 		
 		keyTimeList.sort()
 		lastKey = keyTimeList[-1]
 		for i in range(len(keyTimeList) - 2, -1, -1):
 			if lastKey == keyTimeList[i]:
 				del(keyTimeList[i])
 			else:
 				lastKey = keyTimeList[i]
 		
 		#doing this four iterations seems to solve the whole thing in the cleanest way
 		lastKeyVal = c4d.Vector(0.0, 0.0, 0.0)
 		for keyTimeEntry in keyTimeList:
 			keyTime = keyTimeEntry[1]
 			valH = trackH.GetValue(doc, keyTime, doc.GetFps()) if trackH is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_X]
 			valP = trackP.GetValue(doc, keyTime, doc.GetFps()) if trackP is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Y]
 			valB = trackB.GetValue(doc, keyTime, doc.GetFps()) if trackB is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Z]
 			
 			valVector = c4d.Vector(valH, valP, valB)
 			
 			if keyTime == keyTimeList[0][1]:
 				lastKeyVal = valVector
 				continue
 			
 			hpb = c4d.utils.GetOptimalAngle(lastKeyVal, valVector, op[c4d.ID_BASEOBJECT_ROTATION_ORDER])
 			
 			for curve, val, lastValue in [[curveH, hpb.x, lastKeyVal.x], [curveP, hpb.y, lastKeyVal.y], [curveB, hpb.z, lastKeyVal.z]]:
 				if curve is None:
 					continue
 				
 				key, index = curve.FindKey(keyTime).values()
 				
 				if key is None:
 					key, index = curve.addKey(keyTime)
 					doc.AddUndo(c4d.UNDO_NEW, key)
 				
 				shiftKey(doc, key, index, curve, val)
 				
 			lastKeyVal = hpb
 	
 	c4d.EventAdd()
 
 if __name__=='__main__':
 	main()
 

#31

And here’s a version that respects your key/track selections a little bit better (although no guarantees on the output if you select only a single track!), and fixes a bug with adding keys automatically.

import c4d
 import math
 from c4d import gui
 
 #Cinema 4D Euler Fiter Command
 #Select an object, it's tracks, or keys and run to apply the filter
 #Have no selection to apply the filter to the entire scene!
 #Per-Anders Edwards 2013
 #Happy Thanksgiving!
 
 #check for selection, limited to just the first timeline in order to keep thigns simple
 #it could be possible to extend this to cover all four timelines
 #but determining the active f-curve/key window between all four isn't possible in python currently
 def checkSelect(obj):
 	#Check selection in timeline
 	if c4d.IsCommandChecked(465001191) is True:
 		if obj.GetNBit(c4d.NBIT_TL1_SELECT):
 			return True
 		
 		if obj.GetNBit(c4d.NBIT_TL1_SELECT2):
 			return True
 		
 	#Check selection in fcurve manager
 	if c4d.IsCommandChecked(465001190) is True:
 		if obj.GetNBit(c4d.NBIT_TL1_FCSELECT):
 			return True
 
 	
 	#Key specific check
 	if obj.GetNBit(c4d.NBIT_CKEY_ACTIVE):
 		return True
 	
 	#No selection
 	return False
 
 def shiftKey(doc, key, keyIndex, curve, newValue):
 	keyCount = curve.GetKeyCount()
 	kval = key.GetValue()
 	
 	if kval == newValue:
 		return
 	
 	delta = newValue - kval
 	
 	if keyIndex > 0:
 		#left tangent
 		leftKey = curve.GetKey(keyIndex - 1)
 		leftKVal = leftKey.GetValue()			 
 		dif = kval - leftKVal
 		if dif != 0.0:
 			doc.AddUndo(c4d.UNDO_CHANGE, leftKey)
 			kLeftVal = key.GetValueLeft()
 			leftKRightVal = leftKey.GetValueRight()
 			
 			proportion = (delta + dif) / dif
 		
 			kLeftVal = kLeftVal * proportion
 			leftKRightVal = leftKRightVal * proportion
 			leftKey.SetValueRight(curve, leftKRightVal)
 			key.SetValueLeft(curve, kLeftVal)
 		
 	if keyIndex < keyCount - 1:
 		#right tangent
 		rightKey = curve.GetKey(keyIndex + 1)
 		rightKVal = rightKey.GetValue()			 
 		dif = kval - rightKVal
 		if dif != 0.0:
 			doc.AddUndo(c4d.UNDO_CHANGE, rightKey)
 			kRightVal = key.GetValueRight()
 			rightKLeftVal = rightKey.GetValueLeft()
 			
 			proportion = (delta + dif) / dif
 		
 			kRightVal = kRightVal * proportion
 			rightKLeftVal = rightKLeftVal * proportion
 			rightKey.SetValueLeft(curve, rightKLeftVal)
 			key.SetValueRight(curve, kRightVal)								   
 	
 	#finally set the key value itself
 	key.SetValue(curve, newValue)
 
 #main euler filter function
 def main():
 	arr = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
 	if len(arr) == 0:
 		op = doc.GetFirstObject()
 		while op:
 			arr.append(op)
 			if op.GetDown():
 				op = op.GetDown()
 			else:
 				while (not op.GetNext()) and op.GetUp():
 					op = op.GetUp()
 				op = op.GetNext()
 				
 	hasSelectedTracks = False
 	hasSelectedKeys = False
 	
 	#find out if there's a selection to use
 	for op in arr:
 		track = op.GetFirstCTrack()
 		
 		while track is not None and not hasSelectedTracks and not hasSelectedKeys:
 			if track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_REL_ROTATION or track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_ABS_ROTATION:
 					
 				if checkSelect(track):
 					hasSelectedTracks = True
 					break
 				
 				curve = track.GetCurve()
 				while curve is not None and not hasSelectedKeys:
 					keyCount = curve.GetKeyCount()
 					for i in range(0, keyCount):
 						key = curve.GetKey(i)
 						if checkSelect(key):
 							hasSelectedKeys = True
 							break
 						
 					curve = curve.GetNext()
 			
 			track = track.GetNext()
 		
 	#Filter the keys to be within 180 degrees of their previous neighbors
 	for op in arr:
 		trackH = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_X])
 		trackP = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Y])
 		trackB = op.FindCTrack([c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Z])
 		
 		if hasSelectedTracks and not (checkSelect(trackH) or checkSelect(trackP) or checkSelect(trackB)):
 			continue
    
 		curveH = trackH.GetCurve() if trackH is not None else None
 		curveP = trackP.GetCurve() if trackP is not None else None
 		curveB = trackB.GetCurve() if trackB is not None else None
 		
 		keyTimeList = []
 		
 		for curve in [curveH, curveP, curveB]:
 			if curve is None:
 				continue
 			keyCount = curve.GetKeyCount()
 			for i in range(0, keyCount):
 				key = curve.GetKey(i)
 				if not hasSelectedKeys or checkSelect(key):
 					doc.AddUndo(c4d.UNDO_CHANGE, key)
 					keyTimeList.append([key.GetTime().Get(), key.GetTime()])
 		
 		keyTimeList.sort()
 		lastKey = keyTimeList[-1]
 		for i in range(len(keyTimeList) - 2, -1, -1):
 			if lastKey == keyTimeList[i]:
 				del(keyTimeList[i])
 			else:
 				lastKey = keyTimeList[i]
 		
 		lastKeyVal = c4d.Vector(0.0, 0.0, 0.0)
 		for keyTimeEntry in keyTimeList:
 			keyTime = keyTimeEntry[1]
 			valH = trackH.GetValue(doc, keyTime, doc.GetFps()) if trackH is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_X]
 			valP = trackP.GetValue(doc, keyTime, doc.GetFps()) if trackP is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Y]
 			valB = trackB.GetValue(doc, keyTime, doc.GetFps()) if trackB is not None else op[c4d.ID_BASEOBJECT_REL_ROTATION,c4d.VECTOR_Z]
 			
 			valVector = c4d.Vector(valH, valP, valB)
 			
 			if keyTime == keyTimeList[0][1]:
 				lastKeyVal = valVector
 				continue
 			
 			hpb = c4d.utils.GetOptimalAngle(lastKeyVal, valVector, op[c4d.ID_BASEOBJECT_ROTATION_ORDER])
 			
 			for curve, val, lastValue in [[curveH, hpb.x, lastKeyVal.x], [curveP, hpb.y, lastKeyVal.y], [curveB, hpb.z, lastKeyVal.z]]:
 				if curve is None:
 					continue
 				
 				if hasSelectedTracks and not checkSelect(curve.GetTrack()):
 					continue
 				
 				try:
 					key, index = curve.FindKey(keyTime).values()
 				except:
 					try:
 						index, key = curve.AddKey(keyTime).values()
 						doc.AddUndo(c4d.UNDO_NEW, key)
 						if hasSelectedKeys:
 							key.ChangeNBit(c4d.NBIT_TL1_SELECT, True)
 					except:
 						continue
 					
 				if not hasSelectedKeys or checkSelect(key):
 					shiftKey(doc, key, index, curve, val)
 				
 			lastKeyVal = hpb
 	
 	c4d.EventAdd()
 
 if __name__=='__main__':
 	main()
 
   

#32

thanks Per ! . I’m testing it now .


#33

I will try these out and i’ll post an example file later too…


#34

Hello Per
Thank you


#35

Boom. I just tested Ferris euler 3 and (on this clip) we have a winner. It popped everything back into place like some dark voodoo magic.

I have an example rendering now which i’ll be uploading shortly.

I’m going to go and try version 4 out now…

Per your a star…


#36

And here you go. 30 fps motion capture data being played at 25 fps.

So simple…

//youtu.be/1w_Tul9u5bc


#37

I think the one on the left is better… :wink:
This is a problem I’ve been vaguely aware of but I’ve never dug into mocap, will bookmark this one for future, good job.

Also, there’s not enough mention of how awesome the script naming is :scream:

cheers
brasc


#38

Fantastic, haven’t tried the newer versions yet but it looks like they work! Many thanks to Per Anders.

Great example video too which demonstrates very succinctly why this is such an important tool for animation :slight_smile:

Cheers,
Brian


#39

Many thanks Per !


#40

very legendary. thx !