/// <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(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); if (BoneIndex == -1) continue; Bone Bne = Skel.Bones[BoneIndex]; 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; Bne.GlobalTranslation = 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; } } }
/// <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)); m_Skeleton.AssignChildren(ref m_Skeleton); } 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_Tex = Texture2D.FromFile(this.Device, new MemoryStream( ContentManager.GetResourceFromLongID(Bindings[0].TextureAssetID))); string SelectedStr = (string)LstHeads.SelectedItem; if (SelectedStr.Contains("bodies")) { m_CurrentMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), true); m_CurrentMesh.TransformVertices2(m_Skeleton.Bones[0], ref mSimpleEffect); m_CurrentMesh.BlendVertices2(); } else m_CurrentMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), false); LoadMesh(m_CurrentMesh); }
/// <summary> /// User clicked on an item in the list containing available heads and bodies. /// </summary> private void LstHeads_SelectedIndexChanged(object sender, EventArgs e) { if (m_Skeleton == null) { m_Skeleton = new Skeleton(this.Device, ContentManager.GetResourceFromLongID(0x100000005)); m_Skeleton.AssignChildren(ref m_Skeleton); } foreach(KeyValuePair<ulong, string> Pair in ContentManager.Resources) { if ((string)LstHeads.SelectedItem == Pair.Value) { //HAndGroup files are used to group together different hand meshes and textures. if (Pair.Value.Contains(".hag")) { 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_Tex = Texture2D.FromFile(this.Device, new MemoryStream( ContentManager.GetResourceFromLongID(Bindings[0].TextureAssetID))); m_CurrentMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), false); LoadMesh(m_CurrentMesh); } else { 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_Tex = 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_CurrentMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), true); m_CurrentMesh.TransformVertices2(m_Skeleton.Bones[0], ref mSimpleEffect); m_CurrentMesh.BlendVertices2(); LoadMesh(m_CurrentMesh); } else { m_CurrentMesh = new Mesh(ContentManager.GetResourceFromLongID(Bindings[0].MeshAssetID), false); LoadMesh(m_CurrentMesh); } } m_LoadComplete = true; } } }
/// <summary> /// Assigns children to all the bones in a skeleton. /// </summary> /// <param name="Skel">The skeleton to whose bones to assign children to.</param> public void AssignChildren(ref Skeleton Skel) { for(int i = 0; i < Skel.Bones.Length; i++) { for (int j = 0; j < m_Bones.Length; j++) { if (m_Bones[j].ParentName == Skel.Bones[i].BoneName) { //Skel.Bones[i] is the parent of m_Bones[j] Skel.Bones[i].Children[Skel.Bones[i].NumChildren] = m_Bones[j]; Skel.Bones[i].NumChildren++; } } } }