Either animated or non-skinned models can now be attached to animated characters. By non-skinned I mean just regular 3D models with no bones or animations in them. So models don't necessarily have to be rigged. I am using the XNAnimation Library and I recently got it to work in XNA 4.0. I was also able to get it to support multiple animation takes from a single .fbx file by creating a merge content processor. You can find the solution to this in my blog post here. In the future, attaching models will work great for character customization, vehicle customization and weapon customization. Gamers love being able to add armor to their characters and weapons to their vehicles.
For now, I am keept it brief with just a simple cowboy hat and one of my gun models to make sure I was able to get the program working right. I will soon make the entire menu system into a 3D scene. The camera will transition to different areas of the 3D scene after the player make certain selections. One of the major problems I encountered after figuring out how to attach models was making sure the model "looks" like its attached and is in the correct position I wanted it to be in. For example, after I attached the gun model, it appeared to look detached from the arm of the player. To fix this, I multiplied the parent bone transform of the gun model I wanted to attach to the world matrix of the character model and then multiplied that to the character model's right hand bone.
When attaching non-skinned models to your character, make sure you position them in the correct location in your modeling program.
Also, make sure that you are positioning them on the non-skinned model of your characters and creatures and input the correct bone number and information in the program.
Example source code below:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using XNAnimation;
using XNAnimation.Controllers;
using System;
namespace ModelAttachment
{
public class ModelAttachment : Microsoft.Xna.Framework.Game
{
int RIGHT_HAND_BONE_ID;
///
/// The AnimationController class handles the animation playback
///
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using XNAnimation;
using XNAnimation.Controllers;
using System;
namespace ModelAttachment
{
public class ModelAttachment : Microsoft.Xna.Framework.Game
{
int RIGHT_HAND_BONE_ID;
///
/// The AnimationController class handles the animation playback
///
// The SkinnedModel class handles the skeletal animated models
SkinnedModel marine;
Matrix view;
Matrix projection;
Model gun;
public override void LoadContent()
{
RIGHT_HAND_BONE_ID = 15;
// Calculate camera view and projection matrices.
view = Matrix.CreateLookAt(new Vector3(-5, 5, -30),
new Vector3(1, 3, 0), Vector3.Up);
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
GraphicsDevice.Viewport.AspectRatio, 10, 10000);
LoadMarineModel();
}
///
/// Unloads graphics content for this screen.
///
public override void UnloadContent()
{
content.Unload();
}
public string idle = "Idle";
private void LoadMarineModel()
{
gun = content.Load<Model>
marine = content.Load<SkinnedModel>
animationController = new AnimationController(marine.SkeletonBones);
animationController.StartClip(marine.AnimationClips["Idle"]);
}
public override void Update(GameTime gameTime)
{
float time = (float)gameTime.TotalGameTime.TotalSeconds;
// Update the animation according to the elapsed time
animationController.Update(gameTime.ElapsedGameTime, Matrix.Identity);
base.Update(gameTime);
}
///
/// Draws the background screen.
///
public override void Draw(GameTime gameTime)
{
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
Drawmarine(marine, view, projection);
DrawGun(gun,view, projection, animationController.SkinnedBoneTransforms[RIGHT_HAND_BONE_ID];
}
///
/// Draws the marine model
///
private void Drawmarine(SkinnedModel marine, Matrix view, Matrix projection)
{
foreach (ModelMesh mesh in marine.Model.Meshes)
{
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
foreach (SkinnedEffect effect in mesh.Effects)
{
effect.SetBoneTransforms(animationController.SkinnedBoneTransforms);
effect.World = Matrix.Identity;
// OPTIONAL - Configure lights
effect.EnableDefaultLighting();
effect.AmbientLightColor = new Vector3(0.1f);
effect.DiffuseColor = new Vector3(0.8f);
effect.SpecularColor = new Vector3(0.3f);
effect.SpecularPower = 8;
// Camera Properties
effect.View = view;
effect.Projection = projection;
}
}
mesh.Draw();
}
}
public void DrawGun(Model model, Matrix view, Matrix projection, Matrix world)
{
Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mm in model.Meshes)
{
foreach (BasicEffect be in mm.Effects)
{
be.View = view;
be.Projection = projection;
be.World = transforms[mm.ParentBone.Index] * world;
be.EnableDefaultLighting();
be.AmbientLightColor = new Vector3(0.1f);
be.SpecularPower = 5;
}
mm.Draw();
}
}
}
}
No comments:
Post a Comment