Latest  | Search | Go
Edit this page   |   Attach file 

  Home | Tutorials | Technical Reference | Runtime | API Documentation | ContentCreation > MaxToolsPlugin > KnowledgeMenu > SkinnedCharacters  

Using Skinned Characters

Author:Jeremy Stieglitz

Updated Jan 25 2005: No longer necessary to specify animation loading scale.


Completed Alien Character in Reality Builder

Getting your character out of 3d Studio Max or Maya

Note:       

To begin with, install the following plugins to allow you to export X files from your respective modelling application. X files are the intermediate file format that you'll use to import your characters into Reality Builder.

As you rig your character, heed the following guidelines to ensure you have no problems with the exported model:

  • Ensure your character mesh is facing the positive forward Z-axis (Y-axis in 3dsmax) before rigging it, and that the mesh is centered on the origin. This will ensure a correct identity transformation when you bring it into your game. Otherwise, you'll have to manually offset the transformation in Reality Builder or in your game.

  • Your character mesh should have all vertices welded.

  • Your character's performance does benefit from fewer bones. Though it's worth noting that the Eval Kit character uses a Character Studio Biped set of bones, so performance is optimized to handle large numbers of bones if that's what you require.

  • In 3d Studio Max, when initializing your character mesh to a biped, initialize it with "Rigid" Vertex Link Assignments. Otherwise, erroneously using "Deformable" Vertex Link Assignment setting, the animated mesh's appearance in 3dsmax will differ from Reality. Also, use a maximum of 3 Links for "Blending Between Links" setting.

  • You can add extra bones to your biped using Dummy objects, which is also discussed below, and you can also export arbritrary skeletal hierarchies using Dummies.

  • You can Link rigid meshes to any part of your character's hierarchy as well, and they'll be exported and animated without surface deformation.

Note:        Your model can actually consist -only- of keyframe animated rigid meshes if you so desire. That enables you to animate rigid objects such as machinery via this standard system. Simply follow the tutorial the same way, minus the bits specific to character skinning.

Once you have your character rigged in Maya or physiqued in 3d Studio Max, you're ready to export it to Reality Builder. In 3d Studio Max, ensure the biped itself is hidden, but leave all extra (non-Biped) Dummies visible so they will be exported in the hierarchy as well. Linked Dummies are useful since they will be transformed along with the rest of the animation (and can have their own keyframes too), allowing to use them as invisible attachment points in-game for things like Weapons and Visual FX. In either application, Export with .X as the file format, and you'll see a dialog that will allow you to set some options. So, the character mesh itself should be visible, and any extra Linked rigid meshes and Dummies, but not the biped! Set them as in the left image:

character_export.jpg

You do not want to export any Animations with this initial file, because right now you're only exporting the base character model itself. Once you've exported it, you're ready to import the X file into Reality Builder to set up its Materials, and make it an XML that the game can load like any other Model.

Importing & saving your character in Reality Builder

Open RB (new scene), goto File/Import, and select your X file. The following dialog will appear, and you should use the necessary Unit Scale to convert from Meters to whatever unit system you were using in your character's scene, such as inches.

import_X.jpg

You should then see the character in your scene, in front of your view (wherever that is). If you can't see your character, try moving the camera since, depending on your unit scale, the character may be large or small.

Then immediately translate your character to 0,0,0 by selecting it and using the Move gizmo, this is important to keep him centered during in-game transformations!

The character will be all black since there are no lights, so feel free to add a Light (though you should delete it before saving, since it's good practice to only include meshes in a Model file like we're creating). Now you're good to configure your characters Materials, adding and modifying Materials as you wish.

Submaterial ID's that were in your original model should hold through the X conversion and be available to you in the Rendering/Submat tab now (though they'll all be "Default Internal Material", so overwrite them by setting your new Materials onto each Submaterial as described in RealityBuilder). Once you're done configuring the Materials (you can always open the XML Model later in Reality Builder to further adjust them), save the character into a new subdirectory in your "Models" folder and you're ready to get this character animated!

Exporting Character Animations

