Game Development Community

dev|Pro Game Development Curriculum

Ragdoll Pack Gets Multiple Character Support

by Chris Calef · 09/13/2005 (4:28 pm) · 20 comments

Well, once again it took about six times as long as projected, what with all the annoying details of real life that tend to obstruct the flow of coding, but... as of today, the ragdoll animation pack is NO LONGER LIMITED TO ORCS!!

www.hardrealitygames.com/images/jill_RTS_1.gif
It now works with Jill (Tank Girl Pack), Adam Pack, AND Soldier Pack. If you've held off buying it because you want support for one of the above models, wait no more!! Each of the supported models has its own dsq file containing a full set of ragdoll animations that work with that model -- kork_ragdoll.dsq, jill_ragdoll.dsq, etc.

www.hardrealitygames.com/images/adam_RTS_ragdoll_4.gif
Furthermore (and this is even more exciting from my perspective), I achieved a technological breakthrough in the process!!! (While I strive to maintain an atmosphere of humility, I think I deserve some bonus points for this one!!!) This is the reason this update took so darn long. I'm one of those people who, given a choice between an easy way (short-term payoff) and a hard way (long-term benefits), just has to take the hard way. In this case, the "easy way" would have meant building a separate ragdoll model for each character, and doing all 72 animations again for each one, and saving them out. This would have taken a few days, and could have been done a long time ago, but it wouldn't have done me much good in the long term. It would have been a laborious process that would have had to be repeated every time a new model came along.

Instead, being a true masochist, I decided that it should be possible to dispense with this whole process and instead simply convert the animations from the orc model to the other models. It stood to reason that any roughly bipedal form should be able to perform motions designed for another similar bipedal form, given that they have the same number of arms and legs, after all.

Thus:

www.hardrealitygames.com/images/soldier_dance_5.gif
As you can see, it worked! (For those who don't recognize it, that soldier is doing the "Kork dance" from the Torque demo. As far as I know, it's the first time anyone without a Kork skeleton has ever done those particular moves!)

The difficulty here, of course, is that each model is built with nodes having totally different local orientations. If you open up the Show Tool Pro, and load Kork, then Jill, then Soldier, and start clicking around on their bodyparts and observing the red, green, and blue axis indicators, you will see what I mean. Even if you fix all the node names, which need to match for the dsq to even load, you will find unexpected and undesirable results if you don't deal with the node orientations.

However, except for Jill's feet, the one similarity they all share is the fact that the bodyparts all tend to be aligned at 90 degree angles to the global axes. Without that fortuitous coincidence, I don't know if I would have ever figured this out. As is, though, it usually only requires some number of 90 or 180 degree rotations around one axis or another to "fix" the movement of each node in a sequence, so that it will work with a different model.

Anyway, the long and short of it is: it was really hard, but I got it, and now I can finally work on something else! Yay!!!

Before I go, I have one question for the community: Is anyone else beset with the problem of animations they wish they could convert from one model to another? And more to the point, would the solution be worth a few bucks to you? I'm thinking of incorporating my new logic into a GUI-based tool for sale if there's demand. It seems pretty handy to me, anyway. Speak up now if you want it, otherwise I'm back to churning out animations!

Peace out,
Chris

#1
09/13/2005 (4:54 pm)
Do the normal death sequences work again? Or does installing the ragdoll pack destroy the normal (non-ragdoll explosion) death animations so that when the client dies it just freezes at the last position?
#2
09/13/2005 (5:00 pm)
sick!! (as in cool)
#3
09/13/2005 (5:06 pm)
@Chris: Uh.. whoops, sorry! Forgot that was an issue. It's an easy fix, I'll post it up here in a bit. There's a bit of a toss-up, though, because if you happen to die from a ragdoll-related injury, then it's nice to leave the player body in the ragdoll pose. However, it is admittedly ugly to die in other positions. Maybe I can hook it up to test for that.
#4
09/13/2005 (5:22 pm)
Good to see this out in the open :)

Great work chris.
#5
09/13/2005 (5:39 pm)
Thanks Chris, hope you solve it soon :) Then I'll add it back into my project.
#6
09/13/2005 (5:45 pm)
@Chris: Okay, solved! Actually, turns out it was entirely unnecessary anyway, doh!

In server/scripts/player.cs, find this function:

function Player::playDeathAnimation(%this)
{
//if (%this.deathIdx++ > 11)
// %this.deathIdx = 1;
//%this.setActionThread("Death" @ %this.deathIdx);
}

The solution is: don't comment it out. I thought that was going to interfere with the ragdoll animations, but it doesn't. Everything works fine if you leave it in, and it still plays the ragdoll if applicable. If you die from a fall or something, it plays the death animation.
#7
09/13/2005 (6:18 pm)
Great ... I never had any problems with the initial ragdolls. I look forward to using this with more skeletons though.
#8
09/13/2005 (7:01 pm)
Nice dude. That freaking rocks. I already told you how much it rocks in private, but seriously people: THIS ROCKS. Great work.

