Skinned Animation

Started by
0 comments, last by mdias 18 years, 7 months ago
Hi everybody, I am using an example from the book "Sams - Managed DirectX 9 Kick Start, Graphics and Game Programming" about animations & keyframes on meshes. This example is working, but the program bug when I use textures on my mesh. I analysed the sample code and it seems it should be able to use textures. The bug occurs on this line in function CreateAnimation() rootFrame = Mesh.LoadHierarchyFromFile(file, MeshFlags.Managed, device, alloc, null); Error : Microsoft.DirectX.Direct3D.Direct3DXException If you have any idea to solve the problem, reply ! Thanx /********** Here is the full source *************/

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using DirectX=Microsoft.DirectX;
using Direct3D=Microsoft.DirectX.Direct3D;


namespace DX9Sample
{
	public class Form1 : System.Windows.Forms.Form
	{
		private AnimationRootFrame rootFrame;
		private Vector3 objectCenter;
		private float objectRadius;
		private float elapsedTime;
		private Device device;

		static void Main() 
		{
			Application.Run(new Form1());
		}

		public Form1()
		{
			if (!this.InitializeGraphics())
			{
				MessageBox.Show("Your card can not perform skeletal animation on " +
					"this file in hardware. This application will run in " +
					"reference mode instead.");
			}
		}


		public void GenerateSkinnedMesh(MeshContainerDerived mesh)
		{
			if (mesh.SkinInformation == null)
				throw new ArgumentException();

			int numInfl = 0;
			BoneCombination[] bones;

			// Use ConvertToBlendedMesh to generate a drawable mesh
			MeshData m = mesh.MeshData;
			m.Mesh = mesh.SkinInformation.ConvertToBlendedMesh(m.Mesh, MeshFlags.Managed
				| MeshFlags.OptimizeVertexCache, mesh.GetAdjacencyStream(), out numInfl,
				out bones);

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

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

			mesh.MeshData = m;
		}


		public bool InitializeGraphics()
		{
			// Set our presentation parameters
			PresentParameters presentParams = new PresentParameters();

			presentParams.Windowed = true;
			presentParams.SwapEffect = SwapEffect.Discard;
			presentParams.AutoDepthStencilFormat = DepthFormat.D16;
			presentParams.EnableAutoDepthStencil = true;

			bool canDoHardwareSkinning = true;
			// Does a hardware device support shaders?
			Caps hardware = Manager.GetDeviceCaps(0, DeviceType.Hardware);
			// We will need at least four blend matrices
			if (hardware.MaxVertexBlendMatrices >= 4)
			{
				// Default to software processing
				CreateFlags flags = CreateFlags.SoftwareVertexProcessing;

				// Use hardware if it's available
				if (hardware.DeviceCaps.SupportsHardwareTransformAndLight)
					flags = CreateFlags.HardwareVertexProcessing;

				// Use pure if it's available
				if (hardware.DeviceCaps.SupportsPureDevice)
					flags |= CreateFlags.PureDevice;

				// Yes, Create our device
				device = new Device(0, DeviceType.Hardware, this, flags, presentParams);
			}
			else
			{
				// No shader support
				canDoHardwareSkinning = false;

				// Create a reference device
				device = new Device(0, DeviceType.Reference, this,
					CreateFlags.SoftwareVertexProcessing, presentParams);
			}

			// Create the animation
			CreateAnimation(Application.StartupPath + "\\tiny.x", presentParams);

			// Hook the device reset event
			device.DeviceReset += new EventHandler(OnDeviceReset);
			OnDeviceReset(device, null);

			return canDoHardwareSkinning;
		}


		private void OnDeviceReset(object sender, EventArgs e)
		{
			Device dev = (Device)sender;

			// Set the view matrix
			Vector3 vEye = new Vector3( 0, 0, -1.8f * objectRadius );
			Vector3 vUp = new Vector3( 0, 1, 0 );

			dev.Transform.View = Matrix.LookAtLH(vEye, objectCenter, vUp);

			// Setup the projection matrix
			float aspectRatio = (float)dev.PresentationParameters.BackBufferWidth
				/ (float)dev.PresentationParameters.BackBufferHeight;

			dev.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4,
				aspectRatio, objectRadius/64.0f, objectRadius*200.0f );

