1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.IO;
|
---|
4 | using System.Xml;
|
---|
5 |
|
---|
6 | namespace Oni.Level
|
---|
7 | {
|
---|
8 | using Akira;
|
---|
9 | using Metadata;
|
---|
10 | using Motoko;
|
---|
11 | using Physics;
|
---|
12 |
|
---|
13 | partial class LevelImporter
|
---|
14 | {
|
---|
15 | private List<Dae.Scene> roomScenes;
|
---|
16 | private PolygonMesh model;
|
---|
17 | private AkiraDaeReader daeReader;
|
---|
18 |
|
---|
19 | private void ReadModel(XmlReader xml, string basePath)
|
---|
20 | {
|
---|
21 | xml.ReadStartElement("Environment");
|
---|
22 |
|
---|
23 | xml.ReadStartElement("Model");
|
---|
24 |
|
---|
25 | daeReader = new AkiraDaeReader();
|
---|
26 | model = daeReader.Mesh;
|
---|
27 | level.model = model;
|
---|
28 |
|
---|
29 | info.WriteLine("Reading environment...");
|
---|
30 |
|
---|
31 | while (xml.IsStartElement())
|
---|
32 | {
|
---|
33 | switch (xml.LocalName)
|
---|
34 | {
|
---|
35 | case "Import":
|
---|
36 | case "Scene":
|
---|
37 | ImportModelScene(xml, basePath);
|
---|
38 | break;
|
---|
39 |
|
---|
40 | case "Object":
|
---|
41 | xml.Skip();
|
---|
42 | break;
|
---|
43 |
|
---|
44 | case "Camera":
|
---|
45 | ReadCamera(xml, basePath);
|
---|
46 | break;
|
---|
47 |
|
---|
48 | case "Texture":
|
---|
49 | textureImporter.ReadOptions(xml, basePath);
|
---|
50 | break;
|
---|
51 |
|
---|
52 | default:
|
---|
53 | error.WriteLine("Unknown element {0}", xml.LocalName);
|
---|
54 | xml.Skip();
|
---|
55 | break;
|
---|
56 | }
|
---|
57 | }
|
---|
58 |
|
---|
59 | info.WriteLine("Reading rooms...");
|
---|
60 |
|
---|
61 | roomScenes = new List<Dae.Scene>();
|
---|
62 |
|
---|
63 | xml.ReadEndElement();
|
---|
64 | xml.ReadStartElement("Rooms");
|
---|
65 |
|
---|
66 | while (xml.IsStartElement("Import"))
|
---|
67 | {
|
---|
68 | string filePath = xml.GetAttribute("Path");
|
---|
69 |
|
---|
70 | if (filePath == null)
|
---|
71 | filePath = xml.ReadElementContentAsString();
|
---|
72 | else
|
---|
73 | xml.Skip();
|
---|
74 |
|
---|
75 | filePath = Path.Combine(basePath, filePath);
|
---|
76 |
|
---|
77 | roomScenes.Add(LoadScene(filePath));
|
---|
78 | }
|
---|
79 |
|
---|
80 | xml.ReadEndElement();
|
---|
81 |
|
---|
82 | if (xml.IsStartElement("Textures"))
|
---|
83 | ReadTextures(xml, basePath);
|
---|
84 |
|
---|
85 | xml.ReadEndElement();
|
---|
86 | }
|
---|
87 |
|
---|
88 | private class NodePropertiesReader
|
---|
89 | {
|
---|
90 | private readonly string basePath;
|
---|
91 | private readonly TextWriter error;
|
---|
92 | public readonly Dictionary<string, AkiraDaeNodeProperties> properties = new Dictionary<string, AkiraDaeNodeProperties>(StringComparer.Ordinal);
|
---|
93 |
|
---|
94 | public NodePropertiesReader(string basePath, TextWriter error)
|
---|
95 | {
|
---|
96 | this.basePath = basePath;
|
---|
97 | this.error = error;
|
---|
98 | }
|
---|
99 |
|
---|
100 | public Dictionary<string, AkiraDaeNodeProperties> Properties
|
---|
101 | {
|
---|
102 | get { return properties; }
|
---|
103 | }
|
---|
104 |
|
---|
105 | public void ReadScene(XmlReader xml, Dae.Node scene)
|
---|
106 | {
|
---|
107 | var nodeProperties = new ObjectDaeNodeProperties();
|
---|
108 | properties.Add(scene.Id, nodeProperties);
|
---|
109 |
|
---|
110 | while (xml.IsStartElement())
|
---|
111 | {
|
---|
112 | switch (xml.LocalName)
|
---|
113 | {
|
---|
114 | case "GunkFlags":
|
---|
115 | nodeProperties.GunkFlags = xml.ReadElementContentAsEnum<GunkFlags>();
|
---|
116 | break;
|
---|
117 | case "ScriptId":
|
---|
118 | nodeProperties.ScriptId = xml.ReadElementContentAsInt();
|
---|
119 | break;
|
---|
120 | case "Node":
|
---|
121 | ReadNode(xml, scene, nodeProperties);
|
---|
122 | break;
|
---|
123 | default:
|
---|
124 | xml.Skip();
|
---|
125 | break;
|
---|
126 | }
|
---|
127 | }
|
---|
128 | }
|
---|
129 |
|
---|
130 | private void ReadNode(XmlReader xml, Dae.Node parentNode, ObjectDaeNodeProperties parentNodeProperties)
|
---|
131 | {
|
---|
132 | string id = xml.GetAttribute("Id");
|
---|
133 |
|
---|
134 | if (string.IsNullOrEmpty(id))
|
---|
135 | {
|
---|
136 | error.Write("Each import node must have an Id attribute");
|
---|
137 | xml.Skip();
|
---|
138 | return;
|
---|
139 | }
|
---|
140 |
|
---|
141 | var nodeProperties = new ObjectDaeNodeProperties {
|
---|
142 | GunkFlags = parentNodeProperties.GunkFlags,
|
---|
143 | ScriptId = parentNodeProperties.ScriptId,
|
---|
144 | HasPhysics = parentNodeProperties.HasPhysics
|
---|
145 | };
|
---|
146 |
|
---|
147 | properties.Add(id, nodeProperties);
|
---|
148 |
|
---|
149 | xml.ReadStartElement("Node");
|
---|
150 |
|
---|
151 | while (xml.IsStartElement())
|
---|
152 | {
|
---|
153 | switch (xml.LocalName)
|
---|
154 | {
|
---|
155 | case "GunkFlags":
|
---|
156 | nodeProperties.GunkFlags |= xml.ReadElementContentAsEnum<GunkFlags>();
|
---|
157 | break;
|
---|
158 | case "ScriptId":
|
---|
159 | nodeProperties.ScriptId = xml.ReadElementContentAsInt();
|
---|
160 | break;
|
---|
161 | case "Physics":
|
---|
162 | nodeProperties.PhysicsType = xml.ReadElementContentAsEnum<ObjectPhysicsType>();
|
---|
163 | nodeProperties.HasPhysics = true;
|
---|
164 | break;
|
---|
165 | case "ObjectFlags":
|
---|
166 | nodeProperties.ObjectFlags = xml.ReadElementContentAsEnum<ObjectSetupFlags>();
|
---|
167 | nodeProperties.HasPhysics = true;
|
---|
168 | break;
|
---|
169 | case "Animation":
|
---|
170 | nodeProperties.Animations.Add(ReadAnimationClip(xml));
|
---|
171 | nodeProperties.HasPhysics = true;
|
---|
172 | break;
|
---|
173 | case "Particles":
|
---|
174 | nodeProperties.Particles.AddRange(ReadParticles(xml, basePath));
|
---|
175 | nodeProperties.HasPhysics = true;
|
---|
176 | break;
|
---|
177 | default:
|
---|
178 | error.WriteLine("Unknown physics object element {0}", xml.LocalName);
|
---|
179 | xml.Skip();
|
---|
180 | break;
|
---|
181 | }
|
---|
182 | }
|
---|
183 |
|
---|
184 | xml.ReadEndElement();
|
---|
185 | }
|
---|
186 |
|
---|
187 | private ObjectAnimationClip ReadAnimationClip(XmlReader xml)
|
---|
188 | {
|
---|
189 | var animClip = new ObjectAnimationClip(xml.GetAttribute("Name"));
|
---|
190 |
|
---|
191 | if (!xml.SkipEmpty())
|
---|
192 | {
|
---|
193 | xml.ReadStartElement();
|
---|
194 |
|
---|
195 | while (xml.IsStartElement())
|
---|
196 | {
|
---|
197 | switch (xml.LocalName)
|
---|
198 | {
|
---|
199 | case "Start":
|
---|
200 | animClip.Start = xml.ReadElementContentAsInt();
|
---|
201 | break;
|
---|
202 | case "Stop":
|
---|
203 | animClip.Stop = xml.ReadElementContentAsInt();
|
---|
204 | break;
|
---|
205 | case "End":
|
---|
206 | animClip.End = xml.ReadElementContentAsInt();
|
---|
207 | break;
|
---|
208 | case "Flags":
|
---|
209 | animClip.Flags = xml.ReadElementContentAsEnum<ObjectAnimationFlags>();
|
---|
210 | break;
|
---|
211 | default:
|
---|
212 | error.WriteLine("Unknown object animation property {0}", xml.LocalName);
|
---|
213 | xml.Skip();
|
---|
214 | break;
|
---|
215 | }
|
---|
216 | }
|
---|
217 |
|
---|
218 | xml.ReadEndElement();
|
---|
219 | }
|
---|
220 |
|
---|
221 | return animClip;
|
---|
222 | }
|
---|
223 | }
|
---|
224 |
|
---|
225 | private void ImportModelScene(XmlReader xml, string basePath)
|
---|
226 | {
|
---|
227 | var filePath = Path.Combine(basePath, xml.GetAttribute("Path"));
|
---|
228 | var scene = LoadScene(filePath);
|
---|
229 |
|
---|
230 | var propertiesReader = new NodePropertiesReader(basePath, error);
|
---|
231 |
|
---|
232 | if (!xml.SkipEmpty())
|
---|
233 | {
|
---|
234 | xml.ReadStartElement();
|
---|
235 | propertiesReader.ReadScene(xml, scene);
|
---|
236 | xml.ReadEndElement();
|
---|
237 | }
|
---|
238 |
|
---|
239 | daeReader.ReadScene(scene, propertiesReader.Properties);
|
---|
240 |
|
---|
241 | if (propertiesReader.Properties.Values.Any(p => p.HasPhysics))
|
---|
242 | {
|
---|
243 | var imp = new ObjectDaeImporter(textureImporter, propertiesReader.Properties);
|
---|
244 |
|
---|
245 | imp.Import(scene);
|
---|
246 |
|
---|
247 | foreach (var node in imp.Nodes.Where(n => n.Geometries.Length > 0))
|
---|
248 | {
|
---|
249 | var setup = new ObjectSetup {
|
---|
250 | Name = node.Name,
|
---|
251 | FileName = node.FileName,
|
---|
252 | ScriptId = node.ScriptId,
|
---|
253 | Flags = node.Flags,
|
---|
254 | PhysicsType = ObjectPhysicsType.Animated
|
---|
255 | };
|
---|
256 |
|
---|
257 | setup.Geometries = node.Geometries
|
---|
258 | .Where(n => (n.Flags & GunkFlags.Invisible) == 0)
|
---|
259 | .Select(n => n.Geometry.Name).ToArray();
|
---|
260 |
|
---|
261 | foreach (var nodeGeometry in node.Geometries.Where(g => (g.Flags & GunkFlags.Invisible) == 0))
|
---|
262 | {
|
---|
263 | var writer = new DatWriter();
|
---|
264 | GeometryDatWriter.Write(nodeGeometry.Geometry, writer.ImporterFile);
|
---|
265 | writer.Write(outputDirPath);
|
---|
266 | }
|
---|
267 |
|
---|
268 | setup.Position = Vector3.Zero;
|
---|
269 | setup.Orientation = Quaternion.Identity;
|
---|
270 | setup.Scale = 1.0f;
|
---|
271 | setup.Origin = Matrix.CreateFromQuaternion(setup.Orientation)
|
---|
272 | * Matrix.CreateScale(setup.Scale)
|
---|
273 | * Matrix.CreateTranslation(setup.Position);
|
---|
274 |
|
---|
275 | //int i = 0;
|
---|
276 |
|
---|
277 | foreach (var animation in node.Animations)
|
---|
278 | {
|
---|
279 | //if (nodes.Count > 1)
|
---|
280 | // animation.Name += i.ToString("d2", CultureInfo.InvariantCulture);
|
---|
281 |
|
---|
282 | if ((animation.Flags & ObjectAnimationFlags.Local) == 0)
|
---|
283 | {
|
---|
284 | //animation.Scale = Matrix.CreateScale(setup.Scale);
|
---|
285 |
|
---|
286 | foreach (var key in animation.Keys)
|
---|
287 | {
|
---|
288 | key.Rotation = setup.Orientation * key.Rotation;
|
---|
289 | key.Translation += setup.Position;
|
---|
290 | }
|
---|
291 | }
|
---|
292 |
|
---|
293 | if ((animation.Flags & ObjectAnimationFlags.AutoStart) != 0)
|
---|
294 | {
|
---|
295 | setup.Animation = animation;
|
---|
296 | setup.PhysicsType = ObjectPhysicsType.Animated;
|
---|
297 | }
|
---|
298 |
|
---|
299 | var writer = new DatWriter();
|
---|
300 | writer.BeginImport();
|
---|
301 | ObjectDatWriter.WriteAnimation(animation, writer);
|
---|
302 | writer.Write(outputDirPath);
|
---|
303 | }
|
---|
304 |
|
---|
305 | if (setup.Animation == null && node.Animations.Length > 0)
|
---|
306 | {
|
---|
307 | setup.Animation = node.Animations[0];
|
---|
308 | }
|
---|
309 |
|
---|
310 | if (setup.Animation != null)
|
---|
311 | {
|
---|
312 | var frame0 = setup.Animation.Keys[0];
|
---|
313 |
|
---|
314 | setup.Scale = frame0.Scale.X;
|
---|
315 | setup.Orientation = frame0.Rotation;
|
---|
316 | setup.Position = frame0.Translation;
|
---|
317 | }
|
---|
318 |
|
---|
319 | level.physics.Add(setup);
|
---|
320 | }
|
---|
321 | }
|
---|
322 | }
|
---|
323 |
|
---|
324 | private void ImportModel(string basePath)
|
---|
325 | {
|
---|
326 | info.WriteLine("Importing objects...");
|
---|
327 | ImportGunkObjects();
|
---|
328 |
|
---|
329 | info.WriteLine("Importing textures...");
|
---|
330 | ImportModelTextures();
|
---|
331 |
|
---|
332 | info.WriteLine("Generating grids...");
|
---|
333 |
|
---|
334 | string gridFilePath = Path.Combine(basePath, string.Format("temp/grids/{0}_grids.dae", level.name));
|
---|
335 |
|
---|
336 | var gridBuilder = new RoomGridBuilder(roomScenes[0], model);
|
---|
337 | gridBuilder.Build();
|
---|
338 | AkiraDaeWriter.WriteRooms(gridBuilder.Mesh, gridFilePath);
|
---|
339 |
|
---|
340 | daeReader.ReadScene(Dae.Reader.ReadFile(gridFilePath), new Dictionary<string, AkiraDaeNodeProperties>());
|
---|
341 |
|
---|
342 | info.WriteLine("Writing environment...");
|
---|
343 |
|
---|
344 | var writer = new DatWriter();
|
---|
345 | AkiraDatWriter.Write(model, writer, level.name, debug);
|
---|
346 | writer.Write(outputDirPath);
|
---|
347 | }
|
---|
348 |
|
---|
349 | private void ImportGunkNode(int gunkId, Matrix transform, GunkFlags flags, Geometry geometry)
|
---|
350 | {
|
---|
351 | ImportGunk(gunkId, transform, flags, geometry, null);
|
---|
352 | }
|
---|
353 |
|
---|
354 | private void ImportGunk(int gunkId, Matrix transform, GunkFlags flags, Geometry geometry, string textureName)
|
---|
355 | {
|
---|
356 | TextureFormat? textureFormat = null;
|
---|
357 |
|
---|
358 | if (geometry.Texture != null)
|
---|
359 | {
|
---|
360 | Texture texture = null;
|
---|
361 |
|
---|
362 | if (!geometry.Texture.IsPlaceholder)
|
---|
363 | {
|
---|
364 | texture = TextureDatReader.ReadInfo(geometry.Texture);
|
---|
365 | }
|
---|
366 | else
|
---|
367 | {
|
---|
368 | var txmp = FindSharedInstance(TemplateTag.TXMP, geometry.Texture.Name);
|
---|
369 |
|
---|
370 | if (txmp != null)
|
---|
371 | texture = TextureDatReader.ReadInfo(txmp);
|
---|
372 | }
|
---|
373 |
|
---|
374 | if (texture != null)
|
---|
375 | textureFormat = texture.Format;
|
---|
376 | }
|
---|
377 | else
|
---|
378 | {
|
---|
379 | if (geometry.TextureName != null)
|
---|
380 | {
|
---|
381 | var options = textureImporter.GetOptions(geometry.TextureName, false);
|
---|
382 |
|
---|
383 | if (options != null)
|
---|
384 | textureFormat = options.Format;
|
---|
385 | }
|
---|
386 | }
|
---|
387 |
|
---|
388 | switch (textureFormat)
|
---|
389 | {
|
---|
390 | case TextureFormat.BGRA4444:
|
---|
391 | case TextureFormat.BGRA5551:
|
---|
392 | case TextureFormat.RGBA:
|
---|
393 | flags |= GunkFlags.Transparent | GunkFlags.TwoSided | GunkFlags.NoOcclusion;
|
---|
394 | break;
|
---|
395 | }
|
---|
396 |
|
---|
397 | Material material;
|
---|
398 |
|
---|
399 | if (!string.IsNullOrEmpty(textureName))
|
---|
400 | material = model.Materials.GetMaterial(textureName);
|
---|
401 | else if (!string.IsNullOrEmpty(geometry.TextureName))
|
---|
402 | material = model.Materials.GetMaterial(geometry.TextureName);
|
---|
403 | else if (geometry.Texture != null)
|
---|
404 | material = model.Materials.GetMaterial(geometry.Texture.Name);
|
---|
405 | else
|
---|
406 | material = model.Materials.GetMaterial("NONE");
|
---|
407 |
|
---|
408 | int pointIndexBase = model.Points.Count;
|
---|
409 | int texCoordIndexBase = model.TexCoords.Count;
|
---|
410 |
|
---|
411 | model.Points.AddRange(Vector3.Transform(geometry.Points, ref transform));
|
---|
412 | model.TexCoords.AddRange(geometry.TexCoords);
|
---|
413 |
|
---|
414 | foreach (var quad in Quadify.Do(geometry))
|
---|
415 | {
|
---|
416 | var pointIndices = new int[quad.Length];
|
---|
417 | var texCoordIndices = new int[quad.Length];
|
---|
418 | var colors = new Imaging.Color[quad.Length];
|
---|
419 |
|
---|
420 | for (int j = 0; j < quad.Length; j++)
|
---|
421 | {
|
---|
422 | pointIndices[j] = pointIndexBase + quad[j];
|
---|
423 | texCoordIndices[j] = texCoordIndexBase + quad[j];
|
---|
424 | colors[j] = new Imaging.Color(207, 207, 207);
|
---|
425 | }
|
---|
426 |
|
---|
427 | var poly = new Polygon(model, pointIndices) {
|
---|
428 | TexCoordIndices = texCoordIndices,
|
---|
429 | Colors = colors,
|
---|
430 | Material = material,
|
---|
431 | ObjectId = gunkId & 0xffffff,
|
---|
432 | ObjectType = gunkId >> 24
|
---|
433 | };
|
---|
434 |
|
---|
435 | poly.Flags |= flags;
|
---|
436 | model.Polygons.Add(poly);
|
---|
437 | }
|
---|
438 | }
|
---|
439 |
|
---|
440 | private void ImportModelTextures()
|
---|
441 | {
|
---|
442 | int imported = 0;
|
---|
443 | int copied = 0;
|
---|
444 |
|
---|
445 | var copy = new List<InstanceDescriptor>();
|
---|
446 |
|
---|
447 | foreach (var material in model.Polygons.Select(p => p.Material).Distinct())
|
---|
448 | {
|
---|
449 | //if (material.IsMarker)
|
---|
450 | // continue;
|
---|
451 |
|
---|
452 | if (File.Exists(material.ImageFilePath))
|
---|
453 | {
|
---|
454 | var options = textureImporter.AddMaterial(material);
|
---|
455 |
|
---|
456 | if (options != null)
|
---|
457 | material.Flags |= options.GunkFlags;
|
---|
458 |
|
---|
459 | imported++;
|
---|
460 | }
|
---|
461 | else
|
---|
462 | {
|
---|
463 | var txmp = FindSharedInstance(TemplateTag.TXMP, material.Name);
|
---|
464 |
|
---|
465 | if (txmp != null)
|
---|
466 | copy.Add(txmp);
|
---|
467 | }
|
---|
468 | }
|
---|
469 |
|
---|
470 | Parallel.ForEach(copy, txmp => {
|
---|
471 | var texture = TextureDatReader.Read(txmp);
|
---|
472 |
|
---|
473 | if ((texture.Flags & TextureFlags.HasMipMaps) == 0)
|
---|
474 | {
|
---|
475 | texture.GenerateMipMaps();
|
---|
476 | TextureDatWriter.Write(texture, outputDirPath);
|
---|
477 | System.Threading.Interlocked.Increment(ref imported);
|
---|
478 | }
|
---|
479 | else
|
---|
480 | {
|
---|
481 | string sourceFilePath = txmp.File.FilePath;
|
---|
482 | File.Copy(sourceFilePath, Path.Combine(outputDirPath, Path.GetFileName(sourceFilePath)), true);
|
---|
483 | System.Threading.Interlocked.Increment(ref copied);
|
---|
484 | }
|
---|
485 | });
|
---|
486 |
|
---|
487 | error.WriteLine("Imported {0} textures, copied {1} textures", imported, copied);
|
---|
488 | }
|
---|
489 |
|
---|
490 | private ObjectAnimationClip ReadAnimationClip(XmlReader xml)
|
---|
491 | {
|
---|
492 | var anim = new ObjectAnimationClip(xml.GetAttribute("Name"));
|
---|
493 |
|
---|
494 | if (!xml.SkipEmpty())
|
---|
495 | {
|
---|
496 | xml.ReadStartElement();
|
---|
497 |
|
---|
498 | while (xml.IsStartElement())
|
---|
499 | {
|
---|
500 | switch (xml.LocalName)
|
---|
501 | {
|
---|
502 | case "Start":
|
---|
503 | anim.Start = xml.ReadElementContentAsInt();
|
---|
504 | break;
|
---|
505 | case "Stop":
|
---|
506 | anim.Stop = xml.ReadElementContentAsInt();
|
---|
507 | break;
|
---|
508 | case "End":
|
---|
509 | anim.End = xml.ReadElementContentAsInt();
|
---|
510 | break;
|
---|
511 | case "Flags":
|
---|
512 | anim.Flags = xml.ReadElementContentAsEnum<ObjectAnimationFlags>();
|
---|
513 | break;
|
---|
514 | default:
|
---|
515 | error.WriteLine("Unknown object animation parameter {0}", xml.LocalName);
|
---|
516 | xml.Skip();
|
---|
517 | break;
|
---|
518 | }
|
---|
519 | }
|
---|
520 |
|
---|
521 | xml.ReadEndElement();
|
---|
522 | }
|
---|
523 |
|
---|
524 | return anim;
|
---|
525 | }
|
---|
526 | }
|
---|
527 | }
|
---|