source: OniSplit/Level/ModelImporter.cs

Last change on this file was 1114, checked in by iritscen, 5 years ago

Adding OniSplit source code (v0.9.99.0). Many thanks to Neo for all his work over the years.

File size: 19.2 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Xml;
5
6namespace 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}
Note: See TracBrowser for help on using the repository browser.