public static void LoadM2(string filename, CacheStorage cache) { filename = filename.ToLower().Replace(".mdx", ".m2"); if (cache.doodadBatches.ContainsKey(filename)) { return; } WoWFormatLib.Structs.M2.M2Model model = new WoWFormatLib.Structs.M2.M2Model(); if (cache.models.ContainsKey(filename)) { model = cache.models[filename]; } else { //Load model from file if (WoWFormatLib.Utils.CASC.FileExists(filename)) { var modelreader = new M2Reader(); modelreader.LoadM2(filename); cache.models.Add(filename, modelreader.model); model = modelreader.model; } else { throw new Exception("Model " + filename + " does not exist!"); } } var ddBatch = new TerrainWindow.DoodadBatch(); // Textures ddBatch.mats = new TerrainWindow.Material[model.textures.Count()]; for (int i = 0; i < model.textures.Count(); i++) { string texturefilename = model.textures[i].filename; ddBatch.mats[i].flags = model.textures[i].flags; switch (model.textures[i].type) { case 0: // Console.WriteLine(" Texture given in file!"); texturefilename = model.textures[i].filename; break; case 1: string[] csfilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)model.textures[i].type, i); if (csfilenames.Count() > 0) { texturefilename = csfilenames[0]; } else { //Console.WriteLine(" No type 1 texture found, falling back to placeholder texture"); } break; case 2: if (WoWFormatLib.Utils.CASC.FileExists(System.IO.Path.ChangeExtension(filename, ".blp"))) { // Console.WriteLine(" BLP exists!"); texturefilename = System.IO.Path.ChangeExtension(filename, ".blp"); } else { //Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: string[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { if (WoWFormatLib.Utils.CASC.FileExists(filename.Replace(model.name + ".M2", cdifilenames[ti] + ".blp"))) { texturefilename = filename.Replace(model.name + ".M2", cdifilenames[ti] + ".blp"); } } break; default: //Console.WriteLine(" Falling back to placeholder texture"); texturefilename = "Dungeons\\Textures\\testing\\COLOR_13.blp"; break; } ddBatch.mats[i].textureID = BLPLoader.LoadTexture(texturefilename, cache); ddBatch.mats[i].filename = texturefilename; } // Submeshes ddBatch.submeshes = new TerrainWindow.Submesh[model.skins[0].submeshes.Count()]; for (int i = 0; i < model.skins[0].submeshes.Count(); i++) { if (filename.StartsWith("character")) { if (model.skins[0].submeshes[i].submeshID != 0) { if (!model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } ddBatch.submeshes[i].firstFace = model.skins[0].submeshes[i].startTriangle; ddBatch.submeshes[i].numFaces = model.skins[0].submeshes[i].nTriangles; for (int tu = 0; tu < model.skins[0].textureunit.Count(); tu++) { if (model.skins[0].textureunit[tu].submeshIndex == i) { ddBatch.submeshes[i].blendType = model.renderflags[model.skins[0].textureunit[tu].renderFlags].blendingMode; if (!cache.materials.ContainsKey(model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower())) { throw new Exception("MaterialCache does not have texture " + model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()); } ddBatch.submeshes[i].material = (uint)cache.materials[model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()]; } } } // Vertices & indices ddBatch.vertexBuffer = GL.GenBuffer(); ddBatch.indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); List<uint> modelindicelist = new List<uint>(); for (int i = 0; i < model.skins[0].triangles.Count(); i++) { modelindicelist.Add(model.skins[0].triangles[i].pt1); modelindicelist.Add(model.skins[0].triangles[i].pt2); modelindicelist.Add(model.skins[0].triangles[i].pt3); } uint[] modelindices = modelindicelist.ToArray(); //Console.WriteLine(modelindicelist.Count() + " indices!"); ddBatch.indices = modelindices; GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(ddBatch.indices.Length * sizeof(uint)), ddBatch.indices, BufferUsageHint.StaticDraw); TerrainWindow.M2Vertex[] modelvertices = new TerrainWindow.M2Vertex[model.vertices.Count()]; for (int i = 0; i < model.vertices.Count(); i++) { modelvertices[i].Position = new Vector3(model.vertices[i].position.X, model.vertices[i].position.Y, model.vertices[i].position.Z); modelvertices[i].Normal = new Vector3(model.vertices[i].normal.X, model.vertices[i].normal.Y, model.vertices[i].normal.Z); modelvertices[i].TexCoord = new Vector2(model.vertices[i].textureCoordX, model.vertices[i].textureCoordY); } GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(modelvertices.Length * 8 * sizeof(float)), modelvertices, BufferUsageHint.StaticDraw); cache.doodadBatches.Add(filename, ddBatch); }
public void LoadM2() { //Load model M2Reader reader = new M2Reader(basedir); string filename = modelPath; reader.LoadM2(filename); //Load vertices List<float> verticelist = new List<float>(); for (int i = 0; i < reader.model.vertices.Count(); i++) { verticelist.Add(reader.model.vertices[i].position.X); verticelist.Add(reader.model.vertices[i].position.Z * -1); verticelist.Add(reader.model.vertices[i].position.Y); verticelist.Add(1.0f); verticelist.Add(reader.model.vertices[i].normal.X); verticelist.Add(reader.model.vertices[i].normal.Z * -1); verticelist.Add(reader.model.vertices[i].normal.Y); verticelist.Add(reader.model.vertices[i].textureCoordX); verticelist.Add(reader.model.vertices[i].textureCoordY); } //Load indices List<ushort> indicelist = new List<ushort>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { indicelist.Add(reader.model.skins[0].triangles[i].pt1); indicelist.Add(reader.model.skins[0].triangles[i].pt2); indicelist.Add(reader.model.skins[0].triangles[i].pt3); } //Convert to array ushort[] indices = indicelist.ToArray(); float[] vertices = verticelist.ToArray(); //Get texture, what a mess this could be much better M2Material[] materials = new M2Material[reader.model.textures.Count()]; for (int i = 0; i < reader.model.textures.Count(); i++) { materials[i].flags = reader.model.textures[i].flags; var blp = new BLPReader(basedir); if (File.Exists(Path.Combine(basedir, reader.model.filename.Replace("M2", "blp")))) { blp.LoadBLP(reader.model.filename.Replace("M2", "blp")); } else { blp.LoadBLP(reader.model.textures[i].filename); } if (blp.bmp == null) { materials[i].texture = Texture2D.FromFile<Texture2D>(device, "missingtexture.jpg"); } else { MemoryStream s = new MemoryStream(); blp.bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); s.Seek(0, SeekOrigin.Begin); materials[i].texture = Texture2D.FromMemory<Texture2D>(device, s.ToArray()); } } M2RenderBatch[] renderbatches = new M2RenderBatch[reader.model.skins[0].submeshes.Count()]; for (int i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; for (int tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].materialID = reader.model.skins[0].textureunit[tu].texture; } } } m2.indices = indices; m2.vertices = vertices; m2.materials = materials; m2.renderBatches = renderbatches; }
private void LoadM2(string modelpath) { //M2Loader.LoadM2(modelpath, cache); Console.WriteLine("Loading M2 file ("+modelpath+").."); M2Reader reader = new M2Reader(); string filename = modelpath; reader.LoadM2(filename); VBOid = new uint[2]; GL.GenBuffers(2, VBOid); GL.BindBuffer(BufferTarget.ArrayBuffer, VBOid[0]); GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBOid[1]); GL.EnableClientState(ArrayCap.VertexArray); GL.EnableClientState(ArrayCap.NormalArray); List<uint> indicelist = new List<uint>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { indicelist.Add(reader.model.skins[0].triangles[i].pt1); indicelist.Add(reader.model.skins[0].triangles[i].pt2); indicelist.Add(reader.model.skins[0].triangles[i].pt3); } uint[] indices = indicelist.ToArray(); GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBOid[1]); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(uint)), indices, BufferUsageHint.StaticDraw); Vertex[] vertices = new Vertex[reader.model.vertices.Count()]; for (int i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new Vector3(reader.model.vertices[i].position.X, reader.model.vertices[i].position.Z, reader.model.vertices[i].position.Y); vertices[i].Normal = new Vector3(reader.model.vertices[i].normal.X, reader.model.vertices[i].normal.Z, reader.model.vertices[i].normal.Y); vertices[i].TexCoord = new Vector2(reader.model.vertices[i].textureCoordX, reader.model.vertices[i].textureCoordY); } GL.BindBuffer(BufferTarget.ArrayBuffer, VBOid[0]); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * 8 * sizeof(float)), vertices, BufferUsageHint.StaticDraw); GL.Enable(EnableCap.Texture2D); materials = new Material[reader.model.textures.Count()]; Console.WriteLine("Loading textures.."); for (int i = 0; i < reader.model.textures.Count(); i++) { Console.WriteLine("Loading texture " + i); string texturefilename = "Dungeons\\Textures\\testing\\COLOR_13.blp"; materials[i].flags = reader.model.textures[i].flags; Console.WriteLine(" Requires type " + reader.model.textures[i].type + " texture"); switch (reader.model.textures[i].type) { case 0: Console.WriteLine(" Texture given in file!"); texturefilename = reader.model.textures[i].filename; break; case 1: string[] csfilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)reader.model.textures[i].type, i); if(csfilenames.Count() > 0){ texturefilename = csfilenames[0]; } else { Console.WriteLine(" No type 1 texture found, falling back to placeholder texture"); } break; case 2: if (WoWFormatLib.Utils.CASC.FileExists(Path.ChangeExtension(modelpath, ".blp"))) { Console.WriteLine(" BLP exists!"); texturefilename = Path.ChangeExtension(modelpath, ".blp"); } else { Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: string[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)reader.model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { if (WoWFormatLib.Utils.CASC.FileExists(modelpath.Replace(reader.model.name + ".M2", cdifilenames[ti] + ".blp"))) { texturefilename = modelpath.Replace(reader.model.name + ".M2", cdifilenames[ti] + ".blp"); } } break; default: Console.WriteLine(" Falling back to placeholder texture"); break; } Console.WriteLine(" Eventual filename is " + texturefilename); materials[i].textureID = BLPLoader.LoadTexture(texturefilename, cache); materials[i].filename = texturefilename; } renderbatches = new RenderBatch[reader.model.skins[0].submeshes.Count()]; for (int i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { if(filename.StartsWith("character", StringComparison.CurrentCultureIgnoreCase)){ if (reader.model.skins[0].submeshes[i].submeshID != 0) { if (!reader.model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } Console.WriteLine("Loading submesh " + reader.model.skins[0].submeshes[i].submeshID + "("+ reader.model.skins[0].submeshes[i].unk2 + ")"); } renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; for (int tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].blendType = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode; renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; } } } Console.WriteLine(" " + reader.model.skins.Count() + " skins"); Console.WriteLine(" " + renderbatches.Count() + " renderbatches"); Console.WriteLine(" " + reader.model.vertices.Count() + " vertices"); Console.WriteLine("Done loading M2 file!"); gLoaded = true; }
public Render() { using (var dg = new DisposeGroup()) { //Load Shaders var pVSBlob = dg.Add(ShaderBytecode.CompileFromFile("RenderWithCam.fx", "VS", "vs_4_0")); var inputSignature = dg.Add(ShaderSignature.GetInputSignature(pVSBlob)); m_pVertexShader = new VertexShader(Device, pVSBlob); var pPSBlob = dg.Add(ShaderBytecode.CompileFromFile("RenderWithCam.fx", "PS", "ps_4_0")); m_pPixelShader = new PixelShader(Device, pPSBlob); //Define layout var layout = dg.Add(new InputLayout(Device, inputSignature, new[]{ new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), new InputElement("TEXCOORD", 0, Format.R32G32_Float, 16, 0) })); //Load model M2Reader reader = new M2Reader("Z:\\18566_full\\"); reader.LoadM2(@"World\ArtTest\Boxtest\xyz.m2"); //Load vertices List<float> verticelist = new List<float>(); for (int i = 0; i < reader.model.vertices.Count(); i++) { verticelist.Add(reader.model.vertices[i].position.X); verticelist.Add(reader.model.vertices[i].position.Z * -1); verticelist.Add(reader.model.vertices[i].position.Y); verticelist.Add(1.0f); verticelist.Add(reader.model.vertices[i].textureCoordX); verticelist.Add(reader.model.vertices[i].textureCoordY); } //Load indices List<ushort> indicelist = new List<ushort>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { indicelist.Add(reader.model.skins[0].triangles[i].pt1); indicelist.Add(reader.model.skins[0].triangles[i].pt2); indicelist.Add(reader.model.skins[0].triangles[i].pt3); } //Convert to array ushort[] indices = indicelist.ToArray(); float[] vertices = verticelist.ToArray(); //Set count for use in draw later on indicecount = indices.Count(); //Create buffers var vertexBuffer = dg.Add(Buffer.Create(Device, BindFlags.VertexBuffer, vertices)); var vertexBufferBinding = new VertexBufferBinding(vertexBuffer, Utilities.SizeOf<Vector4>() + Utilities.SizeOf<Vector2>(), 0); var indexBuffer = dg.Add(Buffer.Create(Device, BindFlags.IndexBuffer, indices)); Device.ImmediateContext.InputAssembler.InputLayout = (layout); Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, vertexBufferBinding); Device.ImmediateContext.InputAssembler.SetIndexBuffer(indexBuffer, Format.R16_UInt, 0); Device.ImmediateContext.InputAssembler.PrimitiveTopology = (PrimitiveTopology.TriangleList); //Get texture, what a mess this could be much better var blp = new BLPReader("Z:\\18566_full\\"); if (File.Exists(Path.Combine("Z:\\18566_full\\", reader.model.filename.Replace("M2", "blp")))) { blp.LoadBLP(reader.model.filename.Replace("M2", "blp")); } else { if (reader.model.textures.Count() > 0) { blp.LoadBLP(reader.model.textures[0]); } else { throw new Exception("No forking textures, mate."); } } MemoryStream s = new MemoryStream(); blp.bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); s.Seek(0, SeekOrigin.Begin); Texture2D texture = Texture2D.FromMemory<Texture2D>(Device, s.ToArray()); var textureView = new ShaderResourceView(Device, texture); var sampler = new SamplerState(Device, new SamplerStateDescription() { Filter = Filter.MinMagMipLinear, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap, BorderColor = SharpDX.Color.Black, ComparisonFunction = Comparison.Never, MaximumAnisotropy = 16, MipLodBias = 0, MinimumLod = 0, MaximumLod = 16, }); Device.ImmediateContext.PixelShader.SetSampler(0, sampler); Device.ImmediateContext.PixelShader.SetShaderResource(0, textureView); //End of texture stuff, Set(ref m_pConstantBuffer, new ConstantBuffer<Projections>(Device)); Device.ImmediateContext.VertexShader.SetConstantBuffer(0, m_pConstantBuffer.Buffer); } //Make camera Camera = new FirstPersonCamera(); Camera.SetProjParams((float)Math.PI / 2, 1, 0.01f, 100.0f); Camera.SetViewParams(new Vector3(0.0f, 0.0f, -5.0f), new Vector3(0.0f, 1.0f, 0.0f)); }
public static void exportM2(string file, BackgroundWorker exportworker = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); } var outdir = ConfigurationManager.AppSettings["outdir"]; var reader = new M2Reader(); exportworker.ReportProgress(15, "Reading M2.."); if (!CASC.FileExists(file)) { throw new Exception("404 M2 not found!"); } reader.LoadM2(file); Structs.Vertex[] vertices = new Structs.Vertex[reader.model.vertices.Count()]; for (int i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new OpenTK.Vector3(reader.model.vertices[i].position.X, reader.model.vertices[i].position.Z, reader.model.vertices[i].position.Y * -1); vertices[i].Normal = new OpenTK.Vector3(reader.model.vertices[i].normal.X, reader.model.vertices[i].normal.Z, reader.model.vertices[i].normal.Y); vertices[i].TexCoord = new Vector2(reader.model.vertices[i].textureCoordX, reader.model.vertices[i].textureCoordY); } // Create output directory if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } var objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".obj"))); objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file) + ".mtl"); foreach (var vertex in vertices) { objsw.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); objsw.WriteLine("vt " + vertex.TexCoord.X + " " + -vertex.TexCoord.Y); objsw.WriteLine("vn " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Normal.Z); } List<uint> indicelist = new List<uint>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { var t = reader.model.skins[0].triangles[i]; indicelist.Add(t.pt1); indicelist.Add(t.pt2); indicelist.Add(t.pt3); } var indices = indicelist.ToArray(); exportworker.ReportProgress(35, "Writing files.."); var renderbatches = new Structs.RenderBatch[reader.model.skins[0].submeshes.Count()]; for (int i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { if (file.StartsWith("character", StringComparison.CurrentCultureIgnoreCase)) { if (reader.model.skins[0].submeshes[i].submeshID != 0) { if (!reader.model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; renderbatches[i].groupID = (uint)i; for (int tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].blendType = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode; renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; } } } exportworker.ReportProgress(65, "Exporting textures.."); var mtlsb = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".mtl"))); var textureID = 0; var materials = new Structs.Material[reader.model.textures.Count()]; for (int i = 0; i < reader.model.textures.Count(); i++) { string texturefilename = "Dungeons\\Textures\\testing\\COLOR_13.blp"; materials[i].flags = reader.model.textures[i].flags; switch (reader.model.textures[i].type) { case 0: //Console.WriteLine(" Texture given in file!"); texturefilename = reader.model.textures[i].filename; break; case 1: string[] csfilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(file, (int)reader.model.textures[i].type, i); if (csfilenames.Count() > 0) { texturefilename = csfilenames[0]; } else { //Console.WriteLine(" No type 1 texture found, falling back to placeholder texture"); } break; case 2: if (WoWFormatLib.Utils.CASC.FileExists(Path.ChangeExtension(file, ".blp"))) { //Console.WriteLine(" BLP exists!"); texturefilename = Path.ChangeExtension(file, ".blp"); } else { //Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: string[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(file, (int)reader.model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { if (WoWFormatLib.Utils.CASC.FileExists(file.Replace(reader.model.name + ".M2", cdifilenames[ti] + ".blp"))) { texturefilename = file.Replace(reader.model.name + ".M2", cdifilenames[ti] + ".blp"); } } break; default: // Console.WriteLine(" Falling back to placeholder texture"); break; } //Console.WriteLine(" Eventual filename is " + texturefilename); materials[i].textureID = textureID + i; materials[i].filename = Path.GetFileNameWithoutExtension(texturefilename); var blpreader = new BLPReader(); blpreader.LoadBLP(texturefilename); try { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png")); } catch (Exception e) { Console.WriteLine(e.Message); } } exportworker.ReportProgress(85, "Writing files.."); foreach (var material in materials) { mtlsb.WriteLine("newmtl " + material.filename); mtlsb.WriteLine("illum 2"); mtlsb.WriteLine("map_Ka " + material.filename + ".png"); mtlsb.WriteLine("map_Kd " + material.filename + ".png"); } mtlsb.Close(); objsw.WriteLine("g " + Path.GetFileNameWithoutExtension(file)); foreach (var renderbatch in renderbatches) { var i = renderbatch.firstFace; objsw.WriteLine("o " + Path.GetFileNameWithoutExtension(file) + renderbatch.groupID); objsw.WriteLine("usemtl " + materials[renderbatch.materialID].filename); objsw.WriteLine("s 1"); while (i < (renderbatch.firstFace + renderbatch.numFaces)) { objsw.WriteLine("f " + (indices[i] + 1) + "/" + (indices[i] + 1) + "/" + (indices[i] + 1) + " " + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + " " + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1)); i = i + 3; } } objsw.Close(); // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials // http://wiki.unity3d.com/index.php?title=ExportOBJ // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm Console.WriteLine("Done loading model!"); }