Hi.
I'm working on a game like everyone here, I can't deny that I learnt a lot from here, so I felt that is my time to try give a small contribution to pay back all that I learnt, hope that this helps someone. I warn that there must be some other ways, this one is just a solution that I got hacking some matrices and experiencing, so if you don't have another solution until now you can give this one a try ^^.
Well, here we go:
What you'll need:
- Full access to your skinning processor code (You'll need do some changes if you can't access some bone properties)
- Your skinned character already working.
- A model from your weapon (don't need be a skinned one).
- A bit of patience while setting up your weapon pivot on the 3D software.
1 - The absolute transform matrix
You need full access to the Absolute Transform from your hand bone, why? This Matrix sums all the accumulated transforms that our character's hand suffer starting from the root bone, this means that if we replace the original world matrix from our weapon model by this one on the draw call, the weapon will be rendered already on the character's hand. If you are using some of the samples found on the internet to skinned processing, you'll probably find the declaration of your bone class in the "skinning info" namespace.
public class Bone
{
public int id; //whatever
public String name; //whatever
public Matrix transform; //whatever
public Matrix absoluteTransform; //YOU REALLY NEED ACCESS TO THIS!
public Matrix inverseTransform; //whatever
public List<AnimationClip> animationClips; //whatever
public int parentID; //whatever
public List<int> childrenIDs; //whatever
public Bone()
{
childrenIDs = new List<int>();
animationClips = new List<AnimationClip>();
}
}
So, find your bone class and make sure that the Absolute Transform is freely accessible, like a
public parameter or a
property.
2 - Add a getHandMatrix( ) method to your Animation Processor
To get the skinned transformation to apply on our character's hand bone, we'll first update the Animation Controller (we do this when we call
SkinModel.Update(GameTime)) this matrix will point all the transformations to the current frame, if you know exactly which skinnedtransformation matrix you'll need just need point it. There are reasons to the Animation Controller class keep all the matrices like protected, so imo the best way to get just the matrix without break the class security level is add a new method.
public class AnimationProcessor
{
protected List<TrilhaAnimao> tracks;
protected MalhaAnimao model;
protected Matrix[] combinedFrameTransformations;
protected Matrix[] worldTrandformations;
protected Matrix[] skinTransformations;
int boneCount;
//Add this method
public Matrix getHandMatrix(int ind)
{
return skinTransformations[ind];
}
...
This way you can call this method inside all weapon.update( )s to move the weapon -
you can probably access the animation controller from your character, because it is commonly a property in most Skinned Samples - you just need point the index of your bone hand and the returned matrix will be exactly the transformations that must be applied on the animation frame. We are done with the skinnedmodel library and processors, so, just recompile your updated dll and reload on your project references.This way you can call this method inside all weapon.update( )s to move the weapon, you just need point the index of your bone hand and the returned matrix will be exactly the transformations that must be applied on the animation frame. We are done with the skinnedmodel library and processors, so, just recompile your updated dll and reload on your project references.
3 - Preparing your weapon/equipment class
Now, inside your weapon class, you must do some changes to use the information, first you'll add 2 new matrices, one to keep the hand original position, and another to be a variable that will be updated:
public class GraphWeapon
{
Model WeapModel;
String ResourceName;
// The trick is use the same animation controller than your character, so add a property
AnimationController AnimControl;
public AnimationController Animator
{
get { return AnimControl; }
set { AnimControl = value; }
}
Matrix OriginalPos; // The first weapon state
Matrix AnimatedPos; // The animated weapon state
The next step is initialize the matrices, first the OriginalPos one, but, unfortunatelly we can't do this on the constructor because it needs information about a pre-initialized character, so the best place to get it is on inside the Load( ) method, and after your character be loaded by your application, the character or the own application could provide the needed info:
public void Load(AnimationController Controller_IN, ...)
{
AnimControl = Controller_IN; // links the character controller to the weapon one
Bone Hand = new Bone(); // temporary bone
Hand = Controller_IN.SkinnedModel.Skeleton.skeleton[X];
OriginalPos = Hand.absoluteTransform; // fills the matrix
...
}
The other matrix, will get updated info doing a call inside the weapon's update( ) method:
public void Update( )
{
...
AnimatedPos = AnimControl.getHandMatrix(X); // here we go! the animated matrix...
...
}
Now, on our GraphWeapon.Draw( ) method, instead of point the matrix acquired from the model like world parameter, we provide the result from OriginalPos*AnimatedPos:
public void Draw( )
{
...
effect.Parameters["World"].SetValue(OriginalPos * AnimatedPos);
...
}
Done, now we just need care about move and animate our heroes!
A small note: Inside your 3D package, your weapon's pivot will be the point that will be attached, so if you use the character hand bone, you can consider pull your model pivot a bit forward or the weapon will be drawn on the middle of the character's wrist. You can get this easily in 3dsMax using the Move tool and the Affect Pivot Only option from the hierarchy panel.