Cracking the Ragdoll Code


AlienOne

 

Posted

Well, I still haven't figured out what the third parameter (ie, the second timing number) does. However, I (may have) figured out the position information, and I have a (supposedly) working proof-of-concept animator available in case anybody wants to give it a spin:

http://www.mediafire.com/?gzgngmyyj3j

Just unzip the files somewhere and double-click on the jar. You'll need to have a recent version of Java installed: if you don't, you can get it at www.java.com.

Note that the DLL files are for the Windows x86 version of Java3D, although they should probably also work for Windows x64. If you want to try the program out on a different platform, you'll need to download and install the appropriate files from the Java3D project here. As I only have Windows machines available, I can't help you with any of that, but it shouldn't be too hard if you're reasonably tech-savvy.

As I mentioned, this program is intended to see if I have the correct algorithm down. Here are some of its features and limitations:
<ul type="square">[*]This is a simple keyframe animator. You can set the position of any of the 11 bones on each keyframe. The program will automatically interpolate between these set values.[*]To change a bone position, you can either edit the numbers in the key frame grid or drag the bone itself in a fixed view. If you check the "Drag Lock" box, the currently selected bone will be the one that moves when you drag.[*]You can change the adjustable views via dragging. Left-drag should rotate, right-drag should move, and center-drag should zoom. The mouse wheel will also zoom, but there's a bug where it will zoom every adjustable window you have open.[*]The P/Y/R parameters are incorrectly labeled. They really should be X/Z/Y parameters.[*]The "S" parameter is a smoothing factor for interpolating between the previous keyframe and the current keyframe. It does nothing on the first keyframe. Set it to a number between 0.0 (for normal linear interpolation) and 0.5 (for maximum smoothness).[*]Play and stop are very primitive right now. Play always plays from the beginning. Stop will go back to time zero. This means that the edit window and the display may be out of sync after you push stop. Just select a different keyframe to resync them.[*]Exported animations end up as export.cohdemo. In recognition of all the work Leandro put into his Poser program, I went ahead and stuck him as the model in mine. (Leandro: if this bugs you, speak up and I'll change the default model.)[*]Exported animations probably won't work in any other demos that have ragdoll commands, as I've set the initial timing information arbitrarily.[*]Animations are saved and loaded to animate.xml. I was only testing one animation script at a time, so I just hard-coded the file names.[*]The in-game model has some limits on how much you can rotate each joint. Mr. Rainbow Man does not. As long as the animations are reasonably physically possible, the animations in-game should match the animations in-editor, but anything beyond these limits won't match.[*]This is a proof-of-concept release only. Guaranteed to be buggy. During development, this program did not become self-aware and murder its creator; however, I make no promises for you.[/list]
At this point, I'm mostly looking for feedback about whether this program correctly generates animations and about what sort of features and interface you would be looking for in a full-fledged animation editor. So, tell me what you think!

Edit: Okay, it's not quite cracked. Updated thread title to reflect that.


 

Posted

Cool... I'll be checking it out over this weekend, and I'll be sure to give you feedback, as something like this will help me infinitely with creating some great CoX machinima.

"The One"


Quote:
Originally Posted by Infernus_Hades View Post
The way you play changes your IO slotting..


76 characters and Twenty-four 50s later, I still love this game.
AlienOne's Human-Form Warshade Guide (Old guide+New guide = 12,000+ views!)

 

Posted

&lt;Zloth drools profusely&gt;


 

Posted

Hey Balshor, nice work. But it suffers from the same kind of posing problem (when compared to the COH engine) that made me throw the mouse against the wall and not touch Poser again.

Bend your elbow 90° front. Then your upper arm. Now your fist should be in front of you, at neck-height. Poser shows that fine, Ragdoll shows that fine:

http://s55.photobucket.com/albums/g1...tlz/poser1.jpg
http://s55.photobucket.com/albums/g1...z/ragdoll1.jpg
http://s55.photobucket.com/albums/g1.../poser1out.jpg
http://s55.photobucket.com/albums/g1...agdoll1out.jpg