			// Initialize our light
			dev.Lights[0].Type = LightType.Directional;
			dev.Lights[0].Direction = new Vector3(0.0f, 0.0f, 1.0f);
			dev.Lights[0].Diffuse = Color.White;
			dev.Lights[0].Enabled = true;
		}


		private void CreateAnimation(string file, PresentParameters presentParams)
		{
			// Create our allocate hierarchy derived class
			AllocateHierarchyDerived alloc = new AllocateHierarchyDerived(this);

			// Load our file
			rootFrame = Mesh.LoadHierarchyFromFile(file, MeshFlags.Managed,
				device, alloc, null);

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

			// Setup the matrices for animation
			SetupBoneMatrices((FrameDerived)rootFrame.FrameHierarchy);

			// Start the timer
			DXUtil.Timer(DirectXTimer.Start);
		}


		private void SetupBoneMatrices(FrameDerived frame)
		{
			if (frame.MeshContainer != null)
			{
				SetupBoneMatrices((MeshContainerDerived)frame.MeshContainer);
			}

			if (frame.FrameSibling != null)
			{
				SetupBoneMatrices((FrameDerived)frame.FrameSibling);
			}

			if (frame.FrameFirstChild != null)
			{
				SetupBoneMatrices((FrameDerived)frame.FrameFirstChild);
			}
		}


		private void SetupBoneMatrices(MeshContainerDerived mesh)
		{
			// Is there skin information?  If so, setup the matrices
			if (mesh.SkinInformation != null)
			{
				int numBones = mesh.SkinInformation.NumberBones;

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

					if (frame == null)
						throw new ArgumentException();

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


		protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
		{
			ProcessNextFrame();

			device.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
				Color.CornflowerBlue, 1.0f, 0);

			device.BeginScene();

			// Draw our root frame
			DrawFrame((FrameDerived)rootFrame.FrameHierarchy);

			device.EndScene();

			device.Present();

			this.Invalidate();
		}


		private void ProcessNextFrame()
		{
			// Get the current elapsed time
			elapsedTime = DXUtil.Timer(DirectXTimer.GetElapsedTime);

			// Set the world matrix
			Matrix worldMatrix = Matrix.Translation(objectCenter);
			device.Transform.World = worldMatrix;

			if (rootFrame.AnimationController != null)
				rootFrame.AnimationController.AdvanceTime(elapsedTime, null);

			UpdateFrameMatrices((FrameDerived)rootFrame.FrameHierarchy, worldMatrix);
		}


		private void UpdateFrameMatrices(FrameDerived frame, Matrix parentMatrix)
		{
			frame.CombinedTransformationMatrix = frame.TransformationMatrix *
				parentMatrix;

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

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


		private void DrawFrame(FrameDerived frame)
		{
			MeshContainerDerived mesh = (MeshContainerDerived)frame.MeshContainer;
			while(mesh != null)
			{
				DrawMeshContainer(mesh, frame);

				mesh = (MeshContainerDerived)mesh.NextContainer;
			}

			if (frame.FrameSibling != null)
			{
				DrawFrame((FrameDerived)frame.FrameSibling);
			}

			if (frame.FrameFirstChild != null)
			{
				DrawFrame((FrameDerived)frame.FrameFirstChild);
			}
		}

		private void DrawMeshContainer(MeshContainerDerived mesh, FrameDerived frame)
		{
			// Is there skin information?
			if (mesh.SkinInformation != null)
			{

				int attribIdPrev = -1;

				// Draw
				for (int iattrib = 0; iattrib < mesh.NumberAttributes; iattrib++)
				{
					int numBlend = 0;
					BoneCombination[] bones = mesh.GetBones();
					for (int i = 0; i < mesh.NumberInfluences; i++)
					{
						if (bones[iattrib].BoneId != -1)
						{
							numBlend = i;
						}
					}

					if (device.DeviceCaps.MaxVertexBlendMatrices >= numBlend + 1)
					{
						// first calculate the world matrices for the current set of
						// blend weights and get the accurate count of the number of
						// blends
						Matrix[] offsetMatrices = mesh.GetOffsetMatrices();
						FrameDerived[] frameMatrices = mesh.GetFrames();
						for (int i = 0; i < mesh.NumberInfluences; i++)
						{
							int matrixIndex = bones[iattrib].BoneId;
							if (matrixIndex != -1)
							{
								Matrix tempMatrix = offsetMatrices[matrixIndex] *
									frameMatrices[matrixIndex].
									CombinedTransformationMatrix;

								device.Transform.SetWorldMatrixByIndex(i, tempMatrix);

							}
						}

						device.RenderState.VertexBlend = (VertexBlend)numBlend;
						// lookup the material used for this subset of faces
						if ((attribIdPrev != bones[iattrib].AttributeId) ||
							(attribIdPrev == -1))
						{
							device.Material = mesh.GetMaterials()[
								bones[iattrib].AttributeId].Material3D;

							device.SetTexture(0, mesh.GetTextures()[
								bones[iattrib].AttributeId]); 

							attribIdPrev = bones[iattrib].AttributeId;
						}

						mesh.MeshData.Mesh.DrawSubset(iattrib);
					}
				}
			}
			else // standard mesh, just draw it after setting material properties
			{
				device.Transform.World = frame.CombinedTransformationMatrix;

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

	}




	public class AllocateHierarchyDerived : AllocateHierarchy
	{
		private Form1 app = null;

		public AllocateHierarchyDerived(Form1 parent)
		{
			app = parent;
		}
		public override Frame CreateFrame(string name)
		{
			FrameDerived frame = new FrameDerived();
			frame.Name = name;
			frame.TransformationMatrix = Matrix.Identity;
			frame.CombinedTransformationMatrix = Matrix.Identity;

			return frame;
		}


		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();

			MeshContainerDerived mesh = new MeshContainerDerived();

			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);

				meshData.Mesh = tempMesh;
				meshData.Mesh.ComputeNormals();
			}

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

			// Create any textures
			for (int i = 0; i < materials.Length; i++)
			{
				if (materials.TextureFilename != null)
				{
					meshTextures = TextureLoader.FromFile(dev, @"..\..\" +
						materials.TextureFilename);
				}
			}
			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);

				app.GenerateSkinnedMesh(mesh);
			}

