SkinnedMesh class in C#?

Started by
5 comments, last by perrs 18 years, 5 months ago
Hi everybody. I am trying to make a skeletal animated model in managed DirectX (C#). So far I have tried to use some of the code from the SimpleAnimation sample, but unfortunately it is not very simple. Or rather, it is not very modular designed making it very difficult to reuse the code. After struggling with it for some time, I figured that I could not be the first to have this problem. Therefore I will ask: Has anyone made a SkinnedMesh class that incapsulates functionallity such as Loading, Updating, Rendering and setting animation? If so, would you be willing to share it? I know I would greatly appreciate it and I am sure others would too. Thanks in advance, Per Rasmussen.
Advertisement

hi perrs i think that u r a bigginner in game development so u can make from the simple animation program a class to load hirarchy so u can use it whenever u want use animated mesh if u didnot know how to do this i will send u my class ok
can u send also to me?
marcodev et email.it
big tnx
jad_salloum: I have tried to make my own class from the sample but there is three problems:

1. I can't get it to work. I keep getting a weird NullReferenceException from within D3DX.

2. When caps.MaxVertexBlendMatrixIndex is read it is 0 in my program but 255 in the sample. I do not get my caps from the SampleFramework, though, but from Manager with Manager.GetDeviceCaps(0, DeviceType.Hardware);

3. The sample does not show how to change animation.

Therefore it would be great if you have a working class you could send to me. Please send it to per (dot) rasmussen (at) c (dot) dk

Thank you very much.
using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Diagnostics;

namespace Salloum
{
/// <summary>
/// Summary description for HierarchyClass.
/// </summary>
public class HierarchyClass
{
public AnimationRootFrame rootFrame;
public float objectRadius = 0.0f; // Radius of the object
public Vector3 objectCenter; // Center of the object
public AnimationAllocation alloc;
// public Matrix WorldMatrix;
private Salloum.Game game;
private Caps caps;
public float TransX=0 , TransY=0 , TransZ=0 , RotX=0 , RotY=0 , RotZ=0 ;

public HierarchyClass(Salloum.Game gamefrm)
{
game=gamefrm;
}

public void CreateAnimationFile(string file,Device dev)
{
// Create our allocate hierarchy derived class
alloc = new AnimationAllocation(this);
// ExtendedMaterial[] mtrl;

// mesh=Mesh.FromFile(file, MeshFlags.Managed, dev,out mtrl);
rootFrame= Mesh.LoadHierarchyFromFile(file, MeshFlags.Managed, dev, alloc, null);

// Calculate the center and radius of a bounding sphere
objectRadius = Frame.CalculateBoundingSphere(rootFrame.FrameHierarchy,
out objectCenter);

// Setup the matrices for animation
SetupBoneMatrices(rootFrame.FrameHierarchy as AnimationFrame);
}


/// <summary>Update the frames matrices and combine it with it's parents</summary>
public void UpdateFrameMatrices(AnimationFrame frame, Matrix parentMatrix)
{
frame.CombinedTransformationMatrix = frame.TransformationMatrix *
parentMatrix;

if (frame.FrameSibling != null)
{
UpdateFrameMatrices(frame.FrameSibling as AnimationFrame, parentMatrix);
}

if (frame.FrameFirstChild != null)
{
UpdateFrameMatrices(frame.FrameFirstChild as AnimationFrame,
frame.CombinedTransformationMatrix);
}
}

/// <summary>Draw a frame and all child and sibling frames</summary>
public void DrawFrame(Device dev, AnimationFrame frame, float scaleX , float scaleY , float scaleZ , float rotX , float rotY , float rotZ , float translateX , float translateY , float translateZ )
{

TransX=translateX;
TransY=translateY;
TransZ=translateZ;

RotX=rotX;
RotY=rotY;
RotZ=rotZ;



AnimationMeshContainer mesh = frame.MeshContainer as AnimationMeshContainer;
while(mesh != null)
{
DrawMeshContainer(dev, mesh, frame, scaleX , scaleY , scaleZ , rotX , rotY , rotZ , translateX , translateY , translateZ );

mesh = mesh.NextContainer as AnimationMeshContainer;
}

if (frame.FrameSibling != null)
{
//Debug.WriteLine(frame.Name+" ");
DrawFrame(dev, frame.FrameSibling as AnimationFrame, scaleX , scaleY , scaleZ , rotX , rotY , rotZ , translateX , translateY , translateZ );
}

if (frame.FrameFirstChild != null)
{
// Debug.WriteLine(frame.Name+" ");
DrawFrame(dev, frame.FrameFirstChild as AnimationFrame, scaleX , scaleY , scaleZ , rotX , rotY , rotZ , translateX , translateY , translateZ );
}
}

/// <summary>Render a mesh container</summary>
private void DrawMeshContainer(Device dev, AnimationMeshContainer mesh, AnimationFrame parent , float scaleX , float scaleY , float scaleZ , float rotX , float rotY , float rotZ , float translateX , float translateY , float translateZ )
{
Device device = dev;
caps = device.DeviceCaps;

// first check for skinning
if (mesh.SkinInformation != null)
{
if (mesh.NumberInfluences == 1)
device.RenderState.VertexBlend = VertexBlend.ZeroWeights;
else
device.RenderState.VertexBlend = (VertexBlend)(mesh.NumberInfluences - 1);

if (mesh.NumberInfluences > 0)
device.RenderState.IndexedVertexBlendEnable = true;

BoneCombination[] bones = mesh.GetBones();

for(int iAttrib = 0; iAttrib < mesh.NumberAttributes; iAttrib++)
{

// first, get world matrices
for (int iPaletteEntry = 0; iPaletteEntry < mesh.NumberPaletteEntries;
++iPaletteEntry)
{
int iMatrixIndex = bones[iAttrib].BoneId[iPaletteEntry];
if (iMatrixIndex != -1)
{
device.Transform.SetWorldMatrixByIndex(iPaletteEntry,
mesh.GetOffsetMatrices()[iMatrixIndex] *
mesh.GetFrames()[iMatrixIndex].
CombinedTransformationMatrix * Matrix.Scaling(scaleX,scaleY,scaleZ)* Matrix.RotationYawPitchRoll( rotY,rotX,rotZ) * Matrix.Translation(translateX,translateY,translateZ));
}
}

// Setup the material
device.Material = mesh.GetMaterials()[bones[iAttrib].AttributeId].Material3D;
device.SetTexture(0, mesh.GetTextures()[bones[iAttrib].AttributeId]);

// Finally draw the subset

mesh.MeshData.Mesh.DrawSubset(iAttrib);
}
}
else
{
// Standard mesh, just draw it using FF
device.RenderState.VertexBlend = VertexBlend.Disable;

// Set up transforms
device.Transform.World = parent.CombinedTransformationMatrix * Matrix.Scaling(scaleX,scaleY,scaleZ)* Matrix.RotationYawPitchRoll( rotY,rotX,rotZ)*Matrix.Translation(translateX,translateY,translateZ);

/// device.Transform.World = Matrix.Scaling(scaleX,scaleY,scaleZ)* Matrix.RotationYawPitchRoll( rotY,rotX,rotZ)*Matrix.Translation(translateX,translateY,translateZ);

ExtendedMaterial[] materials = mesh.GetMaterials();
for (int i = 0; i < materials.Length; ++i)
{
device.Material = materials.Material3D;
device.SetTexture(0, mesh.GetTextures());
mesh.MeshData.Mesh.DrawSubset(i);
}
}


}

/// <summary>
/// Generate the skinned mesh information
/// </summary>
public void GenerateSkinnedMesh( AnimationMeshContainer mesh)
{
if (mesh.SkinInformation == null)
throw new ArgumentException(); // There is nothing to generate

MeshFlags flags = MeshFlags.OptimizeVertexCache;


if (caps.VertexShaderVersion >= new Version(1,1))
{
flags |= MeshFlags.Managed;
}
else
{
flags |= MeshFlags.SystemMemory;
}

int numMaxFaceInfl;
using(IndexBuffer ib = mesh.MeshData.Mesh.IndexBuffer)
{
numMaxFaceInfl = mesh.SkinInformation.GetMaxFaceInfluences(ib,
mesh.MeshData.Mesh.NumberFaces);
}
// 12 entry palette guarantees that any triangle (4 independent
// influences per vertex of a tri) can be handled
numMaxFaceInfl = (int)Math.Min(numMaxFaceInfl, 12);

if (caps.MaxVertexBlendMatrixIndex + 1 >= numMaxFaceInfl)
{
mesh.NumberPaletteEntries = (int)Math.Min((caps.
MaxVertexBlendMatrixIndex+ 1) / 2,
mesh.SkinInformation.NumberBones);

flags |= MeshFlags.Managed;
}

int influences = 0;
BoneCombination[] bones = null;

// Use ConvertToBlendedMesh to generate a drawable mesh
MeshData data = mesh.MeshData;
data.Mesh = mesh.SkinInformation.ConvertToIndexedBlendedMesh(data.Mesh, flags,
mesh.GetAdjacencyStream(), mesh.NumberPaletteEntries, out influences,
out bones);

// Store this info
mesh.NumberInfluences = influences;
mesh.SetBones(bones);

// Get the number of attributes
mesh.NumberAttributes = bones.Length;

mesh.MeshData = data;
}

/// <summary>This method will set the bone matrices for a frame</summary>
private void SetupBoneMatrices(AnimationFrame frame)
{
// First do the mesh container this frame contains (if it does)
if (frame.MeshContainer != null)
{
SetupBoneMatrices(frame.MeshContainer as AnimationMeshContainer);
}
// Next do any siblings this frame may contain
if (frame.FrameSibling != null)
{
SetupBoneMatrices(frame.FrameSibling as AnimationFrame);
}
// Finally do the children of this frame
if (frame.FrameFirstChild != null)
{
SetupBoneMatrices(frame.FrameFirstChild as AnimationFrame);
}
}

/// <summary>Sets the bone matrices for a mesh container</summary>
private void SetupBoneMatrices(AnimationMeshContainer mesh)
{
// Is there skin information? If so, setup the matrices
if (mesh.SkinInformation != null)
{
int numberBones = mesh.SkinInformation.NumberBones;

AnimationFrame[] frameMatrices = new AnimationFrame[numberBones];
for(int i = 0; i< numberBones; i++)
{
AnimationFrame frame = Frame.Find(rootFrame.FrameHierarchy,
mesh.SkinInformation.GetBoneName(i)) as AnimationFrame;

if (frame == null)
throw new InvalidOperationException("Could not find valid bone.");

frameMatrices = frame;
}
mesh.SetFrames(frameMatrices);
}
}




}


#region Derived Frame Class
/// <summary>
/// The frame that will hold mesh animation
/// </summary>
public class AnimationFrame : Frame
{
// Store the combined transformation matrix
private Matrix combined = Matrix.Identity;
/// <summary>The combined transformation matrix</summary>
public Matrix CombinedTransformationMatrix
{
get { return combined; } set { combined = value; }
}
}
#endregion
#region Derived Mesh Container
/// <summary>
/// The mesh container class that will hold the animation data
/// </summary>
public class AnimationMeshContainer : MeshContainer
{
// Array data
private Texture[] meshTextures = null;
private BoneCombination[] bones;
private Matrix[] offsetMatrices;
private AnimationFrame[] frameMatrices;

// Instance data
private int numAttributes = 0;
private int numInfluences = 0;
private int numPalette = 0;

// Public properties

/// <summary>Retrieve the textures used for this container</summary>
public Texture[] GetTextures() { return meshTextures; }
/// <summary>Set the textures used for this container</summary>
public void SetTextures(Texture[] textures) { meshTextures = textures; }

/// <summary>Retrieve the bone combinations used for this container</summary>
public BoneCombination[] GetBones() { return bones; }
/// <summary>Set the bone combinations used for this container</summary>
public void SetBones(BoneCombination[] b) { bones = b; }

/// <summary>Retrieve the animation frames used for this container</summary>
public AnimationFrame[] GetFrames() { return frameMatrices; }
/// <summary>Set the animation frames used for this container</summary>
public void SetFrames(AnimationFrame[] frames) { frameMatrices = frames; }

/// <summary>Retrieve the offset matrices used for this container</summary>
public Matrix[] GetOffsetMatrices() { return offsetMatrices; }
/// <summary>Set the offset matrices used for this container</summary>
public void SetOffsetMatrices(Matrix[] matrices) { offsetMatrices = matrices; }

/// <summary>Total number of attributes this mesh container contains</summary>
public int NumberAttributes { get { return numAttributes; } set { numAttributes = value; } }
/// <summary>Total number of influences this mesh container contains</summary>
public int NumberInfluences { get { return numInfluences; } set { numInfluences = value; } }
/// <summary>Total number of palette entries this mesh container contains</summary>
public int NumberPaletteEntries { get { return numPalette; } set { numPalette = value; } }
}
#endregion
#region Animation Allocation Hierarchy
/// <summary>
/// AllocateHierarchy derived class
/// </summary>
public class AnimationAllocation : AllocateHierarchy
{
HierarchyClass parent = null;
public Texture[] meshTextures;
/// <summary>Create new instance of this class</summary>
public AnimationAllocation(HierarchyClass p) { parent = p; }

/// <summary>Create a new frame</summary>
public override Frame CreateFrame(string name)
{
AnimationFrame frame = new AnimationFrame();
frame.Name = name;
frame.TransformationMatrix = Matrix.Identity;
frame.CombinedTransformationMatrix = Matrix.Identity;

return frame;
}

/// <summary>Create a new mesh container</summary>
public override MeshContainer CreateMeshContainer(string name,
MeshData meshData, ExtendedMaterial[] materials,
EffectInstance[] effectInstances, GraphicsStream adjacency,
SkinInformation skinInfo)
{
// We only handle meshes here
if (meshData.Mesh == null)
throw new ArgumentException();

// We must have a vertex format mesh
if (meshData.Mesh.VertexFormat == VertexFormats.None)
throw new ArgumentException();

AnimationMeshContainer mesh = new AnimationMeshContainer();

mesh.Name = name;
int numFaces = meshData.Mesh.NumberFaces;
Device dev = meshData.Mesh.Device;

// Make sure there are normals
if ((meshData.Mesh.VertexFormat & VertexFormats.Normal) == 0)
{
// Clone the mesh
Mesh tempMesh = meshData.Mesh.Clone(meshData.Mesh.Options.Value,
meshData.Mesh.VertexFormat | VertexFormats.Normal, dev);

// Destroy current mesh, use the new one
meshData.Mesh.Dispose();
meshData.Mesh = tempMesh;
meshData.Mesh.ComputeNormals();
}

// Store the materials
mesh.SetMaterials(materials);
mesh.SetAdjacency(adjacency);
meshTextures = new Texture[materials.Length];

// Create any textures
for (int i = 0; i < materials.Length; i++)
{
if (materials.TextureFilename != null)
{
meshTextures = TextureLoader.FromFile(dev,@"..\\..\\xfiles\\"+ materials.TextureFilename);
// Debug.WriteLine(materials.TextureFilename + " i = " + i.ToString());
}
}
mesh.SetTextures(meshTextures);
mesh.MeshData = meshData;

// If there is skinning info, save any required data
if (skinInfo != null)
{
mesh.SkinInformation = skinInfo;
int numBones = skinInfo.NumberBones;
Matrix[] offsetMatrices = new Matrix[numBones];

for (int i = 0; i < numBones; i++)
offsetMatrices = skinInfo.GetBoneOffsetMatrix(i);

mesh.SetOffsetMatrices(offsetMatrices);

parent.GenerateSkinnedMesh(mesh);
}

return mesh;
}

}
#endregion
}
this is the code hope that nobody gets upset coz i pasted it like this . it needs some changes in the name of ur main form class and when loading the textures of the mesh put your directory i mean change the " xfiles " folder path ok yalla have fun
I get the same problem with your class. I receive a Direct3DXException on the line:

data.Mesh =
mesh.SkinInformation.ConvertToIndexedBlendedMesh(data.Mesh, flags, mesh.GetAdjacencyStream(), mesh.NumberPaletteEntries, out influences, out bones);

I'm guessing that the problem is that mesh.NumberPaletteEntries is 0. This is because caps.MaxVertexBlendMatrixIndex is 0. In the sample this is 255.

Can you help me out?

This topic is closed to new replies.

Advertisement