internal void CreateWalls(Mesh.Factory factory, BuildingPlan plan) { for (var i = 0; i < plan.Polygon.Corners.Length; i++) { var c0 = plan.Polygon.Corners[i]; var c1 = plan.Polygon.Corners[(i + 1) % plan.Polygon.Corners.Length]; var c0To1 = c1 - c0; var v0 = new Mesh.Vertex { Pos = new Vector3(c0.X, plan.LowGroundLevel, c0.Y), TexCoords = new Vector2() }; var v0To1 = new Mesh.Vertex { Pos = new Vector3(c0To1.X, 0, c0To1.Y), TexCoords = new Vector2(c0To1.Length, 0) }; var v0To2 = new Mesh.Vertex { Pos = new Vector3(0, plan.RoofLevel - plan.LowGroundLevel, 0), TexCoords = new Vector2(0, plan.RoofLevel - plan.LowGroundLevel) }; factory.AddSurface(v0, v0To1, v0To2); } }
public static string Export(Model model, NormalExportMode normalMode, bool exportingIndividually = false) { StringBuilder obj = new StringBuilder(); for (int i = 0; i < model.Meshes.Count; i++) { for (int j = 0; j < model.Meshes[i].Vertices.Count; j++) { Mesh.Vertex v = model.Meshes[i].Vertices[j]; obj.AppendLine($"v {v.Position.X} {v.Position.Y} {v.Position.Z}"); } for (int j = 0; j < model.Meshes[i].Vertices.Count; j++) { Mesh.Vertex v = model.Meshes[i].Vertices[j]; Vector3 n = v.Normal; if (normalMode == NormalExportMode.CALCULATED) { n = model.Meshes[i].CalculatedNormals[j]; } if (normalMode != NormalExportMode.NONE) { obj.AppendLine($"vn {n.X} {n.Y} {n.Z}"); } } for (int j = 0; j < model.Meshes[i].Vertices.Count; j++) { Mesh.Vertex v = model.Meshes[i].Vertices[j]; v.TextureCoordinate = new Vector2(v.TextureCoordinate.X / 32768, v.TextureCoordinate.Y / -32768); obj.AppendLine($"vt {v.TextureCoordinate.X.ToString("F4")} {v.TextureCoordinate.Y.ToString("F4")}"); } obj.AppendLine($"g mesh{i}"); for (int j = 0; j < model.Meshes[i].Faces.Count; j++) { Mesh.Face f = model.Meshes[i].Faces[j]; if (exportingIndividually) { f.X -= model.Meshes[i].NumOfVerticesBeforeMe; f.Y -= model.Meshes[i].NumOfVerticesBeforeMe; f.Z -= model.Meshes[i].NumOfVerticesBeforeMe; } string x = $"{f.X + 1}/{f.X + 1}"; string y = $"{f.Y + 1}/{f.Y + 1}"; string z = $"{f.Z + 1}/{f.Z + 1}"; if (normalMode != NormalExportMode.NONE) { x += $"/{f.X + 1}"; y += $"/{f.Y + 1}"; z += $"/{f.Z + 1}"; } obj.AppendLine($"f {y} {x} {z}"); } } return(obj.ToString()); }
/// <summary> /// Does not load correctly, yet /// </summary> /// <param name="obj"></param> /// <returns></returns> public static Model LoadFromString(string obj) { List <string> lines = new List <string>(obj.Split('\n')); // Lists to hold model data List <Vector3> verts = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); List <Vector2> texs = new List <Vector2>(); List <Tuple <TempVertex, TempVertex, TempVertex> > faces = new List <Tuple <TempVertex, TempVertex, TempVertex> >(); List <Tuple <int, int, int> > faceIndices = new List <Tuple <int, int, int> >(); // Base values verts.Add(new Vector3()); texs.Add(new Vector2()); normals.Add(new Vector3()); // Read file line by line foreach (string line in lines) { if (line.StartsWith("v ")) // Vertex definition { // Cut off beginning of line string temp = line.Substring(2); Vector3 vec = new Vector3(); if (temp.Trim().Count((char c) => c == ' ') == 2) // Check if there's enough elements for a vertex { string[] vertparts = temp.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // Attempt to parse each part of the vertice bool success = float.TryParse(vertparts[0], out vec.X); success |= float.TryParse(vertparts[1], out vec.Y); success |= float.TryParse(vertparts[2], out vec.Z); // If any of the parses failed, report the error if (!success) { Console.WriteLine("Error parsing vertex: {0}", line); } } else { Console.WriteLine("Error parsing vertex: {0}", line); } verts.Add(vec); } else if (line.StartsWith("vt ")) // Texture coordinate { // Cut off beginning of line string temp = line.Substring(2); Vector2 vec = new Vector2(); if (temp.Trim().Count((char c) => c == ' ') > 0) // Check if there's enough elements for a vertex { string[] texcoordparts = temp.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // Attempt to parse each part of the vertice bool success = float.TryParse(texcoordparts[0], out vec.X); success |= float.TryParse(texcoordparts[1], out vec.Y); // If any of the parses failed, report the error if (!success) { Console.WriteLine("Error parsing texture coordinate: {0}", line); } } else { Console.WriteLine("Error parsing texture coordinate: {0}", line); } texs.Add(vec); } else if (line.StartsWith("vn ")) // Normal vector { // Cut off beginning of line string temp = line.Substring(2); Vector3 vec = new Vector3(); if (temp.Trim().Count((char c) => c == ' ') == 2) // Check if there's enough elements for a normal { string[] vertparts = temp.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // Attempt to parse each part of the vertice bool success = float.TryParse(vertparts[0], out vec.X); success |= float.TryParse(vertparts[1], out vec.Y); success |= float.TryParse(vertparts[2], out vec.Z); // If any of the parses failed, report the error if (!success) { Console.WriteLine("Error parsing normal: {0}", line); } } else { Console.WriteLine("Error parsing normal: {0}", line); } normals.Add(vec); } else if (line.StartsWith("f ")) // Face definition { // Cut off beginning of line string temp = line.Substring(2); Tuple <TempVertex, TempVertex, TempVertex> face = new Tuple <TempVertex, TempVertex, TempVertex>(new TempVertex(), new TempVertex(), new TempVertex()); if (temp.Trim().Count((char c) => c == ' ') == 2) // Check if there's enough elements for a face { string[] faceparts = temp.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int v1, v2, v3; int t1, t2, t3; int n1, n2, n3; // Attempt to parse each part of the face bool success = int.TryParse(faceparts[0].Split('/')[0], out v1); success |= int.TryParse(faceparts[1].Split('/')[0], out v2); success |= int.TryParse(faceparts[2].Split('/')[0], out v3); if (faceparts[0].Count((char c) => c == '/') >= 2) { success |= int.TryParse(faceparts[0].Split('/')[1], out t1); success |= int.TryParse(faceparts[1].Split('/')[1], out t2); success |= int.TryParse(faceparts[2].Split('/')[1], out t3); success |= int.TryParse(faceparts[0].Split('/')[2], out n1); success |= int.TryParse(faceparts[1].Split('/')[2], out n2); success |= int.TryParse(faceparts[2].Split('/')[2], out n3); } else { if (texs.Count > v1 && texs.Count > v2 && texs.Count > v3) { t1 = v1; t2 = v2; t3 = v3; } else { t1 = 0; t2 = 0; t3 = 0; } if (normals.Count > v1 && normals.Count > v2 && normals.Count > v3) { n1 = v1; n2 = v2; n3 = v3; } else { n1 = 0; n2 = 0; n3 = 0; } } // If any of the parses failed, report the error if (!success) { Console.WriteLine("Error parsing face: {0}", line); } else { faceIndices.Add(new Tuple <int, int, int>(v1, v2, v3)); TempVertex tv1 = new TempVertex(v1, n1, t1); TempVertex tv2 = new TempVertex(v2, n2, t2); TempVertex tv3 = new TempVertex(v3, n3, t3); face = new Tuple <TempVertex, TempVertex, TempVertex>(tv1, tv2, tv3); faces.Add(face); } } else { Console.WriteLine("Error parsing face: {0}", line); } } } Mesh mesh = new Mesh(); for (int i = 0; i < faces.Count; i += 3) { var face = faces[i]; Mesh.Vertex vert1 = new Mesh.Vertex { Position = verts[face.Item1.Vertex], Normal = normals[face.Item1.Normal], TextureCoordinate = texs[face.Item1.Texcoord] }; mesh.Vertices.Add(vert1); Mesh.Vertex vert2 = new Mesh.Vertex { Position = verts[face.Item2.Vertex], Normal = normals[face.Item2.Normal], TextureCoordinate = texs[face.Item2.Texcoord] }; mesh.Vertices.Add(vert2); Mesh.Vertex vert3 = new Mesh.Vertex { Position = verts[face.Item3.Vertex], Normal = normals[face.Item3.Normal], TextureCoordinate = texs[face.Item3.Texcoord] }; mesh.Vertices.Add(vert3); mesh.Faces.Add(new Mesh.Face { X = faceIndices[i].Item1, Y = faceIndices[i].Item2, Z = faceIndices[i].Item3 }); } for (int i = 0; i < faces.Count; i++) { mesh.Indices.Add(faceIndices[i].Item1); mesh.Indices.Add(faceIndices[i].Item2); mesh.Indices.Add(faceIndices[i].Item3); } mesh.VertexCount = mesh.Vertices.Count; mesh.FaceCount = mesh.Faces.Count; mesh.IndexCount = mesh.Indices.Count; return(Model.CreateFromMesh(mesh)); }
public VertexClass(Mesh.Vertex vertex) { this.vertex = vertex; }
public static Model ExtractModel(string fileName, Action <string> completionAction = null) { // ToDo: add skeleton/skeleton-related data support for Origins Model model = new Model(); StringBuilder str = new StringBuilder(); // used to print information in the Text Viewer byte[] allData = File.ReadAllBytes(fileName); using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (stream.Length == 0) { return(null); } using (BinaryReader reader = new BinaryReader(stream)) { // header DatafileHeader header = new DatafileHeader { ResourceIdentifier = reader.ReadUInt32(), FileSize = reader.ReadInt32(), FileNameSize = reader.ReadInt32() }; header.FileName = reader.ReadChars(header.FileNameSize); if (header.ResourceIdentifier != (uint)ResourceIdentifier.MESH) { Message.Fail("This is not proper Mesh data."); return(model); } // skip to the Mesh block, ignoring the Mesh block identifier reader.BaseStream.Seek(10, SeekOrigin.Current); try { // Mesh block MeshBlock meshBlock = new MeshBlock(); reader.BaseStream.Seek(3, SeekOrigin.Current); meshBlock.ModelType = reader.ReadInt32(); meshBlock.ACount = reader.ReadInt32(); if (meshBlock.ACount > 0) { reader.BaseStream.Seek(4, SeekOrigin.Current); meshBlock.BoneCount = reader.ReadInt32(); } // Bones block if (meshBlock.BoneCount > 0) { str.AppendLine($"Bone Count: {meshBlock.BoneCount}"); // Bones block continued Mesh.Bone[] bones = new Mesh.Bone[meshBlock.BoneCount]; for (int i = 0; i < meshBlock.BoneCount; i++) { bones[i] = new Mesh.Bone { ID = reader.ReadInt64(), Type = reader.ReadInt32(), Name = reader.ReadInt32(), TransformMatrix = new System.Numerics.Matrix4x4 { M11 = reader.ReadSingle(), M12 = reader.ReadSingle(), M13 = reader.ReadSingle(), M14 = reader.ReadSingle(), M21 = reader.ReadSingle(), M22 = reader.ReadSingle(), M23 = reader.ReadSingle(), M24 = reader.ReadSingle(), M31 = reader.ReadSingle(), M32 = reader.ReadSingle(), M33 = reader.ReadSingle(), M34 = reader.ReadSingle(), M41 = reader.ReadSingle(), M42 = reader.ReadSingle(), M43 = reader.ReadSingle(), M44 = reader.ReadSingle() } }; // invert matrix System.Numerics.Matrix4x4 matrix = System.Numerics.Matrix4x4.Identity; if (System.Numerics.Matrix4x4.Invert(bones[i].TransformMatrix, out matrix)) { bones[i].TransformMatrix = matrix; } reader.BaseStream.Seek(1, SeekOrigin.Current); } } // locate the Compiled Mesh Tuple <int[], long> cmOffset = Helpers.LocateBytes(reader, BitConverter.GetBytes((uint)ResourceIdentifier.COMPILED_MESH)); reader.BaseStream.Seek(cmOffset.Item1[0], SeekOrigin.Begin); //Console.WriteLine("Compiled Mesh OFFSET: " + cmOffset.Item1[0]); // Compiled Mesh block if (reader.ReadUInt32() != (uint)ResourceIdentifier.COMPILED_MESH) { Message.Fail("Failed to read model."); return(new Model()); } reader.BaseStream.Seek(22, SeekOrigin.Current); CompiledMeshBlock compiledMesh = new CompiledMeshBlock { VertexTableSize = reader.ReadInt32(), Unknown1 = reader.ReadInt32(), Unknown2 = reader.ReadInt32() }; reader.BaseStream.Seek(29, SeekOrigin.Current); // Submesh block //Console.WriteLine("Submesh OFFSET: " + reader.BaseStream.Position); SubmeshBlock submeshBlock = new SubmeshBlock(); submeshBlock.MeshCount = reader.ReadInt32(); if (submeshBlock.MeshCount < 0) { Message.Fail("Failed to read the model."); return(model); } submeshBlock.Entries = new SubmeshEntry[submeshBlock.MeshCount]; for (int i = 0; i < submeshBlock.MeshCount; i++) { reader.BaseStream.Seek(16, SeekOrigin.Current); submeshBlock.Entries[i] = new SubmeshEntry() { FaceOffset = reader.ReadInt32(), VertexOffset = reader.ReadInt32() }; } // Unknown0 block - does not exist in every model //Console.WriteLine("Unknown0 OFFSET: " + reader.BaseStream.Position); if (compiledMesh.Unknown2 != 0) { int unknown0DataSize = reader.ReadInt32(); reader.BaseStream.Seek(unknown0DataSize, SeekOrigin.Current); } else { //reader.BaseStream.Seek(-8, SeekOrigin.Current); } // Vertex block //Console.WriteLine("Vertex OFFSET: " + reader.BaseStream.Position); int vertexDataSize = reader.ReadInt32(); int actualVertexTableSize = compiledMesh.VertexTableSize != 8 && compiledMesh.VertexTableSize != 16 ? compiledMesh.VertexTableSize : compiledMesh.Unknown1; // this variable now holds the true vertex table size long vertexOffset = reader.BaseStream.Position; reader.BaseStream.Seek(vertexDataSize, SeekOrigin.Current); // Vertex Weight block - does not exist in every model //Console.WriteLine("Vertex Weight OFFSET: " + reader.BaseStream.Position); if (compiledMesh.Unknown2 == 0) { reader.BaseStream.Seek(8, SeekOrigin.Current); } else { int vertexWeightDataSize = reader.ReadInt32(); reader.BaseStream.Seek(vertexWeightDataSize, SeekOrigin.Current); } // Face block //Console.WriteLine("Face OFFSET: " + reader.BaseStream.Position); int faceDataSize = reader.ReadInt32(); long faceOffset = reader.BaseStream.Position; reader.BaseStream.Seek(faceDataSize, SeekOrigin.Current); // Unknown1 block int unknown1DataSize = reader.ReadInt32(); reader.BaseStream.Seek(unknown1DataSize, SeekOrigin.Current); // Unknown2 block int unknown2DataSize = reader.ReadInt32(); reader.BaseStream.Seek(unknown2DataSize, SeekOrigin.Current); // Unknown3 block int unknown3DataSize = reader.ReadInt32(); reader.BaseStream.Seek(unknown3DataSize, SeekOrigin.Current); // go to the Mesh Data reader.BaseStream.Seek(18, SeekOrigin.Current); //Tuple<int[], long> mdOffset = Helpers.LocateBytes(reader, BitConverter.GetBytes((uint)ResourceIdentifier.MESH_DATA)); //reader.BaseStream.Position = mdOffset.Item2; //reader.BaseStream.Seek(mdOffset.Item1[0] + 10, SeekOrigin.Begin); // skip the identifier and 6 bytes //Console.WriteLine("Mesh Data OFFSET: " + (reader.BaseStream.Position)); // Mesh Data block uint meshCount = reader.ReadUInt32(); int totalVertices = 0; for (int i = 0; i < meshCount; i++) { Mesh mesh = new Mesh(); mesh.ID = reader.ReadInt16(); reader.BaseStream.Seek(18, SeekOrigin.Current); mesh.VertexCount = reader.ReadInt32(); reader.BaseStream.Seek(4, SeekOrigin.Current); mesh.FaceCount = reader.ReadInt32(); mesh.IndexCount = mesh.FaceCount * 3; mesh.MinFaceIndex = short.MaxValue; // an unlikely value, this will be set later mesh.TextureIndex = reader.ReadInt32(); // store the position for later use long pos = reader.BaseStream.Position; // populate the meshes with vertices reader.BaseStream.Seek(vertexOffset + (submeshBlock.Entries[i].VertexOffset * actualVertexTableSize), SeekOrigin.Begin); for (int j = 0; j < mesh.VertexCount; j++) { short x = reader.ReadInt16(); short y = reader.ReadInt16(); short z = reader.ReadInt16(); Mesh.Vertex v = new Mesh.Vertex { Position = new Vector3 // the coordinates are read like this, so that the model stands upright { Z = x, X = y, Y = z } }; int scaleFactor; switch (actualVertexTableSize) { case 12: reader.BaseStream.Seek(2, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); break; case 16: scaleFactor = reader.ReadInt16(); v.Position /= scaleFactor; reader.BaseStream.Seek(4, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); break; case 20: scaleFactor = reader.ReadInt16(); v.Position /= scaleFactor; v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(2, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); break; case 24: reader.BaseStream.Seek(2, SeekOrigin.Current); v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(6, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); break; case 28: scaleFactor = reader.ReadInt16(); v.Position /= scaleFactor; v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(6, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(4, SeekOrigin.Current); break; case 32: reader.BaseStream.Seek(2, SeekOrigin.Current); v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(6, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); v.BoneIndices = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); break; case 36: reader.BaseStream.Seek(2, SeekOrigin.Current); v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(6, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(4, SeekOrigin.Current); v.BoneIndices = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); break; case 40: reader.BaseStream.Seek(2, SeekOrigin.Current); v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(6, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); v.BoneIndices = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneIndices2 = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights2 = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); break; case 44: reader.BaseStream.Seek(14, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); v.BoneIndices = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneIndices2 = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights2 = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); reader.BaseStream.Seek(4, SeekOrigin.Current); break; case 48: reader.BaseStream.Seek(2, SeekOrigin.Current); v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); reader.BaseStream.Seek(6, SeekOrigin.Current); v.TextureCoordinate = new Vector2(reader.ReadInt16(), reader.ReadInt16()); v.BoneIndices = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneIndices2 = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); v.BoneWeights2 = new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); reader.BaseStream.Seek(4, SeekOrigin.Current); break; default: reader.BaseStream.Seek(actualVertexTableSize - 6, SeekOrigin.Current); break; } //v.TextureCoordinate = new Vector2(v.TextureCoordinate.X / 65536 * 32, 1 - (v.TextureCoordinate.Y / 65536 * 32)); mesh.Vertices.Add(v); //mesh.Normals.Add(v.Normal); } // add to the total vertices if (i > 0) { totalVertices += model.Meshes[i - 1].VertexCount; mesh.NumOfVerticesBeforeMe = totalVertices; } // populate the meshes with faces reader.BaseStream.Seek(faceOffset + (submeshBlock.Entries[i].FaceOffset * 2), SeekOrigin.Begin); for (int j = 0; j < mesh.FaceCount; j++) { int x = reader.ReadUInt16() + mesh.NumOfVerticesBeforeMe; int y = reader.ReadUInt16() + mesh.NumOfVerticesBeforeMe; int z = reader.ReadUInt16() + mesh.NumOfVerticesBeforeMe; if (x != y && y != z && x != z) { if (x < mesh.MinFaceIndex) { mesh.MinFaceIndex = x; } if (y < mesh.MinFaceIndex) { mesh.MinFaceIndex = y; } if (z < mesh.MinFaceIndex) { mesh.MinFaceIndex = z; } if (x > mesh.MaxFaceIndex) { mesh.MaxFaceIndex = x; } if (y > mesh.MaxFaceIndex) { mesh.MaxFaceIndex = y; } if (z > mesh.MaxFaceIndex) { mesh.MaxFaceIndex = z; } Mesh.Face f = new Mesh.Face { X = x, Y = y, Z = z }; mesh.Faces.Add(f); mesh.Indices.Add(x); mesh.Indices.Add(y); mesh.Indices.Add(z); } } model.Meshes.Add(mesh); // go back to the Mesh Data reader.BaseStream.Seek(pos, SeekOrigin.Begin); } // print information str.AppendLine($"Vertex table size: {actualVertexTableSize}"); str.AppendLine($"Meshes: {meshCount}"); for (int i = 0; i < model.Meshes.Count; i++) { model.Meshes[i].CalculateNormals(); str.AppendLine($"\tMesh {i}:"); str.AppendLine($"\t\tVertices: {model.Meshes[i].Vertices.Count}"); str.AppendLine($"\t\tFaces: {model.Meshes[i].FaceCount}"); str.AppendLine($"\t\tIndices: {model.Meshes[i].IndexCount}"); str.AppendLine($"\t\tMin Face Index: {model.Meshes[i].MinFaceIndex}"); str.AppendLine($"\t\tMax Face Index: {model.Meshes[i].MaxFaceIndex}"); } } catch (Exception e) { Message.Fail("Failed to read model. " + e.Message); } finally { completionAction(str.ToString()); } #region Partially working code /* BONES * * // skip two Unknown chunks * reader.BaseStream.Seek(2 * 0x10, SeekOrigin.Current); * reader.BaseStream.Seek(1 + (2 * 0x11), SeekOrigin.Current); * * // vertex table size * int vertexTableSize = reader.ReadInt32(); * if (vertexTableSize == 8) // read again if the result is 8 * { * vertexTableSize = reader.ReadInt32(); * reader.BaseStream.Seek(33, SeekOrigin.Current); * } * else * { * reader.BaseStream.Seek(37, SeekOrigin.Current); * } * * // Unknown 1 chunk * uint unknown1DataCount = reader.ReadUInt32(); * for (int i = 0; i < unknown1DataCount; i++) * { * reader.BaseStream.Seek(0x18, SeekOrigin.Current); // skip it for now * } * * // Internal UV chunk * uint uvDataSize = reader.ReadUInt32(); * long uvOffset = reader.BaseStream.Position; // we revisit this later * reader.BaseStream.Seek(uvDataSize, SeekOrigin.Current); * * // Vertices chunk * uint verticesDataSize = reader.ReadUInt32(); * long verticesOffset = reader.BaseStream.Position; // we revisit this later * reader.BaseStream.Seek(verticesDataSize, SeekOrigin.Current); * * // Unknown 2 chunk * uint unknown2DataCount = reader.ReadUInt32(); * reader.BaseStream.Seek(unknown2DataCount, SeekOrigin.Current); // skip it for now * * // Faces chunk * uint facesDataSize = reader.ReadUInt32(); * long facesOffset = reader.BaseStream.Position; // we revisit this later * reader.BaseStream.Seek(facesDataSize, SeekOrigin.Current); * * // Unknown 3 chunk * uint unknown3DataSize = reader.ReadUInt32(); * reader.BaseStream.Seek(unknown3DataSize, SeekOrigin.Current); // skip it for now * * // Unknown 4 chunk * uint unknown4DataSize = reader.ReadUInt32(); * reader.BaseStream.Seek(unknown4DataSize, SeekOrigin.Current); // skip it for now * * // Unknown 5 chunk * uint unknown5DataSize = reader.ReadUInt32(); * reader.BaseStream.Seek(unknown5DataSize, SeekOrigin.Current); // skip it for now * * reader.BaseStream.Seek(1 * 0x12, SeekOrigin.Current); * * // Mesh Data chunk * uint meshCount = reader.ReadUInt32(); * sb.AppendLine($"Meshes: {meshCount}"); * meshes = new Mesh[meshCount]; * for (int i = 0; i < meshCount; i++) * { * meshes[i].ID = reader.ReadInt16(); * reader.BaseStream.Seek(18, SeekOrigin.Current); * meshes[i] = new Mesh(); * meshes[i].Vertices = new Vertex[reader.ReadInt32()]; * reader.BaseStream.Seek(4, SeekOrigin.Current); * meshes[i].Faces = new Face[reader.ReadInt32()]; * meshes[i].TextureIndex = reader.ReadInt32(); * meshes[i].OBJData = ""; * * sb.AppendLine($"\tMesh {i}: {meshes[i].Faces.Length} faces\t{meshes[i].Vertices.Length} vertices"); * } * * // revisit the Vertices * reader.BaseStream.Seek(verticesOffset, SeekOrigin.Begin); * for (int i = 0; i < meshes.Length; i++) * { * for (int j = 0; j < meshes[i].Vertices.Length; j++) * { * Vertex v = new Vertex * { * Position = new Vector3 * { * X = reader.ReadInt16(), * Y = reader.ReadInt16(), * Z = reader.ReadInt16() * } * }; * * meshes[i].Vertices[j] = v; * meshes[i].OBJData += $"v {v}\n"; * obj.AppendLine($"v {v}"); * * reader.BaseStream.Seek(vertexTableSize - 6, SeekOrigin.Current); // skip the other data for now * } * } * * // revisit the Faces * reader.BaseStream.Seek(facesOffset, SeekOrigin.Begin); * for (int i = 0; i < meshes.Length; i++) * { * for (int j = 0; j < meshes[i].Faces.Length; j++) * { * Face f = new Face * { * X = reader.ReadUInt16() + 1, * Y = reader.ReadUInt16() + 1, * Z = reader.ReadUInt16() + 1 * }; * * meshes[i].Faces[j] = f; * meshes[i].OBJData += $"f {f}\n"; * obj.AppendLine($"f {f}"); * } * * sb.AppendLine(meshes[i].OBJData); * sb.AppendLine(); * } */ #endregion #region Not working code /*// locate the Mesh block * reader.BaseStream.Seek(0, SeekOrigin.Begin); * int meshBlockLoc = Helpers.RecurringIndexes(allData, MESH_DATA, 4).LastOrDefault().Item1; * reader.BaseStream.Seek(meshBlockLoc + 7, SeekOrigin.Begin); // skip the identifier + 3 bytes * * // Mesh block * MeshBlock meshBlock = new MeshBlock * { * ModelType = reader.ReadInt32(), * ACount = reader.ReadInt32(), * BoneCount = reader.ReadInt32() * }; * * // locate the Compiled Mesh block * reader.BaseStream.Seek(0, SeekOrigin.Begin); * IList<Tuple<int, int>> tt = Helpers.RecurringIndexes(allData, COMPILED_MESH_DATA, 4); * int compiledMeshBlockLoc = tt.LastOrDefault().Item1; * reader.BaseStream.Seek(compiledMeshBlockLoc + 26, SeekOrigin.Begin); // identifier + 22 bytes * * // Compiled Mesh block * CompiledMeshBlock compiledMeshBlock = new CompiledMeshBlock * { * VertexTableSize = reader.ReadInt32() * }; * reader.BaseStream.Seek(37, SeekOrigin.Current); * compiledMeshBlock.MeshCount = reader.ReadInt32(); * compiledMeshBlock.ShadowCount = reader.ReadInt32(); * reader.BaseStream.Seek(compiledMeshBlock.MeshCount * 16, SeekOrigin.Current); * reader.BaseStream.Seek(4, SeekOrigin.Current); // unknown1 * * int sizeOfVertexData = reader.ReadInt32(); * * // show error * if (compiledMeshBlock.MeshCount > 1) * { * Messages.Fail("Currently, Blacksmith cannot handle models with more than 1 submesh.", "Failure"); * return null; * } * * // populate data in the Submeshes * submeshes = new Submesh[compiledMeshBlock.MeshCount]; * for (int i = 0; i < submeshes.Length; i++) * { * // Vertices * List<Vertex> vertices = new List<Vertex>(); * for (int j = 0; j < sizeOfVertexData / compiledMeshBlock.VertexTableSize; j++) * { * Vertex v = new Vertex(); * v.Position = new Vector3 * { * X = reader.ReadInt16(), * Y = reader.ReadInt16(), * Z = reader.ReadInt16() * }; * * //short scaleFactor = reader.ReadInt16(); * //v.Position /= scaleFactor; * * /*if (compiledMeshBlock.VertexTableSize == 16) * { * v.Normal = new Vector3(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); * }* * * vertices.Add(v); * sb.AppendLine("v " + v); * * reader.BaseStream.Seek(compiledMeshBlock.VertexTableSize - 6, SeekOrigin.Current); * } * * // onto the Faces * reader.BaseStream.Seek(8, SeekOrigin.Current); * * // Faces * List<Face> faces = new List<Face>(); * int sizeOfFaceData = reader.ReadInt32(); * for (int j = 0; j < sizeOfFaceData / 6; j++) // a Face contains 6 bytes * { * Face f = new Face * { * Y = reader.ReadUInt16() + 1, * X = reader.ReadUInt16() + 1, * Z = reader.ReadUInt16() + 1 * }; * * faces.Add(f); * sb.AppendLine("f " + f); * } * * // set properties of this Submesh * submeshes[i].Vertices = vertices.ToArray(); * submeshes[i].Faces = faces.ToArray(); * submeshes[i].OBJData = sb.ToString(); * }*/ #endregion #region Not working code /*// block A - 1 * uint aCount = reader.ReadUInt32(); * Block blockA = new Block(); * blockA.Bytes = new byte[aCount][]; * for (int i = 0; i < aCount; i++) * { * blockA.Bytes[i] = reader.ReadBytes(0x51); * } * * // block B - 1-0 * Block blockB = new Block(); * blockB.Bytes = new byte[2][]; * for (int i = 0; i < 2; i++) * { * blockB.Bytes[i] = reader.ReadBytes(0x10); * } * * // block C - 1-1 * reader.BaseStream.Seek(1, SeekOrigin.Current); * Block blockC = new Block(); * blockC.Bytes = new byte[2][]; * for (int i = 0; i < 2; i++) * { * blockC.Bytes[i] = reader.ReadBytes(0x11); * } * * // block D - 1-2 * reader.BaseStream.Seek(0x29, SeekOrigin.Current); * uint dCount = reader.ReadUInt32(); * Block blockD = new Block(); * blockD.Bytes = new byte[dCount][]; * for (int i = 0; i < dCount; i++) * { * blockD.Bytes[i] = reader.ReadBytes(0x18); * } * * // internal/external UVs (block E) - 20 * uint eCount = reader.ReadUInt32(); * reader.BaseStream.Seek(eCount, SeekOrigin.Current); * * // vertices (block F) - 30 * uint fCount = reader.ReadUInt32() / 12; * long verticesOffset = reader.BaseStream.Position; * Block blockF = new Block(); * blockF.Bytes = new byte[fCount][]; * for (int i = 0; i < fCount; i++) * { * blockF.Bytes[i] = reader.ReadBytes(0x0c); * } * * // block G - 40 * uint gSize = reader.ReadUInt32(); * Block blockG = new Block(); * blockG.Bytes = new byte[gSize][]; * reader.BaseStream.Seek(gSize, SeekOrigin.Current); * * // faces (block) H - 50 * uint hCount = reader.ReadUInt32() / 6; * long facesOffset = reader.BaseStream.Position; * Block blockH = new Block(); * blockH.Bytes = new byte[hCount][]; * for (int i = 0; i < hCount; i++) * { * blockH.Bytes[i] = reader.ReadBytes(0x06); * } * * // block I - 60 * uint iSize = reader.ReadUInt32(); * Block blockI = new Block(); * blockI.Bytes = new byte[iSize][]; * reader.BaseStream.Seek(iSize, SeekOrigin.Current); * * // block J - 70 * uint jSize = reader.ReadUInt32(); * Block blockJ = new Block(); * blockJ.Bytes = new byte[jSize][]; * reader.BaseStream.Seek(jSize, SeekOrigin.Current); * * // block K - 80 * uint kSize = reader.ReadUInt32(); * Block blockK = new Block(); * blockK.Bytes = new byte[kSize][]; * reader.BaseStream.Seek(kSize, SeekOrigin.Current); * * // block L * Block blockL = new Block(); * blockL.Bytes = new byte[1][]; * for (int i = 0; i < 1; i++) * { * blockL.Bytes[i] = reader.ReadBytes(0x12); * } * * // block M - 90 * uint mSize = reader.ReadUInt32(); // number of submeshes * * // create submeshes * submeshes = new Submesh[mSize]; * for (int i = 0; i < mSize; i++) * { * submeshes[i] = new Submesh(); * * reader.BaseStream.Seek(0x14, SeekOrigin.Current); * submeshes[i].VertexCount = reader.ReadUInt32(); * reader.BaseStream.Seek(0x04, SeekOrigin.Current); * submeshes[i].FaceCount = reader.ReadUInt32(); * reader.BaseStream.Seek(0x04, SeekOrigin.Current); * } * * /*reader.BaseStream.Seek(0x10, SeekOrigin.Current); * * // block N - 100 * uint nSize = reader.ReadUInt32(); * Block blockN = new Block(); * blockN.Bytes = new byte[nSize][]; * for (int i = 0; i < nSize; i++) * { * blockN.Bytes[i] = reader.ReadBytes(0x1e); * } * * // block O - 110 * reader.BaseStream.Seek(0x16, SeekOrigin.Current); * uint oSize = reader.ReadUInt32(); * Block blockO = new Block(); * blockO.Bytes = new byte[nSize][]; * for (int i = 0; i < oSize; i++) * { * blockO.Bytes[i] = reader.ReadBytes(0x0a); * }* * * // get faces * reader.BaseStream.Seek(facesOffset, SeekOrigin.Begin); * for (int i = 0; i < submeshes.Length; i++) * { * submeshes[i].Faces = new Face[submeshes[i].FaceCount]; * for (int j = 0; j < submeshes[i].FaceCount; j++) * { * submeshes[i].Faces[j] = new Face * { * X = reader.ReadUInt16(), * Y = reader.ReadUInt16(), * Z = reader.ReadUInt16() * }; * * /*ObjParser.Types.Face f = new ObjParser.Types.Face * { * X = submeshes[i].Vertices[j].X, * Y = submeshes[i].Vertices[j].Y, * Z = submeshes[i].Vertices[j].Z * }; * faces.Add(f);* * } * } * * // get vertices * reader.BaseStream.Seek(verticesOffset, SeekOrigin.Begin); * List<ObjParser.Types.Vertex> verts = new List<ObjParser.Types.Vertex>(); * for (int i = 0; i < submeshes.Length; i++) * { * submeshes[i].Vertices = new Vertex[submeshes[i].VertexCount]; * for (int j = 0; j < submeshes[i].VertexCount; j++) * { * submeshes[i].Vertices[j] = new Vertex * { * X = reader.ReadInt16(), * Y = reader.ReadInt16(), * Z = reader.ReadInt16(), * U = reader.ReadInt16(), * V = reader.ReadInt16(), * Dummy = reader.ReadInt16() * }; * * ObjParser.Types.Vertex v = new ObjParser.Types.Vertex * { * X = submeshes[i].Vertices[j].X, * Y = submeshes[i].Vertices[j].Y, * Z = submeshes[i].Vertices[j].Z * }; * verts.Add(v); * } * } * * Obj obj = new Obj(); * obj.VertexList = verts; * obj.WriteObjFile(@"C:\Users\pinea\bag-new.obj", new string[] { "" }); * * completionAction();*/ #endregion } } return(model); }