// ReSharper disable once InconsistentNaming // data is object[] { bool exportAttachments, string materialReference, string modelName, bool onlyOneLOD, bool skipCollision } public void Write(ICLIFlags flags, Chunked chunked, Stream output, List <byte> LODs, object[] data, FindLogic.Combo.ModelInfoNew modelInfo) { byte?flagLOD = null; if (flags is ExtractFlags extractFlags) { flagLOD = extractFlags.LOD; } IChunk chunk = chunked.FindNextChunk("MNRM").Value; if (chunk == null) { return; } MNRM model = (MNRM)chunk; chunk = chunked.FindNextChunk("CLDM").Value; CLDM materials = null; if (chunk != null) { materials = (CLDM)chunk; } chunk = chunked.FindNextChunk("lksm").Value; lksm skeleton = null; if (chunk != null) { skeleton = (lksm)chunk; } chunk = chunked.FindNextChunk("PRHM").Value; PRHM hardpoints = null; if (chunk != null) { hardpoints = (PRHM)chunk; } HTLC cloth = chunked.FindNextChunk("HTLC").Value as HTLC; short[] hierarchy = (short[])skeleton?.Hierarchy.Clone(); Dictionary <int, HTLC.ClothNode> nodeMap = new Dictionary <int, HTLC.ClothNode>(); if (cloth != null) { uint clothIndex = 0; foreach (HTLC.ClothNode[] nodeCollection in cloth.Nodes) { if (nodeCollection == null) { continue; } int nodeIndex = 0; foreach (HTLC.ClothNode node in nodeCollection) { int parentRaw = node.VerticalParent; if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex) && cloth.NodeBones[clothIndex].ContainsKey(parentRaw)) { if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = cloth.NodeBones[clothIndex][parentRaw]; if (cloth.NodeBones[clothIndex][parentRaw] == -1) { HTLC.ClothNodeWeight weightedBone = node.Bones.Aggregate((i1, i2) => i1.Weight > i2.Weight ? i1 : i2); hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = weightedBone.Bone; } } } else { if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex)) { if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = -1; HTLC.ClothNodeWeight weightedBone = node.Bones.Aggregate((i1, i2) => i1.Weight > i2.Weight ? i1 : i2); hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = weightedBone.Bone; } } } if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex)) { if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { nodeMap[cloth.NodeBones[clothIndex][nodeIndex]] = node; } } nodeIndex++; } clothIndex++; } } using (BinaryWriter writer = new BinaryWriter(output)) { writer.Write((ushort)1); // version major writer.Write((ushort)5); // version minor if (data.Length > 1 && data[1] is string && ((string)data[1]).Length > 0) { writer.Write((string)data[1]); } else { writer.Write((byte)0); } if (data.Length > 2 && data[2] is string && ((string)data[2]).Length > 0) { writer.Write((string)data[2]); } else { writer.Write((byte)0); } if (skeleton == null) { writer.Write((ushort)0); // number of bones } else { writer.Write(skeleton.Data.bonesAbs); } // ReSharper disable once InconsistentNaming Dictionary <byte, List <int> > LODMap = new Dictionary <byte, List <int> >(); uint sz = 0; uint lookForLod = 0; if (model.Submeshes.Any(x => x.lod == flagLOD)) { lookForLod = (byte)flagLOD; } else if (flagLOD != null) { SubmeshDescriptor nextLowest = model.Submeshes.Where(p => p.lod < flagLOD).OrderBy(x => x.lod).LastOrDefault(); if (nextLowest.verticesToDraw == 0 && nextLowest.indexCount == 0) // not real mesh { SubmeshDescriptor nextHighest = model.Submeshes.Where(p => p.lod > flagLOD).OrderBy(x => x.lod).FirstOrDefault(); lookForLod = nextHighest.lod; } else { lookForLod = nextLowest.lod; } } for (int i = 0; i < model.Submeshes.Length; ++i) { SubmeshDescriptor submesh = model.Submeshes[i]; if (data.Length > 4 && data[4] is bool && (bool)data[4]) { if (submesh.flags == SubmeshFlags.COLLISION_MESH) { continue; } } if (lookForLod > 0 && submesh.lod != lookForLod && submesh.lod != 255) { continue; } if (!LODMap.ContainsKey(submesh.lod)) { LODMap.Add(submesh.lod, new List <int>()); } sz++; LODMap[submesh.lod].Add(i); } writer.Write(sz); writer.Write(hardpoints?.HardPoints.Length ?? 0); if (skeleton != null) { for (int i = 0; i < skeleton.Data.bonesAbs; ++i) { writer.Write(IdToString("bone", skeleton.IDs[i])); short parent = hierarchy[i]; if (parent == -1) { parent = (short)i; } writer.Write(parent); Matrix3x4 bone = skeleton.Matrices34[i]; Quaternion rot = new Quaternion(bone[0, 0], bone[0, 1], bone[0, 2], bone[0, 3]); Vector3 scl = new Vector3(bone[1, 0], bone[1, 1], bone[1, 2]); Vector3 pos = new Vector3(bone[2, 0], bone[2, 1], bone[2, 2]); if (nodeMap.ContainsKey(i)) { HTLC.ClothNode thisNode = nodeMap[i]; pos.X = thisNode.X; pos.Y = thisNode.Y; pos.Z = thisNode.Z; } writer.Write(pos.X); writer.Write(pos.Y); writer.Write(pos.Z); writer.Write(scl.X); writer.Write(scl.Y); writer.Write(scl.Z); writer.Write(rot.X); writer.Write(rot.Y); writer.Write(rot.Z); writer.Write(rot.W); } } foreach (KeyValuePair <byte, List <int> > kv in LODMap) { foreach (int i in kv.Value) { SubmeshDescriptor submesh = model.Submeshes[i]; ModelVertex[] vertex = model.Vertices[i]; ModelVertex[] normal = model.Normals[i]; ModelUV[][] uv = model.TextureCoordinates[i]; ModelIndice[] index = model.Indices[i]; ModelBoneData[] bones = model.Bones[i]; writer.Write($"Submesh_{i}.{kv.Key}.{materials.Materials[submesh.material]:X16}"); writer.Write(materials.Materials[submesh.material]); writer.Write((byte)uv.Length); writer.Write(vertex.Length); writer.Write(index.Length); for (int j = 0; j < vertex.Length; ++j) { writer.Write(vertex[j].x); writer.Write(vertex[j].y); writer.Write(vertex[j].z); writer.Write(-normal[j].x); writer.Write(-normal[j].y); writer.Write(-normal[j].z); foreach (ModelUV[] t in uv) { writer.Write(t[j].u); writer.Write(t[j].v); } if (skeleton != null && bones != null && bones[j].boneIndex != null && bones[j].boneWeight != null) { writer.Write((byte)4); writer.Write(skeleton.Lookup[bones[j].boneIndex[0]]); writer.Write(skeleton.Lookup[bones[j].boneIndex[1]]); writer.Write(skeleton.Lookup[bones[j].boneIndex[2]]); writer.Write(skeleton.Lookup[bones[j].boneIndex[3]]); writer.Write(bones[j].boneWeight[0]); writer.Write(bones[j].boneWeight[1]); writer.Write(bones[j].boneWeight[2]); writer.Write(bones[j].boneWeight[3]); } else { // bone -> size + index + weight writer.Write((byte)0); } } List <ModelIndiceModifiable> indexNew = new List <ModelIndiceModifiable>(); foreach (ModelIndice indice in index) { indexNew.Add(new ModelIndiceModifiable { v1 = indice.v1, v2 = indice.v2, v3 = indice.v3 }); } foreach (ModelIndiceModifiable indice in indexNew) { writer.Write((byte)3); writer.Write(indice.v1); writer.Write(indice.v2); writer.Write(indice.v3); } } } if (hardpoints != null) { // attachments foreach (PRHM.HardPoint hp in hardpoints.HardPoints) { writer.Write(IdToString("hardpoint", GUID.Index(hp.HardPointGUID))); Matrix4 mat = hp.Matrix.ToOpenTK(); Vector3 pos = mat.ExtractTranslation(); Quaternion rot = mat.ExtractRotation(); writer.Write(pos.X); writer.Write(pos.Y); writer.Write(pos.Z); writer.Write(rot.X); writer.Write(rot.Y); writer.Write(rot.Z); writer.Write(rot.W); } // extension 1.1 foreach (PRHM.HardPoint hp in hardpoints.HardPoints) { writer.Write(IdToString("bone", GUID.Index(hp.GUIDx012))); } } // ext 1.3: cloth writer.Write(0); // ext 1.4: embedded refpose if (skeleton != null) { for (int i = 0; i < skeleton.Data.bonesAbs; ++i) { writer.Write(IdToString("bone", skeleton.IDs[i])); short parent = hierarchy[i]; writer.Write(parent); Matrix3x4 bone = skeleton.Matrices34Inverted[i]; Quaternion3D quat = new Quaternion3D(bone[0, 3], bone[0, 0], bone[0, 1], bone[0, 2]); Vector3D rot = C3D.ToEulerAngles(quat); // ReSharper disable CompareOfFloatsByEqualityOperator if (rot.X == -3.14159274f && rot.Y == 0 && rot.Z == 0) { rot = new Vector3D(0, 3.14159274f, 3.14159274f); // effectively the same but you know, eulers. } // ReSharper restore CompareOfFloatsByEqualityOperator Vector3 scl = new Vector3(bone[1, 0], bone[1, 1], bone[1, 2]); Vector3 pos = new Vector3(bone[2, 0], bone[2, 1], bone[2, 2]); writer.Write(pos.X); writer.Write(pos.Y); writer.Write(pos.Z); writer.Write(scl.X); writer.Write(scl.Y); writer.Write(scl.Z); writer.Write(rot.X); writer.Write(rot.Y); writer.Write(rot.Z); } } // ext 1.5: guid writer.Write(GUID.Index(modelInfo.GUID)); // ext 1.6: cloth 2.0 if (cloth == null) { writer.Write(0); } else { writer.Write(cloth.Descriptors.Length); for (int i = 0; i < cloth.Descriptors.Length; i++) { var desc = cloth.Descriptors[i]; writer.Write(desc.Name); writer.Write(cloth.Nodes[i].Length); foreach (HTLC.ClothNode clothNode in cloth.Nodes[i]) { writer.Write(clothNode.Bones.Length); foreach (HTLC.ClothNodeWeight clothNodeWeight in clothNode.Bones) { writer.Write(clothNodeWeight.Bone); writer.Write(clothNodeWeight.Weight); } } } } } }
public bool Write(Chunked model, Stream output, bool keepOpen = false) { culture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = culture; IChunk chunk = model.FindNextChunk("lksm").Value; if (chunk == null) { return(false); } lksm skeleton = (lksm)chunk; short[] hierarchy = (short[])skeleton.Hierarchy.Clone(); HashSet <short> weightedParNodes = new HashSet <short>(); Dictionary <int, HTLC.ClothNode> nodeMap = new Dictionary <int, HTLC.ClothNode>(); HTLC cloth = model.FindNextChunk("HTLC").Value as HTLC; if (cloth != null) { uint clothIndex = 0; foreach (HTLC.ClothNode[] nodeCollection in cloth.Nodes) { if (nodeCollection == null) { continue; } int nodeIndex = 0; foreach (HTLC.ClothNode node in nodeCollection) { int parentRaw = node.VerticalParent; if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex) && cloth.NodeBones[clothIndex].ContainsKey(parentRaw)) { // good code: if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = cloth.NodeBones[clothIndex][parentRaw]; if (cloth.NodeBones[clothIndex][parentRaw] == -1) { HTLC.ClothNodeWeight weightedBone = node.Bones.Aggregate((i1, i2) => i1.Weight > i2.Weight ? i1 : i2); hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = weightedBone.Bone; weightedParNodes.Add(cloth.NodeBones[clothIndex][nodeIndex]); } } // else: on subskele // todo: add subskelebones? } else { if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex)) // if on main skele // good code: { if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = -1; HTLC.ClothNodeWeight weightedBone = node.Bones.Aggregate((i1, i2) => i1.Weight > i2.Weight ? i1 : i2); hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = weightedBone.Bone; weightedParNodes.Add(cloth.NodeBones[clothIndex][nodeIndex]); } // else: on subskele // todo: add subskelebones? } } if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex)) { // good code: nodeMap[cloth.NodeBones[clothIndex][nodeIndex]] = node; } nodeIndex++; } clothIndex++; } } using (StreamWriter writer = new StreamWriter(output, Encoding.Default, 512, keepOpen)) { writer.WriteLine("{0}", skeleton.Data.bonesAbs); writer.WriteLine("version 1"); writer.WriteLine("nodes"); for (int i = 0; i < skeleton.Data.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.Data.bonesAbs; ++i) { Matrix3x4 bone = skeleton.Matrices34Inverted[i]; Quaternion3D quat = new Quaternion3D(bone[0, 3], bone[0, 0], bone[0, 1], bone[0, 2]); Vector3D rot = C3D.ToEulerAngles(quat); Vector3 scale = new Vector3(bone[1, 0], bone[1, 1], bone[1, 2]); Vector3 pos = new Vector3(bone[2, 0], bone[2, 1], bone[2, 2]); if (nodeMap.ContainsKey(i)) { HTLC.ClothNode thisNode = nodeMap[i]; if (weightedParNodes.Contains((short)i)) { Vector3 pos2 = GetGlobalPos(skeleton.Matrices34Inverted, hierarchy[i], hierarchy); pos.X = thisNode.X - pos2.X; pos.Y = thisNode.Y - pos2.Y; pos.Z = thisNode.Z - pos2.Z; } else if (nodeMap.ContainsKey(hierarchy[i])) { HTLC.ClothNode parentNode = nodeMap[hierarchy[i]]; pos.X = thisNode.X - parentNode.X; pos.Y = thisNode.Y - parentNode.Y; pos.Z = thisNode.Z - parentNode.Z; } else { pos.X = thisNode.X; pos.Y = thisNode.Y; pos.Z = thisNode.Z; } } if (rot.X == -3.14159274f && rot.Y == 0 && rot.Z == 0) { rot = new Vector3D(0, 3.14159274f, 3.14159274f); // effectively the same but you know, eulers. } 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)); } } return(true); }
// ReSharper disable once InconsistentNaming public bool Write(Chunked chunked, Stream output, List <byte> LODs, Dictionary <ulong, List <ImageLayer> > layers, object[] data) { IChunk chunk = chunked.FindNextChunk("MNRM").Value; if (chunk == null) { return(false); } MNRM model = (MNRM)chunk; chunk = chunked.FindNextChunk("CLDM").Value; CLDM materials = null; if (chunk != null) { materials = (CLDM)chunk; } chunk = chunked.FindNextChunk("lksm").Value; lksm skeleton = null; if (chunk != null) { skeleton = (lksm)chunk; } chunk = chunked.FindNextChunk("PRHM").Value; PRHM hardpoints = null; if (chunk != null) { hardpoints = (PRHM)chunk; } short[] hierarchy = (short[])skeleton?.Hierarchy.Clone(); Dictionary <int, HTLC.ClothNode> nodeMap = new Dictionary <int, HTLC.ClothNode>(); if (chunked.FindNextChunk("HTLC").Value is HTLC cloth) { uint clothIndex = 0; foreach (HTLC.ClothNode[] nodeCollection in cloth.Nodes) { if (nodeCollection == null) { continue; } int nodeIndex = 0; foreach (HTLC.ClothNode node in nodeCollection) { int parentRaw = node.VerticalParent; if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex) && cloth.NodeBones[clothIndex].ContainsKey(parentRaw)) { if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = cloth.NodeBones[clothIndex][parentRaw]; if (cloth.NodeBones[clothIndex][parentRaw] == -1) { HTLC.ClothNodeWeight weightedBone = node.Bones.Aggregate((i1, i2) => i1.Weight > i2.Weight ? i1 : i2); hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = weightedBone.Bone; } } } else { if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex)) // if on main skele { if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = -1; HTLC.ClothNodeWeight weightedBone = node.Bones.Aggregate((i1, i2) => i1.Weight > i2.Weight ? i1 : i2); hierarchy[cloth.NodeBones[clothIndex][nodeIndex]] = weightedBone.Bone; } } } if (cloth.NodeBones[clothIndex].ContainsKey(nodeIndex)) { // good code: if (cloth.NodeBones[clothIndex][nodeIndex] != -1) { nodeMap[cloth.NodeBones[clothIndex][nodeIndex]] = node; } } nodeIndex++; } clothIndex++; } } using (BinaryWriter writer = new BinaryWriter(output)) { writer.Write((ushort)1); // version major writer.Write((ushort)4); // version minor if (data.Length > 1 && data[1] is string && ((string)data[1]).Length > 0) { writer.Write((string)data[1]); } else { writer.Write((byte)0); } if (data.Length > 2 && data[2] is string && ((string)data[2]).Length > 0) { writer.Write((string)data[2]); } else { writer.Write((byte)0); } if (skeleton == null) { writer.Write((ushort)0); // number of bones } else { writer.Write(skeleton.Data.bonesAbs); } // ReSharper disable once InconsistentNaming Dictionary <byte, List <int> > LODMap = new Dictionary <byte, List <int> >(); uint sz = 0; uint lookForLod = 0; bool lodOnly = data.Length > 3 && data[3] is bool && (bool)data[3]; for (int i = 0; i < model.Submeshes.Length; ++i) { SubmeshDescriptor submesh = model.Submeshes[i]; if (data.Length > 4 && data[4] is bool && (bool)data[4]) { if (submesh.flags == SubmeshFlags.COLLISION_MESH) { continue; } } if (LODs != null && !LODs.Contains(submesh.lod)) { continue; } if (lodOnly && lookForLod > 0 && submesh.lod != lookForLod) { continue; } if (!LODMap.ContainsKey(submesh.lod)) { LODMap.Add(submesh.lod, new List <int>()); } lookForLod = submesh.lod; sz++; LODMap[submesh.lod].Add(i); } //long meshCountPos = writer.BaseStream.Position; writer.Write(sz); writer.Write(hardpoints?.HardPoints.Length ?? 0); if (skeleton != null) { for (int i = 0; i < skeleton.Data.bonesAbs; ++i) { writer.Write(IdToString("bone", skeleton.IDs[i])); short parent = hierarchy[i]; if (parent == -1) { parent = (short)i; } writer.Write(parent); Matrix3x4 bone = skeleton.Matrices34[i]; Quaternion rot = new Quaternion(bone[0, 0], bone[0, 1], bone[0, 2], bone[0, 3]); Vector3 scl = new Vector3(bone[1, 0], bone[1, 1], bone[1, 2]); Vector3 pos = new Vector3(bone[2, 0], bone[2, 1], bone[2, 2]); if (nodeMap.ContainsKey(i)) { HTLC.ClothNode thisNode = nodeMap[i]; pos.X = thisNode.X; pos.Y = thisNode.Y; pos.Z = thisNode.Z; } writer.Write(pos.X); writer.Write(pos.Y); writer.Write(pos.Z); writer.Write(scl.X); writer.Write(scl.Y); writer.Write(scl.Z); writer.Write(rot.X); writer.Write(rot.Y); writer.Write(rot.Z); writer.Write(rot.W); } } foreach (KeyValuePair <byte, List <int> > kv in LODMap) { foreach (int i in kv.Value) { SubmeshDescriptor submesh = model.Submeshes[i]; ModelVertex[] vertex = model.Vertices[i]; ModelVertex[] normal = model.Normals[i]; ModelUV[][] uv = model.TextureCoordinates[i]; ModelIndice[] index = model.Indices[i]; ModelBoneData[] bones = model.Bones[i]; writer.Write($"Submesh_{i}.{kv.Key}.{materials.Materials[submesh.material]:X16}"); writer.Write(materials.Materials[submesh.material]); writer.Write((byte)uv.Length); writer.Write(vertex.Length); writer.Write(index.Length); for (int j = 0; j < vertex.Length; ++j) { writer.Write(vertex[j].x); writer.Write(vertex[j].y); writer.Write(vertex[j].z); writer.Write(-normal[j].x); writer.Write(-normal[j].y); writer.Write(-normal[j].z); foreach (ModelUV[] t in uv) { writer.Write(t[j].u); writer.Write(t[j].v); } if (skeleton != null && bones != null && bones[j].boneIndex != null && bones[j].boneWeight != null) { writer.Write((byte)4); writer.Write(skeleton.Lookup[bones[j].boneIndex[0]]); writer.Write(skeleton.Lookup[bones[j].boneIndex[1]]); writer.Write(skeleton.Lookup[bones[j].boneIndex[2]]); writer.Write(skeleton.Lookup[bones[j].boneIndex[3]]); writer.Write(bones[j].boneWeight[0]); writer.Write(bones[j].boneWeight[1]); writer.Write(bones[j].boneWeight[2]); writer.Write(bones[j].boneWeight[3]); } else { // bone -> size + index + weight writer.Write((byte)0); } } List <ModelIndiceModifiable> indexNew = new List <ModelIndiceModifiable>(); foreach (ModelIndice indice in index) { indexNew.Add(new ModelIndiceModifiable { v1 = indice.v1, v2 = indice.v2, v3 = indice.v3 }); } foreach (ModelIndiceModifiable indice in indexNew) { writer.Write((byte)3); writer.Write(indice.v1); writer.Write(indice.v2); writer.Write(indice.v3); } } } if (hardpoints != null) { // attachments foreach (PRHM.HardPoint hp in hardpoints.HardPoints) { writer.Write(IdToString("attachment_", GUID.Index(hp.HardPointGUID))); Matrix4 mat = hp.Matrix.ToOpenTK(); Vector3 pos = mat.ExtractTranslation(); Quaternion rot = mat.ExtractRotation(); writer.Write(pos.X); writer.Write(pos.Y); writer.Write(pos.Z); writer.Write(rot.X); writer.Write(rot.Y); writer.Write(rot.Z); writer.Write(rot.W); } // extension 1.1 foreach (PRHM.HardPoint hp in hardpoints.HardPoints) { writer.Write(IdToString("bone", GUID.Index(hp.GUIDx012))); } } // ext 1.3: cloth writer.Write(0); // ext 1.4: embedded refpose if (skeleton != null) { for (int i = 0; i < skeleton.Data.bonesAbs; ++i) { writer.Write(IdToString("bone", skeleton.IDs[i])); short parent = hierarchy[i]; // if (parent == -1) { // parent = (short)i; // } writer.Write(parent); Matrix3x4 bone = skeleton.Matrices34Inverted[i]; // Quaternion3D quat = new Quaternion3D(bone[0, 0], bone[0, 1], bone[0, 2], bone[0, 3]); // why are they different Quaternion3D quat = new Quaternion3D(bone[0, 3], bone[0, 0], bone[0, 1], bone[0, 2]); Vector3D rot = C3D.ToEulerAngles(quat); if (rot.X == -3.14159274f && rot.Y == 0 && rot.Z == 0) { rot = new Vector3D(0, 3.14159274f, 3.14159274f); // effectively the same but you know, eulers. } Vector3 scl = new Vector3(bone[1, 0], bone[1, 1], bone[1, 2]); Vector3 pos = new Vector3(bone[2, 0], bone[2, 1], bone[2, 2]); writer.Write(pos.X); writer.Write(pos.Y); writer.Write(pos.Z); writer.Write(scl.X); writer.Write(scl.Y); writer.Write(scl.Z); writer.Write(rot.X); writer.Write(rot.Y); writer.Write(rot.Z); } } } return(true); }
public static void ConvertAnimation(string refpose, string input, string output) { NumberFormatInfo format = new NumberFormatInfo(); format.NumberDecimalSeparator = "."; StreamReader refpose_reader = new StreamReader(refpose); int refpose_bonecount = Convert.ToInt32(refpose_reader.ReadLine()); Dictionary <int, int> refpose_parentmap = new Dictionary <int, int>(); Dictionary <int, int> refpose_indexmap = new Dictionary <int, int>(); int[] refpose_bonearray = new int[refpose_bonecount]; int[] refpose_hierarchy = new int[refpose_bonecount]; refpose_reader.ReadLine(); refpose_reader.ReadLine(); for (int index = 0; index < refpose_bonecount; ++index) { string[] array = refpose_reader.ReadLine().Replace("\"", string.Empty).Split(' '); refpose_bonearray[index] = Convert.ToInt32(array[1].Split('_').Last(), 16); refpose_hierarchy[index] = Convert.ToInt32(array[2]); } for (int index = 0; index < refpose_bonecount; ++index) { if (refpose_hierarchy[index] != -1) { refpose_parentmap.Add(refpose_bonearray[index], refpose_bonearray[refpose_hierarchy[index]]); } else { refpose_parentmap.Add(refpose_bonearray[index], -1); } refpose_indexmap.Add(refpose_bonearray[index], index); } refpose_reader.ReadLine(); refpose_reader.ReadLine(); refpose_reader.ReadLine(); Vector3D[] refpose_position = new Vector3D[refpose_bonecount]; Vector3D[] refpose_rotation = new Vector3D[refpose_bonecount]; Vector3D[] refpose_scale = new Vector3D[refpose_bonecount]; for (int index = 0; index < refpose_bonecount; ++index) { refpose_position[index] = new Vector3D(); refpose_rotation[index] = new Vector3D(); refpose_scale[index] = new Vector3D(1, 1, 1); string[] array = refpose_reader.ReadLine().Replace("\"", string.Empty).Split(' '); refpose_position[index].X = Convert.ToSingle(array[2], format); refpose_position[index].Y = Convert.ToSingle(array[3], format); refpose_position[index].Z = Convert.ToSingle(array[4], format); refpose_rotation[index].X = Convert.ToSingle(array[6], format); refpose_rotation[index].Y = Convert.ToSingle(array[7], format); refpose_rotation[index].Z = Convert.ToSingle(array[8], format); } refpose_reader.Close(); FileStream inputStream = new FileStream(input, FileMode.Open); BinaryReader input_reader = new BinaryReader(inputStream); input_reader.ReadInt32(); float duration = input_reader.ReadSingle(); float fps = input_reader.ReadSingle(); ushort bone_count = input_reader.ReadUInt16(); input_reader.ReadUInt16(); int frame_count = (int)(fps * (double)duration) + 1; inputStream.Seek(24L, SeekOrigin.Current); long offset_bone_list = input_reader.ReadInt64(); long offset_info_table = input_reader.ReadInt64(); inputStream.Seek(24L, SeekOrigin.Current); StreamWriter output_writer = new StreamWriter(output); output_writer.WriteLine("version 1"); output_writer.WriteLine("nodes"); int[] bone_list = new int[bone_count]; inputStream.Seek(offset_bone_list, SeekOrigin.Begin); Dictionary <int, int> bone_translation_map = new Dictionary <int, int>(); int bone_id; for (int index = 0; index < bone_count; ++index) { bone_id = input_reader.ReadInt32(); bone_list[index] = bone_id; bone_translation_map.Add(bone_id, index); } for (int index = 0; index < bone_count; ++index) { bone_id = bone_list[index]; int num3 = -1; if (refpose_parentmap.ContainsKey(bone_id)) { int key2 = refpose_parentmap[bone_id]; num3 = !bone_translation_map.ContainsKey(key2) ? -1 : bone_translation_map[key2]; } output_writer.WriteLine(index.ToString() + " \"bone_" + bone_id.ToString("X4") + "\" " + num3); } int last_bone_index = bone_count; Dictionary <int, int> secondary_bone_translation_map = new Dictionary <int, int>(); for (int index = 0; index < refpose_bonecount; ++index) { if (!bone_translation_map.ContainsKey(refpose_bonearray[index])) { secondary_bone_translation_map.Add(refpose_bonearray[index], last_bone_index); } } for (int index = 0; index < refpose_bonecount; ++index) { if (!bone_translation_map.ContainsKey(refpose_bonearray[index])) { int key2 = refpose_parentmap[refpose_bonearray[index]]; int num3 = !bone_translation_map.ContainsKey(key2) ? (!secondary_bone_translation_map.ContainsKey(key2) ? -1 : secondary_bone_translation_map[key2]) : bone_translation_map[key2]; output_writer.WriteLine(last_bone_index.ToString() + " \"bone_" + refpose_bonearray[index].ToString("X4") + "\" " + num3); ++last_bone_index; } } output_writer.WriteLine("end"); float[,] x_array = new float[last_bone_index, frame_count]; float[,] y_array = new float[last_bone_index, frame_count]; float[,] z_array = new float[last_bone_index, frame_count]; float[,] sx_array = new float[last_bone_index, frame_count]; float[,] sy_array = new float[last_bone_index, frame_count]; float[,] sz_array = new float[last_bone_index, frame_count]; float[,] rx_array = new float[last_bone_index, frame_count]; float[,] ry_array = new float[last_bone_index, frame_count]; float[,] rz_array = new float[last_bone_index, frame_count]; float[,] rw_array = new float[last_bone_index, frame_count]; bool[,] has_rotation_frame = new bool[last_bone_index, frame_count]; bool[,] has_position_frame = new bool[last_bone_index, frame_count]; bool[,] has_scale_frame = new bool[last_bone_index, frame_count]; output_writer.WriteLine("skeleton"); output_writer.WriteLine("time 0"); for (int index = 0; index < bone_count; ++index) { if (refpose_indexmap.ContainsKey(bone_list[index])) { bone_id = refpose_indexmap[bone_list[index]]; output_writer.Write(index); output_writer.Write(" " + refpose_position[bone_id].X.ToString("0.000000", format)); output_writer.Write(" " + refpose_position[bone_id].Y.ToString("0.000000", format)); output_writer.Write(" " + refpose_position[bone_id].Z.ToString("0.000000", format)); output_writer.Write(" " + refpose_rotation[bone_id].X.ToString("0.000000", format)); output_writer.Write(" " + refpose_rotation[bone_id].Y.ToString("0.000000", format)); output_writer.Write(" " + refpose_rotation[bone_id].Z.ToString("0.000000", format)); output_writer.WriteLine(); } else { output_writer.WriteLine(index.ToString() + " 0 0 0 0 0 0"); } } int num4 = bone_count; for (int index = 0; index < refpose_bonecount; ++index) { if (!bone_translation_map.ContainsKey(refpose_bonearray[index])) { output_writer.Write(num4++); output_writer.Write(" " + refpose_position[index].X.ToString("0.000000", format)); output_writer.Write(" " + refpose_position[index].Y.ToString("0.000000", format)); output_writer.Write(" " + refpose_position[index].Z.ToString("0.000000", format)); output_writer.Write(" " + refpose_rotation[index].X.ToString("0.000000", format)); output_writer.Write(" " + refpose_rotation[index].Y.ToString("0.000000", format)); output_writer.Write(" " + refpose_rotation[index].Z.ToString("0.000000", format)); output_writer.WriteLine(); } } Quaternion3D q = new Quaternion3D(); Vector3D vector3D = new Vector3D(); inputStream.Seek(offset_info_table, SeekOrigin.Begin); for (int index1 = 0; index1 < bone_count; ++index1) { long position = inputStream.Position; int scale_count = input_reader.ReadInt16(); int position_count = input_reader.ReadInt16(); int rotation_count = input_reader.ReadInt16(); int flags = input_reader.ReadInt16(); long scale_indices_offset = input_reader.ReadInt32() * 4 + position; long position_indices_offset = input_reader.ReadInt32() * 4 + position; long rotation_indices_offset = input_reader.ReadInt32() * 4 + position; long scale_data_offset = input_reader.ReadInt32() * 4 + position; long position_data_offset = input_reader.ReadInt32() * 4 + position; long rotation_data_offset = input_reader.ReadInt32() * 4 + position; int[] scale_indices = new int[scale_count]; inputStream.Seek(scale_indices_offset, SeekOrigin.Begin); for (int index2 = 0; index2 < scale_count; ++index2) { scale_indices[index2] = frame_count > byte.MaxValue ? input_reader.ReadInt16() : input_reader.ReadByte(); } inputStream.Seek(scale_data_offset, SeekOrigin.Begin); for (int index2 = 0; index2 < scale_count; ++index2) { int index3 = scale_indices[index2]; has_scale_frame[index1, index3] = true; float x = input_reader.ReadUInt16() / 1024.0f; sx_array[index1, index3] = x; float y = input_reader.ReadUInt16() / 1024.0f; sy_array[index1, index3] = y; float z = input_reader.ReadUInt16() / 1024.0f; sz_array[index1, index3] = z; } int[] position_indices = new int[position_count]; inputStream.Seek(position_indices_offset, SeekOrigin.Begin); for (int index2 = 0; index2 < position_count; ++index2) { position_indices[index2] = frame_count > byte.MaxValue ? input_reader.ReadInt16() : input_reader.ReadByte(); } inputStream.Seek(position_data_offset, SeekOrigin.Begin); for (int index2 = 0; index2 < position_count; ++index2) { int index3 = position_indices[index2]; has_position_frame[index1, index3] = true; float x = input_reader.ReadSingle(); x_array[index1, index3] = x; float y = input_reader.ReadSingle(); y_array[index1, index3] = y; float z = input_reader.ReadSingle(); z_array[index1, index3] = z; } int[] rotation_indices = new int[rotation_count]; inputStream.Seek(rotation_indices_offset, SeekOrigin.Begin); for (int index2 = 0; index2 < rotation_count; ++index2) { rotation_indices[index2] = frame_count > byte.MaxValue ? input_reader.ReadInt16() : input_reader.ReadByte(); } inputStream.Seek(rotation_data_offset, SeekOrigin.Begin); for (int index2 = 0; index2 < rotation_count; ++index2) { ushort rot_a = input_reader.ReadUInt16(); ushort rot_b = input_reader.ReadUInt16(); ushort rot_c = input_reader.ReadUInt16(); Vec4d rot = Animation.UnpackRotation(rot_a, rot_b, rot_c); int index3 = rotation_indices[index2]; has_rotation_frame[index1, index3] = true; rx_array[index1, index3] = (float)rot.x; ry_array[index1, index3] = (float)rot.y; rz_array[index1, index3] = (float)rot.z; rw_array[index1, index3] = (float)rot.w; } inputStream.Seek(position + 32L, SeekOrigin.Begin); } for (int index1 = 0; index1 < frame_count; ++index1) { output_writer.WriteLine($"time {index1 + 1}"); for (int index2 = 0; index2 < last_bone_index; ++index2) { if (has_position_frame[index2, index1] || has_rotation_frame[index2, index1] || has_scale_frame[index2, index1]) { if (!has_position_frame[index2, index1]) { int index3 = index1; int index4 = index1; while (!has_position_frame[index2, index3]) { --index3; } while (index4 < frame_count && !has_position_frame[index2, index4]) { ++index4; } if (index4 == frame_count) { x_array[index2, index1] = x_array[index2, index3]; y_array[index2, index1] = y_array[index2, index3]; z_array[index2, index1] = z_array[index2, index3]; } else { float num3 = (index1 - index3) / (float)(index4 - index3); x_array[index2, index1] = (x_array[index2, index4] - x_array[index2, index3]) * num3 + x_array[index2, index3]; y_array[index2, index1] = (y_array[index2, index4] - y_array[index2, index3]) * num3 + y_array[index2, index3]; z_array[index2, index1] = (z_array[index2, index4] - z_array[index2, index3]) * num3 + z_array[index2, index3]; } } if (!has_scale_frame[index2, index1]) { int index3 = index1; int index4 = index1; while (!has_scale_frame[index2, index3]) { --index3; } while (index4 < frame_count && !has_scale_frame[index2, index4]) { ++index4; } if (index4 == frame_count) { sx_array[index2, index1] = sx_array[index2, index3]; sy_array[index2, index1] = sy_array[index2, index3]; sz_array[index2, index1] = sz_array[index2, index3]; } else { float num3 = (index1 - index3) / (float)(index4 - index3); sx_array[index2, index1] = (sx_array[index2, index4] - sx_array[index2, index3]) * num3 + sx_array[index2, index3]; sy_array[index2, index1] = (sy_array[index2, index4] - sy_array[index2, index3]) * num3 + sy_array[index2, index3]; sz_array[index2, index1] = (sz_array[index2, index4] - sz_array[index2, index3]) * num3 + sz_array[index2, index3]; } } if (!has_rotation_frame[index2, index1]) { int index3 = index1; int index4 = index1; while (!has_rotation_frame[index2, index3]) { --index3; } while (index4 < frame_count && !has_rotation_frame[index2, index4]) { ++index4; } if (index4 == frame_count) { rx_array[index2, index1] = rx_array[index2, index3]; ry_array[index2, index1] = ry_array[index2, index3]; rz_array[index2, index1] = rz_array[index2, index3]; rw_array[index2, index1] = rw_array[index2, index3]; } else { double num3 = rx_array[index2, index4] * (double)rx_array[index2, index3] + ry_array[index2, index4] * (double)ry_array[index2, index3] + rz_array[index2, index4] * (double)rz_array[index2, index3] + rw_array[index2, index4] * (double)rw_array[index2, index3]; float num9 = (index1 - index3) / (float)(index4 - index3); if (num3 < 0.0) { rx_array[index2, index1] = (-rx_array[index2, index4] - rx_array[index2, index3]) * num9 + rx_array[index2, index3]; ry_array[index2, index1] = (-ry_array[index2, index4] - ry_array[index2, index3]) * num9 + ry_array[index2, index3]; rz_array[index2, index1] = (-rz_array[index2, index4] - rz_array[index2, index3]) * num9 + rz_array[index2, index3]; rw_array[index2, index1] = (-rw_array[index2, index4] - rw_array[index2, index3]) * num9 + rw_array[index2, index3]; } else { rx_array[index2, index1] = (rx_array[index2, index4] - rx_array[index2, index3]) * num9 + rx_array[index2, index3]; ry_array[index2, index1] = (ry_array[index2, index4] - ry_array[index2, index3]) * num9 + ry_array[index2, index3]; rz_array[index2, index1] = (rz_array[index2, index4] - rz_array[index2, index3]) * num9 + rz_array[index2, index3]; rw_array[index2, index1] = (rw_array[index2, index4] - rw_array[index2, index3]) * num9 + rw_array[index2, index3]; } } } output_writer.Write(index2); output_writer.Write(" " + x_array[index2, index1].ToString("0.000000", format)); output_writer.Write(" " + y_array[index2, index1].ToString("0.000000", format)); output_writer.Write(" " + z_array[index2, index1].ToString("0.000000", format)); q.i = rx_array[index2, index1]; q.j = ry_array[index2, index1]; q.k = rz_array[index2, index1]; q.real = rw_array[index2, index1]; Vector3D eulerAngles = C3D.ToEulerAngles(q); output_writer.Write(" " + eulerAngles.X.ToString("0.000000", format)); output_writer.Write(" " + eulerAngles.Y.ToString("0.000000", format)); output_writer.Write(" " + eulerAngles.Z.ToString("0.000000", format)); output_writer.WriteLine(); } } } output_writer.WriteLine("end"); output_writer.Close(); }