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 |
} |