Euler Filter for Cinema 4D


#1

There is a big issue with using motion-capture in Cinema 4D (in recollection i’ve seen this a lot over the years). While some works just fine, others recordings will jump all over the place if you change the frame rate or use motion blur.

This is due to a recording technique on the rig. It can happily record one frame with a rotation of 0 degrees & the next frame with a rotation of 360. The viewer sees nothing happen from one frame to the next, but a frame in-between those would be at 180 degrees…

While Maya, Soft image, Motion builder, 3d max, Lightwave & Blender have a “euler filter” to fix these rotations. Cinema 4D still doesn’t.

So this makes an awful lot of mo-cap data useless for - differing frame rates, motion blending, speed variance, use with dynamics & motion blur. Just because of this issue.

Horganovski has been pointing this fact out for years (as i look across the forums) but Maxon remain very quiet on the subject.

So i’m going to try to do something about it… Not sure how just yet…


#2

I know its possible to analyse keyframes against each other using python, so it would be possible to run a script that would check the rotations of each keyframe.

Though there is a way of checking the velocity of each transition, i don’t think it would be necessary. A quick and dirty way would just be to check if the rotation flips nearly 360 degrees, if so flip it back…


#3

Heh, I was excited there for a moment when I read the thread title :slight_smile:

Unfortunately what’s working against this is that Maxon don’t seem to appreciate the need for a tool like this. They are happy to add the ‘make your character walk with a push of a button’ tools, but not some of the fundamental stuff essential to a character animation pipeline. I guess it’s because in some ways the former makes for much more exciting marketing material whereas the latter sounds like some technical tool for geeks.

I used to think that this was a fault of Maxons, but to be honest I think they know where to focus and they realize that while some like me and you really want tools like this, the vast majority of C4D users have no interest in them. You can see this very clearly if you look at the post Bret made the other day about him converting a useful Maya animation tool to Cinema (tradigi tools). I think that topic got response from about 4 people in total, one of whom was the creator of the Maya one. By contrast someone posted a free rope shader and it caused a lot more excitement here. No offense to that person, the shader looked cool, I’m just using that example to illustrate my point that the C4D userbase generally has a different focus to say the Maya one for example, which is much more character-animation centric.

My own solution for this is to do the majority of my character work in Maya these days and then I export and bake the animation for my Cinema clients to render. That’s the best of both worlds for me but of course not ideal for everyone.

So… long story short - if we do get a Euler Filter I think it’s going to be from a third party, not Maxon.

The way you describe it by the way is pretty much how it works I think.

Cheers,
Brian


#4

I agree that a third party could do this. But i’m not ready to jump ship just yet, there might be a small hole in the boat, but it could be fixed with a clever bit of python…

I used to do basic years ago & this is all that is needed…

[I]Check every rotational keyframe (A) against the following one (B).

C = A - B

If C > -340 & < -380 then B = B - 360

else

	If C &gt; 340 & &lt; 380 then B = B + 360

end if

then onto the next comparison…[/I]

But i have no understanding of python (yet). So I have to find someone that does…


#5

I think it’s a little more complex than that because your dealing with 3 different rotation coordinates, x,y and z and the combinations that can cause a flip are not always simple 360 changes in one curve, it can be a combination of 2 or 3 in many cases. For example 0,0,0 and 180,180,180 will look the same visually but that will be a flip too when interpolated.

Changing key values via Python isn’t too complex really, I’ve written some tools that do this myself, it’s figuring out if a flip has occurred and resolving that cleanly that’s the tricky part.

BTW, by ‘third party’ I meant an independent C4D developer/scripter.

Cheers,
Brian


#6

Sorry for getting your hopes up maybe by the end of this we can re-heighten your hopes.

I see what you mean with the flip Brian. Maybe its just that i’m looking at it from a motion capture perspective but the flipping for me is continually the euler issue and not the gimbal lock. Have you seen much of this treble twist?

In the last few days i’ve been going through various old (from different sources) mo-cap files i’ve worked with. So far the basic that i wrote (or tried to write) would have worked on all of them…

Do you know any good scripting people / developers mate?


#7

I’d rather see Maxon work better with Autodesk products, especially Motion Builder. There used to be setting on the FBX importer that would let you update just the animation on the skeleton, but sadly that was taken out years ago.


#8

I’ve used motion blending to great avail before. What would you say is better in motion builder as i never really got into it?


#9

Probably everything. Its like motion blending on Steroids. It the industry standard for motion capture by far. Its so simple to bring in a take of motion capture and retarget it on to a character that is different size and proportion. This is really hard to do in Cinema without having serious rigging chops.


#10

Niklas is a python genius, and he frequents this forum quite a bit.
http://niklasrosenstein.de/


#11

You can also have a look at IKinema webanimate for a free mocap retargeting tool.


#12

I agree with you Brian , I’d really like such tool too . ( I guess we are more than “4” animators which would need such tool …even if we all don’t reply to the topics posts )
(off topic but in a more global way , I feel that Maxon has choosen a way which is not the animator’s one since R13 : I still feel more confortable using R12 because R13 and after has brought a broken orbit camera, an unusable live selection (for selecting controlers I mean ) etc ) So before Maxon could bring such Euler filter tool , I’d like to have a correct animation workflow tools :slight_smile: ) . Maybe I should start learning maya like you did Brian :stuck_out_tongue: .