			return mesh;
		}		
	}


	public class FrameDerived : Frame
	{
		// Store the combined transformation matrix
		private Matrix combined = Matrix.Identity;
		public Matrix CombinedTransformationMatrix
		{
			get { return combined; } set { combined = value; }
		}
	}


	public class MeshContainerDerived : MeshContainer
	{
		private Texture[] meshTextures = null;
		private int numAttr = 0;
		private int numInfl = 0;
		private BoneCombination[] bones;
		private FrameDerived[] frameMatrices;
		private Matrix[] offsetMatrices;

		// Public properties
		public Texture[] GetTextures() { return meshTextures; }
		public void SetTextures(Texture[] textures) { meshTextures = textures; }

		public BoneCombination[] GetBones() { return bones; }
		public void SetBones(BoneCombination[] b) { bones = b; }

		public FrameDerived[] GetFrames() { return frameMatrices; }
		public void SetFrames(FrameDerived[] frames) { frameMatrices = frames; }

		public Matrix[] GetOffsetMatrices() { return offsetMatrices; }
		public void SetOffsetMatrices(Matrix[] matrices){offsetMatrices = matrices; }

		public int NumberAttributes {get{return numAttr;}set{numAttr = value;}}
		public int NumberInfluences {get{return numInfl;}set{numInfl = value;}}
	}

}

[Edited by - goundy on August 26, 2005 12:17:15 AM]
Advertisement
Please read the section "How do I put links/quotes/images/code/smileys in my posts?" of the forums faq and edit your post to make sure it use the proper code tags to facilitate the reading of someone who wants to help.

As for your original question, I don't know how to help you.

This topic is closed to new replies.

Advertisement