Now, rotate your elbow 90° up. As if you were holding from a pole in the ceiling. It's a simple pose that any human can do, and that the COH engine renders fine. But both Poser and Ragdoll screw it up in different ways: Poser rotates the joint incorrectly (over its own axis) while Ragdoll rotates it about 80° upwards and 10° outwards.

http://s55.photobucket.com/albums/g1...tlz/poser2.jpg
http://s55.photobucket.com/albums/g1...z/ragdoll2.jpg
http://s55.photobucket.com/albums/g1.../poser2out.jpg
http://s55.photobucket.com/albums/g1...agdoll2out.jpg

That "halfway up and outwards" render happened to me a lot while I was still using the stick figure and it's why I dumped all that code and tried using OpenGL instead.


www.SaveCOH.com: Calls to Action and Events Calendar
This is what 3700 heroes in a single zone looks like.
Thanks to @EnsonsDeath for the GVE code that made me VIP again!

 

Posted

Hmm...after studying this example, I think that my rotation axes are correct, but there's something weird going on with the angles. Here's my current (incorrect) model:

Positions are defined using Axis-Angle notation. In a nutshell, the three components give you a vector to rotate around, and the length of the vector somehow represents the amount to rotate. After playing around with the possibilities, I still haven't figured out exactly how the quantities of rotation work.

In what follows, all directions are according to the viewer's perspective.

Call the left-right axis "X", the up-down axis "Y", and the in-out axis "Z". Map the range {0 -&gt; 1024} to {-180 -&gt; 180}. So, (768,512,512) maps to (90,0,0) which means a rotation of 90 degrees around the X-axis, which is what we expect.

If you look at the difference between ragdoll1out.jpg and ragdoll2out.jpg, you'll see that the lower arm does not make a simple rotation around the Z-axis because the back of the hand goes from facing up to facing the viewer. A Z-axis rotation would leave the back of the hand facing to the left.

Using my Axis-Angle description, ragdoll1out.jpg = (0,90,0) is a rotation of 90 degrees around the Y-axis. ragdoll2.jpg = (90,90,0) is a rotation of 180 degrees around a diagonal line in the XY-plane. (Keep in mind that from the perspective of the lower arm, the XY-plane is the YZ-plane in world coordinates, because the upper arm has also rotated 90 degrees around the Y axis.) This causes the back of the hand to end up in the correct orientation. So, it would seem that rotations might be calculated by simply adding the (absolute values, probably) of each component.

The problem with this is if we animate the transitions. From this perspective, we can see that Ragdoll is incorrectly calculating the intermediate positions: Ragdoll makes the hand moves closer to the face in the second motion, where CoH has the hand moving in a vertical plane.

So, there's either something else going on with calculating rotation amounts, or my Axis-Angle interpretation is completely wrong. Rotation amounts aren't L2-norm (which is what the version you have calculates) or the L1-norm (which is what I described above). I'll play around with it some more and see if I can come up with anything.


 

Posted

Leandro --

After a bit more thought, the arm movement you describe isn't really physically possible. Move your arm into the position described in the first step by bending your shoulder and elbow 90 degrees each. Now, rotate your ELBOW 90 degrees so your hand points to the ceiling. It can't be done. If you put a dot on your bicep and move your arm so your hand points to the ceiling, you can see that what you're really doing is rotating your shoulder 90 degrees along the axis of your upper arm.

That doesn't fully explain the discrepancies between what Ragdoll computes and what CoH computers, of course, but it does explain why the (768,768,512) position for the elbow joint appears to be on the edge of what the client considers to be "physically possible". See this demo. After rotating to the (768,768,512) position, I move the elbow to (1024,768,512) and back, then (768,1024,512) and back. I have no explaination for why these two movements are both along the same axis, except that we're running into some sort of limit that prevents the joint from rotating in a more intuitive way. (It may be a bit tricky to see from the side, but the two last arm movements both occur in a vertical plane.)


 