#13

I have a euler filter script I wrote ages ago I can bring out for you guys, just needs a minor change or two, I’ll grab it and put it up tomorrow.


#14

thx Per :slight_smile: !


#15

that would be awesome. :applause:


#16

Hi Clement, yeah I have similar issues with the navigation and selection tools in C4D. It’s weird as I jump between several 3d apps pretty regularly from C4D to Maya, ZBrush, Topogun and UV layout, and C4D is the only one where I struggle with the navigation. I’ve tried all of the different options and I use the script that was written by a member here to replicate the behavior I find more predictable (which helps but takes a bit of working with to use) but it’s still something that never feels natural to me.

I also run into issues with the selection tools - I find I’ll often select a particular controller, it will highlight for a moment but then the selection will jump to another object, almost as if C4D is assigning different selection priority to different objects even when they are the same type (typically curves) it’s weird.

I find that for me Maya is generally an easier package to animate characters in and I also love it from a rigging point of view because the nodal nature of it allows you to see the ‘layout’ of the nodes in the Node Editor and very quickly assess the connections between objects, it’s really nice to get that clear overview without digging through hierarchies in the Object Manager. Sort of like the difference between being in a hedge maze and having a ladder to see the entire map at once :slight_smile: It’s also much easier to script for IMO due to how deeply implemented MEL is and finally there’s the scene response with rigged characters and heavy animation data which always feels much more fluid to me than a comparable rig in C4D.

So yes, I find I have a much easier time of it with Maya for character stuff. To balance things though I will say that parts of Maya are clunky too. Recently I’ve tried out the Bullet physics in Maya and almost immediately I ran into some pretty fundamental bugs with it, and when I tried the same things in C4D they worked much better, I got the results I needed without fuss.

So there’s no ‘perfect’ app in my book, each has different strengths, but character work is my focus so I follow the path of least resistance for that. The projects I work on tend to have very short deadlines so I’m focused on having the fastest workflow possible.

That’s great news though that Per Anders has a Euler filter script though, fingers crossed it works well as remember trying one out before on the beta that didn’t seem to work as expected.

Cheers,
Brian


#17

I’ve always been able to get my head around issues. Though this one has me stumped.

Exciting stuff Per, the (cinema 4d) world is watching…


#18

Thanks Per. I’m starting on making a rig that will have mocap stuff but they want to be able to animate on top of that so this will help when/if we run into problems.

I know it would have helped on my last C4D character project. We had maya animators running into issues when using C4D and they just to run the euler filter to fix them.


#19

Clamping on, I’ve had this issue as well.


#20

Here you chaps go, I’ve not done extensive testing, but this script is a slightly tarted up version of my old euler filter script. It’s a python script, the attached zip also contains a small icon if you fancy it. Simply select an object, it’s tracks or even just specific keys and hit the command to apply. It is set to only work with the first timeline window (so if you make use of four timeline windows be aware of this), this is simply a limitation due to python not being able to determine which is the active window, but apart from that you should be good to go. It should also work with the f-curve mode of the timeline, and in theory it should adjust the f-curve tangents for you too.

Have fun, happy thanksgiving.

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 within all four timelines
 	if c4d.IsCommandChecked(456001191):
 		if obj.GetNBit(c4d.NBIT_TL1_SELECT):
 			return True
 		
 		#Check selection within all four timelines
 		if obj.GetNBit(c4d.NBIT_TL1_SELECT2):
 			return True
 		
 	#Check selection within all four fcurve managers
 	if c4d.IsCommandChecked(465001190):
 		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
 
 #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:
 		track = op.GetFirstCTrack()
 		
 		while track is not None:
 			lastValue = 0.0
 			if track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_REL_ROTATION or track.GetDescriptionID()[0].id == c4d.ID_BASEOBJECT_ABS_ROTATION:
 				if not hasSelectedTracks or checkSelect(track):
 					curve = track.GetCurve()
 					while curve is not None:
 						keyCount = curve.GetKeyCount()
 						for i in range(0,keyCount):
 							key = curve.GetKey(i)
 							kval = key.GetValue()
 							
 							if hasSelectedKeys and not checkSelect(key):
 								lastValue = kval
 								continue
 							
 							if i != 0:
 								#Start an undo for the change made
 								doc.AddUndo(c4d.UNDO_CHANGE, key)
 								
 								#Calculate the modulo key offset
 								mval = math.fmod((kval - lastValue), math.pi * 2.0)
 								delta = (lastValue + mval) - kval
 								
 								#Now handle the key tangents to maintain a nice predictable effect
 								if i > 0:
 									#left tangent
 									leftKey = curve.GetKey(i - 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 i < keyCount - 1:
 									#right tangent
 									rightKey = curve.GetKey(i + 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
 								kval = lastValue + mval
 								key.SetValue(curve, kval)
 								
 								
 							lastValue = kval
 							
 						curve = curve.GetNext()
 					
 			track = track.GetNext()
 		
 	c4d.EventAdd()
 
 if __name__=='__main__':
 	main()