View Full Version : Conquering flipping when mixing biped and custom bones
06-16-2008, 03:39 PM
I'm sure someone has come across this issue before and solved it.
I'm scripting a layer of rollbones/twistbones over the Max (v9) biped and I'm continually running into issues with my skinned object having verts flipping when the driver bone rotates past a certain angle - where positive changes to negative.
Since I'm using biped as a base (I'm stuck with it) and can't query biped rotations directly I'm using an intermediate layer of expose transform helpers. This would mean that my forearm twist bone is a child of the forearm bone, and has it's X rotation wired to be the Local Euler X (* 0.5) of the hand bone.
It doesn't seem to matter what the inital orientation of the rollbone is, because as soon as I perform the wiring (either manually or via script) then the rotation will update - this is obvious, since I setting the rotation to be 50% of another bone. I've tried various things here, such as aligning the forearm twist to be the same as the forearm, and then applying an offset when I perform the wiring, but I still get the same issue which convinces me that it is the driverbone flipping from +ve to -ve that causes the rollbone to flip 180 degrees.
I'm not sure how common it is for people to mix rigs like this, but I'm sure it must have been solved before, so if anyone has any pointers I'd love to hear them.
06-17-2008, 12:09 PM
I've done some work on this and have a partial solution, but I hope that posting it here can trigger some ideas to get a better one.
Apologies for the massive amount of text.
Firstly, the cause:
Sometimes there are issues with the skin collapsing. A circle is 360 degrees, but max treats it as -180 degrees though to +180 degrees. As you rotate a bone it's angle changes - using an example of 1 degree of rotation at each step a bones X axis might be 178.5, 179.5, and then instead of 180.5 it becomes -179.5. When the parent bone cross the threshold from +ve to -ve, the rollbone finds the shortest rotation between it's starting postion and the position you want to to be at, so it flips. On a skinned mesh that will cause the mesh to collapse on itself.
This isn't always going to be an issue, as the collapse might occur at a rotation that would be impossible for a bone to ever be in - for example, with a calf roll bone the foot would need to be rotated 180 degrees for this to occur.
The calf rolls work because of the orientation of the parent bone and the driver bone, so as I mentioned the foot has to rotate a full 180 for the collapsing to occur.
The forearm twist will however have a problem because the orientation of the forearm and hand - they are at 90 degrees to each other with Biped, so you can rotate 270 degrees in one direction before the collapse, but only 90 degrees in the other. Technically this still shouldn't be an issue because the hand probably shouldn't rotate that far, but in animation it sometimes will, which is annoying. Although this may not be an issue when a model goes into your games engine, it is visible in Max and will get in the way when skinning the model.
It would be much better if the hand collapsing could be shifted so that it also only occurs when you rotate it 180 degrees in any direction. Try as I might, I cannot shift the flip from 90 degrees to 180 degrees. I can however change the flip from 90 degrees in one direction to 90 degrees in the other - with my forearm twists they wold work much better if this flipping was changed, since the hands are more likely to rotate in one direction than the other.
What we need to do is monitor the angle of rotation, and attempt to correct it when it crosses the threshold.
When experimenting with this, I firstly noticed that max uses Radians to measure rotation in the wiring parameters dialogue, so that a 360 of rotation is measured internally as 2pi. Instead of -180 degrees to 0 through to +180 degrees, Max is using -3.14 to 0 through to +3.14. You can easily watch this by adding a little debug code to the wiring of the rollbone - just open the Wire Parameters dialogue for your forearm twist and add a print staement:
You'll now see the reported angle in the listener window - rotate the hand until you see the mesh flipping and you'll notice that it occured when the X rotation moved between +ve and -ve.
So how to fix this? The fix itself is actually quite simple - you want to monitor the angle of the hand, and when it starts to report a -ve number, you want to change it. Depending on the orientation of the bones, you may to apply the correction if the values become negative, or you may want to apply the fix of the values become positive. Using the output from the previous debug you can tell when the rotation is correct and when the correction needs to be made. I noticed that on my character right forearm the twist was correct when the hand was in +ve space, and the flip occurred when the hand moved to -ve space
The first thing to do is to move the value of Local_Euler_X out to another variable and use that variable to control the rotation instead:
lex = Local_Euler_X
lex = lex*0.5
Next I check to see if the flip will occur by monitoring the value of lex to see if it is less that zero:
lex = Local_Euler_X
if lex < 0 then
lex * 0.5
When I rotate the hand enough so that the flip occurs, I can also see my debug window printing "Broken". Now we have to perform the fix:
lex = Local_Euler_X
if lex < 0 then
lex = (2 * 3.141) + lex
lex * 0.5
Now when I rotate the hand bone, the collapsing happens at a much nicer place.
Sorry, I didn't read the massive amount of text but I will try to explain how it all works. First off drop by my site and read all the write ups on rotations and transforms that I have there in the tutorials section, then return back here and read on.
So, Euler rotations will always have a point at which gimble will happen. In any axis order it is the middle axis that will cause this. Once it is rotated 90deg the first axis will be flat on the third axis. There is no way around this and it isn't a bug, it is just the way the math works.
When you use the Expose TM helper you are converting the Quaternion rotation values to Euler so you will have gimble problems. You can limit the problems caused by this by setting the correct axis order. For instance on a write bone you can rotate it up and down past 90 or at least very close to it. When you twist your wrist it can also go quite far but when you rotate it side to side it can only go about 30 degrees. Set the axis order in the Expose Tm Helper so the middle axis is the side to side axis. What this does is ensure that the axis that will cause the problem will not rotate up to 90 and cause gimble.
06-17-2008, 03:20 PM
Cheers for the reply Paul - work were kind enough to purchase your DVD set from CG Academy for me several months back, I've learned a lot of new rigging techniques from them. I was especially impressed by your scapula/pectoral setup, it formed the basis of a new shoulder rig I've been working on with lookat constraints to fake the top of the trapezius.
Anyway, back to my collapsing mesh.
I know the issue is with me using Eulers and not Quaternions, unfortunatley it seems I have to work with Euler angles as the Expose Transform helpers can't expose the rotations as quaternions.
This is a shame, because I can select a biped bone and use $.transform.rotation to return the quat value - but as I mentioned the expose transform can't give me those values.
As for the Gimbal locking, that's more of an issue around the shoulders with the ball joint from the clavicle to the upper arm. On wrists however the rollbone is twisting only in one axis so it's much less of an issue. From what I can see the collapsing is due to the orientation of the biped hand bone being 90 degrees out from the rest of the arm (thats the default setting).
Thankfully my maths hack does seem to work, and also handles thigh rolls when I parent then to the pelvis and use the thigh to drive them.
I'm still searching for a more elegant solution.
Ah, biped, nuf said, Best thing to do there is link a point helper forearm bone and orientationaly align it to the hand bone. Then in the Expose TM select the point helper helper as the parent node. You know how an euler value that is at 0,0,0 for a starting value.
As for quat values they wouldn't be of much use to you as they don't have seperate x,y and z axes. Easy test is rotate an object over a number of frames then print out the quat values, see if you can make heads or tails of it.
06-17-2008, 06:28 PM
at this point with biped, i usually bang my head on the table repeatedly, then enable the biped twist links. is there any reason for the custom bone setup over using the CS enabled ones?
06-17-2008, 06:54 PM
Paul - a genius idea!
LoneRobot - I was using some of the CS twist links before, but it only supports a few of the extra helper bones that we might end up using. We may for example add in some kneecap and elbows, or perhaps an extra rolls to hold the shape of the bum a little better.
Another thing I didn't particularly like was the visual representation of the CS twisting bones, I prefer to have shorter, fatter colour coded bones for this extra animation layer.
Finally, rigging via a script means that I can adjust all the rollbone settings over myriad characters. Our last project had several hundred characters...
06-17-2008, 08:03 PM
if you're making that many characters that certainly makes sense. There is also an option of bone xtras in 2008 but they are FK only. probably not good enough to do the blending you're talking about. As for visual representation, you can put an edit mesh and replace the biped bone for any geometry you want.
Several hundred Biped! You poor sole. I personaly would go with custom rigs but that is just me. I can make a custom rig do anything that biped does, or at least anything that you would want to try and make biped do.
As for the Biped twist bones I don't like the result they give, it isn't what I want. So if I'm tied down, smoothered in honey, and told that 5 million fire ants will be released if I don't use Biped, I will always build my own twist bone setup.
06-17-2008, 08:41 PM
That would certainly be an interesting meeting, Paul. With a project of that scale, biped has to make sense though despite it's limitations. I mean, ive added stretch rigs and additional bones with reasonable sucess in biped, but how do you make custom rigs adaptable enough to be used across multiple characters of different sizes? Brad Noble's skeleton has a neat resizer, but what do you do in order to re-use rigs and resize complex setups? you just couldn't build 100 custom rigs in a production, that would take forever, even for you! Though im sure the threat of those fire ants would get you working faster!
Well for Sitting ducks years ago I have 350 characters per season that I setup.
I build them with the abillity to be resized easily with the use of like controllers. I have done if for a project that I'm on right now where I will create all other biped characters from one default rig. Just need to set the setup controller active and repurpose and realigna few things. I can do it in about half and hour at most.
I have generic character tools that allow me to make any rig usable with mocap data direct from TRC files and allow for mirroring of poses and pose capturing. Takes about an hour to setup the first character so the rest are all done as it doesn't require much or any changes to the base rig.
Biped for me is never the better choice, it always has limitations and issues that cause me problems. I even tried CAT and that was worse, just one head ache after another. We tried to use it on a current project because the client asked for it. After a week of basing on it to get it to do what the client wanted I built a custom rig that did everything that we needed and more in about four days.
06-17-2008, 10:24 PM
Custom rigs would be my preferred solution, and it is something that is being considered and experimented with. I just need to make sure that if Biped is kept then we can have the best possible rig with that as a base.
The extra bone idea on the hand is such a simple elegant solution.
I actually like the solution I came up with earlier for a different reason - it forced me to look hard at what the rig was actually doing and that's when I also learned that I could use more complex script and conditional statements inside a wire parameter.
Tomorrow I'll try to figure out why I can't copy the rotation part of a transform matrix from one object to another.
You can multiply the matrix parts together.
((eulerAngles 90 0 0) as matrix3)*(transMatrix [10,100,10])
Is that the sort if thing that you are looking for?
06-18-2008, 12:24 PM
I was looking to do this:
to copy the orientation.
bone1.transform= bone2.transform copies position too, which isn't what I need.
I ended up with this messy code:
b1p = bone1.pos
It works, but doesn't seem very elegant.
You can't set transform.rotation so you need .rotation only and you need to rotate it in the space of the object or it will move.
in coordsys (transMatrix bone01.pos) bone01.rotation=inverse bone02.rotation
Not sure the inverse is needed in there
06-18-2008, 07:45 PM
Cheers for that! I really do appreciate the help.
I've found that I'm lacking in some of the underlying maxscript fundamentals since I've tended to solve a problem and leave it that (as you saw, I did manage to get the result I wanted, but knew it wasn't very nice). Recently though we've not been in crunch at work and I've been revisiting a lot of my earlier work to spruce it up.
Anyway, today I got work to order me the CG academy maxscript fundamentals collection. Hopefully that will give me a better understanding of some of the more basic features - and stop me badgering our tools guys every few days.
06-18-2008, 07:45 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.