Animation of joints
by Kynan Eng · in Torque Game Engine · 07/15/2005 (7:24 am) · 10 replies
Is there a tutorial to show how to programmatically access certain nodes in an object, and apply transformations to the nodes? If we have a hand model with skeleton exported from MilkShape3D, how do we access joints by name and apply transformations to joints (driven by input events)?
About the author
#4
moving a player object on a platform while the platform itself is moving has been a nagging thorn in the sides of many here for quite a while (or so it seems)... myself included...
i finally got something that worked... (finally!!!!) and before i go any further i must THANK all who preceeded me and posted their findings and ideas on the topic...
i wound up subscribing to the 'moving mount point' school of thought, since that seemed like it would best work within my requirement for having a cross platform, scripting only solution...
building on top of the example 6 code in the Ken Finney book, i used the Milkshape model of the cylinder thing i was using for my testing of client/server communications and collisions, and since i already had a mount) joint, i simply added a 20 frame animation, for the joint only, that moved it from point a to point b within the cylinder model...
... i then added a single animation sequence material to the model, and named it in accordance with table 14.3 on page 458.
the last thing in Milkshape was to export it to DTS (making sure that the Anim button is OFF first).
now in Torque script i simply wanted to see if it worked... so i ran the app, collided the player with the cylinder model... opened the console, and entered 1294.playThread(0,"root")... and to my amazement the player started moving along the animated route... just like he was moving on the platform.
oh happy days.... oh joyous days... may wonders and miracles never cease :)
it actually worked...
well... now i'm thinking myself a big time Toque scripter... with newfound unlimited powers to do anything in script... heyyyyy :) :) :)
so, i next is to try this over the server... should be easy right... and after that, lets start and stop the movement...
anyway... this isn't a perfect solution for ever situation... but in my scaled down vision of my sub thing, where the players now point and click to where they wanna go as opposed to freely moving around, this should work...
while there is no free roaming, with this you can still give point to point movement of a player character aboard any sort of moving platform without having to worry about collisions or engine code changes...
--Mike
07/20/2005 (7:10 am)
Ok... i finally had some time (and motivation) to look further into this...moving a player object on a platform while the platform itself is moving has been a nagging thorn in the sides of many here for quite a while (or so it seems)... myself included...
i finally got something that worked... (finally!!!!) and before i go any further i must THANK all who preceeded me and posted their findings and ideas on the topic...
i wound up subscribing to the 'moving mount point' school of thought, since that seemed like it would best work within my requirement for having a cross platform, scripting only solution...
building on top of the example 6 code in the Ken Finney book, i used the Milkshape model of the cylinder thing i was using for my testing of client/server communications and collisions, and since i already had a mount) joint, i simply added a 20 frame animation, for the joint only, that moved it from point a to point b within the cylinder model...
... i then added a single animation sequence material to the model, and named it in accordance with table 14.3 on page 458.
the last thing in Milkshape was to export it to DTS (making sure that the Anim button is OFF first).
now in Torque script i simply wanted to see if it worked... so i ran the app, collided the player with the cylinder model... opened the console, and entered 1294.playThread(0,"root")... and to my amazement the player started moving along the animated route... just like he was moving on the platform.
oh happy days.... oh joyous days... may wonders and miracles never cease :)
it actually worked...
well... now i'm thinking myself a big time Toque scripter... with newfound unlimited powers to do anything in script... heyyyyy :) :) :)
so, i next is to try this over the server... should be easy right... and after that, lets start and stop the movement...
anyway... this isn't a perfect solution for ever situation... but in my scaled down vision of my sub thing, where the players now point and click to where they wanna go as opposed to freely moving around, this should work...
while there is no free roaming, with this you can still give point to point movement of a player character aboard any sort of moving platform without having to worry about collisions or engine code changes...
--Mike
#5
07/20/2005 (2:54 pm)
For people who never bought that book, could you please post your Script? If you are releasing it. I think I know of a way to make it work like normal. I made something like this to, but I gave up on the feature so I could wait for matthews code. Thanks
#6
easier to grasp... and add stuff...
ok... here goes...
background
it all started from my need to be able to move a player around on a moving (pitching and rolling) platform... in particular, inside a submarine model or on a ship.
requirements
whatever solution i came up with had to be within the constraints of my limited working knowledge of TGE, and had to be cross platform... ideally, a scripting solution would be the easiest to implement.
the plan
after reading a lot about moving interiors, moving platforms, collisions, networking implications... i finally came up upon a post (forgive me, i can't remember who it was) that suggested a defacto solution which would give predetermined movement on a moving object with a minimum of work... the suggestion was elegant in its simplicity... since we already can attach a player to a node in a model, why not animate the node to provide the predetermined movement... you can animate nodes in a model!!??? news to me... but if this is possible, this may be the answer...
step1 - the model
first thing to do was to make a simple model with an unconnected joint, to be used as a mount node... i did this in Milkshape, added it to my project, then ran it to verify the object came across ok.
then i went back to Milkshape, and selected the node, clicked the Anim button, changed the number of frames to 20, scrolled the index to the first frame, positioned the node, and clicked it off as the first keyframe...
next i moved the index up 5 ticks for the next key frame, moved the node, and set that one... i did this 3 more times for a total of 5 key frames for the animation to run on... then i played the animation in Milkshape to verify that that was working...
step2 - prepping for export
next comes the part that was new to me... preparing the model and the animation for export to dts (you need to have the milkshape dts export plugin, i think it comes with Milkshape, i'm using version 1.7.4) format... i only had one animation so i had to add 1 material to the model (normally used for textures, the dts exporter will use this info to organize animation threads)... and then name the material to reflect certain info for the animation... the format is as follows for the name:
seq: option, option, the name i used was seq:root=1-20, fps=5, cyclic
now that you have a model, with a single animated joint (named mount0 so it can be used as one of the mounts in Torque) you are ready to export it to dts... just one thing, make sure the Anim button is not depresssed... animations should be off when exporting.
step3 - export it
export it and move it (and any related textures) to the proper directory... (i used ..\Emaga6\control\data\shapes\ and added a folder called TestShip).
ok... that's it for the model... you are ready to first add the object to your project...
*** see next post ***
--Mike
07/20/2005 (8:05 pm)
Sure Robert... i'll post the scripts, and maybe a lil bit on the modeling and animating the single mount point in Milkshape... but, in all respects, the script code from the book has very little to do with using the joints as animated mount points... i use it because the code gets rid of a lot of stuff that the base fps and realm wars demos come with... stuff that only serves to confuse me... pretty much making it a minimal application...easier to grasp... and add stuff...
ok... here goes...
background
it all started from my need to be able to move a player around on a moving (pitching and rolling) platform... in particular, inside a submarine model or on a ship.
requirements
whatever solution i came up with had to be within the constraints of my limited working knowledge of TGE, and had to be cross platform... ideally, a scripting solution would be the easiest to implement.
the plan
after reading a lot about moving interiors, moving platforms, collisions, networking implications... i finally came up upon a post (forgive me, i can't remember who it was) that suggested a defacto solution which would give predetermined movement on a moving object with a minimum of work... the suggestion was elegant in its simplicity... since we already can attach a player to a node in a model, why not animate the node to provide the predetermined movement... you can animate nodes in a model!!??? news to me... but if this is possible, this may be the answer...
step1 - the model
first thing to do was to make a simple model with an unconnected joint, to be used as a mount node... i did this in Milkshape, added it to my project, then ran it to verify the object came across ok.
then i went back to Milkshape, and selected the node, clicked the Anim button, changed the number of frames to 20, scrolled the index to the first frame, positioned the node, and clicked it off as the first keyframe...
next i moved the index up 5 ticks for the next key frame, moved the node, and set that one... i did this 3 more times for a total of 5 key frames for the animation to run on... then i played the animation in Milkshape to verify that that was working...
step2 - prepping for export
next comes the part that was new to me... preparing the model and the animation for export to dts (you need to have the milkshape dts export plugin, i think it comes with Milkshape, i'm using version 1.7.4) format... i only had one animation so i had to add 1 material to the model (normally used for textures, the dts exporter will use this info to organize animation threads)... and then name the material to reflect certain info for the animation... the format is as follows for the name:
seq: option, option, the name i used was seq:root=1-20, fps=5, cyclic
now that you have a model, with a single animated joint (named mount0 so it can be used as one of the mounts in Torque) you are ready to export it to dts... just one thing, make sure the Anim button is not depresssed... animations should be off when exporting.
step3 - export it
export it and move it (and any related textures) to the proper directory... (i used ..\Emaga6\control\data\shapes\ and added a folder called TestShip).
ok... that's it for the model... you are ready to first add the object to your project...
*** see next post ***
--Mike
#7
you need this document - torque_scripting_basics021103.pdf
this is a concise read and goes over everything the noob like me needs to understand, before going anywhere with TGE... if you don't understand this before moving on, i suggest that you don't move on...
seriously, and forgive me for being a lil blunt here... i wasted weeks before looking at this...
this doc was on the original TGE cd release... and is part of the TotalTorqueDocs compilation that was going around a while back...
ok... if you're ready, lets add the new object to our project...
forget just adding it with the editor... that's a dead end practice to get used to... make a testship.cs script file like the one below, and add it to the server directory subtree of the control folder... in my project it is Emaga6\control\server...
you want it here so that the server scripts can easily find it and execute it in response to the Server::OnServerCreated event... add Exec("./testship.cs"); there...
one thing i forgot to mention above... get an editor that you not only feel comfortable with... but one that lets you see all the script files and directories in the context of a single application... start thinking application and events... get familiar with the events that the engine raises... this will help in figuring out where to add your code...
i'm still using the TribalIDE editor... it works for me...
ok, back to getting the object in the project... the TestShip.cs is below...
step 1 TestShip.cs
datablock ItemData(TestShip){
className="TestShip";
shapeFile="control/data/shapes/TestShip/Hull2.dts";
mass=1;
eyeoffset="0 -330 -211";
friction=1;
};
function InsertTestShip(){
$TestShip=new Item(){
rotation="0 0 1 0";
position = "-45.8946 -21.6984 211.921";
scale="1 1 1";
dataBlock= "TestShip";
};
MissionCleanup.add($TestShip);
$TestShip.setTransform( "-45.8946 -21.6984 211.921");
//return %TestShip;
//commandToClient(%client,'ShowTestShip');
}
function ServerCmdMoveTestShip(%client){
%xfrm=$TestShip.getTransform();
%lx=getword(%xfrm,0);
%ly=getword(%xfrm,1);
%lz=getword(%xfrm,2);
%lx-=0.19;
$TestShip.setTransform(%lx SPC %ly SPC %lz );
}
function ServerCmdStartPlayerMoveAnim(){
$TestShip.playThread(0,"root");
}
function ServerCmdStopPlayerMoveAnim(){
$TestShip.pauseThread(0);
}
now edit Server.cs to have it exec TestShip.cs at ServerCreate time...
Exec("./testship.cs");
now all the functions and datablocks are available... to the server... which is what you want...
step2 - adding your testship
next you have to actually add the new object... this is also done here (in Server.cs), but in response to a different event... look for the OnMissionLoad() event handler... it should look something like this
function onMissionLoaded()
{
// Called by loadMission() once the mission is finished loading.
// Nothing special for now, just start up the game play.
startGame();
inserttestship();
}
you see inserttestship() is called here, and since it was defined in TestShip.cs it is available to the project... if you run the project now, you should see the ship... go through the code above... it's light, and illustrative...
*** see next post ***
07/21/2005 (7:14 am)
Now here is where you've gotta be a lil careful... you've gotta decide where to place the code... this implies that you have some knowledge and understand the client server concept that Torque runs under... it took me months to come to grips with this, mostly because i didn't work on it continously and i had to find the correct references... it should take you a lot less... more like a few minutes... an hour at the most...you need this document - torque_scripting_basics021103.pdf
this is a concise read and goes over everything the noob like me needs to understand, before going anywhere with TGE... if you don't understand this before moving on, i suggest that you don't move on...
seriously, and forgive me for being a lil blunt here... i wasted weeks before looking at this...
this doc was on the original TGE cd release... and is part of the TotalTorqueDocs compilation that was going around a while back...
ok... if you're ready, lets add the new object to our project...
forget just adding it with the editor... that's a dead end practice to get used to... make a testship.cs script file like the one below, and add it to the server directory subtree of the control folder... in my project it is Emaga6\control\server...
you want it here so that the server scripts can easily find it and execute it in response to the Server::OnServerCreated event... add Exec("./testship.cs"); there...
one thing i forgot to mention above... get an editor that you not only feel comfortable with... but one that lets you see all the script files and directories in the context of a single application... start thinking application and events... get familiar with the events that the engine raises... this will help in figuring out where to add your code...
i'm still using the TribalIDE editor... it works for me...
ok, back to getting the object in the project... the TestShip.cs is below...
step 1 TestShip.cs
datablock ItemData(TestShip){
className="TestShip";
shapeFile="control/data/shapes/TestShip/Hull2.dts";
mass=1;
eyeoffset="0 -330 -211";
friction=1;
};
function InsertTestShip(){
$TestShip=new Item(){
rotation="0 0 1 0";
position = "-45.8946 -21.6984 211.921";
scale="1 1 1";
dataBlock= "TestShip";
};
MissionCleanup.add($TestShip);
$TestShip.setTransform( "-45.8946 -21.6984 211.921");
//return %TestShip;
//commandToClient(%client,'ShowTestShip');
}
function ServerCmdMoveTestShip(%client){
%xfrm=$TestShip.getTransform();
%lx=getword(%xfrm,0);
%ly=getword(%xfrm,1);
%lz=getword(%xfrm,2);
%lx-=0.19;
$TestShip.setTransform(%lx SPC %ly SPC %lz );
}
function ServerCmdStartPlayerMoveAnim(){
$TestShip.playThread(0,"root");
}
function ServerCmdStopPlayerMoveAnim(){
$TestShip.pauseThread(0);
}
now edit Server.cs to have it exec TestShip.cs at ServerCreate time...
Exec("./testship.cs");
now all the functions and datablocks are available... to the server... which is what you want...
step2 - adding your testship
next you have to actually add the new object... this is also done here (in Server.cs), but in response to a different event... look for the OnMissionLoad() event handler... it should look something like this
function onMissionLoaded()
{
// Called by loadMission() once the mission is finished loading.
// Nothing special for now, just start up the game play.
startGame();
inserttestship();
}
you see inserttestship() is called here, and since it was defined in TestShip.cs it is available to the project... if you run the project now, you should see the ship... go through the code above... it's light, and illustrative...
*** see next post ***
#8
function MaleAvatar::onCollision(%this,%obj,%col,%vec,%speed)
//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
{
%obj_state = %obj.getState();
%col_className = %col.getClassName();
%col_dblock_className = %col.getDataBlock().className;
%colName = %col.getDataBlock().getName();
if ( %obj_state $= "Dead")
return;
if (%col_className $= "Item" || %col_className $= "Weapon" ) // Deal with all items
{
%obj.pickup(%col); // otherwise, pick the item up
}
if (%col_dblock_className $= "TestShip"){
%node=0;
%col.mountObject(%obj,%node);
%obj.mVehicle=%col;
}
// Mount vehicles
%this = %col.getDataBlock();
%pushForce = %obj.getDataBlock().pushForce; // Try to push the object away
if (!%pushForce)
%pushForce = 20;
%eye = %obj.getEyeVector(); // Start with the shape's eye vector...
%vec = vectorScale(%eye, %pushForce);
%vec = vectorAdd(%vec,%obj.getVelocity()); // Add the shape's velocity
%pos = %col.getPosition(); // then push
%vec = getWords(%vec, 0, 1) @ " 0.0";
%col.applyImpulse(%pos,%vec);
}
look at the conditional if (%col_dblock_className $= "TestShip"), about midway down... this mounts your player guy to the node (joint) mount0 in the model...
also, a lil bit more to add in the same file...
function HumanMaleAvatar::onMount(%this, %obj, %vehicle, %node) {
if (%node=0){
%obj.setControlObject(%vehicle);
}
}
i think that's all you need to have the player guy mount the testship... run the project, and have the player guy run towards and collide with the ship... he should mount the mountpoint...
finally - some movement
now comes the really fun part... we're gonna add the code to trigger the joint animation, thus moving our guy around on the platform...
actually, you already have some of this code in place... look up a post or two to the TestShip.cs code... the functions ServerCmdStartPlayerMoveAnim and ServerCmdStopPlayerMoveAnim() both respond to commands from the client will start the animation thread running... all we've gotta do is add those functions.
i decided to add em in response to keypresses, and added the code to Emaga6\control\server\presetkeys.cs , but i suppose this can go anywhere your keybinds are...
PlayerKeymap.Bind(Keyboard, m, StartPlayerMoveAnim );
PlayerKeymap.Bind(Keyboard, j, StopPlayerMoveAnim );
and above these add...
function StartPlayerMoveAnim(){
commandToServer('StartPlayerMoveAnim');
}
function StopPlayerMoveAnim(){
commandToServer('StopPlayerMoveAnim');
}
i think that's it... now run the app... run into the ship to mount it... and press the M key to start the player moving along the moving mount points... press the J key to pause...
*** see next post ***
--Mike
07/21/2005 (7:14 am)
Next, we have to verify that you have a good mount point... do this by adding some script code to the collision handler of the player in Player.csfunction MaleAvatar::onCollision(%this,%obj,%col,%vec,%speed)
//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
{
%obj_state = %obj.getState();
%col_className = %col.getClassName();
%col_dblock_className = %col.getDataBlock().className;
%colName = %col.getDataBlock().getName();
if ( %obj_state $= "Dead")
return;
if (%col_className $= "Item" || %col_className $= "Weapon" ) // Deal with all items
{
%obj.pickup(%col); // otherwise, pick the item up
}
if (%col_dblock_className $= "TestShip"){
%node=0;
%col.mountObject(%obj,%node);
%obj.mVehicle=%col;
}
// Mount vehicles
%this = %col.getDataBlock();
%pushForce = %obj.getDataBlock().pushForce; // Try to push the object away
if (!%pushForce)
%pushForce = 20;
%eye = %obj.getEyeVector(); // Start with the shape's eye vector...
%vec = vectorScale(%eye, %pushForce);
%vec = vectorAdd(%vec,%obj.getVelocity()); // Add the shape's velocity
%pos = %col.getPosition(); // then push
%vec = getWords(%vec, 0, 1) @ " 0.0";
%col.applyImpulse(%pos,%vec);
}
look at the conditional if (%col_dblock_className $= "TestShip"), about midway down... this mounts your player guy to the node (joint) mount0 in the model...
also, a lil bit more to add in the same file...
function HumanMaleAvatar::onMount(%this, %obj, %vehicle, %node) {
if (%node=0){
%obj.setControlObject(%vehicle);
}
}
i think that's all you need to have the player guy mount the testship... run the project, and have the player guy run towards and collide with the ship... he should mount the mountpoint...
finally - some movement
now comes the really fun part... we're gonna add the code to trigger the joint animation, thus moving our guy around on the platform...
actually, you already have some of this code in place... look up a post or two to the TestShip.cs code... the functions ServerCmdStartPlayerMoveAnim and ServerCmdStopPlayerMoveAnim() both respond to commands from the client will start the animation thread running... all we've gotta do is add those functions.
i decided to add em in response to keypresses, and added the code to Emaga6\control\server\presetkeys.cs , but i suppose this can go anywhere your keybinds are...
PlayerKeymap.Bind(Keyboard, m, StartPlayerMoveAnim );
PlayerKeymap.Bind(Keyboard, j, StopPlayerMoveAnim );
and above these add...
function StartPlayerMoveAnim(){
commandToServer('StartPlayerMoveAnim');
}
function StopPlayerMoveAnim(){
commandToServer('StopPlayerMoveAnim');
}
i think that's it... now run the app... run into the ship to mount it... and press the M key to start the player moving along the moving mount points... press the J key to pause...
*** see next post ***
--Mike
#9
the app should run as a single app, or as one of many client apps connected to a server...
keep in mind that the app is in no way a complete program... just an incomplete demo to show what i've managed to learn from the contributions of many generous coders who have passed by where i'm at now and left their help markers for me to follow...
there is no error checking... the sloppy collison method of mounting the testship was only done so i could make sure i got a basic understanding of how it works...
there is no code to dismount... i was so thrilled with getting the player to mount, i completely forgot that part :)
at best, it is a rudimentary learning excercise...
i hope that it can be of some help to others moving in a similar direction...
please chime in with questions, or more importantly, to correct errors you see, or offer suggestions...
i'll try to add a few pics here and there in the next few hours...
good luck
[added]
two short video clips of the code in action in a multiplayer session...
one player watches the other mounted and moving in the model

from the mounted players perspective...

now that that stuff seems to be working, the next step is to sync the player walking animation with the joint animation so it will look like the player avatar is walking while the node is moving...
won't that be fun...
--Mike
07/21/2005 (7:14 am)
Ok... that's about it...the app should run as a single app, or as one of many client apps connected to a server...
keep in mind that the app is in no way a complete program... just an incomplete demo to show what i've managed to learn from the contributions of many generous coders who have passed by where i'm at now and left their help markers for me to follow...
there is no error checking... the sloppy collison method of mounting the testship was only done so i could make sure i got a basic understanding of how it works...
there is no code to dismount... i was so thrilled with getting the player to mount, i completely forgot that part :)
at best, it is a rudimentary learning excercise...
i hope that it can be of some help to others moving in a similar direction...
please chime in with questions, or more importantly, to correct errors you see, or offer suggestions...
i'll try to add a few pics here and there in the next few hours...
good luck
[added]
two short video clips of the code in action in a multiplayer session...
one player watches the other mounted and moving in the model

from the mounted players perspective...

now that that stuff seems to be working, the next step is to sync the player walking animation with the joint animation so it will look like the player avatar is walking while the node is moving...
won't that be fun...
--Mike
#10
06/28/2008 (3:28 pm)
Hey Michael, any progress on this subject since July of 2005?
Torque Owner Michael Hense
please...someone in the know chime in on this one...
--Mike