using System; using System.Collections.Generic; using System.Globalization; using System.IO; using Oni.Collections; namespace Oni.Dae.IO { internal class ObjWriter { private int vBase = 1, vtBase = 1, vnBase = 1; private Set materials = new Set(); private StreamWriter objWriter; private StreamWriter mtlWriter; internal static void WriteFile(string filePath, Scene scene) { var writer = new ObjWriter(); writer.Write(filePath, scene); } private void Write(string filePath, Scene scene) { using (objWriter = File.CreateText(filePath)) { objWriter.WriteLine("# Generated by OniSplit v{0}", Utils.Version); objWriter.WriteLine(); objWriter.WriteLine("mtllib {0}", Path.ChangeExtension(Path.GetFileName(filePath), ".mtl")); objWriter.WriteLine(); var rootTransform = Matrix.Identity; WriteNode(scene, ref rootTransform); } using (mtlWriter = File.CreateText(Path.ChangeExtension(filePath, ".mtl"))) { mtlWriter.WriteLine("# Generated by OniSplit v{0}", Utils.Version); mtlWriter.WriteLine(); WriteMaterialLibrary(); } } private void WriteNode(Node node, ref Matrix parentTransform) { if (!string.IsNullOrEmpty(node.Name)) objWriter.WriteLine("g {0}", node.Name); var transform = node.Transforms.ToMatrix() * parentTransform; foreach (var geometryInstance in node.GeometryInstances) WriteGeometry(geometryInstance, ref transform); foreach (var child in node.Nodes) WriteNode(child, ref transform); } private void WriteGeometry(GeometryInstance geometryInstance, ref Matrix transform) { var geometry = geometryInstance.Target; foreach (var primitives in geometry.Primitives) { var posInput = WritePositions(primitives, ref transform); var texCoordInput = WriteTexCoords(primitives); var normalInput = WriteNormals(primitives, ref transform); WriteUseMaterial(geometryInstance, primitives); WriteFaces(primitives, posInput, texCoordInput, normalInput); vBase += posInput.Source.Count; vtBase += texCoordInput == null ? 0 : texCoordInput.Source.Count; vnBase += normalInput == null ? 0: normalInput.Source.Count; } } private IndexedInput WritePositions(MeshPrimitives primitives, ref Matrix transform) { var input = primitives.Inputs.FirstOrDefault(i => i.Semantic == Semantic.Position); var source = input.Source; for (int i = 0; i < source.Count; i++) { var position = Vector3.Transform(Source.ReadVector3(source, i), ref transform); objWriter.Write("v "); objWriter.Write(position.X.ToString(CultureInfo.InvariantCulture)); objWriter.Write(' '); objWriter.Write(position.Y.ToString(CultureInfo.InvariantCulture)); objWriter.Write(' '); objWriter.Write(position.Z.ToString(CultureInfo.InvariantCulture)); objWriter.WriteLine(); } objWriter.WriteLine(); return input; } private IndexedInput WriteTexCoords(MeshPrimitives primitives) { var input = primitives.Inputs.FirstOrDefault(i => i.Semantic == Semantic.TexCoord); if (input == null) return null; var source = input.Source; var data = source.FloatData; for (int i = 0; i < data.Length; i += source.Stride) { objWriter.Write("vt "); objWriter.Write(data[i + 0].ToString(CultureInfo.InvariantCulture)); objWriter.Write(' '); objWriter.Write(data[i + 1].ToString(CultureInfo.InvariantCulture)); objWriter.WriteLine(); } objWriter.WriteLine(); return input; } private IndexedInput WriteNormals(MeshPrimitives primitives, ref Matrix transform) { var input = primitives.Inputs.FirstOrDefault(i => i.Semantic == Semantic.Normal); if (input == null) return null; var source = input.Source; for (int i = 0; i < source.Count; i++) { var normal = Vector3.TransformNormal(Source.ReadVector3(source, i), ref transform); objWriter.Write("vn "); objWriter.Write(normal.X.ToString(CultureInfo.InvariantCulture)); objWriter.Write(' '); objWriter.Write(normal.Y.ToString(CultureInfo.InvariantCulture)); objWriter.Write(' '); objWriter.Write(normal.Z.ToString(CultureInfo.InvariantCulture)); objWriter.WriteLine(); } objWriter.WriteLine(); return input; } private void WriteFaces(MeshPrimitives primitives, IndexedInput posInput, IndexedInput texCoordInput, IndexedInput normalInput) { var positionIndices = posInput.Indices; var texCoordIndices = texCoordInput == null ? null : texCoordInput.Indices; var normalIndices = normalInput == null ? null : normalInput.Indices; int vertexIndex = 0; foreach (var vertexCount in primitives.VertexCounts) { objWriter.Write("f"); for (int i = vertexIndex; i < vertexIndex + vertexCount; i++) { objWriter.Write(' '); objWriter.Write((vBase + positionIndices[i]).ToString(CultureInfo.InvariantCulture)); if (texCoordIndices != null) { objWriter.Write('/'); objWriter.Write((vtBase + texCoordIndices[i]).ToString(CultureInfo.InvariantCulture)); } else if (normalIndices != null) { objWriter.Write('/'); } if (normalIndices != null) { objWriter.Write('/'); objWriter.Write((vnBase + normalIndices[i]).ToString(CultureInfo.InvariantCulture)); } } objWriter.WriteLine(); vertexIndex += vertexCount; } objWriter.WriteLine(); } private void WriteUseMaterial(GeometryInstance geometryInstance, MeshPrimitives primitives) { if (string.IsNullOrEmpty(primitives.MaterialSymbol)) { objWriter.WriteLine("usemtl"); } else { var materialInstance = geometryInstance.Materials.Find(m => m.Symbol == primitives.MaterialSymbol); if (materialInstance != null && materialInstance.Target != null) { objWriter.WriteLine("usemtl {0}", materialInstance.Target.Name); materials.Add(materialInstance.Target); } } } private void WriteMaterialLibrary() { foreach (var material in materials) { mtlWriter.WriteLine("newmtl {0}", material.Name); var effect = material.Effect; WriteMaterialColor("Ka", effect.Ambient); WriteMaterialColor("Kd", effect.Diffuse); mtlWriter.WriteLine("Ks 0 0 0"); mtlWriter.WriteLine("Ns 0"); WriteMaterialTextureMap("map_Kd", effect.Diffuse); WriteMaterialTextureMap("map_Tr", effect.Transparent); if (effect.TransparentValue is EffectTexture) mtlWriter.WriteLine("illum 9"); else mtlWriter.WriteLine("illum 2"); mtlWriter.WriteLine(); } } private void WriteMaterialColor(string mtlCommand, EffectParameter effectParam) { if (effectParam == null || !(effectParam.Value is Vector4)) return; var color = (Vector4)effectParam.Value; mtlWriter.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", mtlCommand, color.X, color.Y, color.Z)); } private void WriteMaterialTextureMap(string mtlCommand, EffectParameter effectParam) { if (effectParam == null) return; var texture = effectParam.Value as EffectTexture; if (texture == null) return; mtlWriter.WriteLine("{0} {1}", mtlCommand, texture.Sampler.Surface.InitFrom.FilePath); } } }