public static void GetRefPoseTransform(int i, short[] hierarchy, teModelChunk_Skeleton skeleton, Dictionary <int, teModelChunk_Cloth.ClothNode> clothNodeMap, out teVec3 scale, out teQuat quat, out teVec3 translation) { if (clothNodeMap != null && clothNodeMap.ContainsKey(i)) { Matrix thisMat = skeleton.GetWorldSpace(i); Matrix parentMat = skeleton.GetWorldSpace(hierarchy[i]); Matrix newParentMat = thisMat * Matrix.Invert(parentMat); newParentMat.Decompose(out Vector3 scl2, out Quaternion rot2, out Vector3 pos2); quat = new teQuat(rot2.X, rot2.Y, rot2.Z, rot2.W); scale = new teVec3(scl2.X, scl2.Y, scl2.Z); translation = new teVec3(pos2.X, pos2.Y, pos2.Z); } else { teMtx43 bone = skeleton.Matrices34Inverted[i]; scale = new teVec3(bone[1, 0], bone[1, 1], bone[1, 2]); quat = new teQuat(bone[0, 0], bone[0, 1], bone[0, 2], bone[0, 3]); translation = new teVec3(bone[2, 0], bone[2, 1], bone[2, 2]); } }
public void GetWorldSpace(int idx, out teVec3 scale, out teQuat rotation, out teVec3 translation) { teMtx43 parBoneMat = Matrices34[idx]; scale = new teVec3(parBoneMat[1, 0], parBoneMat[1, 1], parBoneMat[1, 2]); rotation = new teQuat(parBoneMat[0, 0], parBoneMat[0, 1], parBoneMat[0, 2], parBoneMat[0, 3]); translation = new teVec3(parBoneMat[2, 0], parBoneMat[2, 1], parBoneMat[2, 2]); }
/// <summary> /// Unpack scale value /// </summary> /// <param name="x">Packed X component</param> /// <param name="y">Packed Y component</param> /// <param name="z">Packed Z component</param> /// <returns>Unpacked scale value</returns> private static teVec3 UnpackScale(ushort x, ushort y, ushort z) { double xd = x / 1024d; double yd = y / 1024d; double zd = z / 1024d; teVec3 value = new teVec3(xd, yd, zd); return(value); }
public void Write(Stream stream) { System.Threading.Thread.CurrentThread.CurrentCulture = _culture; teModelChunk_Skeleton skeleton = ChunkedData.GetChunk <teModelChunk_Skeleton>(); teModelChunk_Cloth cloth = ChunkedData.GetChunk <teModelChunk_Cloth>(); if (skeleton == null) { return; } short[] hierarchy; Dictionary <int, teModelChunk_Cloth.ClothNode> clothNodeMap = null; if (cloth != null) { hierarchy = cloth.CreateFakeHierarchy(skeleton, out clothNodeMap); } else { hierarchy = skeleton.Hierarchy; } using (StreamWriter writer = new StreamWriter(stream, Encoding.Default, 512)) { writer.WriteLine("{0}", skeleton.Header.BonesAbs); writer.WriteLine("version 1"); writer.WriteLine("nodes"); for (int i = 0; i < skeleton.Header.BonesAbs; ++i) { writer.WriteLine("{0} \"bone_{1:X4}\" {2}", i, skeleton.IDs[i], hierarchy[i]); } writer.WriteLine("end"); writer.WriteLine("skeleton"); writer.WriteLine("time 0"); for (int i = 0; i < skeleton.Header.BonesAbs; ++i) { OverwatchModel.GetRefPoseTransform(i, hierarchy, skeleton, clothNodeMap, out teVec3 scale, out teQuat quat, out teVec3 pos); teVec3 rot = quat.ToEulerAngles(); writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1:0.000000} {2:0.000000} {3:0.000000} {4:0.000000} {5:0.000000} {6:0.000000} {7:0.000000} {8:0.000000} {9:0.000000}", i, pos.X, pos.Y, pos.Z, rot.X, rot.Y, rot.Z, scale.X, scale.Y, scale.Z)); } } }
public void Write(Stream stream) { teModelChunk_RenderMesh renderMesh = _data.GetChunk <teModelChunk_RenderMesh>(); teModelChunk_Model model = _data.GetChunk <teModelChunk_Model>(); teModelChunk_Skeleton skeleton = _data.GetChunk <teModelChunk_Skeleton>(); teModelChunk_Cloth cloth = _data.GetChunk <teModelChunk_Cloth>(); teModelChunk_STU stu = _data.GetChunk <teModelChunk_STU>(); using (BinaryWriter writer = new BinaryWriter(stream)) { writer.Write((ushort)1); writer.Write((ushort)6); if (ModelLookFileName == null) // mat ref { writer.Write((byte)0); } else { writer.Write(ModelLookFileName); } if (Name == null) // model name { writer.Write((byte)0); } else { writer.Write(Name); } short[] hierarchy = null; Dictionary <int, teModelChunk_Cloth.ClothNode> clothNodeMap = null; if (skeleton != null) { if (cloth != null) { hierarchy = cloth.CreateFakeHierarchy(skeleton, out clothNodeMap); } else { hierarchy = skeleton.Hierarchy; } writer.Write(skeleton.Header.BonesAbs); } else { writer.Write((ushort)0); } if (renderMesh == null) { writer.Write(0u); writer.Write(0); return; } teModelChunk_RenderMesh.Submesh[] submeshes = renderMesh.Submeshes.Where(x => x.Descriptor.LOD == TargetLod || x.Descriptor.LOD == -1).ToArray(); writer.Write((uint)submeshes.Length); if (stu?.StructuredData.m_hardPoints != null) { writer.Write(stu.StructuredData.m_hardPoints.Length); } else { writer.Write(0); // hardpoints } if (skeleton != null) { for (int i = 0; i < skeleton.Header.BonesAbs; ++i) { writer.Write(GetBoneName(skeleton.IDs[i])); short parent = hierarchy[i]; if (parent == -1) { parent = (short)i; } writer.Write(parent); skeleton.GetWorldSpace(i, out teVec3 scale, out teQuat rotation, out teVec3 translation); writer.Write(translation); writer.Write(scale); writer.Write(rotation); } } int submeshIdx = 0; foreach (teModelChunk_RenderMesh.Submesh submesh in submeshes) { writer.Write($"Submesh_{submeshIdx}.{model.Materials[submesh.Descriptor.Material]:X16}"); writer.Write(model.Materials[submesh.Descriptor.Material]); writer.Write(submesh.UVCount); writer.Write(submesh.Vertices.Length); writer.Write(submesh.Indices.Length / 3); for (int j = 0; j < submesh.Vertices.Length; j++) { writer.Write(submesh.Vertices[j]); writer.Write(-submesh.Normals[j].X); writer.Write(-submesh.Normals[j].Y); writer.Write(-submesh.Normals[j].Z); foreach (teVec2 uv in submesh.UV[j]) { writer.Write(uv); } if (skeleton != null && submesh.BoneIndices[j] != null) { writer.Write((byte)4); writer.Write(skeleton.Lookup[submesh.BoneIndices[j][0]]); writer.Write(skeleton.Lookup[submesh.BoneIndices[j][1]]); writer.Write(skeleton.Lookup[submesh.BoneIndices[j][2]]); writer.Write(skeleton.Lookup[submesh.BoneIndices[j][3]]); writer.Write(submesh.BoneWeights[j][0]); writer.Write(submesh.BoneWeights[j][1]); writer.Write(submesh.BoneWeights[j][2]); writer.Write(submesh.BoneWeights[j][3]); } else { writer.Write((byte)0); } writer.Write(submesh.Color1.ElementAtOrDefault(j)); writer.Write(submesh.Color2.ElementAtOrDefault(j)); } for (int j = 0; j < submesh.Descriptor.IndicesToDraw; j += 3) { writer.Write((byte)3); writer.Write((int)submesh.Indices[j]); writer.Write((int)submesh.Indices[j + 1]); writer.Write((int)submesh.Indices[j + 2]); } submeshIdx++; } if (stu?.StructuredData.m_hardPoints != null) { foreach (STUModelHardpoint hardPoint in stu.StructuredData.m_hardPoints) { writer.Write(IdToString("hardpoint", teResourceGUID.Index(hardPoint.m_EDF0511C))); Matrix parentMat = Matrix.Identity; if (hardPoint.m_FF592924 != 0 && skeleton != null) { int?boneIdx = null; var hardPointBoneID = teResourceGUID.Index(hardPoint.m_FF592924); for (int i = 0; i < skeleton.IDs.Length; i++) { var currBoneID = skeleton.IDs[i]; if (currBoneID == hardPointBoneID) { boneIdx = i; break; } } if (boneIdx == null) { parentMat = Matrix.Identity; Logger.Debug("OverwatchModel", $"Hardpoint {teResourceGUID.AsString(hardPoint.m_EDF0511C)} is attached to bone {teResourceGUID.AsString(hardPoint.m_FF592924)} which doesn't exist on model {teResourceGUID.AsString(GUID)}"); } else { parentMat = skeleton.GetWorldSpace(boneIdx.Value); } } Matrix hardPointMat = Matrix.RotationQuaternion(hardPoint.m_rotation) * Matrix.Translation(hardPoint.m_position); hardPointMat = hardPointMat * parentMat; hardPointMat.Decompose(out Vector3 _, out Quaternion rot, out Vector3 pos); writer.Write(pos); writer.Write(rot); } foreach (STUModelHardpoint modelHardpoint in stu.StructuredData.m_hardPoints) { writer.Write(IdToString("bone", teResourceGUID.Index(modelHardpoint.m_FF592924))); } } // ext 1.3: old cloth writer.Write(0); // ext 1.4: embedded refpose if (skeleton != null) { for (int i = 0; i < skeleton.Header.BonesAbs; ++i) { writer.Write(IdToString("bone", skeleton.IDs[i])); short parent = hierarchy[i]; writer.Write(parent); GetRefPoseTransform(i, hierarchy, skeleton, clothNodeMap, out teVec3 scale, out teQuat quat, out teVec3 translation); teVec3 rot = quat.ToEulerAngles(); writer.Write(translation); writer.Write(scale); writer.Write(rot); } } writer.Write(teResourceGUID.Index(GUID)); } }