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 void render() { glControl1.MakeCurrent(); GL.Viewport(0, 0, glControl1.ClientSize.Width, glControl1.ClientSize.Height); //clear GL.ClearColor(backgroundColorDialog.Color); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); //projection matrix Matrix4 projection = glControl1.Camera.Projection; GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref projection); //view matrix Matrix4 view = glControl1.Camera.View; GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref view); if (showAxesButton.Checked) { // debug axes GL.Begin(PrimitiveType.Lines); //x GL.Color3(Color.Red); GL.Vertex3(Vector3.Zero); GL.Vertex3(Vector3.UnitX); GL.Vertex3(Vector3.UnitX); GL.Vertex3(Vector3.UnitX + new Vector3(-0.125f, 0.125f, 0.0f)); GL.Vertex3(Vector3.UnitX); GL.Vertex3(Vector3.UnitX + new Vector3(-0.125f, -0.125f, 0.0f)); //y GL.Color3(Color.Green); GL.Vertex3(Vector3.Zero); GL.Vertex3(Vector3.UnitY); GL.Vertex3(Vector3.UnitY); GL.Vertex3(Vector3.UnitY + new Vector3(0.125f, -0.125f, 0.0f)); GL.Vertex3(Vector3.UnitY); GL.Vertex3(Vector3.UnitY + new Vector3(-0.125f, -0.125f, 0.0f)); //z GL.Color3(Color.Blue); GL.Vertex3(Vector3.Zero); GL.Vertex3(Vector3.UnitZ); GL.Vertex3(Vector3.UnitZ); GL.Vertex3(Vector3.UnitZ + new Vector3(0, -0.125f, -0.125f)); GL.Vertex3(Vector3.UnitZ); GL.Vertex3(Vector3.UnitZ + new Vector3(0, 0.125f, -0.125f)); GL.End(); } //TODO: Decide what to do with non-version 4 models. if (model != null && model.Version == 4) { GL.PushMatrix(); GL.PushAttrib(AttribMask.PolygonBit | AttribMask.EnableBit | AttribMask.LightingBit | AttribMask.CurrentBit); GL.UseProgram(currentShader); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.Texture2D); GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); GL.CullFace(CullFaceMode.Back); GL.FrontFace(FrontFaceDirection.Cw); for (Int32 i = 0; i < model.Meshes.Length; ++i) { Mesh mesh = model.Meshes[i]; GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, textures[i]); if (currentShader == texturedShader) { int loc = GL.GetUniformLocation(currentShader, "colorMap"); GL.Uniform1(loc, 0); } //pin handles to stream data GCHandle[] streamDataGCHandles = new GCHandle[mesh.VertexStreams.Length]; for (Int32 j = 0; j < streamDataGCHandles.Length; ++j) { streamDataGCHandles[j] = GCHandle.Alloc(mesh.VertexStreams[j].Data, GCHandleType.Pinned); } //fetch material definition and vertex layout Int32 materialIndex = (Int32)mesh.MaterialIndex; uint materialDefinitionHash = model.Materials[materialIndex].MaterialDefinitionHash; MaterialDefinition materialDefinition = null; VertexLayout vertexLayout = null; if (MaterialDefinitionManager.Instance.MaterialDefinitions.ContainsKey(materialDefinitionHash)) { materialDefinition = MaterialDefinitionManager.Instance.MaterialDefinitions[materialDefinitionHash]; vertexLayout = MaterialDefinitionManager.Instance.VertexLayouts[materialDefinition.DrawStyles[0].VertexLayoutNameHash]; } GL.Color3(meshColors[i % meshColors.Length]); if (renderModeWireframeButton.Checked) { GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); } else { GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); } //position VertexLayout.Entry.DataTypes positionDataType = VertexLayout.Entry.DataTypes.None; Int32 positionStream = 0; Int32 positionOffset = 0; bool positionExists = (vertexLayout != null ? vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Position, 0, out positionDataType, out positionStream, out positionOffset) : false); if (positionExists) { IntPtr positionData = streamDataGCHandles[positionStream].AddrOfPinnedObject(); GL.EnableClientState(ArrayCap.VertexArray); GL.VertexPointer(3, VertexPointerType.Float, mesh.VertexStreams[positionStream].BytesPerVertex, positionData + positionOffset); } //normal VertexLayout.Entry.DataTypes normalDataType = VertexLayout.Entry.DataTypes.None; Int32 normalStream = 0; Int32 normalOffset = 0; bool normalExists = (vertexLayout != null ? vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Normal, 0, out normalDataType, out normalStream, out normalOffset) : false); if (normalExists) { IntPtr normalData = streamDataGCHandles[normalStream].AddrOfPinnedObject(); GL.EnableClientState(ArrayCap.NormalArray); GL.NormalPointer(NormalPointerType.Float, mesh.VertexStreams[normalStream].BytesPerVertex, normalData + normalOffset); } //texture coordiantes VertexLayout.Entry.DataTypes texCoord0DataType = VertexLayout.Entry.DataTypes.None; Int32 texCoord0Stream = 0; Int32 texCoord0Offset = 0; bool texCoord0Exists = (vertexLayout != null ? vertexLayout.GetEntryInfoFromDataUsageAndUsageIndex(VertexLayout.Entry.DataUsages.Texcoord, 0, out texCoord0DataType, out texCoord0Stream, out texCoord0Offset) : false); if (texCoord0Exists) { IntPtr texCoord0Data = streamDataGCHandles[texCoord0Stream].AddrOfPinnedObject(); GL.EnableClientState(ArrayCap.TextureCoordArray); TexCoordPointerType texCoord0PointerType = TexCoordPointerType.Float; switch (texCoord0DataType) { case VertexLayout.Entry.DataTypes.Float2: texCoord0PointerType = TexCoordPointerType.Float; break; case VertexLayout.Entry.DataTypes.float16_2: texCoord0PointerType = TexCoordPointerType.HalfFloat; break; default: break; } GL.TexCoordPointer(2, texCoord0PointerType, mesh.VertexStreams[texCoord0Stream].BytesPerVertex, texCoord0Data + texCoord0Offset); } //indices GCHandle indexDataHandle = GCHandle.Alloc(mesh.IndexData, GCHandleType.Pinned); IntPtr indexData = indexDataHandle.AddrOfPinnedObject(); GL.DrawElements(PrimitiveType.Triangles, (Int32)mesh.IndexCount, DrawElementsType.UnsignedShort, indexData); indexDataHandle.Free(); GL.DisableClientState(ArrayCap.VertexArray); GL.DisableClientState(ArrayCap.NormalArray); GL.DisableClientState(ArrayCap.TextureCoordArray); //free stream data handles for (Int32 j = 0; j < streamDataGCHandles.Length; ++j) { streamDataGCHandles[j].Free(); } } GL.UseProgram(0); GL.PopAttrib(); ////bounding box //if (showBoundingBoxButton.Checked) //{ // GL.PushAttrib(AttribMask.CurrentBit | AttribMask.EnableBit); // GL.Color3(Color.Red); // GL.Enable(EnableCap.DepthTest); // Vector3 min = model.Min; // Vector3 max = model.Max; // Vector3[] vertices = new Vector3[8]; // UInt32[] indices = { 0, 1, 1, 2, 2, 3, 3, 0, 0, 4, 1, 5, 2, 6, 3, 7, 4, 5, 5, 6, 6, 7, 7, 4 }; // vertices[0] = min; // vertices[1] = new Vector3(max.X, min.Y, min.Z); // vertices[2] = new Vector3(max.X, min.Y, max.Z); // vertices[3] = new Vector3(min.X, min.Y, max.Z); // vertices[4] = new Vector3(min.X, max.Y, min.Z); // vertices[5] = new Vector3(max.X, max.Y, min.Z); // vertices[6] = max; // vertices[7] = new Vector3(min.X, max.Y, max.Z); // GL.EnableClientState(ArrayCap.VertexArray); // GL.VertexPointer(3, VertexPointerType.Float, 0, vertices); // GL.DrawRangeElements(BeginMode.Lines, 0, 23, 24, DrawElementsType.UnsignedInt, indices); // GL.PopAttrib(); //} } glControl1.SwapBuffers(); }
/// <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()); } }