public void ReadHalf2s(ICollection <Vector2> half2s, int offset, int startVertexIndex, int vertexCount) { Vector2 half2; for (int vertexIndex = startVertexIndex; vertexIndex < startVertexIndex + vertexCount; ++vertexIndex) { half2.X = Half.FromBytes(Data, (vertexIndex * BytesPerVertex) + offset + 0).ToSingle(); half2.Y = Half.FromBytes(Data, (vertexIndex * BytesPerVertex) + offset + 2).ToSingle(); half2s.Add(half2); } }
public void ExportModelToDirectoryWithExportOptions(Model model, String directory, ExportOptions exportOptions) { //TODO: Figure out what to do with non-version 4 models. if (model != null && model.Version != 4) { return; } NumberFormatInfo format = new NumberFormatInfo(); format.NumberDecimalSeparator = "."; if (exportOptions.Package) { try { DirectoryInfo directoryInfo = Directory.CreateDirectory(directory + @"\" + Path.GetFileNameWithoutExtension(model.Name)); directory = directoryInfo.FullName; } catch (Exception) { } } if (exportOptions.Textures) { ImageImporter imageImporter = new ImageImporter(); ImageExporter imageExporter = new ImageExporter(); foreach (String textureString in model.TextureStrings) { MemoryStream textureMemoryStream = AssetManager.Instance.CreateAssetMemoryStreamByName(textureString); if (textureMemoryStream == null) { continue; } Image textureImage = imageImporter.LoadImageFromStream(textureMemoryStream); if (textureImage == null) { continue; } imageExporter.SaveImage(textureImage, exportOptions.TextureFormat.ImageType, directory + @"\" + Path.GetFileNameWithoutExtension(textureString) + @"." + exportOptions.TextureFormat.Extension); } } String path = directory + @"\" + Path.GetFileNameWithoutExtension(model.Name) + ".obj"; FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write); StreamWriter streamWriter = new StreamWriter(fileStream); for (Int32 i = 0; i < model.Meshes.Length; ++i) { Mesh mesh = model.Meshes[i]; MaterialDefinition materialDefinition = MaterialDefinitionManager.Instance.MaterialDefinitions[model.Materials[(Int32)mesh.MaterialIndex].MaterialDefinitionHash]; VertexLayout vertexLayout = MaterialDefinitionManager.Instance.VertexLayouts[materialDefinition.DrawStyles[0].VertexLayoutNameHash]; //position VertexLayout.Entry.DataTypes positionDataType; Int32 positionOffset; Int32 positionStreamIndex; vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Position, 0, out positionDataType, out positionStreamIndex, out positionOffset); Mesh.VertexStream positionStream = mesh.VertexStreams[positionStreamIndex]; for (Int32 j = 0; j < mesh.VertexCount; ++j) { Vector3 position = readVector3(exportOptions, positionOffset, positionStream, j); position.X *= exportOptions.Scale.X; position.Y *= exportOptions.Scale.Y; position.Z *= exportOptions.Scale.Z; streamWriter.WriteLine("v " + position.X.ToString(format) + " " + position.Y.ToString(format) + " " + position.Z.ToString(format)); } //texture coordinates if (exportOptions.TextureCoordinates) { VertexLayout.Entry.DataTypes texCoord0DataType; Int32 texCoord0Offset = 0; Int32 texCoord0StreamIndex = 0; Boolean texCoord0Present = vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Texcoord, 0, out texCoord0DataType, out texCoord0StreamIndex, out texCoord0Offset); if (texCoord0Present) { Mesh.VertexStream texCoord0Stream = mesh.VertexStreams[texCoord0StreamIndex]; for (Int32 j = 0; j < mesh.VertexCount; ++j) { Vector2 texCoord; switch (texCoord0DataType) { case VertexLayout.Entry.DataTypes.Float2: texCoord.X = BitConverter.ToSingle(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + 0); texCoord.Y = 1.0f - BitConverter.ToSingle(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + 4); break; case VertexLayout.Entry.DataTypes.float16_2: texCoord.X = Half.FromBytes(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + texCoord0Offset + 0).ToSingle(); texCoord.Y = 1.0f - Half.FromBytes(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + texCoord0Offset + 2).ToSingle(); break; default: texCoord.X = 0; texCoord.Y = 0; break; } streamWriter.WriteLine("vt " + texCoord.X.ToString(format) + " " + texCoord.Y.ToString(format)); } } } } //faces UInt32 vertexCount = 0; for (Int32 i = 0; i < model.Meshes.Length; ++i) { Mesh mesh = model.Meshes[i]; streamWriter.WriteLine("g Mesh" + i); for (Int32 j = 0; j < mesh.IndexCount; j += 3) { UInt32 index0, index1, index2; switch (mesh.IndexSize) { case 2: index0 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 0) + 1; index1 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 2) + 1; index2 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 4) + 1; break; case 4: index0 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 0) + 1; index1 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 4) + 1; index2 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 8) + 1; break; default: index0 = 0; index1 = 0; index2 = 0; break; } if (exportOptions.Normals && exportOptions.TextureCoordinates) { streamWriter.WriteLine("f " + index2 + "/" + index2 + "/" + index2 + " " + index1 + "/" + index1 + "/" + index1 + " " + index0 + "/" + index0 + "/" + index0); } else if (exportOptions.Normals) { streamWriter.WriteLine("f " + index2 + "//" + index2 + " " + index1 + "//" + index1 + " " + index0 + "//" + index0); } else if (exportOptions.TextureCoordinates) { streamWriter.WriteLine("f " + index2 + "/" + index2 + " " + index1 + "/" + index1 + " " + index0 + "/" + index0); } else { streamWriter.WriteLine("f " + index2 + " " + index1 + " " + index0); } } vertexCount += (UInt32)mesh.VertexCount; } streamWriter.Close(); }
private static void TransformRgba16161616F(byte[] buffer, BinaryReader br, uint width, uint height) { // I have no idea how this works. It's just converted straight from VTFLib. // I think the half format is slightly different to what it should be, which causes the result to be different to VTFLib. // Fortunately Sledge does not need to care about cubemaps, which is what this format seems to be used for... const int a = 6; const int r = 0; const int g = 2; const int b = 4; var bytes = br.ReadBytes((int)(width * height * 8)); var log = 0d; for (int i = 0, j = 0; i < bytes.Length; i += 8, j += 4) { var hb = Half.FromBytes(bytes, i + b).ToSingle(); var hg = Half.FromBytes(bytes, i + g).ToSingle(); var hr = Half.FromBytes(bytes, i + r).ToSingle(); var lum = hr * 0.299f + hg * 0.587f + hb * 0.114f; log += Math.Log(0.0000000001d + lum); } log = Math.Exp(log / (width * height)); for (int i = 0, j = 0; i < bytes.Length; i += 8, j += 4) { var hb = Half.FromBytes(bytes, i + b).ToSingle(); var hg = Half.FromBytes(bytes, i + g).ToSingle(); var hr = Half.FromBytes(bytes, i + r).ToSingle(); var ha = Half.FromBytes(bytes, i + a).ToSingle(); var y = hr * 0.299f + hg * 0.587f + hb * 0.114f; var u = (hb - y) * 0.565f; var v = (hr - y) * 0.713f; var mul = 4 * y / log; mul = mul / (1 + mul); mul /= y; hr = (float)Math.Pow((y + 1.403f * v) * mul, 2.25f); hg = (float)Math.Pow((y - 0.344f * u - 0.714f * v) * mul, 2.25f); hb = (float)Math.Pow((y + 1.770f * u) * mul, 2.25f); if (hr < 0) { hr = 0; } if (hr > 1) { hr = 1; } if (hg < 0) { hg = 0; } if (hg > 1) { hg = 1; } if (hb < 0) { hb = 0; } if (hb > 1) { hb = 1; } buffer[j + 0] = (byte)(hb * 255); // b buffer[j + 1] = (byte)(hg * 255); // g buffer[j + 2] = (byte)(hr * 255); // r buffer[j + 3] = (byte)(ha * 255); // a } }
MeshDrawer ReadMeshChunk(int streamOffset) { //mesh name reader.BaseStream.Position = streamOffset + 0x7; int chunkSize = reader.ReadByte(); int meshNamePos = (int)reader.BaseStream.Position + reader.ReadInt32(); reader.BaseStream.Position = meshNamePos; string meshName = reader.readString(); //material reference reader.BaseStream.Position = streamOffset + 0x14; int matNamePos = (int)reader.BaseStream.Position + reader.ReadInt32(); reader.BaseStream.Position = matNamePos + 0x8; string matName = reader.readString(); //bones reference reader.BaseStream.Position = streamOffset + 0x58; int weightBoneNameTableStart = (int)reader.BaseStream.Position + reader.ReadInt32(); reader.BaseStream.Position = streamOffset + 0x5c; int WeightBoneTableStart = (int)reader.BaseStream.Position + reader.ReadInt32(); reader.BaseStream.Position = streamOffset + 0x78; int facesCount = reader.ReadInt32(); reader.BaseStream.Position = streamOffset + 0x84; int verticesCount = reader.ReadInt32(); byte size = 4; int SizeTest = verticesCount * chunkSize; if (SizeTest < 0x100) { size = 1; } else if (SizeTest < 0x10000) { size = 2; } reader.BaseStream.Position += 8; int vertSize = reader.readVal(size); int VertOffset = (int)reader.BaseStream.Position; List <VertexRigged3D> vertices = new List <VertexRigged3D>(); for (int i = 0; i < verticesCount; i++) { VertexRigged3D vertice = new VertexRigged3D(); vertice.Position = reader.readVector3(); //vertex color unsupported reader.BaseStream.Position += 4; //if (chunkSize == 0x24 || chunkSize == 0x28) if (chunkSize == 0x24) { reader.BaseStream.Position += 4; } else if (chunkSize == 0x30) { reader.BaseStream.Position += 4; reader.BaseStream.Position += 0xc; } vertice.UV = new Vector2(); vertice.UV.X = Half.FromBytes(reader.ReadBytes(2), 0); vertice.UV.Y = Half.FromBytes(reader.ReadBytes(2), 0); /* * if (chunkSize == 0x28) * file.BaseStream.Position += 4; */ vertice.BoneIndices = new IVector4(new int[] { reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte() }); if (chunkSize == 0x28) { vertice.BoneWeigths = reader.readVector4(); } else { vertice.BoneWeigths = new Vector4(reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt16()); vertice.BoneWeigths /= 65535f; } vertices.Add(vertice); } int unknownSize = 4; if (size == 1) { unknownSize = 2; } reader.BaseStream.Position = VertOffset + vertSize + size + unknownSize; int unknownCount = reader.ReadInt32(); reader.BaseStream.Position += 0x10 * unknownCount; reader.ReadInt32(); //Read Faces size = 4; if (facesCount < 0x100) { size = 1; } else if (facesCount < 0x10000) { size = 2; } int faceSize = reader.readVal(size); byte indexValueSize = 4; if (verticesCount < 0x100) { indexValueSize = 1; } else if (verticesCount < 0x10000) { indexValueSize = 2; } int faceOffset = (int)reader.BaseStream.Position; int[] indexes = new int[facesCount]; for (int i = 0; i < facesCount; i++) { indexes[i] = reader.readVal(indexValueSize); } //Read Weigth Bones dictionary reader.BaseStream.Position = weightBoneNameTableStart; int weightBoneCount = reader.ReadInt32(); int[] boneIdDict = new int[weightBoneCount]; for (int i = 0; i < weightBoneCount; i++) { reader.BaseStream.Position = weightBoneNameTableStart + i * 4 + 4; reader.BaseStream.Position += reader.ReadInt32(); string weightBoneName = reader.readString(); boneIdDict[i] = Array.FindIndex(bones, (b) => b.Name == weightBoneName); } var vv = vertices.ToArray(); //set bone id to global for (int i = 0; i < vv.Length; i++) { IVector4 boneIndexes = vv[i].BoneIndices; boneIndexes.bone1 = boneIdDict[boneIndexes.bone1]; boneIndexes.bone2 = boneIdDict[boneIndexes.bone2]; boneIndexes.bone3 = boneIdDict[boneIndexes.bone3]; boneIndexes.bone4 = boneIdDict[boneIndexes.bone4]; vv[i].BoneIndices = boneIndexes; } CalculateVertNormals.CalculateNormals(vv, indexes); var mesh = new Mesh(vv, indexes); Material mat; if (materialTable.TryGetValue(matName, out mat)) { return(new MeshDrawerRigged(mesh, new Material[] { mat }, boneControll)); } else { return(new MeshDrawerRigged(mesh, boneControll)); } }
/// <summary> /// Exports a model to the given directory. /// </summary> private void ExportModel(Model model, StringBuilder stringBuilder, ref byte[] textureBuffer) { //TODO: Figure out what to do with non-version 4 models. if (model == null || model.Version != 4) { return; } string directory = ResourceDir + "/Models"; string path = directory + @"\" + Path.GetFileNameWithoutExtension(model.Name) + ".obj"; if (File.Exists(path)) { return; } // Validate meshes attached to the model foreach (Mesh mesh in model.Meshes) { if (!ForgelightGame.MaterialDefinitionManager.MaterialDefinitions.ContainsKey(model.Materials[(int)mesh.MaterialIndex].MaterialDefinitionHash)) { return; } } // The texture directory may not exist yet. Directory.CreateDirectory(directory + @"\Textures"); // We reset the string builder so we don't have any previous buffer left over. stringBuilder.Length = 0; // Materials and Textures foreach (Mesh mesh in model.Meshes) { if (mesh.BaseDiffuse != null) { ExportTexture(mesh.BaseDiffuse, directory, ref textureBuffer); ExportMaterial(mesh, directory); stringBuilder.AppendLine("mtllib " + Path.GetFileNameWithoutExtension(mesh.BaseDiffuse) + ".mtl"); } if (mesh.SpecMap != null) { ExportTexture(mesh.SpecMap, directory, ref textureBuffer); } if (mesh.BumpMap != null) { ExportTexture(mesh.BumpMap, directory, ref textureBuffer); } } // Meshes foreach (Mesh mesh in model.Meshes) { MaterialDefinition materialDefinition = ForgelightGame.MaterialDefinitionManager.MaterialDefinitions[model.Materials[(int)mesh.MaterialIndex].MaterialDefinitionHash]; VertexLayout vertexLayout = ForgelightGame.MaterialDefinitionManager.VertexLayouts[materialDefinition.DrawStyles[0].VertexLayoutNameHash]; //position VertexLayout.Entry.DataTypes positionDataType; int positionOffset; int positionStreamIndex; vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Position, 0, out positionDataType, out positionStreamIndex, out positionOffset); Mesh.VertexStream positionStream = mesh.VertexStreams[positionStreamIndex]; for (int j = 0; j < mesh.VertexCount; ++j) { Vector3 position = ReadVector3(positionOffset, positionStream, j); stringBuilder.AppendLine("v " + position.x.ToString(format) + " " + position.y.ToString(format) + " " + position.z.ToString(format)); } //texture coordinates VertexLayout.Entry.DataTypes texCoord0DataType; int texCoord0Offset; int texCoord0StreamIndex; bool texCoord0Present = vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex( VertexLayout.Entry.DataUsages.Texcoord, 0, out texCoord0DataType, out texCoord0StreamIndex, out texCoord0Offset); if (texCoord0Present) { Mesh.VertexStream texCoord0Stream = mesh.VertexStreams[texCoord0StreamIndex]; for (int j = 0; j < mesh.VertexCount; ++j) { Vector2 texCoord; switch (texCoord0DataType) { case VertexLayout.Entry.DataTypes.Float2: { texCoord.x = BitConverter.ToSingle(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + 0); texCoord.y = 1.0f - BitConverter.ToSingle(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + 4); break; } case VertexLayout.Entry.DataTypes.float16_2: { texCoord.x = Half.FromBytes(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + texCoord0Offset + 0); texCoord.y = 1.0f - Half.FromBytes(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + texCoord0Offset + 2); break; } default: texCoord.x = 0; texCoord.y = 0; break; } stringBuilder.AppendLine("vt " + texCoord.x.ToString(format) + " " + texCoord.y.ToString(format)); } } } // Faces uint vertexCount = 0; for (int i = 0; i < model.Meshes.Count; ++i) { Mesh mesh = model.Meshes[i]; stringBuilder.AppendLine("g Mesh" + i); // Specify Material if (mesh.BaseDiffuse != null) { stringBuilder.AppendLine("usemtl " + Path.GetFileNameWithoutExtension(mesh.BaseDiffuse)); } for (int j = 0; j < mesh.IndexCount; j += 3) { uint index0, index1, index2; switch (mesh.IndexSize) { case 2: index0 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 0) + 1; index1 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 2) + 1; index2 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 4) + 1; break; case 4: index0 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 0) + 1; index1 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 4) + 1; index2 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 8) + 1; break; default: index0 = 0; index1 = 0; index2 = 0; break; } stringBuilder.AppendLine("f " + index2 + "/" + index2 + "/" + index2 + " " + index1 + "/" + index1 + "/" + index1 + " " + index0 + "/" + index0 + "/" + index0); } vertexCount += mesh.VertexCount; File.WriteAllText(path, stringBuilder.ToString()); } }
public static void ExportModel(ForgelightGame forgelightGame, Model model, string directory) { //TODO: Figure out what to do with non-version 4 models. if (model == null || model.Version != 4) { return; } //Validate this mesh. for (int i = 0; i < model.Meshes.Count; ++i) { Mesh mesh = model.Meshes[i]; if (!forgelightGame.MaterialDefinitionManager.MaterialDefinitions.ContainsKey(model.Materials[(int)mesh.MaterialIndex].MaterialDefinitionHash)) { return; } } NumberFormatInfo format = new NumberFormatInfo(); format.NumberDecimalSeparator = "."; Directory.CreateDirectory(directory + @"\Textures"); List <string> usedTextures = new List <string>(); foreach (Mesh mesh in model.Meshes) { if (mesh.BaseDiffuse != null) { usedTextures.Add(mesh.BaseDiffuse); } if (mesh.SpecMap != null) { usedTextures.Add(mesh.SpecMap); } if (mesh.BumpMap != null) { usedTextures.Add(mesh.BumpMap); } } foreach (string textureString in usedTextures) { using (MemoryStream textureMemoryStream = forgelightGame.CreateAssetMemoryStreamByName(textureString)) { if (textureMemoryStream == null) { continue; } if (!File.Exists(directory + @"\Textures\" + textureString)) { try { using (FileStream file = File.Create(directory + @"\Textures\" + textureString)) { byte[] bytes = new byte[textureMemoryStream.Length]; textureMemoryStream.Read(bytes, 0, (int)textureMemoryStream.Length); file.Write(bytes, 0, bytes.Length); } } catch (IOException) {} } } } string path = directory + @"\" + Path.GetFileNameWithoutExtension(model.Name) + ".obj"; if (!File.Exists(path)) { using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write)) { using (StreamWriter streamWriter = new StreamWriter(fileStream)) { //Custom Material foreach (Mesh mesh in model.Meshes) { if (mesh.BaseDiffuse != null) { if (!File.Exists(directory + @"\" + Path.GetFileNameWithoutExtension(mesh.BaseDiffuse) + @".mtl")) { List <string> mtl = new List <string>(); string[] baseMtl = { "newmtl " + Path.GetFileNameWithoutExtension(mesh.BaseDiffuse), "Ka 1.000000 1.000000 1.000000", "Kd 1.000000 1.000000 1.000000", "Ks 0.000000 0.000000 0.000000", "d 1.0", "illum 2", "map_Ka " + mesh.BaseDiffuse, "map_Kd " + mesh.BaseDiffuse, "map_d " + mesh.BaseDiffuse }; mtl.AddRange(baseMtl); if (mesh.SpecMap != null) { mtl.Add("map_Ks " + mesh.BaseDiffuse); mtl.Add("map_Ns " + mesh.SpecMap); } if (mesh.BumpMap != null) { mtl.Add("bump " + mesh.BumpMap); } try { File.WriteAllLines(directory + @"\" + Path.GetFileNameWithoutExtension(mesh.BaseDiffuse) + @".mtl", mtl.ToArray()); } //Another thread is already writing this material. No need to take any further action. catch (IOException) {} } streamWriter.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(mesh.BaseDiffuse) + ".mtl"); } } foreach (Mesh mesh in model.Meshes) { MaterialDefinition materialDefinition = forgelightGame.MaterialDefinitionManager.MaterialDefinitions[model.Materials[(int)mesh.MaterialIndex].MaterialDefinitionHash]; VertexLayout vertexLayout = forgelightGame.MaterialDefinitionManager.VertexLayouts[materialDefinition.DrawStyles[0].VertexLayoutNameHash]; //position VertexLayout.Entry.DataTypes positionDataType; int positionOffset; int positionStreamIndex; vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Position, 0, out positionDataType, out positionStreamIndex, out positionOffset); Mesh.VertexStream positionStream = mesh.VertexStreams[positionStreamIndex]; for (int j = 0; j < mesh.VertexCount; ++j) { Vector3 position = ReadVector3(positionOffset, positionStream, j); streamWriter.WriteLine("v " + position.x.ToString(format) + " " + position.y.ToString(format) + " " + position.z.ToString(format)); } //texture coordinates VertexLayout.Entry.DataTypes texCoord0DataType; int texCoord0Offset; int texCoord0StreamIndex; bool texCoord0Present = vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Texcoord, 0, out texCoord0DataType, out texCoord0StreamIndex, out texCoord0Offset); if (texCoord0Present) { Mesh.VertexStream texCoord0Stream = mesh.VertexStreams[texCoord0StreamIndex]; for (int j = 0; j < mesh.VertexCount; ++j) { Vector2 texCoord; switch (texCoord0DataType) { case VertexLayout.Entry.DataTypes.Float2: { texCoord.x = BitConverter.ToSingle(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + 0); texCoord.y = 1.0f - BitConverter.ToSingle(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + 4); break; } case VertexLayout.Entry.DataTypes.float16_2: { texCoord.x = Half.FromBytes(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + texCoord0Offset + 0); texCoord.y = 1.0f - Half.FromBytes(texCoord0Stream.Data, (j * texCoord0Stream.BytesPerVertex) + texCoord0Offset + 2); break; } default: texCoord.x = 0; texCoord.y = 0; break; } streamWriter.WriteLine("vt " + texCoord.x.ToString(format) + " " + texCoord.y.ToString(format)); } } } //faces uint vertexCount = 0; for (int i = 0; i < model.Meshes.Count; ++i) { Mesh mesh = model.Meshes[i]; streamWriter.WriteLine("g Mesh" + i); //Custom Material if (mesh.BaseDiffuse != null) { streamWriter.WriteLine("usemtl " + Path.GetFileNameWithoutExtension(mesh.BaseDiffuse)); } for (int j = 0; j < mesh.IndexCount; j += 3) { uint index0, index1, index2; switch (mesh.IndexSize) { case 2: index0 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 0) + 1; index1 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 2) + 1; index2 = vertexCount + BitConverter.ToUInt16(mesh.IndexData, (j * 2) + 4) + 1; break; case 4: index0 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 0) + 1; index1 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 4) + 1; index2 = vertexCount + BitConverter.ToUInt32(mesh.IndexData, (j * 4) + 8) + 1; break; default: index0 = 0; index1 = 0; index2 = 0; break; } streamWriter.WriteLine("f " + index2 + "/" + index2 + "/" + index2 + " " + index1 + "/" + index1 + "/" + index1 + " " + index0 + "/" + index0 + "/" + index0); } vertexCount += mesh.VertexCount; } } } } }