public Sim(Matrix WorldMatrix) { m_WorldMat = WorldMatrix; SimSkeleton = new Skeleton(); SimSkeleton.Read(ContentManager.GetResourceFromLongID(0x100000005)); SimSkeleton.ComputeBonePositions(SimSkeleton.RootBone, m_WorldMat); }
/// <summary> /// Processes the loaded mesh's data and populates an array of /// VertexPositionNormalTexture elements that can be looped to /// render the mesh. Assumes that TransformVertices2() and /// BlendVertices2() has been called for bodymeshes! /// </summary> public void ProcessMesh(Skeleton Skel, bool IsHeadMesh) { VertexPositionNormalTexture[] NormVerticies = new VertexPositionNormalTexture[TotalVertexCount]; for (int i = 0; i < TotalVertexCount; i++) { NormVerticies[i] = new VertexPositionNormalTexture(); NormVerticies[i].Position.X = TransformedVertices[i].Vertex.Coord.X; NormVerticies[i].Position.Y = TransformedVertices[i].Vertex.Coord.Y; NormVerticies[i].Position.Z = TransformedVertices[i].Vertex.Coord.Z; NormVerticies[i].Normal.X = TransformedVertices[i].Vertex.NormalCoord.X; NormVerticies[i].Normal.Y = TransformedVertices[i].Vertex.NormalCoord.Y; NormVerticies[i].Normal.Z = TransformedVertices[i].Vertex.NormalCoord.Z; if (IsHeadMesh) { //Transform the head vertices' position by the absolute transform //for the headbone (which is always bone 17) to render the head in place. NormVerticies[i].Position = Vector3.Transform(Vertex[i].Vertex.Coord, Skel.Bones[16].AbsoluteMatrix); //Transform the head normals' position by the absolute transform //for the headbone (which is always bone 17) to render the head in place. NormVerticies[i].Normal = Vector3.Transform(Vertex[i].Vertex.NormalCoord, Skel.Bones[16].AbsoluteMatrix); } } for (int i = 0; i < RealVertexCount; i++) { NormVerticies[i].TextureCoordinate.X = TransformedVertices[i].Vertex.TextureCoord.X; NormVerticies[i].TextureCoordinate.Y = TransformedVertices[i].Vertex.TextureCoord.Y; } m_VertexNTexPositions = NormVerticies; }
/// <summary> /// Advances the frame of an animation for a skeleton used on this mesh. /// </summary> /// <param name="Skel">A skeleton used to render this mesh.</param> /// <param name="Animation">The animation to advance.</param> /// <param name="AnimationTime">The playback time for an animation (how long has it been playing for?)</param> /// <param name="TimeDelta">The timedelta of the rendering loop.</param> public void AdvanceFrame(ref Skeleton Skel, Anim Animation, ref float AnimationTime, float TimeDelta) { float Duration = (float)Animation.Motions[0].NumFrames / 30; AnimationTime += TimeDelta; AnimationTime = AnimationTime % Duration; //Loop the animation for (int i = 0; i < Animation.Motions.Count; i++) { int BoneIndex = Skel.FindBone(Animation.Motions[i].BoneName, i); if (BoneIndex == -1) continue; int Frame = (int)(AnimationTime * 30); float FractionShown = AnimationTime * 30 - Frame; int NextFrame = (Frame + 1 != Animation.Motions[0].NumFrames) ? Frame + 1 : 0; if (Animation.Motions[i].HasTranslation == 1) { Vector3 Translation = new Vector3(Animation.Motions[i].Translations[Frame, 0], Animation.Motions[i].Translations[Frame, 1], Animation.Motions[i].Translations[Frame, 2]); Vector3 NextTranslation = new Vector3(Animation.Motions[i].Translations[NextFrame, 0], Animation.Motions[i].Translations[NextFrame, 1], Animation.Motions[i].Translations[NextFrame, 2]); Vector3 UpdatedTranslation = new Vector3(); UpdatedTranslation.X = (1 - FractionShown) * Translation.X + FractionShown * NextTranslation.X; UpdatedTranslation.Y = (1 - FractionShown) * Translation.Y + FractionShown * NextTranslation.Y; UpdatedTranslation.Z = (1 - FractionShown) * Translation.Z + FractionShown * NextTranslation.Z; Skel.Bones[BoneIndex].Translation = UpdatedTranslation; } if (Animation.Motions[i].HasRotation == 1) { Quaternion Rotation = new Quaternion(Animation.Motions[i].Rotations[Frame, 0], Animation.Motions[i].Rotations[Frame, 1], Animation.Motions[i].Rotations[Frame, 2], Animation.Motions[i].Rotations[Frame, 3]); Quaternion NextRotation = new Quaternion(Animation.Motions[i].Rotations[NextFrame, 0], Animation.Motions[i].Rotations[NextFrame, 1], Animation.Motions[i].Rotations[NextFrame, 2], Animation.Motions[i].Rotations[NextFrame, 3]); //Use Slerp to interpolate float W1, W2 = 1.0f; float CosTheta = DotProduct(Rotation, NextRotation); if (CosTheta < 0) { CosTheta *= -1; W2 *= -1; } float Theta = (float)Math.Acos(CosTheta); float SinTheta = (float)Math.Sin(Theta); if (SinTheta > 0.001f) { W1 = (float)Math.Sin((1.0f - FractionShown) * Theta) / SinTheta; W2 *= (float)Math.Sin(FractionShown * Theta) / SinTheta; } else { W1 = 1.0f - FractionShown; W2 = FractionShown; } Quaternion UpdatedRotation = new Quaternion(); UpdatedRotation.X = W1 * Rotation.X + W2 * NextRotation.X; UpdatedRotation.Y = W1 * Rotation.Y + W2 * NextRotation.Y; UpdatedRotation.Z = W1 * Rotation.Z + W2 * NextRotation.Z; UpdatedRotation.W = W1 * Rotation.W + W2 * NextRotation.W; Skel.Bones[BoneIndex].Rotation.X = UpdatedRotation.X; Skel.Bones[BoneIndex].Rotation.Y = UpdatedRotation.Y; Skel.Bones[BoneIndex].Rotation.Z = UpdatedRotation.Z; Skel.Bones[BoneIndex].Rotation.W = UpdatedRotation.W; } } }
/// <summary> /// Transforms the vertices in a mesh to their location in 3D-space based on /// the location of a bone. /// </summary> /// <param name="Bne">The bone to start with (should be a skeleton's ROOT bone).</param> /// <param name="Effect">The BasicEffect instance used for rendering.</param> public void TransformVertices2(ref Skeleton Skel, Bone Bne, ref Matrix World) { int BoneIndex = 0; Matrix WorldMat = World * Bne.AbsoluteTransform; for (BoneIndex = 0; BoneIndex < m_BndCount; BoneIndex++) { if (Bne.BoneName == m_BoneNames[m_BoneBindings[BoneIndex].BoneIndex]) break; } if (BoneIndex < m_BndCount) { for (int i = 0; i < m_BoneBindings[BoneIndex].VertexCount; i++) { int VertexIndex = m_BoneBindings[BoneIndex].FirstVertex + i; Vector3 RelativeVertex = new Vector3(m_VertexData[VertexIndex, 0], m_VertexData[VertexIndex, 1], m_VertexData[VertexIndex, 2]); Vector3 RelativeNormal = new Vector3(m_VertexData[VertexIndex, 3], m_VertexData[VertexIndex, 4], m_VertexData[VertexIndex, 5]); WorldMat *= Matrix.CreateTranslation(RelativeVertex); m_TransformedVertices[VertexIndex].Coord.X = WorldMat.M41; m_TransformedVertices[VertexIndex].Coord.Y = WorldMat.M42; m_TransformedVertices[VertexIndex].Coord.Z = WorldMat.M43; WorldMat *= Matrix.CreateTranslation(new Vector3(-RelativeVertex.X, -RelativeVertex.Y, -RelativeVertex.Z)); WorldMat *= Matrix.CreateTranslation(RelativeNormal); m_TransformedVertices[VertexIndex].Normal.X = WorldMat.M41; m_TransformedVertices[VertexIndex].Normal.Y = WorldMat.M42; m_TransformedVertices[VertexIndex].Normal.Z = WorldMat.M43; WorldMat *= Matrix.CreateTranslation(new Vector3(-RelativeNormal.X, -RelativeNormal.Y, -RelativeNormal.Z)); } for (int i = 0; i < m_BoneBindings[BoneIndex].BlendedVertexCount; i++) { int VertexIndex = m_RealVertexCount + m_BoneBindings[BoneIndex].FirstBlendedVert + i; Vector3 RelativeVertex = new Vector3(m_VertexData[VertexIndex, 0], m_VertexData[VertexIndex, 1], m_VertexData[VertexIndex, 2]); Vector3 RelativeNormal = new Vector3(m_VertexData[VertexIndex, 3], m_VertexData[VertexIndex, 4], m_VertexData[VertexIndex, 5]); WorldMat *= Matrix.CreateTranslation(RelativeVertex); m_TransformedVertices[VertexIndex].Coord.X = WorldMat.M41; m_TransformedVertices[VertexIndex].Coord.Y = WorldMat.M42; m_TransformedVertices[VertexIndex].Coord.Z = WorldMat.M43; WorldMat *= Matrix.CreateTranslation(new Vector3(-RelativeVertex.X, -RelativeVertex.Y, -RelativeVertex.Z)); WorldMat *= Matrix.CreateTranslation(RelativeNormal); m_TransformedVertices[VertexIndex].Normal.X = WorldMat.M41; m_TransformedVertices[VertexIndex].Normal.Y = WorldMat.M42; m_TransformedVertices[VertexIndex].Normal.Z = WorldMat.M43; WorldMat *= Matrix.CreateTranslation(new Vector3(-RelativeNormal.X, -RelativeNormal.Y, -RelativeNormal.Z)); } } if (Bne.NumChildren == 1) TransformVertices2(ref Skel, Skel.Bones[Bne.Children[0]], ref World); else if (Bne.NumChildren > 1) { for (int i = 0; i < Bne.NumChildren; i++) TransformVertices2(ref Skel, Skel.Bones[Bne.Children[i]], ref World); } }
/// <summary> /// Processes the loaded mesh's data and populates an array of /// VertexPositionNormalTexture elements that can be looped to /// render the mesh. Assumes that TransformVertices2() and /// BlendVertices2() has been called for bodymeshes! /// </summary> public void ProcessMesh(Skeleton Skel) { VertexPositionNormalTexture[] NormVerticies = new VertexPositionNormalTexture[m_TotalVertexCount]; if (!IsBodyMesh) { for (int i = 0; i < m_TotalVertexCount; i++) { NormVerticies[i] = new VertexPositionNormalTexture(); //Transform the head vertices' position by the absolute transform //for the headbone (which is always bone 17) to render the head in place. NormVerticies[i].Position = Vector3.Transform(new Vector3(m_VertexData[i, 0], m_VertexData[i, 1], m_VertexData[i, 2]), Skel.Bones[16].AbsoluteTransform); //Transform the head normals' position by the absolute transform //for the headbone (which is always bone 17) to render the head in place. NormVerticies[i].Normal = Vector3.Transform(new Vector3(m_VertexData[i, 3], m_VertexData[i, 4], m_VertexData[i, 5]), Skel.Bones[16].AbsoluteTransform); //Not really sure why this is important, but I think it has something to do //with being able to see the texture. //NormVerticies[i].Normal.Normalize(); } } else { for (int i = 0; i < m_TotalVertexCount; i++) { NormVerticies[i] = new VertexPositionNormalTexture(); NormVerticies[i].Position.X = m_TransformedVertices[i].Coord.X; NormVerticies[i].Position.Y = m_TransformedVertices[i].Coord.Y; NormVerticies[i].Position.Z = m_TransformedVertices[i].Coord.Z; NormVerticies[i].Normal.X = m_TransformedVertices[i].Normal.X; NormVerticies[i].Normal.Y = m_TransformedVertices[i].Normal.Y; NormVerticies[i].Normal.Z = m_TransformedVertices[i].Normal.Z; } } for (int i = 0; i < m_RealVertexCount; i++) { NormVerticies[i].TextureCoordinate.X = m_TexVerticies[i, 1]; NormVerticies[i].TextureCoordinate.Y = m_TexVerticies[i, 2]; } m_VertexNTexPositions = NormVerticies; }
public Bone(Skeleton Skel) { m_Skel = Skel; }
/// <summary> /// User clicked on an item in the list containing available heads. /// </summary> private void LstBodies_SelectedIndexChanged(object sender, EventArgs e) { if (m_Skeleton == null) m_Skeleton = new Skeleton(this.Device, ContentManager.GetResourceFromLongID(0x100000005), mWorldMat); string SelectedStr = (string)LstBodies.SelectedItem; string Type = SelectedStr.Split(":".ToCharArray())[0]; SelectedStr = SelectedStr.Split(":".ToCharArray())[1].Replace(" ", ""); foreach (KeyValuePair<ulong, string> Pair in ContentManager.Resources) { //Check if the selected hexstring equals a ulong ID in ContentManager. if (Pair.Key == Convert.ToUInt64(SelectedStr, 16)) { PurchasableObject PO = new PurchasableObject(ContentManager.GetResourceFromLongID(Pair.Key)); m_CurrentOutfit = new Outfit(ContentManager.GetResourceFromLongID(PO.OutfitID)); m_CurrentAppearance = new Appearance( ContentManager.GetResourceFromLongID(m_CurrentOutfit.LightAppearanceID)); LstAppearances.Items.Clear(); LstAppearances.Items.Add(m_CurrentOutfit.LightAppearanceID); LstAppearances.Items.Add(m_CurrentOutfit.MediumAppearanceID); LstAppearances.Items.Add(m_CurrentOutfit.DarkAppearanceID); List<Binding> Bindings = new List<Binding>(); foreach (ulong BindingID in m_CurrentAppearance.BindingIDs) Bindings.Add(new Binding(ContentManager.GetResourceFromLongID(BindingID))); m_BodyTex = Texture2D.FromFile(this.Device, new MemoryStream( ContentManager.GetResourceFromLongID(Bindings[0].TextureAssetID))); //The file selected was most likely a body-mesh, so apply the adult skeleton to it. if (Pair.Value.Contains("bodies")) { m_CurrentBodyMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), true); m_CurrentBodyMesh.TransformVertices2(ref m_Skeleton, m_Skeleton.Bones[0], ref mWorldMat); m_CurrentBodyMesh.BlendVertices2(); m_CurrentBodyMesh.ProcessMesh(m_Skeleton); } } } m_LoadBodyComplete = true; }
/// <summary> /// User clicked on an item in the list containing available heads. /// </summary> private void LstHeads_SelectedIndexChanged(object sender, EventArgs e) { if (m_Skeleton == null) m_Skeleton = new Skeleton(this.Device, ContentManager.GetResourceFromLongID(0x100000005), ref mWorldMat); string SelectedStr = (string)LstHeads.SelectedItem; string Type = SelectedStr.Split(":".ToCharArray())[0]; SelectedStr = SelectedStr.Split(":".ToCharArray())[1].Replace(" ", ""); foreach(KeyValuePair<ulong, string> Pair in ContentManager.Resources) { //HAndGroup files are used to group together different hand meshes and textures. if (Pair.Key == Convert.ToUInt64(SelectedStr, 16) && Type == "Hand") { Hag HandGroup = new Hag(ContentManager.GetResourceFromLongID(Pair.Key)); m_CurrentAppearance = new Appearance(ContentManager.GetResourceFromLongID( HandGroup.Appearances[0])); LstAppearances.Items.Clear(); foreach (ulong AppearanceID in HandGroup.Appearances) LstAppearances.Items.Add(AppearanceID); List<Binding> Bindings = new List<Binding>(); foreach (ulong BindingID in m_CurrentAppearance.BindingIDs) Bindings.Add(new Binding(ContentManager.GetResourceFromLongID(BindingID))); m_HandTex = Texture2D.FromFile(this.Device, new MemoryStream( ContentManager.GetResourceFromLongID(Bindings[0].TextureAssetID))); m_CurrentHandMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), false); m_CurrentHandMesh.ProcessMesh(m_Skeleton); } else { //Check if the selected hexstring equals a ulong ID in ContentManager. if (Pair.Key == Convert.ToUInt64(SelectedStr, 16)) { PurchasableObject PO = new PurchasableObject(ContentManager.GetResourceFromLongID(Pair.Key)); m_CurrentOutfit = new Outfit(ContentManager.GetResourceFromLongID(PO.OutfitID)); m_CurrentAppearance = new Appearance( ContentManager.GetResourceFromLongID(m_CurrentOutfit.LightAppearanceID)); LstAppearances.Items.Clear(); LstAppearances.Items.Add(m_CurrentOutfit.LightAppearanceID); LstAppearances.Items.Add(m_CurrentOutfit.MediumAppearanceID); LstAppearances.Items.Add(m_CurrentOutfit.DarkAppearanceID); List<Binding> Bindings = new List<Binding>(); foreach (ulong BindingID in m_CurrentAppearance.BindingIDs) Bindings.Add(new Binding(ContentManager.GetResourceFromLongID(BindingID))); m_HeadTex = Texture2D.FromFile(this.Device, new MemoryStream( ContentManager.GetResourceFromLongID(Bindings[0].TextureAssetID))); m_CurrentHeadMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), false); m_CurrentHeadMesh.ProcessMesh(m_Skeleton); } } } m_LoadHeadComplete = true; }
/// <summary> /// User clicked on an item in the list containing available appearances. /// </summary> private void LstAppearances_SelectedIndexChanged(object sender, EventArgs e) { if (m_Skeleton == null) m_Skeleton = new Skeleton(this.Device, ContentManager.GetResourceFromLongID(0x100000005), ref mWorldMat); m_CurrentAppearance = new Appearance(ContentManager.GetResourceFromLongID( (ulong)LstAppearances.SelectedItem)); List<Binding> Bindings = new List<Binding>(); foreach (ulong BindingID in m_CurrentAppearance.BindingIDs) Bindings.Add(new Binding(ContentManager.GetResourceFromLongID(BindingID))); m_BodyTex = Texture2D.FromFile(this.Device, new MemoryStream( ContentManager.GetResourceFromLongID(Bindings[0].TextureAssetID))); string SelectedHeadStr = (string)LstHeads.SelectedItem; string SelectedBodyStr = (string)LstBodies.SelectedItem; m_CurrentBodyMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), true); m_CurrentBodyMesh.TransformVertices2(ref m_Skeleton, m_Skeleton.Bones[0], ref mWorldMat); m_CurrentBodyMesh.BlendVertices2(); m_CurrentBodyMesh.ProcessMesh(m_Skeleton); m_CurrentHeadMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), false); m_CurrentHeadMesh.ProcessMesh(m_Skeleton); }
/// <summary> /// Draws a skeleton. /// </summary> /// <param name="Skel">The skeleton to be drawn.</param> private void DrawSkeleton(Skeleton Skel) { m_SkeletonEffect.Begin(); foreach (var pass in m_SkeletonEffect.Techniques[0].Passes) { pass.Begin(); foreach (var bone in Skel.Bones) { var color = Microsoft.Xna.Framework.Graphics.Color.Green; if (bone.Name == "ROOT") { color = Microsoft.Xna.Framework.Graphics.Color.Red; } else if (bone.Name == "HEAD") { color = Microsoft.Xna.Framework.Graphics.Color.Yellow; } var vertex = new VertexPositionColor(bone.AbsolutePosition, color); var vertexList = new VertexPositionColor[1] { vertex }; this.Device.DrawUserPrimitives(PrimitiveType.PointList, vertexList, 0, 1); } pass.End(); } m_SkeletonEffect.End(); }
/// <summary> /// Advances the frame of an animation for a skeleton used on this mesh. /// </summary> /// <param name="Skel">A skeleton used to render this mesh.</param> /// <param name="Animation">The animation to advance.</param> /// <param name="AnimationTime">The playback time for an animation (how long has it been playing for?)</param> /// <param name="TimeDelta">The timedelta of the rendering loop.</param> public void AdvanceFrame(ref Skeleton Skel, Anim Animation, ref float AnimationTime, float TimeDelta) { float Duration = (float)Animation.Motions[0].NumFrames / 30; AnimationTime += TimeDelta; AnimationTime = AnimationTime % Duration; //Loop the animation for (int i = 0; i < Animation.Motions.Count; i++) { int BoneIndex = Skel.FindBone(Animation.Motions[i].BoneName, i); if (BoneIndex == -1) { continue; } int Frame = (int)(AnimationTime * 30); float FractionShown = AnimationTime * 30 - Frame; int NextFrame = (Frame + 1 != Animation.Motions[0].NumFrames) ? Frame + 1 : 0; if (Animation.Motions[i].HasTranslation == 1) { Vector3 Translation = new Vector3(Animation.Motions[i].Translations[Frame, 0], Animation.Motions[i].Translations[Frame, 1], Animation.Motions[i].Translations[Frame, 2]); Vector3 NextTranslation = new Vector3(Animation.Motions[i].Translations[NextFrame, 0], Animation.Motions[i].Translations[NextFrame, 1], Animation.Motions[i].Translations[NextFrame, 2]); Vector3 UpdatedTranslation = new Vector3(); UpdatedTranslation.X = (1 - FractionShown) * Translation.X + FractionShown * NextTranslation.X; UpdatedTranslation.Y = (1 - FractionShown) * Translation.Y + FractionShown * NextTranslation.Y; UpdatedTranslation.Z = (1 - FractionShown) * Translation.Z + FractionShown * NextTranslation.Z; Skel.Bones[BoneIndex].Translation = UpdatedTranslation; } if (Animation.Motions[i].HasRotation == 1) { Quaternion Rotation = new Quaternion(Animation.Motions[i].Rotations[Frame, 0], Animation.Motions[i].Rotations[Frame, 1], Animation.Motions[i].Rotations[Frame, 2], Animation.Motions[i].Rotations[Frame, 3]); Quaternion NextRotation = new Quaternion(Animation.Motions[i].Rotations[NextFrame, 0], Animation.Motions[i].Rotations[NextFrame, 1], Animation.Motions[i].Rotations[NextFrame, 2], Animation.Motions[i].Rotations[NextFrame, 3]); //Use Slerp to interpolate float W1, W2 = 1.0f; float CosTheta = DotProduct(Rotation, NextRotation); if (CosTheta < 0) { CosTheta *= -1; W2 *= -1; } float Theta = (float)Math.Acos(CosTheta); float SinTheta = (float)Math.Sin(Theta); if (SinTheta > 0.001f) { W1 = (float)Math.Sin((1.0f - FractionShown) * Theta) / SinTheta; W2 *= (float)Math.Sin(FractionShown * Theta) / SinTheta; } else { W1 = 1.0f - FractionShown; W2 = FractionShown; } Quaternion UpdatedRotation = new Quaternion(); UpdatedRotation.X = W1 * Rotation.X + W2 * NextRotation.X; UpdatedRotation.Y = W1 * Rotation.Y + W2 * NextRotation.Y; UpdatedRotation.Z = W1 * Rotation.Z + W2 * NextRotation.Z; UpdatedRotation.W = W1 * Rotation.W + W2 * NextRotation.W; Skel.Bones[BoneIndex].Rotation.X = UpdatedRotation.X; Skel.Bones[BoneIndex].Rotation.Y = UpdatedRotation.Y; Skel.Bones[BoneIndex].Rotation.Z = UpdatedRotation.Z; Skel.Bones[BoneIndex].Rotation.W = UpdatedRotation.W; } } }