Individual animations are loaded in Reality as X files, so you simply have to export each of your character animations from 3d Studio Max or Maya as a separate X file. The export process is the same, except that you should have "Include Animation" checked in the X Export dialog. You can also "Hide" all the meshes in the scene when exporting the animations, this will result in smaller, faster-loading animation files. Looping, transition blending, multi-track weighting, and other playback aspects are determined by the playback code, which will be discussed in the next sections.

Putting your character to work in Reality and the Eval Kit's Game Framework

Now that you've got your XML base model and X animation files, you're ready to put this character to work in your game. If you want to test out your animated character quickly, it's easy to use the AnimatedModel Actor in Reality Builder to have a look at the animation in action. Add a Light to your new scene in front of your view (#1), then Insert an AnimatedModel (#2), and then click the "ModelXMLFileName" property and select your XML model from the file dialog (#3).

Finally select the X animation file in the "XAnimationFileName" Property (#4). The X file should also be located in a "Models" subdirectory, such as the one in which you've saved the XML model. Click away and the animation will begin playing, looping endlessly. You can save this scene for later viewing, or change X Animation Filenames as you wish, to aid in continued tweaking of animations.

insert_animatedmodel.jpg

After testing your animation using the AnimatedModel script, the next step is the programming involved in dynamically animating a game character. First, a few notes about how Reality's animation system works. XML models supporting animation are loaded just like any other Model, so you just load the XML as normal with Model::Load.

Animations themselves are then loaded with Model::LoadAnimation (or MModel.LoadAnimation in the ScriptingSystem). After LoadAnimation returns a numeric handle (good to store for later use), which can then be used in a variety of animation functions detailed in the API. The most important of those is Model::TransitionToAnimation for basic playback, with smooth blending between tracks when that function is called.

Furthermore there are various ways to do advanced animation techniques, such as using Model::SetNodeInfluence for procedural alteration or Model::SetAnimationWeight for multi-track blending, these will be discussed in the next section. With Model::LoadAnimation and Model::TransitionToAnimation you've got the core elements of the animation at your disposal. So let's look at what the Eval Kit Game Framework does to animate the Player character, in Scripts\Player.cs -- and see how you can replace the Eval Kit's character with your own, if you're so inclined.

First off, to do a simple replacement of the Eval Kit character, your character must be using the standard 3dsmax biped due to the uses of those particular biped bone names within the script, and it should also have a Dummy called Weapon_bone; if your character is not using the standard biped, the replacement process is more complex since you'll have to change those bone names to match your own.

So assuming it's using the standard biped, then change the following lines of code to match your own character's data:

Note:        Modifying the values mentioned on following lines from Player.cs will effectively replace the character model and basic movement animations of the Player in the Eval Kit. However, adjusting these particular values will not affect the rest of the Player's animations that are not listed here (of which there are quite a few), or the Player's ragdoll, so be prepared to see visual incongruities there unless you replace (or remove) the rest of the animations and ragdoll setup.

static private MModel StaticModel = MPrecacher.PrecacheModel(ClassName, "Alien.xml"); //Ln 36, replace this with your XML model name

StaticAnimIdle = StaticModel.LoadAnimation("alien_idle.x", true, LowerBodyFrameNames);   //Ln 150, replace with your X idle anim
StaticAnimRunForward = StaticModel.LoadAnimation("alien_runforward.x", true, LowerBodyFrameNames); //Ln 152, replace with your X run-f anim
StaticAnimRunBackward = StaticModel.LoadAnimation("alien_runbackward.x", true, LowerBodyFrameNames); //Ln 153, replace with your X run-b anim
StaticAnimStrafeLeft = StaticModel.LoadAnimation("alien_strafeleft.x", true, LowerBodyFrameNames); //Ln 154, replace with your X strafe-l anim
StaticAnimStrafeRight = StaticModel.LoadAnimation("alien_straferight.x", true, LowerBodyFrameNames); //Ln 155, replace with your X strafe-r anim

// (plays on upper body)
StaticAnimHurt = StaticModel.LoadAnimation("alien_hurt.x", false, UpperBodyFrameNames);  //Ln 155, replace with your X generic hurt anim 

// (plays on upper body)
StaticAnimIdleUpper = StaticModel.LoadAnimation("alien_idle.x", true, UpperBodyFrameNames); //Ln 161, replace with your X idle anim

Note:        In the Eval Kit, you can press Z to enter the Player's Third Person View, or type "spawn Player" into the console to add an idle Player in front of your view.

So you've replaced the Eval Kit character, in the elementary sense of swapping the character model and basic movement animations. Now let's dig into what the Player.cs code actually does to animate the character in some complex ways, by using Reality's more advanced animation functions.

Advanced Character Animation Usage

There are a variety of additional animation concepts that go beyond simple single-track transitioning. The concept of animation tracks is important for our next topics. Whenever you Load an Animation, a track is created for that animation. When you transition between animations, you're actually transitioning between tracks, stopping playback of one track and starting playback of another. Each track has a weighting value, and using this weighting value and the concept of multiple tracks you can overlap tracks with aggregated weighting to achieve multi-track blending. But before we get into the details of that, let's note that animations can be either looping or non-looping, and several functions are provided to allow you to get information about the playback state of an individual track:

StaticAnimIdle = StaticModel.LoadAnimation("alien_idle.x", true); // loads an animation track as looping
StaticAnimIdle = StaticModel.LoadAnimation("alien_idle.x", false); // loads an animation track as non-looping
bool playing = StaticModel.IsPlaying(StaticAnimIdle); //returns whether a looping or non-looping track is currently playing
bool looping = StaticModel.IsLooping(StaticAnimIdle); //returns whether a track is a looping track
float duration = StaticModel.GetDuration(StaticAnimIdle); //returns the length in seconds of a specified track
float remaining = StaticModel.GetRemaining(StaticAnimIdle); //returns the remaining playback time in seconds, if any, of a non-looping track

These functions will prove important in the management of multi-track animation blending, as you'll see below.

Additionally, it's critical to understand how Reality represents bones, how to get and manipulate them. Bones are actually ModelFrames in Reality, namely an element in a Model's hierarchy that determines the hierarchial transformation a sub-Mesh of that Model, or a bone-part of a character, or an invisible transformation for your use elsewhere. You can get ModelFrames from a Model by name, and manipulate their transformations (either relative or absolute), by using the following code:

// get the handles of a couple important bones, 
// the spine bone to bend at the waist and the gun bone to put the TPV gun at
gunBone = MyModel.GetModelFrame("Weapon_bone");

// sets to the absolute transformation of the ModelFrame in the World, including animation transform
weaponActor.Location = gunBone.CombinedTransformationMatrix.m3;
weaponActor.Rotation = gunBone.CombinedTransformationMatrix.GetRotationMatrix();

// sets the relative transformation of the ModelFrame in the Model's hierarchy (this can be overriden by Animation)
gunBone.TransformationMatrix = new MMatrix();

Now that you understand what ModelFrames are (namely bones), we'll examine how to apply arbritrary weighted transformations onto ModelFrames within the character's hierarchy, allowing you to do such varied things as bending a character at the waist or pointing his head towards a point in the World. Let's look at the Eval Kit code that bends the Player at the waist, using Model::SetNodeInfluence (or MModel.SetFrameInfluence in Script):

In the Player ctor:

      MModelFrame spinBone;

      // get the handles of a couple important bones, 
      // the spine bone to bend at the waist and the gun bone to put the TPV gun at
      spineBone = MyModel.GetModelFrame("Bip01_Spine2");

----
----

Then in the Player PreRender override (done there to allow us to alter the Player's Model transformation immediately before rendering):
                
      // get the direction that the player is looking on the XZ plane
      MVector dir = Rotation.GetDir();
      MVector noYdir = new MVector();
      dir.Copy(noYdir);
      noYdir.y = 0;
      
      // force the Player's Model base transformation to rotate only on the XZ plane. 
      // The Y-look will come from bending at the waist with SetFrameInfluence below
      MyModel.RootTransform = MMatrix.LookTowards(noYdir.Normalized())

      // calculate the angle between the player's Y-look direction and the World's XZ plane
      float pitchAngle = dir.RadAngle(MHelpers.VectorDown) - (float)Math.PI / 2;

      // limit max and min bone bend pitch
      if (pitchAngle > 1.1f)
         pitchAngle = 1.1f;
      else if (pitchAngle < -1.3f)
         pitchAngle = -1.3f;

      // bend the Player's Model at the waist according to the Pitch Angle
      MMatrix spineBoneInfluence = new MMatrix();
      spineBoneInfluence.SetRotations(0, pitchAngle, 0);
      MyModel.SetFrameInfluence(spineBone, spineBoneInfluence);

As you can see, in the Player ctor we get the handles of the Model's appropriate ModelFrame (a bone) that we're going to need later. In PreRender (this function allows us to set the Model's transformation before rendering), we calculate the Player's Rotation on the XZ plane, since we don't want the Model's base Rotation to by looking in the Y direction (the bended-waist will take care of that). Then, we calculate the angle of the pitch, limit it to certain extents (so that the Player can't actually bend a full 90 degrees which would look strange). We then use that Pitch value in a new influence MMatrix passed to SetFrameInfluence, which effectively transforms the bone by said influence Matrix (the bone also retains its animation transformation in that multiplication, which is why SetFrameInfluence is so useful).

An overload of SetFrameInfluence takes a float weighting value, allowing you to have variable influences less than 100%. If we wanted the Player's head to look at a point in the World, we'd calculate an Orthonormalized Matrix that's looking in the direction from the Player's head location to the destination location, and use that as the influence Matrix. Model::SetNodeInfluence is a powerful function, effectively allowing you to apply procedural alterations to animated bones.


With the aformentioned concepts in mind, we're ready for a discussion of multi-track blended animation. First, let's go over the general idea: all animation Tracks on a Model are always blended into the final result, the only determination of whether you see them or not is their weighting. When you normally call TransitionToAnimation, the previous track that was playing actually has its weighting set to 0 (smoothly), so you don't see it affecting the Model's animation anymore, while the new track has its weighting ramped up to 1.

The way we achieve multi-track multi-track blending is to manage the track weightings so that we're not dealing with 0's and 1's, but rather intermediate value like .5's (depending on how many blending divisions you want and whether you want equal blending among the divisions). However, if we were blending between 2 different tracks that each had .5 weighting, while each was trying to play equal-weighted keyframes on the same ModelFrames, we'd get undesirable animation since all ModelFrames would be somewhere between the two tracks regardless of how much we wanted each ModelFrame to be affected. Thus we introduce the concept of only loading keyframes for particular parts of the hierarchy onto a given track (or individually weighting different bones, which would be done in your 3d application of choice when creating the original animation).

For instance in the Eval Kit, by simply not loading keyframes for sections of the Player Model's hierarchy on particular animations, we can have a distinction between upper and lower body animations, and manage two simultaneous animations as .5-.5 weighted multi-track blending! Let's look at some of the code to do this.

Our setup here will use lists of ModelFrames for which to load keyframes, so that our lower-body animation tracks will only affect the lower-body ModelFrames, while our upper-body animation tracks will only affect our upper-body AnimationFrames. In this way, we can play simultaneous animation tracks on the lower body and upper body. Alternatively, the weighting of individual bones (ModelFrames) could be specified on a per-animation basis in 3dsmax or Maya. Here's how we get those lists of ModelFrames (upper and lower body), and pass them to the LoadAnimation overload in the Player ctor:

In the Player ctor:

// store two sets of model frame lists -- one for lower body, one for upper body, 
// which can be used to alter which animations affect which list for differentation between upper 
// and lower body animations
ArrayList meshes = StaticModelTPose.GetModelFrame("Bip01_L_Thigh").EnumerateFrames();
foreach (MModelFrame mesh in meshes)
{
   LowerBodyFrameNames.Add(mesh.Name);
}
LowerBodyFrameNames.Add("Bip01_Pelvis");

meshes = StaticModelTPose.GetModelFrame("Bip01_Spine").EnumerateFrames();
foreach (MModelFrame mesh in meshes)
{
   bool found = false;
   foreach (String s in LowerBodyFrameNames)
   {
      if (s == mesh.Name)
      {
         found = true;
         break;
      }
   }
   if (!found)
      UpperBodyFrameNames.Add(mesh.Name);
}
UpperBodyFrameNames.Add("Bip01");

StaticAnimRunForward = StaticModel.LoadAnimation("alien_runforward.x", true, LowerBodyFrameNames);
StaticAnimRunBackward = StaticModel.LoadAnimation("alien_runbackward.x", true, LowerBodyFrameNames);
StaticAnimStrafeLeft = StaticModel.LoadAnimation("alien_strafeleft.x", true, LowerBodyFrameNames);
StaticAnimStrafeRight = StaticModel.LoadAnimation("alien_straferight.x", true, LowerBodyFrameNames);

StaticAnimShoot = StaticModel.LoadAnimation("alien_shoot.x", false, UpperBodyFrameNames);
StaticAnimReload = StaticModel.LoadAnimation("alien_reload.x", false, UpperBodyFrameNames);

Note that the lower-body animations will be looping (since they're continuous movement animations) while the upper body animations will be non-looping (since they're singular event animations).

Now that we've got our lower and upper body animation tracks loaded, we're ready to start playing them simultaneously.

Here's what the animation logic does for Lower-body animations and Upper-body animations. Upper and Lower animations are played by Transitioning to a destination track with .5 weighting, while ramping down the previously-playing track to 0 weighting. We do this separately for our lower body, separately for our upper body. The Lower-body and Upper-body tracks will each reach .5 weighting, and thus Lower-body and Upper-body animations will play simultaneously on their respective ModelFrames, adding to a 1.0 overall weighting.

In the Player Tick, for Lower-body animations:

if (CurrentLowerBodyAnim != DesiredLowerBodyAnim)
{
      // transition to the new animation track at .5 weighting over a transitionTime
      MyModel.TransitionToAnimation(DesiredLowerBodyAnim, transitionTime, .5f, DesiredAnimSpeed);
      // and stop the old animation track (transition its weighting to 0) 
      MyModel.SetAnimationWeight(CurrentLowerBodyAnim, 0, transitionTime);
      CurrentLowerBodyAnim = DesiredLowerBodyAnim;
}
----

Wherever we want to play an Upper-body animation:

// play the event animation if we're not currently playing it, or it's stopped
if (CurrentUpperBodyAnim != AnimationIndex || !MyModel.IsPlaying(CurrentUpperBodyAnim))
{
   // transition to the new animation track at .5 weighting over a transitionTime
   MyModel.TransitionToAnimation(AnimationIndex, transitionTime, .5f, Speed);

   // and stop the old animation track (ramp down its weighting to 0) 
   // but only if the previous track is NOT the current track that we're starting to play now
   if(CurrentUpperBodyAnim != AnimationIndex)
      MyModel.SetAnimationWeight(CurrentUpperBodyAnim, 0, transitionTime);

   CurrentUpperBodyAnim = AnimationIndex;
}


Now you know about setting ModelFrame influences, getting and setting ModelFrame transformations, and managing multi-track blended animations & track weighting. With effective use of these different animation techniques on your characters, you can greatly increase the procedural dynamics of your character animation system!

Attachment sort Action Size Date Who Comment
character_realitybuilder.jpg manage 163.5 K 18 Dec 2004 - 03:21 Main.guest  
character_export.jpg manage 115.4 K 19 Dec 2004 - 01:30 Main.guest  
import_X.jpg manage 13.1 K 19 Dec 2004 - 05:05 Main.guest  
insert_animatedmodel.jpg manage 87.5 K 26 Jan 2005 - 02:38 Main.guest  

SkinnedCharacters   Edit | Attach | Ref-By | Printable | Diffs | r1.13 | > | r1.12 | > | r1.11 | More