Posted

[ QUOTE ]
After a bit more thought, the arm movement you describe isn't really physically possible. Move your arm into the position described in the first step by bending your shoulder and elbow 90 degrees each. Now, rotate your ELBOW 90 degrees so your hand points to the ceiling. It can't be done. If you put a dot on your bicep and move your arm so your hand points to the ceiling, you can see that what you're really doing is rotating your shoulder 90 degrees along the axis of your upper arm.

[/ QUOTE ]

You're right. I fail at anatomy. Still, trying to arrive to that pose in that way has the same problem. Poser displays what I'd consider a "proper" rotation (it's the pose I expect to arrive at by rotating the upper arm 90/90/0 and the elbow 0/90/0), Ragdoll is still on a halfway-through pose, and COH renders something else completely:

http://s55.photobucket.com/albums/g1...tlz/poser3.jpg
http://s55.photobucket.com/albums/g1...z/ragdoll3.jpg
http://s55.photobucket.com/albums/g1.../poser3out.jpg

So, any idea how do we arrive to that pose (hand as if grabbing for a pole) in a way that renders the same with COH's engine and either yours or my rotation code? I'm thinking some joints may be interpreting pitch, yaw and roll in a different order, I will have to play with that a bit (though it'll probably lead to another smashed mouse).

[ QUOTE ]
Hmm...after studying this example, I think that my rotation axes are correct, but there's something weird going on with the angles. Here's my current (incorrect) model:

[/ QUOTE ]

I scrapped the entire model I was using in favor of OpenGL's glRotatef API. I figured that's what COH uses. My rotations became more accurate indeed when using it... but it just isn't rotating second-level joints (elbows, knees) the way it should.


www.SaveCOH.com: Calls to Action and Events Calendar
This is what 3700 heroes in a single zone looks like.
Thanks to @EnsonsDeath for the GVE code that made me VIP again!

 

Posted

[ QUOTE ]
I scrapped the entire model I was using in favor of OpenGL's glRotatef API. I figured that's what COH uses. My rotations became more accurate indeed when using it... but it just isn't rotating second-level joints (elbows, knees) the way it should.


[/ QUOTE ]

Does OpenGL use a scene-graph abstraction? That's what I'm using, so you can just set the lower arm as a child of the upper arm, and it automatically inherits the parent's orientation.


 

Posted

What glRotatef does is rotate the drawing point itself. So every new object I draw will inherit all the previous objects until I call glLoadIdentity to "reset" the drawing point. So what I do is: draw lower torso, translate/rotate; draw upper torso, translate/rotate; draw head (head then inherits lower and upper torso translations and rotations). Reset drawing point, perform the translation and rotation for the lower and upper torso without drawing them, draw left upper arm, translate/rotate, draw lower arm (lower arm inherits lower torso, upper torso, upper arm). Reset drawing point again, same for the right arm branch. Reset view again, one leg. Reset drawing point again, the other leg.

It's not pretty in code and it ignores other tools available, but it gave me something that I couldn't get with the other, more "serious" rotation models: I got rid of that "drift" you see in Ragdoll where you rotate "left then up" 90 degrees, and the final result is not "left then up" 90 degrees, but more like 70 degrees left then 20 degrees up. Poser WILL perform a series of 90° rotations and have no "drift"... but the rotation order doesn't match what COH does all the time. It does "sometimes".

So at this point I'm thinking that some of the joints use a different rotation order: upper arm is fine with RYP, but lower arm maybe wants YPR. I will have to play with it a little.


www.SaveCOH.com: Calls to Action and Events Calendar
This is what 3700 heroes in a single zone looks like.
Thanks to @EnsonsDeath for the GVE code that made me VIP again!

 

Posted

Here's a slightly more correct version:

http://www.mediafire.com/?1bnteo3jqm2

I believe that this version can do right angles a bit better, but it's still off computing the in-between values. I'm going to take a look at getting angles for individual joints correct. This will probably take at least a week -- I have some other projects that will be consuming my attention for the next several days.

Edit: Note that you need the .dll files from the previous version, or you can install Java 3D yourself via the links in the first post.


 

Posted

Isnt OpenGl fun =)

The translates and rotates are pain until you get them right. Lost to the depths of UNIX servers was a program I wrote 10 or so years ago that was, for lack of a better term, a ragdoll simulator/animator in OpenGl (minus the head). Twas a model based off my dimensions that I could create different poses/animations for. Got me an A in the my Comp Animation class (elective for my EE degree).

Anyways, from what I can remember on the gotcha of rotate and translate was that you rotated, then translated to get the next joint. Thats very key. To go from torso point (0,0,0) to neck, you would rotate the torso, then translate (ex. 1unit up) (0,1,0). That way the head, drawn from the neck's reference point would be facing the same way as the torso, or you could rotate it then to look left/right (and then translate if you wanted facial characteristics, like eyes/ears etc.).

Alot of it just how you define your joints and frames of reference. And math.... lots and lots of math.


Tanker Tuesday #72 Oct 5 @Champion

"I am not sure if my portrayal of being insane is accurate, but damn its fun all the same."

 

Posted

[ QUOTE ]
I scrapped the entire model I was using in favor of OpenGL's glRotatef API. I figured that's what COH uses.

[/ QUOTE ]
Actually, I kinda doubt it. If COH drew the various limbs as discrete meshes then yes, a simple rotate / translate chain could be used. Most bone-based animation systems however, stretch the verticies of a single mesh to match the desired bone orientation. OpenGL only handles drawing graphics primitives, so that's normally calculated in software (or possibly vertex shaders). Given the age of the CoH engine I'm thinking probably software calculations.


 

Posted

[ QUOTE ]
So what I do is: draw lower torso, translate/rotate; draw upper torso, translate/rotate; draw head (head then inherits lower and upper torso translations and rotations). Reset drawing point, perform the translation and rotation for the lower and upper torso without drawing them, draw left upper arm, translate/rotate, draw lower arm (lower arm inherits lower torso, upper torso, upper arm). Reset drawing point again, same for the right arm branch. Reset view again, one leg. Reset drawing point again, the other leg.

[/ QUOTE ]
Just out of curiosity, have you considered using glPushMatrix/glPopMatrix to save and restore the transformation matrix of the lower and upper torso? Might reduce code complexity a little if you don't have to keep performing the translation and rotation for them every time you reset.


 

Posted

MTS remember the whole point of both Poser and Ragdoll is to make something that matches COH's movement *first*. There's plenty of time to make pretty and efficient code after that. I'd rather have something where I have to reset a bunch of times but I have exact control of everything, than to screw up something in the glNewList enumerations or the glPushMatrix/glPopMatrix hierarchy and not realize because I'm blaming the rotations. After it works, and it mimics exactly what COH does, fine, then I'll think about a prettier model and easier to understand code.

Right now what matters is the basics; if Poser can't get its own model to match the movements of the COH model exactly, it's poop; I can prettify the code all you want, and it'll still be polishing a poop.

Don't let me or Balshor stop you from making your own version, though, the more hands in the mess the more likely one of us will figure out how to do it properly.


www.SaveCOH.com: Calls to Action and Events Calendar
This is what 3700 heroes in a single zone looks like.
Thanks to @EnsonsDeath for the GVE code that made me VIP again!

 

Posted

Here's an update on where I am in figuring out these angles. Download links are at the end of the post.



I'm working from Leandro's assumption that the three parameters represent three consecutive rotations around the horizontal (X), vertical (Y), and depth (Z) axes. The first thing to figure out is the order these rotations are applied. So, I created an animation that placed the upper right arm at (640,640,640), which should indicate a 45 degree rotation around each axis. Next, I increased each of the three parameters to 768 (90 degrees) and back. The sequence went like this:

(640,640,640)-&gt;(768,640,640)-&gt;(640,640,640)-&gt;(640,768,640)-&gt;(640,640,640)-&gt;(640,640,768)-&gt;(640,640,640)

In the animation that CoH displays, you can see that the first motion looks like a rotation around the X-axis, while the second two are rotating around some skewed axis.

Next, I modified the animation so the first parameter was always 512 (no rotation). Looking at the animation in CoH, the second motion (where we vary the third parameter) appears to rotate around the Z-axis, while the first motion rotates around a skewed axis.

So, this suggests that we first use the second parameter to rotate around the Y-axis, then the third parameter to rotate around the Z-axis, then the first parameter to rotate around the X-axis. (These rotations are around the fixed axes.) I've mocked up a version of my program that does exactly that.

Again, however, the problem is the amount of rotation. If you compare Ragdoll's computed animation with the CoH animation when varying only the second and third parameter. In the first motion (varying the second parameter), Ragdoll moves the hand diagonally towards the shoulder. In CoH, the hand moves more across the waist. The other positions also appear to be slightly too high in Ragdoll.

So, I'm guessing that one of two things are occuring: The first possibility is that the three-rotations-around-fixed-axes model is incorrect. The second possibility is that the limbs are spring-loaded, which messes with the amount of rotations that's applied to them. (This is a real possibility, not just something I'm making up. The PhysX API has provisions for spring-loading joints so the PhysX engine can correctly compute how forces act on limbs.) I'm hoping that the problem is the first possibility and not the second; the next step in my master plan (Mwuhahaha!) will be to try to find some animations that will conclusively disprove the 3-rot model and hopefully suggest a more correct one.

(Of course, there's also the possibility that I'm interpreting what I'm seeing incorrectly. Feel free to take a look at the animations and tell me if I'm seeing something different than what's actually there.)




Ragdoll v0.0.8: still not entirely correct. You need Java3D or the .dll files from the v0.0.6 version. See the first post for links to those.

The animations I used as both .xml and .cohdemo files: rename the .xml files to animate.xml to load them into Ragdoll. This version uses the 3-rot model I describe above, where we rotate first around the Y axis, then the Z axis, then the X axis. These are rotations around fixed axes.


 

Posted

Has any further progress been made with this?

Michelle
aka
Samuraiko/Dark_Respite


Dark_Respite's Farewell Video: "One Last Day"
THE COURSE OF SUPERHERO ROMANCE CONTINUES!
Book I: A Tale of Nerd Flirting! ~*~ Book II: Courtship and Crime Fighting - Chap Nine live!
MA Arcs - 3430: Hell Hath No Fury / 3515: Positron Gets Some / 6600: Dyne of the Times / 351572: For All the Wrong Reasons
378944: Too Clever by Half / 459581: Kill or Cure / 551680: Clerical Errors (NEW!)

 

Posted

Hey Balshor. I've been using your demo editor for years and it is incredible. I noticed that there's a newer version - v0.9.1. I wanted to download the exe version but the link seems to be dead. Do you have another link to it? Here's your original coh thread for it.

http://coh103.gtm.cityofheroes.com/s...25020&langid=4


 

Posted

Quote:
Originally Posted by EarthFury View Post
Hey Balshor. I've been using your demo editor for years and it is incredible. I noticed that there's a newer version - v0.9.1. I wanted to download the exe version but the link seems to be dead. Do you have another link to it? Here's your original coh thread for it.

http://coh103.gtm.cityofheroes.com/s...25020&langid=4
He has not posted anything in the forums since 04-18-2009, 09:52 AM. I dont think he is gonna reply...lol

Nice Necro Post


Proton Sentry Peacebringer:lvl 50+++ - Human Build / Triform Build
Quasar Sentry Warshade:lvl 50+- Human Build / Triform Build
Red Katipo Arachnos Soldier:lvl 50+++ - Crab Build / Bane Build
Black Katipo Arachnos Widowlvl 50+++ - Fortunata Build / Night Widow Build