| 1 | using System; | 
 
 
 
 
 | 2 | using System.Collections.Generic; | 
 
 
 
 
 | 3 | using System.Globalization; | 
 
 
 
 
 | 4 | using System.IO; | 
 
 
 
 
 | 5 | using Oni.Collections; | 
 
 
 
 
 | 6 |  | 
 
 
 
 
 | 7 | namespace Oni.Dae.IO | 
 
 
 
 
 | 8 | { | 
 
 
 
 
 | 9 | internal class ObjWriter | 
 
 
 
 
 | 10 | { | 
 
 
 
 
 | 11 | private int vBase = 1, vtBase = 1, vnBase = 1; | 
 
 
 
 
 | 12 | private Set<Material> materials = new Set<Material>(); | 
 
 
 
 
 | 13 | private StreamWriter objWriter; | 
 
 
 
 
 | 14 | private StreamWriter mtlWriter; | 
 
 
 
 
 | 15 |  | 
 
 
 
 
 | 16 | internal static void WriteFile(string filePath, Scene scene) | 
 
 
 
 
 | 17 | { | 
 
 
 
 
 | 18 | var writer = new ObjWriter(); | 
 
 
 
 
 | 19 | writer.Write(filePath, scene); | 
 
 
 
 
 | 20 | } | 
 
 
 
 
 | 21 |  | 
 
 
 
 
 | 22 | private void Write(string filePath, Scene scene) | 
 
 
 
 
 | 23 | { | 
 
 
 
 
 | 24 | using (objWriter = File.CreateText(filePath)) | 
 
 
 
 
 | 25 | { | 
 
 
 
 
 | 26 | objWriter.WriteLine("# Generated by OniSplit v{0}", Utils.Version); | 
 
 
 
 
 | 27 | objWriter.WriteLine(); | 
 
 
 
 
 | 28 |  | 
 
 
 
 
 | 29 | objWriter.WriteLine("mtllib {0}", Path.ChangeExtension(Path.GetFileName(filePath), ".mtl")); | 
 
 
 
 
 | 30 | objWriter.WriteLine(); | 
 
 
 
 
 | 31 |  | 
 
 
 
 
 | 32 | var rootTransform = Matrix.Identity; | 
 
 
 
 
 | 33 |  | 
 
 
 
 
 | 34 | WriteNode(scene, ref rootTransform); | 
 
 
 
 
 | 35 | } | 
 
 
 
 
 | 36 |  | 
 
 
 
 
 | 37 | using (mtlWriter = File.CreateText(Path.ChangeExtension(filePath, ".mtl"))) | 
 
 
 
 
 | 38 | { | 
 
 
 
 
 | 39 | mtlWriter.WriteLine("# Generated by OniSplit v{0}", Utils.Version); | 
 
 
 
 
 | 40 | mtlWriter.WriteLine(); | 
 
 
 
 
 | 41 |  | 
 
 
 
 
 | 42 | WriteMaterialLibrary(); | 
 
 
 
 
 | 43 | } | 
 
 
 
 
 | 44 | } | 
 
 
 
 
 | 45 |  | 
 
 
 
 
 | 46 | private void WriteNode(Node node, ref Matrix parentTransform) | 
 
 
 
 
 | 47 | { | 
 
 
 
 
 | 48 | if (!string.IsNullOrEmpty(node.Name)) | 
 
 
 
 
 | 49 | objWriter.WriteLine("g {0}", node.Name); | 
 
 
 
 
 | 50 |  | 
 
 
 
 
 | 51 | var transform = node.Transforms.ToMatrix() * parentTransform; | 
 
 
 
 
 | 52 |  | 
 
 
 
 
 | 53 | foreach (var geometryInstance in node.GeometryInstances) | 
 
 
 
 
 | 54 | WriteGeometry(geometryInstance, ref transform); | 
 
 
 
 
 | 55 |  | 
 
 
 
 
 | 56 | foreach (var child in node.Nodes) | 
 
 
 
 
 | 57 | WriteNode(child, ref transform); | 
 
 
 
 
 | 58 | } | 
 
 
 
 
 | 59 |  | 
 
 
 
 
 | 60 | private void WriteGeometry(GeometryInstance geometryInstance, ref Matrix transform) | 
 
 
 
 
 | 61 | { | 
 
 
 
 
 | 62 | var geometry = geometryInstance.Target; | 
 
 
 
 
 | 63 |  | 
 
 
 
 
 | 64 | foreach (var primitives in geometry.Primitives) | 
 
 
 
 
 | 65 | { | 
 
 
 
 
 | 66 | var posInput = WritePositions(primitives, ref transform); | 
 
 
 
 
 | 67 | var texCoordInput = WriteTexCoords(primitives); | 
 
 
 
 
 | 68 | var normalInput = WriteNormals(primitives, ref transform); | 
 
 
 
 
 | 69 |  | 
 
 
 
 
 | 70 | WriteUseMaterial(geometryInstance, primitives); | 
 
 
 
 
 | 71 | WriteFaces(primitives, posInput, texCoordInput, normalInput); | 
 
 
 
 
 | 72 |  | 
 
 
 
 
 | 73 | vBase += posInput.Source.Count; | 
 
 
 
 
 | 74 | vtBase += texCoordInput == null ? 0 : texCoordInput.Source.Count; | 
 
 
 
 
 | 75 | vnBase += normalInput == null ? 0: normalInput.Source.Count; | 
 
 
 
 
 | 76 | } | 
 
 
 
 
 | 77 | } | 
 
 
 
 
 | 78 |  | 
 
 
 
 
 | 79 | private IndexedInput WritePositions(MeshPrimitives primitives, ref Matrix transform) | 
 
 
 
 
 | 80 | { | 
 
 
 
 
 | 81 | var input = primitives.Inputs.FirstOrDefault(i => i.Semantic == Semantic.Position); | 
 
 
 
 
 | 82 | var source = input.Source; | 
 
 
 
 
 | 83 |  | 
 
 
 
 
 | 84 | for (int i = 0; i < source.Count; i++) | 
 
 
 
 
 | 85 | { | 
 
 
 
 
 | 86 | var position = Vector3.Transform(Source.ReadVector3(source, i), ref transform); | 
 
 
 
 
 | 87 |  | 
 
 
 
 
 | 88 | objWriter.Write("v "); | 
 
 
 
 
 | 89 | objWriter.Write(position.X.ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 90 | objWriter.Write(' '); | 
 
 
 
 
 | 91 | objWriter.Write(position.Y.ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 92 | objWriter.Write(' '); | 
 
 
 
 
 | 93 | objWriter.Write(position.Z.ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 94 | objWriter.WriteLine(); | 
 
 
 
 
 | 95 | } | 
 
 
 
 
 | 96 |  | 
 
 
 
 
 | 97 | objWriter.WriteLine(); | 
 
 
 
 
 | 98 |  | 
 
 
 
 
 | 99 | return input; | 
 
 
 
 
 | 100 | } | 
 
 
 
 
 | 101 |  | 
 
 
 
 
 | 102 | private IndexedInput WriteTexCoords(MeshPrimitives primitives) | 
 
 
 
 
 | 103 | { | 
 
 
 
 
 | 104 | var input = primitives.Inputs.FirstOrDefault(i => i.Semantic == Semantic.TexCoord); | 
 
 
 
 
 | 105 |  | 
 
 
 
 
 | 106 | if (input == null) | 
 
 
 
 
 | 107 | return null; | 
 
 
 
 
 | 108 |  | 
 
 
 
 
 | 109 | var source = input.Source; | 
 
 
 
 
 | 110 | var data = source.FloatData; | 
 
 
 
 
 | 111 |  | 
 
 
 
 
 | 112 | for (int i = 0; i < data.Length; i += source.Stride) | 
 
 
 
 
 | 113 | { | 
 
 
 
 
 | 114 | objWriter.Write("vt "); | 
 
 
 
 
 | 115 | objWriter.Write(data[i + 0].ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 116 | objWriter.Write(' '); | 
 
 
 
 
 | 117 | objWriter.Write(data[i + 1].ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 118 | objWriter.WriteLine(); | 
 
 
 
 
 | 119 | } | 
 
 
 
 
 | 120 |  | 
 
 
 
 
 | 121 | objWriter.WriteLine(); | 
 
 
 
 
 | 122 |  | 
 
 
 
 
 | 123 | return input; | 
 
 
 
 
 | 124 | } | 
 
 
 
 
 | 125 |  | 
 
 
 
 
 | 126 | private IndexedInput WriteNormals(MeshPrimitives primitives, ref Matrix transform) | 
 
 
 
 
 | 127 | { | 
 
 
 
 
 | 128 | var input = primitives.Inputs.FirstOrDefault(i => i.Semantic == Semantic.Normal); | 
 
 
 
 
 | 129 |  | 
 
 
 
 
 | 130 | if (input == null) | 
 
 
 
 
 | 131 | return null; | 
 
 
 
 
 | 132 |  | 
 
 
 
 
 | 133 | var source = input.Source; | 
 
 
 
 
 | 134 |  | 
 
 
 
 
 | 135 | for (int i = 0; i < source.Count; i++) | 
 
 
 
 
 | 136 | { | 
 
 
 
 
 | 137 | var normal = Vector3.TransformNormal(Source.ReadVector3(source, i), ref transform); | 
 
 
 
 
 | 138 |  | 
 
 
 
 
 | 139 | objWriter.Write("vn "); | 
 
 
 
 
 | 140 | objWriter.Write(normal.X.ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 141 | objWriter.Write(' '); | 
 
 
 
 
 | 142 | objWriter.Write(normal.Y.ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 143 | objWriter.Write(' '); | 
 
 
 
 
 | 144 | objWriter.Write(normal.Z.ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 145 | objWriter.WriteLine(); | 
 
 
 
 
 | 146 | } | 
 
 
 
 
 | 147 |  | 
 
 
 
 
 | 148 | objWriter.WriteLine(); | 
 
 
 
 
 | 149 |  | 
 
 
 
 
 | 150 | return input; | 
 
 
 
 
 | 151 | } | 
 
 
 
 
 | 152 |  | 
 
 
 
 
 | 153 | private void WriteFaces(MeshPrimitives primitives, IndexedInput posInput, IndexedInput texCoordInput, IndexedInput normalInput) | 
 
 
 
 
 | 154 | { | 
 
 
 
 
 | 155 | var positionIndices = posInput.Indices; | 
 
 
 
 
 | 156 | var texCoordIndices = texCoordInput == null ? null : texCoordInput.Indices; | 
 
 
 
 
 | 157 | var normalIndices = normalInput == null ? null : normalInput.Indices; | 
 
 
 
 
 | 158 | int vertexIndex = 0; | 
 
 
 
 
 | 159 |  | 
 
 
 
 
 | 160 | foreach (var vertexCount in primitives.VertexCounts) | 
 
 
 
 
 | 161 | { | 
 
 
 
 
 | 162 | objWriter.Write("f"); | 
 
 
 
 
 | 163 |  | 
 
 
 
 
 | 164 | for (int i = vertexIndex; i < vertexIndex + vertexCount; i++) | 
 
 
 
 
 | 165 | { | 
 
 
 
 
 | 166 | objWriter.Write(' '); | 
 
 
 
 
 | 167 | objWriter.Write((vBase + positionIndices[i]).ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 168 |  | 
 
 
 
 
 | 169 | if (texCoordIndices != null) | 
 
 
 
 
 | 170 | { | 
 
 
 
 
 | 171 | objWriter.Write('/'); | 
 
 
 
 
 | 172 | objWriter.Write((vtBase + texCoordIndices[i]).ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 173 | } | 
 
 
 
 
 | 174 | else if (normalIndices != null) | 
 
 
 
 
 | 175 | { | 
 
 
 
 
 | 176 | objWriter.Write('/'); | 
 
 
 
 
 | 177 | } | 
 
 
 
 
 | 178 |  | 
 
 
 
 
 | 179 | if (normalIndices != null) | 
 
 
 
 
 | 180 | { | 
 
 
 
 
 | 181 | objWriter.Write('/'); | 
 
 
 
 
 | 182 | objWriter.Write((vnBase + normalIndices[i]).ToString(CultureInfo.InvariantCulture)); | 
 
 
 
 
 | 183 | } | 
 
 
 
 
 | 184 | } | 
 
 
 
 
 | 185 |  | 
 
 
 
 
 | 186 | objWriter.WriteLine(); | 
 
 
 
 
 | 187 |  | 
 
 
 
 
 | 188 | vertexIndex += vertexCount; | 
 
 
 
 
 | 189 | } | 
 
 
 
 
 | 190 |  | 
 
 
 
 
 | 191 | objWriter.WriteLine(); | 
 
 
 
 
 | 192 | } | 
 
 
 
 
 | 193 |  | 
 
 
 
 
 | 194 | private void WriteUseMaterial(GeometryInstance geometryInstance, MeshPrimitives primitives) | 
 
 
 
 
 | 195 | { | 
 
 
 
 
 | 196 | if (string.IsNullOrEmpty(primitives.MaterialSymbol)) | 
 
 
 
 
 | 197 | { | 
 
 
 
 
 | 198 | objWriter.WriteLine("usemtl"); | 
 
 
 
 
 | 199 | } | 
 
 
 
 
 | 200 | else | 
 
 
 
 
 | 201 | { | 
 
 
 
 
 | 202 | var materialInstance = geometryInstance.Materials.Find(m => m.Symbol == primitives.MaterialSymbol); | 
 
 
 
 
 | 203 |  | 
 
 
 
 
 | 204 | if (materialInstance != null && materialInstance.Target != null) | 
 
 
 
 
 | 205 | { | 
 
 
 
 
 | 206 | objWriter.WriteLine("usemtl {0}", materialInstance.Target.Name); | 
 
 
 
 
 | 207 | materials.Add(materialInstance.Target); | 
 
 
 
 
 | 208 | } | 
 
 
 
 
 | 209 | } | 
 
 
 
 
 | 210 | } | 
 
 
 
 
 | 211 |  | 
 
 
 
 
 | 212 | private void WriteMaterialLibrary() | 
 
 
 
 
 | 213 | { | 
 
 
 
 
 | 214 | foreach (var material in materials) | 
 
 
 
 
 | 215 | { | 
 
 
 
 
 | 216 | mtlWriter.WriteLine("newmtl {0}", material.Name); | 
 
 
 
 
 | 217 |  | 
 
 
 
 
 | 218 | var effect = material.Effect; | 
 
 
 
 
 | 219 |  | 
 
 
 
 
 | 220 | WriteMaterialColor("Ka", effect.Ambient); | 
 
 
 
 
 | 221 | WriteMaterialColor("Kd", effect.Diffuse); | 
 
 
 
 
 | 222 |  | 
 
 
 
 
 | 223 | mtlWriter.WriteLine("Ks 0 0 0"); | 
 
 
 
 
 | 224 | mtlWriter.WriteLine("Ns 0"); | 
 
 
 
 
 | 225 |  | 
 
 
 
 
 | 226 | WriteMaterialTextureMap("map_Kd", effect.Diffuse); | 
 
 
 
 
 | 227 | WriteMaterialTextureMap("map_Tr", effect.Transparent); | 
 
 
 
 
 | 228 |  | 
 
 
 
 
 | 229 | if (effect.TransparentValue is EffectTexture) | 
 
 
 
 
 | 230 | mtlWriter.WriteLine("illum 9"); | 
 
 
 
 
 | 231 | else | 
 
 
 
 
 | 232 | mtlWriter.WriteLine("illum 2"); | 
 
 
 
 
 | 233 |  | 
 
 
 
 
 | 234 | mtlWriter.WriteLine(); | 
 
 
 
 
 | 235 | } | 
 
 
 
 
 | 236 | } | 
 
 
 
 
 | 237 |  | 
 
 
 
 
 | 238 | private void WriteMaterialColor(string mtlCommand, EffectParameter effectParam) | 
 
 
 
 
 | 239 | { | 
 
 
 
 
 | 240 | if (effectParam == null || !(effectParam.Value is Vector4)) | 
 
 
 
 
 | 241 | return; | 
 
 
 
 
 | 242 |  | 
 
 
 
 
 | 243 | var color = (Vector4)effectParam.Value; | 
 
 
 
 
 | 244 | mtlWriter.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", mtlCommand, color.X, color.Y, color.Z)); | 
 
 
 
 
 | 245 | } | 
 
 
 
 
 | 246 |  | 
 
 
 
 
 | 247 | private void WriteMaterialTextureMap(string mtlCommand, EffectParameter effectParam) | 
 
 
 
 
 | 248 | { | 
 
 
 
 
 | 249 | if (effectParam == null) | 
 
 
 
 
 | 250 | return; | 
 
 
 
 
 | 251 |  | 
 
 
 
 
 | 252 | var texture = effectParam.Value as EffectTexture; | 
 
 
 
 
 | 253 |  | 
 
 
 
 
 | 254 | if (texture == null) | 
 
 
 
 
 | 255 | return; | 
 
 
 
 
 | 256 |  | 
 
 
 
 
 | 257 | mtlWriter.WriteLine("{0} {1}", mtlCommand, texture.Sampler.Surface.InitFrom.FilePath); | 
 
 
 
 
 | 258 | } | 
 
 
 
 
 | 259 | } | 
 
 
 
 
 | 260 | } |