Hey, I just thought of another implication of this. I think it might be able to fix the problems I'm having with the Blender Torque Skeleton. The problem is that I can't align the axes correctly and I can't make the heirarchy match and still keep the same IK setup. But, if I can get it to export and then remap the bone axes and hierarchy with your workflow, it would solve the problem! I wonder if there's a way to automate the conversion in the python export script with pre-determined offset values...

What do you think?
#9
09/13/2005 (8:38 pm)
Quote:I'm thinking of incorporating my new logic into a GUI-based tool for sale if there's demand. It seems pretty handy to me, anyway. Speak up now if you want it, otherwise I'm back to churning out animations!


I think anything that helps with animations is a great thing for us non-artistic types.
#10
09/13/2005 (9:33 pm)
Very cool stuff!

One question, does scale have an impact on your method of animation conversion? If it doesn't, then hell yes, I see a use for it!

I was hoping to accomplish something very similar using 3dsMax's Biped, but I ran into a couple of brick walls I am currently working around.
#11
09/13/2005 (9:34 pm)
Chris... VERY AWESOME! :) Congrats on the breakthrough, those always feel so great.
#12
09/13/2005 (10:58 pm)
hey, thanks everybody!!!

@Karthik: scale doesn't matter unless your animation uses translations, which most of them don't outside of the default position. Anyway, that's an easy thing to adjust... what I'm thinking of is a gui tool where those values are exposed directly, so you could type in new translation values and even mess with the quats (changing the rotations numerically), without leaving torque.

@Jeff: thanks man! Re: Blender/Python, I've never worked in python but there must be a way to do it, any thing that lets you rotate your nodes via euler angles (i.e. "rotate by 90 on the Y followed by 180 on the X") will work. Yeah, I'm sure you could automate it. Just load the values in from a file after you do the IK or whatever else you need to do with it in its original frame of reference.

Here's a sample cfg file I'm using, just to give you an idea of how complicated it isn't at least. This is to get from Kork to the soldier model. (Which, for people who haven't examined it, is set up very conveniently in that if the guy standing straight up with his arms straight out, facing +Y, with +X to his right and +Z=up, then all of his bones share that same orientation. I.e., his right arm has all the bones lined up on their local +X axis, the left arm is lined up to -X, spine segments are oriented locally on +Z, etc.)

# kork2soldier
0;0;(0,90,0);(0,0,0);(0,90,0);(0,0,0);
1;1;(0,90,0);(0,0,0);(0,0,0);(0,0,0);
2;2;(0,90,0);(0,0,0);(0,0,0);(0,0,0);
3;3;(0,90,0);(0,0,0);(0,0,0);(0,0,0);
4;4;(0,90,0);(0,0,0);(0,0,0);(0,0,0);
5;5;(0,90,0);(0,0,0);(0,0,0);(0,0,0);
7;25;(0,0,180);(0,0,0);(0,0,180);(0,-90,0);
8;26;(0,0,180);(0,0,0);(0,0,0);(0,0,0);
9;28;(0,0,180);(0,0,0);(0,0,0);(0,0,0);
10;30;(0,0,180);(90,0,0);(90,0,0);(0,0,0);
11;9;(180,0,0);(0,0,0);(180,0,0);(0,-90,0);
12;10;(180,0,0);(0,0,0);(0,0,0);(0,0,0);
13;12;(180,0,0);(0,0,0);(0,0,0);(0,0,0);
14;14;(180,0,0);(0,0,0);(-90,0,0);(0,0,0);
18;48;(0,-90,0);(0,0,0);(0,180,0);(0,0,0);
19;49;(0,-90,0);(0,0,0);(0,0,0);(0,0,0);
20;51;(0,-90,0);(0,0,0);(0,0,0);(0,0,0);
22;42;(0,-90,0);(0,0,0);(0,180,0);(0,0,0);
23;43;(0,-90,0);(0,0,0);(0,0,0);(0,0,0);
24;45;(0,-90,0);(0,0,0);(0,0,0);(0,0,0);

The first number is the node on the Kork model, the second is the corresponding node on the soldier model. The first two rotations are to get from the kork orientation to the corresponding soldier orientation, the second two are to get from the child node on the kork model back to the orientation of it's parent node. Hard to explain precisely, but that's all the numbers there are. Here's the core snippet:

