/// <summary> /// Computes the absolute position for all the bones in this skeleton. /// </summary> /// <param name="bone">The bone to start with, should always be the ROOT bone.</param> /// <param name="world">A world matrix to use in the calculation.</param> public void ComputeBonePositions(Bone bone, Matrix world) { var translateMatrix = Matrix.CreateTranslation(bone.Translation); var rotationMatrix = Matrix.CreateFromQuaternion(bone.Rotation); var myWorld = (rotationMatrix * translateMatrix)*world; bone.AbsolutePosition = Vector3.Transform(Vector3.Zero, myWorld); bone.AbsoluteMatrix = myWorld; foreach (var child in bone.Children) { ComputeBonePositions(child, myWorld); } }
/// <summary> /// Clones this bone. /// </summary> /// <returns>A Bone instance with the same values as this one.</returns> public Bone Clone() { var result = new Bone { Unknown = this.Unknown, Name = this.Name, ParentName = this.ParentName, HasProps = this.HasProps, Properties = this.Properties, Translation = this.Translation, Rotation = this.Rotation, CanTranslate = this.CanTranslate, CanRotate = this.CanRotate, CanBlend = this.CanBlend, WiggleValue = this.WiggleValue, WigglePower = this.WigglePower, Index = this.Index }; return result; }
//TODO: assumes that skeleton will be same configuration for all bindings of this mesh. //If any meshes are used by pets and avatars(???) or we implement children this will need //to be changed to support binds to multiple SKEL bases. /// <summary> /// Transforms the verticies making up this mesh into /// the designated bone positions. /// </summary> /// <param name="bone">The bone to start with. Should always be the ROOT bone.</param> /// public void Prepare(Bone bone) { if (Prepared) return; var binding = BoneBindings.FirstOrDefault(x => x.BoneName.Equals(bone.Name, StringComparison.InvariantCultureIgnoreCase)); if (binding != null) { for (var i = 0; i < binding.RealVertexCount; i++) { var vertexIndex = binding.FirstRealVertex + i; VertexBuffer[vertexIndex].Parameters.X = bone.Index; } for (var i = 0; i < binding.BlendVertexCount; i++) { var blendVertexIndex = binding.FirstBlendVertex + i; BlendVertBoneIndices[blendVertexIndex] = bone.Index; } } foreach (var child in bone.Children) { Prepare(child); } if (bone.Name.Equals("ROOT", StringComparison.InvariantCultureIgnoreCase)) { for (int i = 0; i < BlendData.Length; i++) { var data = BlendData[i]; var vert = BlendVertBoneIndices[i]; VertexBuffer[data.OtherVertex].Parameters.Y = BlendVertBoneIndices[i]; VertexBuffer[data.OtherVertex].Parameters.Z = data.Weight; VertexBuffer[data.OtherVertex].BvPosition = BlendVerts[i]; } InvalidateMesh(); Prepared = true; } }
/// <summary> /// Reads a bone from a IOBuffer. /// </summary> /// <param name="reader">An IOBuffer instance used to read from a stream holding a skeleton.</param> /// <returns>A Bone instance.</returns> private Bone ReadBone(IoBuffer reader) { var bone = new Bone(); bone.Unknown = reader.ReadInt32(); bone.Name = reader.ReadPascalString(); bone.ParentName = reader.ReadPascalString(); bone.HasProps = reader.ReadByte(); if (bone.HasProps != 0) { var propertyCount = reader.ReadInt32(); var property = new PropertyListItem(); for (var i = 0; i < propertyCount; i++) { var pairCount = reader.ReadInt32(); for (var x = 0; x < pairCount; x++) { property.KeyPairs.Add(new KeyValuePair<string, string>( reader.ReadPascalString(), reader.ReadPascalString() )); } } bone.Properties.Add(property); } var xx = -reader.ReadFloat(); bone.Translation = new Vector3( xx, reader.ReadFloat(), reader.ReadFloat() ); bone.Rotation = new Quaternion( reader.ReadFloat(), -reader.ReadFloat(), -reader.ReadFloat(), -reader.ReadFloat() ); bone.CanTranslate = reader.ReadInt32(); bone.CanRotate = reader.ReadInt32(); bone.CanBlend = reader.ReadInt32(); bone.WiggleValue = reader.ReadFloat(); bone.WigglePower = reader.ReadFloat(); return bone; }
/// <summary> /// Reads a skeleton from a stream. /// </summary> /// <param name="stream">A Stream instance holding a skeleton.</param> public void Read(Stream stream) { using (var io = IoBuffer.FromStream(stream)) { var version = io.ReadUInt32(); Name = io.ReadPascalString(); var boneCount = io.ReadInt16(); Bones = new Bone[boneCount]; for (var i = 0; i < boneCount; i++) { Bone bone = ReadBone(io); bone.Index = i; Bones[i] = bone; } /** Construct tree **/ foreach (var bone in Bones) { bone.Children = Bones.Where(x => x.ParentName == bone.Name).ToArray(); } RootBone = Bones.FirstOrDefault(x => x.ParentName == "NULL"); ComputeBonePositions(RootBone, Matrix.Identity); } }