sscanf(buf,"%d;%d;(%f,%f,%f);(%f,%f,%f);(%f,%f,%f);(%f,%f,%f);",&node_there,&node_here,&q1x,&q1y,&q1z,&q2x,&q2y,&q2z,&q3x,&q3y,&q3z,&q4x,&q4y,&q4z);

    qa.set(EulerF(mDegToRad(q1x),mDegToRad(q1y),mDegToRad(q1z)));
    qb.set(EulerF(mDegToRad(q2x),mDegToRad(q2y),mDegToRad(q2z)));
    qc.set(EulerF(mDegToRad(q3x),mDegToRad(q3y),mDegToRad(q3z)));
    qd.set(EulerF(mDegToRad(q4x),mDegToRad(q4y),mDegToRad(q4z)));
    seq.rotationMatters.set(node_here);
    for (i=0;i<seq.numKeyframes;i++) {
      q1.mul(qb,qa);
      q2.mul(qd,qc);
    other->nodeRotations[i+(nodes_there_count[node_count]*seq.numKeyframes)+other->sequences[sequence].baseRotation].getQuatF(&q3);

      q4.mul(q3,q2);
      q5.mul(q1,q4);
      q6.mul(q5,q1.inverse());

     int ni = startRot+(nodes_here_count[node_count]*seq.numKeyframes)+i;
     nodeRotations[ni].set(q6);
    }
    node_count++; 
  }
#13
09/14/2005 (6:57 am)
Brilliant! Excellent work. This is the type of product I would purchase, a nice leveraging tool.
#14
09/14/2005 (5:06 pm)
Awesome work! I just went to Discreet's presentation of Max 8, and one of the big features they flaunted (besides pelt mapping *drool*) was a feature that allowed you to transfer animations between models. So IMO, releasing a tool that did the same thing would be a very lucrative decision.
#15
09/14/2005 (5:18 pm)
Hmmm, cool. Thanks for the info!
#16
09/14/2005 (10:46 pm)
Holy cr** you never cease to amaze me. =) Not only have you totally delivered on the multi-model support, but you're going to create a GUI to transfer animations from other models?! Incredible. My only question is...when can I buy it?
#17
09/24/2005 (11:14 pm)
@Chris Calef
Chris, I'm having a time integrating this with the soldier pack. I know that my player selection stuff is probably the culprit, but the only error message it throws out is the first one below, and all the standard animations play properly including the soldier death animation.

"starter.fps/server/scripts/radiusDamage.cs (0): Unknown command chooseRagdollSeqRadius.
Object (2926) Item -> ShapeBase -> GameBase -> SceneObject -> NetObject -> SimObject"

I have four different player selections each with their own directory and /server/player.cs files (i.e. player.cs, player2.cs) each reference the soldier.cs scripts for animation in their respective directories, again each named soldier1.cs, soldier2.cs, etc. Since the soldier dsq sequences are named different than the player dsq sequences, I added the soldier_ragdoll.dsq as "sequence30=soldier_ragdoll.dsq" after the soldier sequence11. The players are selected in the clientConnection.cs in the common/server/ directory and are assigned by the function below -

function GameConnection::onConnect( %client, %name, %armor )
{
switch$(%armor)
{
case "Player4":
%client.armor = "Player4Armor";
case "Player3":
%client.armor = "Player3Armor";
case "Player2":
%client.armor = "Player2Armor";
default:
%client.armor = "Player5Armor";
}

I think that probably right here is where the problem is but I don't know. I've tried a lot of different things in regards to -

function Player::chooseRagdollSeqRadius(%this,%impulseVec)
{ //only works for player model at this point
if( strstr( %this.getDatablock().shapeFile, "player" ) != -1 ) {
%numAngles = 8;

taking into account that my shapeFile is "soldier1" (or soldier2, etc.) I've tried that. I've tried changing the function Player to other things like the %client.armor names, i.e. Player2, Player3, etc. with no luck. I'm guessing what is happening is that "function Player::chooseRagdollSeqRadius" is not getting called because my player is really some other object because of the player selection, but then I wouldn't have expected any of the stadard animations to play either, but they do. I know this is a long, rambling post but If you have any clue I'd appreciate it, but if not I totally understand. I've probably hosed something with the player selection scripts.

Alan
#18
10/02/2005 (5:56 pm)
@Chris Calef
Got it working. It was a combination of things. Used the modification to the Player.cs that James Thompson got from you and posted in the forums, also I had renamed each of the dts files in the unique directories ( i.e., ~shapes/soldier2/soldier2.dts, etc. and discovered that it wasn't required. Renamed them back to just soldier.dts and corrected anything that pointed to them that way and it corrected the problem. Now I have player selection AND ragdoll animations. I'm having a little bit of issue with impulses and finding a nice balance between launching the player and moving some physics objects I have, but all in all it works pretty nicely now.
Thanks again for the resource.
-Alan
#19
07/08/2006 (11:38 am)
Just wondering if you've made any progress on the animation converter and if we'll be able to buy copies at some point in the relative future :)
#20
08/05/2006 (9:06 am)
I may be a day late (or a year :0) but I am not a dollar short... That tool would be awesome and I would buy it.