source: OniSplit/Dae/IO/DaeReader.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: 57.3 KB
RevLine 
[1114]1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Globalization;
5using System.Xml;
6using Oni.Xml;
7
8namespace Oni.Dae.IO
9{
10 internal class DaeReader
11 {
12 public static string[] CommandLineArgs;
13
14 #region Private data
15 private static readonly string[] emptyStrings = new string[0];
16 private static readonly char[] whiteSpaceChars = new char[] { ' ', '\t' };
17 private static readonly Func<string, int> intConverter = XmlConvert.ToInt32;
18 private static readonly Func<string, float> floatConverter = XmlConvert.ToSingle;
19 private TextWriter error;
20 private TextWriter info;
21 private Scene mainScene;
22 private Dictionary<string, Entity> entities;
23 private XmlReader xml;
24 private Axis upAxis = Axis.Y;
25 private float unit = 1.0f;
26 private List<Action> delayedBindActions;
27 private Uri baseUrl;
28 private string fileName;
29 private List<Scene> scenes = new List<Scene>();
30 private List<Light> lights = new List<Light>();
31 private List<Animation> animations = new List<Animation>();
32 private List<Geometry> geometries = new List<Geometry>();
33 private List<Effect> effects = new List<Effect>();
34 private List<Material> materials = new List<Material>();
35 private List<Image> images = new List<Image>();
36 private List<Camera> cameras = new List<Camera>();
37 #endregion
38
39 #region private class Animation
40
41 private class Animation : Entity
42 {
43 private List<Animation> animations;
44 private readonly List<Sampler> samplers = new List<Sampler>();
45
46 public List<Animation> Animations
47 {
48 get
49 {
50 if (animations == null)
51 animations = new List<Animation>();
52
53 return animations;
54 }
55 }
56
57 public List<Sampler> Samplers => samplers;
58 }
59
60 #endregion
61
62 public static Scene ReadFile(string filePath)
63 {
64 var reader = new DaeReader {
65 baseUrl = new Uri("file://" + Path.GetDirectoryName(filePath).Replace('\\', '/').TrimEnd('/') + "/"),
66 fileName = Path.GetFileName(filePath),
67 delayedBindActions = new List<Action>(),
68 error = Console.Error,
69 info = Console.Out
70 };
71
72 var settings = new XmlReaderSettings {
73 IgnoreWhitespace = true,
74 IgnoreProcessingInstructions = true,
75 IgnoreComments = true
76 };
77
78 using (reader.xml = XmlReader.Create(filePath, settings))
79 reader.ReadRoot();
80
81 return reader.mainScene;
82 }
83
84 private void ReadRoot()
85 {
86 while (xml.NodeType != XmlNodeType.Element)
87 xml.Read();
88
89 if (xml.LocalName != "COLLADA")
90 throw new InvalidDataException(string.Format("Unknown root element {0} found", xml.LocalName));
91
92 string version = xml.GetAttribute("version");
93
94 if (version != "1.4.0" && version != "1.4.1")
95 throw new NotSupportedException(string.Format("Unsupported Collada file version {0}", version));
96
97 if (!xml.IsEmptyElement)
98 {
99 xml.ReadStartElement();
100
101 ReadAsset();
102 ReadContent();
103 ReadExtra();
104 }
105
106 foreach (var action in delayedBindActions)
107 action();
108
109 if (mainScene == null && scenes.Count > 0)
110 mainScene = scenes[0];
111
112 BindNodes(mainScene);
113
114 if (upAxis != Axis.Y)
115 AxisConverter.Convert(mainScene, upAxis, Axis.Y);
116
117 float scale = 1.0f;
118
119 if (CommandLineArgs != null)
120 {
121 string scaleArg = Array.Find(CommandLineArgs, x => x.StartsWith("-dae-scale:", StringComparison.Ordinal));
122
123 if (scaleArg != null)
124 scale = float.Parse(scaleArg.Substring(11), CultureInfo.InvariantCulture);
125 }
126
127 if (unit != 0.1f || scale != 1.0f)
128 UnitConverter.Convert(mainScene, 10.0f * unit * scale);
129 }
130
131 private void ReadContent()
132 {
133 while (xml.IsStartElement())
134 {
135 switch (xml.LocalName)
136 {
137 case "library_cameras":
138 ReadLibrary(cameras, "camera", ReadCamera);
139 break;
140
141 case "library_images":
142 ReadLibrary(images, "image", ReadImage);
143 break;
144
145 case "library_effects":
146 ReadLibrary(effects, "effect", ReadEffect);
147 break;
148
149 case "library_materials":
150 ReadLibrary(materials, "material", ReadMaterial);
151 break;
152
153 case "library_geometries":
154 ReadLibrary(geometries, "geometry", ReadGeometry);
155 break;
156
157 case "library_nodes":
158 ReadLibrary(scenes, "node", ReadNode);
159 break;
160
161 case "library_visual_scenes":
162 ReadLibrary(scenes, "visual_scene", ReadScene);
163 break;
164
165 case "library_animations":
166 ReadLibrary(animations, "animation", ReadAnimation);
167 break;
168
169 case "library_lights":
170 ReadLibrary(lights, "light", ReadLight);
171 break;
172
173 case "scene":
174 ReadScene();
175 break;
176
177 default:
178 xml.Skip();
179 break;
180 }
181 }
182 }
183
184 //
185 // Libraries
186 //
187
188 private void ReadLibrary<T>(ICollection<T> library, string elementName, Action<T> entityReader)
189 where T : Entity, new()
190 {
191 if (xml.SkipEmpty())
192 return;
193
194 xml.ReadStartElement();
195 ReadAsset();
196
197 while (xml.IsStartElement(elementName))
198 ReadEntity<T>(library, entityReader);
199
200 ReadExtra();
201 xml.ReadEndElement();
202 }
203
204 private void ReadEntity<T>(ICollection<T> entityCollection, Action<T> entityReader)
205 where T : Entity, new()
206 {
207 T entity = ReadEntity<T>(entityReader);
208 entityCollection.Add(entity);
209 }
210
211 private T ReadEntity<T>(Action<T> entityReader)
212 where T : Entity, new()
213 {
214 string id = xml.GetAttribute("id");
215
216 var entity = new T {
217 Name = xml.GetAttribute("name"),
218 FileName = fileName
219 };
220
221 AddEntity(id, entity);
222
223 if (string.IsNullOrEmpty(entity.Name))
224 entity.Name = id;
225
226 if (xml.IsEmptyElement)
227 {
228 xml.ReadStartElement();
229 return entity;
230 }
231
232 xml.ReadStartElement();
233 ReadAsset();
234
235 entityReader(entity);
236
237 ReadExtra();
238 xml.ReadEndElement();
239
240 return entity;
241 }
242
243 //
244 // Cameras
245 //
246
247 private void ReadCamera(Camera camera)
248 {
249 ReadAsset();
250
251 xml.ReadStartElement("optics");
252 xml.ReadStartElement("technique_common");
253
254 if (xml.IsStartElement("perspective"))
255 ReadCameraParameters(camera, CameraType.Perspective);
256 else if (xml.IsStartElement("orthographic"))
257 ReadCameraParameters(camera, CameraType.Orthographic);
258 else if (xml.IsStartElement())
259 xml.Skip();
260
261 xml.ReadEndElement();
262
263 while (xml.IsStartElement())
264 xml.Skip();
265
266 xml.ReadEndElement();
267
268 if (xml.IsStartElement("imager"))
269 xml.Skip();
270
271 ReadExtra();
272 }
273
274 private void ReadCameraParameters(Camera camera, CameraType type)
275 {
276 xml.ReadStartElement();
277
278 camera.Type = type;
279
280 while (xml.IsStartElement())
281 {
282 switch (xml.LocalName)
283 {
284 case "xfov":
285 camera.XFov = xml.ReadElementContentAsFloat();
286 break;
287 case "yfov":
288 camera.YFov = xml.ReadElementContentAsFloat();
289 break;
290 case "xmag":
291 camera.XMag = xml.ReadElementContentAsFloat();
292 break;
293 case "ymag":
294 camera.YMag = xml.ReadElementContentAsFloat();
295 break;
296 case "aspect_ratio":
297 camera.AspectRatio = xml.ReadElementContentAsFloat();
298 break;
299 case "znear":
300 camera.ZNear = xml.ReadElementContentAsFloat();
301 break;
302 case "zfar":
303 camera.ZFar = xml.ReadElementContentAsFloat();
304 break;
305
306 default:
307 xml.Skip();
308 break;
309 }
310 }
311
312 xml.ReadEndElement();
313 }
314
315 //
316 // Materials
317 //
318
319 private void ReadImage(Image image)
320 {
321 //image.Width = ReadNullableIntAttribute("width");
322 //image.Height = ReadNullableIntAttribute("height");
323 //image.Depth = ReadNullableIntAttribute("depth");
324
325 ReadAsset();
326
327 if (xml.IsStartElement("init_from"))
328 {
329 string filePath = xml.ReadElementContentAsString();
330
331 if (!string.IsNullOrEmpty(filePath))
332 {
333 var imageUri = new Uri(baseUrl, filePath);
334 image.FilePath = imageUri.LocalPath;
335 }
336 }
337 else if (xml.IsStartElement("data"))
338 {
339 throw new NotSupportedException("Embedded image data is not supported");
340 }
341 else
342 {
343 throw new InvalidDataException();
344 }
345
346 ReadExtra();
347 }
348
349 private void ReadEffect(Effect effect)
350 {
351 while (xml.IsStartElement())
352 {
353 switch (xml.LocalName)
354 {
355 case "image":
356 ReadEntity(images, ReadImage);
357 break;
358 case "newparam":
359 ReadEffectParameterDecl(effect);
360 break;
361 case "profile_COMMON":
362 ReadEffectProfileCommon(effect);
363 break;
364 default:
365 xml.Skip();
366 break;
367 }
368 }
369
370 ReadExtra();
371 }
372
373 private void ReadEffectProfileCommon(Effect effect)
374 {
375 xml.ReadStartElement();
376 ReadAsset();
377
378 while (xml.IsStartElement())
379 {
380 switch (xml.LocalName)
381 {
382 case "image":
383 ReadEntity(images, ReadImage);
384 break;
385 case "newparam":
386 ReadEffectParameterDecl(effect);
387 break;
388 case "technique":
389 ReadEffectTechniqueCommon(effect);
390 break;
391 default:
392 xml.Skip();
393 break;
394 }
395 }
396
397 ReadExtra();
398 xml.ReadEndElement();
399 }
400
401 private void ReadEffectTechniqueCommon(Effect effect)
402 {
403 xml.ReadStartElement();
404 ReadAsset();
405
406 while (xml.IsStartElement())
407 {
408 switch (xml.LocalName)
409 {
410 case "image":
411 ReadEntity(images, ReadImage);
412 break;
413
414 case "constant":
415 case "lambert":
416 case "phong":
417 case "blinn":
418 xml.ReadStartElement();
419 ReadEffectTechniqueParameters(effect);
420 xml.ReadEndElement();
421 break;
422
423 default:
424 xml.Skip();
425 break;
426 }
427 }
428
429 ReadExtra();
430 xml.ReadEndElement();
431 }
432
433 private void ReadEffectParameterDecl(Effect effect)
434 {
435 var parameter = new EffectParameter();
436 parameter.Sid = xml.GetAttribute("sid");
437
438 xml.ReadStartElement();
439
440 while (xml.IsStartElement())
441 {
442 switch (xml.LocalName)
443 {
444 case "semantic":
445 parameter.Semantic = xml.ReadElementContentAsString();
446 break;
447 case "float":
448 parameter.Value = xml.ReadElementContentAsFloat();
449 break;
450 case "float2":
451 parameter.Value = xml.ReadElementContentAsVector2();
452 break;
453 case "float3":
454 parameter.Value = xml.ReadElementContentAsVector3();
455 break;
456 case "surface":
457 parameter.Value = ReadEffectSurface(effect);
458 break;
459 case "sampler2D":
460 parameter.Value = ReadEffectSampler2D(effect);
461 break;
462 default:
463 xml.Skip();
464 break;
465 }
466 }
467
468 xml.ReadEndElement();
469
470 effect.Parameters.Add(parameter);
471 }
472
473 private EffectSurface ReadEffectSurface(Effect effect)
474 {
475 var surface = new EffectSurface();
476
477 xml.ReadStartElement();
478
479 while (xml.IsStartElement())
480 {
481 switch (xml.LocalName)
482 {
483 case "init_from":
484 BindId<Image>(xml.ReadElementContentAsString(), image => {
485 surface.InitFrom = image;
486 });
487 break;
488
489 default:
490 xml.Skip();
491 break;
492 }
493 }
494
495 xml.ReadEndElement();
496 return surface;
497 }
498
499 private EffectSampler ReadEffectSampler2D(Effect effect)
500 {
501 var sampler = new EffectSampler();
502
503 xml.ReadStartElement();
504
505 while (xml.IsStartElement())
506 {
507 switch (xml.LocalName)
508 {
509 case "source":
510 string surfaceSid = xml.ReadElementContentAsString();
511 foreach (var parameter in effect.Parameters)
512 {
513 if (parameter.Sid == surfaceSid)
514 sampler.Surface = parameter.Value as EffectSurface;
515 }
516 break;
517 case "wrap_s":
518 sampler.WrapS = (EffectSamplerWrap)Enum.Parse(typeof(EffectSamplerWrap), xml.ReadElementContentAsString(), true);
519 break;
520 case "wrap_t":
521 sampler.WrapT = (EffectSamplerWrap)Enum.Parse(typeof(EffectSamplerWrap), xml.ReadElementContentAsString(), true);
522 break;
523 default:
524 xml.Skip();
525 break;
526 }
527 }
528
529 xml.ReadEndElement();
530 return sampler;
531 }
532
533 private void ReadEffectTechniqueParameters(Effect effect)
534 {
535 while (xml.IsStartElement())
536 {
537 switch (xml.LocalName)
538 {
539 case "emission":
540 ReadColorEffectParameter(effect, effect.Emission, EffectTextureChannel.Emission);
541 break;
542 case "ambient":
543 ReadColorEffectParameter(effect, effect.Ambient, EffectTextureChannel.Ambient);
544 break;
545 case "diffuse":
546 ReadColorEffectParameter(effect, effect.Diffuse, EffectTextureChannel.Diffuse);
547 break;
548 case "specular":
549 ReadColorEffectParameter(effect, effect.Specular, EffectTextureChannel.Specular);
550 break;
551 case "shininess":
552 ReadFloatEffectParameter(effect.Shininess);
553 break;
554 case "reflective":
555 ReadColorEffectParameter(effect, effect.Reflective, EffectTextureChannel.Reflective);
556 break;
557 case "reflectivity":
558 ReadFloatEffectParameter(effect.Reflectivity);
559 break;
560 case "transparent":
561 ReadColorEffectParameter(effect, effect.Transparent, EffectTextureChannel.Transparent);
562 break;
563 case "transparency":
564 ReadFloatEffectParameter(effect.Transparency);
565 break;
566 case "index_of_refraction":
567 ReadFloatEffectParameter(effect.IndexOfRefraction);
568 break;
569 default:
570 xml.Skip();
571 break;
572 }
573 }
574 }
575
576 private void ReadFloatEffectParameter(EffectParameter parameter)
577 {
578 xml.ReadStartElement();
579
580 if (xml.IsStartElement("float"))
581 {
582 parameter.Sid = xml.GetAttribute("sid");
583 parameter.Value = xml.ReadElementContentAsFloat();
584 }
585 else if (xml.IsStartElement("param"))
586 {
587 parameter.Reference = xml.GetAttribute("ref");
588 xml.Skip();
589 }
590
591 xml.ReadEndElement();
592 }
593
594 private void ReadColorEffectParameter(Effect effect, EffectParameter parameter, EffectTextureChannel channel)
595 {
596 xml.ReadStartElement();
597
598 if (xml.IsStartElement("color"))
599 {
600 parameter.Sid = xml.GetAttribute("sid");
601 parameter.Value = xml.ReadElementContentAsVector4();
602 }
603 else if (xml.IsStartElement("param"))
604 {
605 parameter.Sid = null;
606 parameter.Reference = xml.GetAttribute("ref");
607 }
608 else if (xml.IsStartElement("texture"))
609 {
610 parameter.Sid = null;
611
612 string texCoordSymbol = xml.GetAttribute("texcoord");
613 string samplerId = xml.GetAttribute("texture");
614 xml.Skip();
615
616 //
617 // HACK: Maya produces duplicate texture elements, skip them...
618 //
619
620 while (xml.IsStartElement("texture"))
621 xml.Skip();
622
623 EffectSampler sampler = null;
624
625 foreach (var parameterDecl in effect.Parameters)
626 {
627 if (parameterDecl.Sid == samplerId)
628 {
629 sampler = parameterDecl.Value as EffectSampler;
630 break;
631 }
632 }
633
634 if (sampler == null)
635 {
636 info.WriteLine("COLLADA: cannot find sampler {0} in effect {1}, trying to use image directly", samplerId, effect.Name);
637
638 var surface = new EffectSurface();
639 sampler = new EffectSampler(surface);
640
641 BindId<Image>(samplerId, image => {
642 surface.InitFrom = image;
643 });
644 }
645
646 var texture = new EffectTexture {
647 Channel = channel,
648 TexCoordSemantic = texCoordSymbol,
649 Sampler = sampler
650 };
651
652 parameter.Value = texture;
653 }
654
655 xml.ReadEndElement();
656 }
657
658 private void ReadMaterial(Material material)
659 {
660 if (!xml.IsStartElement("instance_effect"))
661 return;
662
663 BindUrlAttribute<Effect>("url", effect => {
664 material.Effect = effect;
665 });
666
667 xml.Skip();
668 }
669
670 //
671 // Geometry
672 //
673
674 private void ReadGeometry(Geometry geometry)
675 {
676 if (xml.IsStartElement("mesh"))
677 {
678 ReadMesh(geometry);
679 }
680 else
681 {
682 throw new NotSupportedException(string.Format("Geometry content of type {0} is not supported", xml.LocalName));
683 }
684 }
685
686 //
687 // Mesh
688 //
689
690 private void ReadMesh(Geometry geometry)
691 {
692 xml.ReadStartElement();
693
694 while (xml.IsStartElement("source"))
695 ReadGeometrySource();
696
697 if (xml.IsStartElement("vertices"))
698 ReadMeshVertices(geometry);
699
700 while (xml.IsStartElement())
701 {
702 var primitives = ReadMeshPrimitives(geometry);
703
704 if (primitives == null)
705 break;
706
707 geometry.Primitives.Add(primitives);
708 }
709
710 ReadExtra();
711 xml.ReadEndElement();
712 }
713
714 //
715 // Mesh Data Source
716 //
717
718 private Source ReadGeometrySource()
719 {
720 string id = xml.GetAttribute("id");
721 string name = xml.GetAttribute("name");
722
723 xml.ReadStartElement();
724 ReadAsset();
725
726 var data = ReadFloatArray();
727 var source = new Source(data, 1) {
728 Name = name
729 };
730
731 if (xml.IsStartElement("technique_common"))
732 {
733 xml.ReadStartElement();
734
735 if (xml.IsStartElement("accessor"))
736 {
737 source.Stride = ReadIntAttribute("stride", 1);
738
739 xml.ReadStartElement();
740
741 while (xml.IsStartElement("param"))
742 ReadParam();
743
744 xml.ReadEndElement();
745 }
746
747 xml.ReadEndElement();
748 }
749
750 xml.SkipSequence("technique");
751 xml.ReadEndElement();
752
753 AddEntity(id, source);
754
755 return source;
756 }
757
758 private string ReadParam()
759 {
760 string name = xml.GetAttribute("name");
761 //string sid = xml.GetAttribute("sid");
762 //string type = xml.GetAttribute("type");
763 //string semantic = xml.GetAttribute("semantic");
764
765 xml.Skip();
766
767 return name;
768 }
769
770 //
771 // Mesh Vertices
772 //
773
774 private void ReadMeshVertices(Geometry mesh)
775 {
776 string id = xml.GetAttribute("id");
777
778 for (xml.ReadStartElement(); xml.IsStartElement("input"); xml.Skip())
779 {
780 var semantic = ReadSemanticAttribute();
781
782 if (semantic != Semantic.None && semantic != Semantic.Vertex)
783 {
784 var input = new Input();
785 input.Semantic = semantic;
786 BindUrlAttribute<Source>("source", s => {
787 input.Source = s;
788 });
789 mesh.Vertices.Add(input);
790 }
791 }
792
793 ReadExtra();
794 xml.ReadEndElement();
795 }
796
797 //
798 // Mesh Polygons
799 //
800
801 private MeshPrimitives ReadMeshPrimitives(Geometry mesh)
802 {
803 MeshPrimitives primitives;
804
805 int primitiveCount = ReadIntAttribute("count", 0);
806 int fixedVertexCount = 0;
807 bool isPolygons = false;
808
809 switch (xml.LocalName)
810 {
811 case "lines":
812 primitives = new MeshPrimitives(MeshPrimitiveType.Lines);
813 fixedVertexCount = 2;
814 break;
815 case "triangles":
816 primitives = new MeshPrimitives(MeshPrimitiveType.Polygons);
817 fixedVertexCount = 3;
818 break;
819
820 case "linestrips":
821 primitives = new MeshPrimitives(MeshPrimitiveType.LineStrips);
822 isPolygons = true;
823 break;
824 case "trifans":
825 primitives = new MeshPrimitives(MeshPrimitiveType.TriangleFans);
826 isPolygons = true;
827 break;
828 case "tristrips":
829 primitives = new MeshPrimitives(MeshPrimitiveType.TriangleStrips);
830 isPolygons = true;
831 break;
832 case "polygons":
833 primitives = new MeshPrimitives(MeshPrimitiveType.Polygons);
834 isPolygons = true;
835 break;
836
837 case "polylist":
838 primitives = new MeshPrimitives(MeshPrimitiveType.Polygons);
839 break;
840
841 default:
842 return null;
843 }
844
845 primitives.MaterialSymbol = xml.GetAttribute("material");
846
847 bool vertexFound = false;
848
849 //
850 // Read the inputs
851 //
852
853 for (xml.ReadStartElement(); xml.IsStartElement("input"); xml.Skip())
854 {
855 var semantic = ReadSemanticAttribute();
856
857 if (semantic == Semantic.None)
858 continue;
859
860 int offset = ReadIntAttribute("offset");
861 string sourceId = xml.GetAttribute("source");
862 int set = ReadIntAttribute("set", -1);
863
864 if (semantic == Semantic.Vertex)
865 {
866 if (vertexFound)
867 {
868 error.WriteLine("Duplicate vertex input found");
869 continue;
870 }
871
872 vertexFound = true;
873
874 foreach (var vertexInput in mesh.Vertices)
875 {
876 primitives.Inputs.Add(new IndexedInput {
877 Source = vertexInput.Source,
878 Offset = offset,
879 Set = set,
880 Semantic = vertexInput.Semantic
881 });
882 }
883 }
884 else
885 {
886 var input = new IndexedInput {
887 Offset = offset,
888 Semantic = semantic,
889 Set = set
890 };
891
892 BindUrl<Source>(sourceId, s => {
893
894 //
895 // Ignore inputs with no source data
896 //
897
898 if (s.Count > 0)
899 {
900 input.Source = s;
901 primitives.Inputs.Add(input);
902 }
903 });
904 }
905 }
906
907 if (!vertexFound)
908 throw new InvalidDataException("no vertex input");
909
910 //
911 // Read vertex counts (if availabled and needed)
912 //
913
914 if (primitiveCount > 0)
915 primitives.VertexCounts.Capacity = primitiveCount;
916
917 int numIndices = 0;
918
919 while (xml.IsStartElement("vcount"))
920 {
921 if (fixedVertexCount != 0 || isPolygons)
922 {
923 xml.Skip();
924 continue;
925 }
926
927 foreach (var token in xml.ReadElementContentAsList())
928 {
929 int count = XmlConvert.ToInt32(token);
930 numIndices += count;
931 primitives.VertexCounts.Add(count);
932 }
933 }
934
935 if (fixedVertexCount != 0)
936 {
937 for (int i = 0; i < primitiveCount; i++)
938 primitives.VertexCounts.Add(fixedVertexCount);
939
940 numIndices = fixedVertexCount * primitiveCount;
941 }
942 else if (!isPolygons)
943 {
944 if (primitives.VertexCounts.Count == 0)
945 throw new InvalidDataException("no vcount");
946 }
947
948 //
949 // Read input indices
950 // 1. Collect all inputs in an array indexed by input offset
951 //
952
953 var maxOffset = primitives.Inputs.Max(x => x.Offset);
954 var inputIndices = new List<int>[maxOffset + 1];
955
956 foreach (var input in primitives.Inputs)
957 {
958 List<int> indices = inputIndices[input.Offset];
959
960 if (indices == null)
961 {
962 indices = new List<int>(numIndices);
963 inputIndices[input.Offset] = indices;
964 }
965 }
966
967 //
968 // 2. Read polygon input indices
969 //
970
971 if (!isPolygons)
972 {
973 while (xml.IsStartElement("p"))
974 ReadInterleavedInputIndices(inputIndices);
975 }
976 else
977 {
978 while (xml.IsStartElement())
979 {
980 if (xml.IsStartElement("p"))
981 {
982 primitives.VertexCounts.Add(ReadInterleavedInputIndices(inputIndices));
983 }
984 else if (xml.IsStartElement("ph"))
985 {
986 xml.ReadStartElement();
987
988 while (xml.IsStartElement())
989 {
990 if (xml.LocalName == "p")
991 primitives.VertexCounts.Add(ReadInterleavedInputIndices(inputIndices));
992 else
993 xml.Skip();
994 }
995
996 xml.ReadEndElement();
997 }
998 else
999 {
1000 break;
1001 }
1002 }
1003 }
1004
1005 foreach (var input in primitives.Inputs)
1006 input.Indices.AddRange(inputIndices[input.Offset]);
1007
1008 ReadExtra();
1009 xml.ReadEndElement();
1010
1011 return primitives;
1012 }
1013
1014 private int ReadInterleavedInputIndices(List<int>[] inputs)
1015 {
1016 int count = 0;
1017 int offset = 0;
1018
1019 foreach (string token in xml.ReadElementContentAsList())
1020 {
1021 var input = inputs[offset++];
1022
1023 if (input != null)
1024 input.Add(XmlConvert.ToInt32(token));
1025
1026 if (offset >= inputs.Length)
1027 {
1028 offset = 0;
1029 count++;
1030 }
1031 }
1032
1033 return count;
1034 }
1035
1036 //
1037 // Scene
1038 //
1039
1040 private void ReadScene(Scene scene)
1041 {
1042 while (xml.IsStartElement("node"))
1043 ReadEntity(scene.Nodes, ReadNode);
1044 }
1045
1046 private void ReadNode(Node node)
1047 {
1048 ReadTransforms(node.Transforms);
1049
1050 while (xml.IsStartElement())
1051 {
1052 switch (xml.LocalName)
1053 {
1054 case "node":
1055 ReadEntity(node.Nodes, ReadNode);
1056 break;
1057
1058 case "instance_geometry":
1059 node.Instances.Add(ReadGeometryInstance());
1060 break;
1061
1062 case "instance_light":
1063 node.Instances.Add(ReadLightInstance());
1064 break;
1065
1066 case "instance_camera":
1067 node.Instances.Add(ReadCameraInstance());
1068 break;
1069
1070 case "instance_node":
1071 node.Instances.Add(ReadNodeInstance());
1072 break;
1073
1074 default:
1075 xml.Skip();
1076 break;
1077 }
1078 }
1079 }
1080
1081 private void ReadSimpleInstance<T>(Instance<T> instance)
1082 where T : Entity
1083 {
1084 instance.Sid = xml.GetAttribute("sid");
1085 instance.Name = xml.GetAttribute("name");
1086 BindUrlAttribute<T>("url", camera => {
1087 instance.Target = camera;
1088 });
1089
1090 xml.Skip();
1091 }
1092
1093 private NodeInstance ReadNodeInstance()
1094 {
1095 var instance = new NodeInstance();
1096 ReadSimpleInstance(instance);
1097 return instance;
1098 }
1099
1100 private CameraInstance ReadCameraInstance()
1101 {
1102 var instance = new CameraInstance();
1103 ReadSimpleInstance(instance);
1104 return instance;
1105 }
1106
1107 private LightInstance ReadLightInstance()
1108 {
1109 var instance = new LightInstance();
1110 ReadSimpleInstance(instance);
1111 return instance;
1112 }
1113
1114 //
1115 // Node Transforms
1116 //
1117
1118 private void ReadTransforms(ICollection<Transform> transforms)
1119 {
1120 while (xml.IsStartElement())
1121 {
1122 Transform transform = null;
1123
1124 switch (xml.LocalName)
1125 {
1126 case "matrix":
1127 transform = new TransformMatrix();
1128 break;
1129 case "rotate":
1130 transform = new TransformRotate();
1131 break;
1132 case "scale":
1133 transform = new TransformScale();
1134 break;
1135 case "translate":
1136 transform = new TransformTranslate();
1137 break;
1138
1139 case "skew":
1140 case "lookat":
1141 xml.Skip();
1142 break;
1143
1144 default:
1145 return;
1146 }
1147
1148 if (transform != null)
1149 {
1150 transform.Sid = xml.GetAttribute("sid");
1151 xml.ReadElementContentAsArray(floatConverter, transform.Values);
1152 transforms.Add(transform);
1153 }
1154 }
1155 }
1156
1157 //
1158 // Instances
1159 //
1160
1161 private void ReadInstances(ICollection<Instance> instances)
1162 {
1163 while (xml.IsStartElement())
1164 {
1165 switch (xml.LocalName)
1166 {
1167 case "instance_geometry":
1168 instances.Add(ReadGeometryInstance());
1169 break;
1170
1171 case "instance_camera":
1172 case "instance_controller":
1173 case "instance_light":
1174 case "instance_node":
1175 xml.Skip();
1176 break;
1177
1178 default:
1179 return;
1180 }
1181 }
1182 }
1183
1184 private GeometryInstance ReadGeometryInstance()
1185 {
1186 var instance = new GeometryInstance {
1187 Name = xml.GetAttribute("name"),
1188 Sid = xml.GetAttribute("sid"),
1189 };
1190
1191 string url = xml.GetAttribute("url");
1192 BindUrl<Geometry>(url, geometry => {
1193 instance.Target = geometry;
1194 });
1195
1196 if (!xml.SkipEmpty())
1197 {
1198 xml.ReadStartElement();
1199
1200 if (xml.IsStartElement("bind_material"))
1201 ReadBindMaterial(instance, url);
1202
1203 ReadExtra();
1204
1205 xml.ReadEndElement();
1206 }
1207
1208 return instance;
1209 }
1210
1211 private void ReadBindMaterial(GeometryInstance geometryInstance, string geometryUrl)
1212 {
1213 xml.ReadStartElement();
1214
1215 while (xml.IsStartElement())
1216 {
1217 if (xml.LocalName != "technique_common")
1218 {
1219 xml.Skip();
1220 continue;
1221 }
1222
1223 xml.ReadStartElement();
1224
1225 while (xml.IsStartElement())
1226 {
1227 if (xml.LocalName == "instance_material")
1228 ReadMaterialInstance(geometryInstance, geometryUrl);
1229 else
1230 xml.Skip();
1231 }
1232
1233 xml.ReadEndElement();
1234 }
1235
1236 xml.ReadEndElement();
1237 }
1238
1239 private void ReadMaterialInstance(GeometryInstance geometryInstance, string geometryUrl)
1240 {
1241 var instance = new MaterialInstance();
1242 instance.Symbol = xml.GetAttribute("symbol");
1243 BindUrlAttribute<Material>("target", material => {
1244 instance.Target = material;
1245 });
1246 geometryInstance.Materials.Add(instance);
1247
1248 if (xml.SkipEmpty())
1249 return;
1250
1251 for (xml.ReadStartElement(); xml.IsStartElement(); xml.Skip())
1252 {
1253 if (xml.LocalName == "bind")
1254 {
1255 var binding = new MaterialBinding();
1256 binding.Semantic = xml.GetAttribute("semantic");
1257 string target = xml.GetAttribute("target");
1258
1259 BindId<Source>(target, s => {
1260 BindUrl<Geometry>(geometryUrl, g => {
1261 var primitives = g.Primitives.Find(p => p.MaterialSymbol == instance.Symbol);
1262
1263 if (primitives == null)
1264 return;
1265
1266 var input = primitives.Inputs.Find(i => i.Source == s);
1267
1268 if (input == null)
1269 return;
1270
1271 binding.VertexInput = input;
1272 instance.Bindings.Add(binding);
1273 });
1274 });
1275 }
1276 else if (xml.LocalName == "bind_vertex_input")
1277 {
1278 var binding = new MaterialBinding();
1279 binding.Semantic = xml.GetAttribute("semantic");
1280 var inputSemantic = ReadSemanticAttribute("input_semantic");
1281 int inputSet = ReadIntAttribute("input_set", 0);
1282
1283 BindUrl<Geometry>(geometryUrl, g => {
1284 var primitives = g.Primitives.Find(p => p.MaterialSymbol == instance.Symbol);
1285
1286 if (primitives == null)
1287 return;
1288
1289 var input = primitives.Inputs.Find(i => i.Semantic == inputSemantic && i.Set == inputSet);
1290
1291 if (input == null)
1292 return;
1293
1294 binding.VertexInput = input;
1295 instance.Bindings.Add(binding);
1296 });
1297 }
1298 }
1299
1300 xml.ReadEndElement();
1301 }
1302
1303 //
1304 // Animations
1305 //
1306
1307 private void ReadAnimation(Animation animation)
1308 {
1309 while (xml.IsStartElement("animation"))
1310 ReadEntity(animation.Animations, ReadAnimation);
1311
1312 while (xml.IsStartElement("source"))
1313 ReadAnimationSource();
1314
1315 while (xml.IsStartElement("sampler"))
1316 animation.Samplers.Add(ReadAnimationSampler());
1317
1318 while (xml.IsStartElement("channel"))
1319 {
1320 BindAnimationSampler(xml.GetAttribute("source"), xml.GetAttribute("target"));
1321 xml.Skip();
1322 }
1323 }
1324
1325 private Source ReadAnimationSource()
1326 {
1327 string id = xml.GetAttribute("id");
1328 string name = xml.GetAttribute("name");
1329
1330 if (string.IsNullOrEmpty(name))
1331 name = id;
1332
1333 xml.ReadStartElement();
1334
1335 ReadAsset();
1336
1337 Source source;
1338
1339 if (xml.IsStartElement("float_array"))
1340 source = new Source(ReadFloatArray(), 1);
1341 else if (xml.IsStartElement("Name_array"))
1342 source = new Source(ReadNameArray(), 1);
1343 else
1344 throw new NotSupportedException(string.Format("Animation sources of type {0} are not supported", xml.LocalName));
1345
1346 source.Name = name;
1347
1348 if (xml.IsStartElement("technique_common"))
1349 {
1350 xml.ReadStartElement();
1351
1352 if (xml.IsStartElement("accessor"))
1353 {
1354 source.Stride = ReadIntAttribute("stride", 1);
1355
1356 xml.ReadStartElement();
1357
1358 while (xml.IsStartElement("param"))
1359 ReadParam();
1360
1361 xml.ReadEndElement();
1362 }
1363
1364 xml.ReadEndElement();
1365 }
1366
1367 xml.SkipSequence("technique");
1368 xml.ReadEndElement();
1369
1370 AddEntity(id, source);
1371
1372 return source;
1373 }
1374
1375 private Input ReadAnimationInput()
1376 {
1377 var input = new Input();
1378 input.Semantic = ReadSemanticAttribute();
1379 BindUrlAttribute<Source>("source", source => {
1380 input.Source = source;
1381 });
1382
1383 xml.Skip();
1384
1385 return input;
1386 }
1387
1388 private Sampler ReadAnimationSampler()
1389 {
1390 string id = xml.GetAttribute("id");
1391
1392 xml.ReadStartElement();
1393
1394 var sampler = new Sampler();
1395
1396 while (xml.IsStartElement())
1397 {
1398 switch (xml.LocalName)
1399 {
1400 case "input":
1401 sampler.Inputs.Add(ReadAnimationInput());
1402 break;
1403
1404 default:
1405 xml.Skip();
1406 break;
1407 }
1408 }
1409
1410 xml.ReadEndElement();
1411
1412 AddEntity(id, sampler);
1413
1414 return sampler;
1415 }
1416
1417 #region private class TargetPath
1418
1419 private class TargetPath
1420 {
1421 private string nodeId;
1422 private string[] path;
1423 private string value;
1424
1425 private TargetPath()
1426 {
1427 }
1428
1429 public static TargetPath Parse(string text)
1430 {
1431 TargetPath path = new TargetPath();
1432 List<string> sids = new List<string>();
1433
1434 int index = text.IndexOf('/');
1435
1436 if (index == -1)
1437 index = text.Length;
1438
1439 path.nodeId = text.Substring(0, index);
1440
1441 for (int start = index + 1; start < text.Length; start = index + 1)
1442 {
1443 index = text.IndexOf('/', start);
1444
1445 if (index == -1)
1446 {
1447 index = text.IndexOf('.', start);
1448
1449 if (index == -1)
1450 {
1451 sids.Add(text.Substring(start));
1452 }
1453 else
1454 {
1455 sids.Add(text.Substring(start, index - start));
1456 path.value = text.Substring(index + 1);
1457 }
1458
1459 break;
1460 }
1461
1462 sids.Add(text.Substring(start, index - start));
1463 }
1464
1465 if (sids.Count > 0)
1466 path.path = sids.ToArray();
1467
1468 return path;
1469 }
1470
1471 public string NodeId => nodeId;
1472 public string[] Path => path;
1473 public string Value => value;
1474 }
1475
1476 #endregion
1477
1478 //
1479 // Lights
1480 //
1481
1482 private void ReadLight(Light light)
1483 {
1484 if (!xml.IsStartElement("technique_common"))
1485 {
1486 xml.Skip();
1487 return;
1488 }
1489
1490 xml.ReadStartElement();
1491
1492 if (xml.IsStartElement())
1493 {
1494 switch (xml.LocalName)
1495 {
1496 case "ambient":
1497 light.Type = LightType.Ambient;
1498 break;
1499
1500 case "directional":
1501 light.Type = LightType.Directional;
1502 break;
1503
1504 case "point":
1505 light.Type = LightType.Point;
1506 break;
1507
1508 case "spot":
1509 light.Type = LightType.Spot;
1510 break;
1511 }
1512
1513 xml.ReadStartElement();
1514
1515 light.Color = xml.ReadElementContentAsVector3("color");
1516
1517 if (light.Type == LightType.Point || light.Type == LightType.Spot)
1518 {
1519 if (xml.LocalName == "constant_attenuation")
1520 light.ConstantAttenuation = xml.ReadElementContentAsFloat();
1521
1522 if (xml.LocalName == "linear_attenuation")
1523 light.LinearAttenuation = xml.ReadElementContentAsFloat();
1524
1525 if (light.Type == LightType.Point)
1526 {
1527 light.QuadraticAttenuation = xml.ReadElementContentAsFloat("quadratic_attenuation", string.Empty);
1528
1529 if (xml.LocalName == "zfar")
1530 light.ZFar = xml.ReadElementContentAsFloat();
1531 }
1532 else if (light.Type == LightType.Spot)
1533 {
1534 if (xml.LocalName == "quadratic_attenuation")
1535 light.QuadraticAttenuation = xml.ReadElementContentAsFloat();
1536
1537 if (xml.LocalName == "falloff_angle")
1538 light.FalloffAngle = xml.ReadElementContentAsFloat();
1539
1540 if (xml.LocalName == "falloff_exponent")
1541 light.FalloffExponent = xml.ReadElementContentAsFloat();
1542 }
1543 }
1544
1545 xml.ReadEndElement();
1546 }
1547
1548 xml.ReadEndElement();
1549 }
1550
1551 //
1552 // Scene
1553 //
1554
1555 private void ReadScene()
1556 {
1557 if (!xml.IsStartElement("scene"))
1558 return;
1559
1560 xml.ReadStartElement();
1561 xml.SkipSequence("instance_physics_scene");
1562
1563 if (xml.IsStartElement("instance_visual_scene"))
1564 {
1565 BindUrlAttribute<Scene>("url", scene => {
1566 mainScene = scene;
1567 });
1568 xml.Skip();
1569 }
1570
1571 ReadExtra();
1572 xml.ReadEndElement();
1573 }
1574
1575 //
1576 // Others
1577 //
1578
1579 private void ReadAsset()
1580 {
1581 if (!xml.IsStartElement("asset"))
1582 return;
1583
1584 xml.ReadStartElement();
1585
1586 while (xml.IsStartElement())
1587 {
1588 switch (xml.LocalName)
1589 {
1590 case "up_axis":
1591 upAxis = ReadUpAxis();
1592 break;
1593 case "contributor":
1594 ReadAssetContributor();
1595 break;
1596 case "unit":
1597 unit = XmlConvert.ToSingle(xml.GetAttribute("meter"));
1598 xml.Skip();
1599 break;
1600 default:
1601 xml.Skip();
1602 break;
1603 }
1604 }
1605
1606 xml.ReadEndElement();
1607 }
1608
1609 private void ReadAssetContributor()
1610 {
1611 xml.ReadStartElement();
1612
1613 while (xml.IsStartElement())
1614 {
1615 switch (xml.LocalName)
1616 {
1617 default:
1618 xml.Skip();
1619 break;
1620 }
1621 }
1622
1623 xml.ReadEndElement();
1624 }
1625
1626 private void ReadExtra()
1627 {
1628 while (xml.IsStartElement("extra"))
1629 xml.Skip();
1630 }
1631
1632 //
1633 // Arrays
1634 //
1635
1636 private float[] ReadFloatArray()
1637 {
1638 if (!xml.IsStartElement("float_array"))
1639 return null;
1640
1641 var id = xml.GetAttribute("id");
1642 var count = ReadIntAttribute("count");
1643 var data = new float[count];
1644 var index = 0;
1645
1646 foreach (var token in xml.ReadElementContentAsList())
1647 {
1648 if (index < data.Length)
1649 data[index++] = XmlConvert.ToSingle(token);
1650 }
1651
1652 return data;
1653 }
1654
1655 private string[] ReadNameArray()
1656 {
1657 if (!xml.IsStartElement("Name_array"))
1658 return null;
1659
1660 var id = xml.GetAttribute("id");
1661 var count = ReadIntAttribute("count");
1662 var data = new string[count];
1663 var index = 0;
1664
1665 foreach (var token in xml.ReadElementContentAsList())
1666 {
1667 if (index < data.Length)
1668 data[index++] = token;
1669 }
1670
1671 return data;
1672 }
1673
1674 //
1675 // Attributes
1676 //
1677
1678 private int ReadIntAttribute(string name)
1679 {
1680 string text = xml.GetAttribute(name);
1681
1682 if (string.IsNullOrEmpty(text))
1683 throw new InvalidDataException(name + " attribute not found");
1684
1685 return XmlConvert.ToInt32(text);
1686 }
1687
1688 private int ReadIntAttribute(string name, int defaultValue)
1689 {
1690 string text = xml.GetAttribute(name);
1691
1692 if (string.IsNullOrEmpty(text))
1693 return defaultValue;
1694
1695 return XmlConvert.ToInt32(text);
1696 }
1697
1698 private int? ReadNullableIntAttribute(string name)
1699 {
1700 string text = xml.GetAttribute(name);
1701
1702 if (string.IsNullOrEmpty(text))
1703 return null;
1704
1705 return XmlConvert.ToInt32(text);
1706 }
1707
1708 private Semantic ReadSemanticAttribute()
1709 {
1710 return ReadSemanticAttribute("semantic");
1711 }
1712
1713 private Semantic ReadSemanticAttribute(string name)
1714 {
1715 string text = xml.GetAttribute(name);
1716
1717 switch (text)
1718 {
1719 case "POSITION":
1720 return Semantic.Position;
1721 case "TEXCOORD":
1722 return Semantic.TexCoord;
1723 case "NORMAL":
1724 return Semantic.Normal;
1725 case "COLOR":
1726 return Semantic.Color;
1727 case "VERTEX":
1728 return Semantic.Vertex;
1729
1730 case "INPUT":
1731 return Semantic.Input;
1732 case "IN_TANGENT":
1733 return Semantic.InTangent;
1734 case "OUT_TANGENT":
1735 return Semantic.OutTangent;
1736 case "INTERPOLATION":
1737 return Semantic.Interpolation;
1738 case "OUTPUT":
1739 return Semantic.Output;
1740
1741 default:
1742 return Semantic.None;
1743 }
1744 }
1745
1746 private string[] ReadStringListAttribute(string name)
1747 {
1748 string text = xml.GetAttribute(name);
1749
1750 if (string.IsNullOrEmpty(text))
1751 return emptyStrings;
1752
1753 return text.Split(whiteSpaceChars, StringSplitOptions.RemoveEmptyEntries);
1754 }
1755
1756 private Axis ReadUpAxis()
1757 {
1758 switch (xml.ReadElementContentAsString())
1759 {
1760 case "X_UP":
1761 return Axis.X;
1762 case "Z_UP":
1763 return Axis.Z;
1764 default:
1765 return Axis.Y;
1766 }
1767 }
1768
1769 private void AddEntity(string id, Entity entity)
1770 {
1771 if (string.IsNullOrEmpty(id))
1772 return;
1773
1774 if (entities == null)
1775 entities = new Dictionary<string, Entity>();
1776
1777 if (entities.ContainsKey(id))
1778 error.WriteLine(string.Format("COLLADA error: duplicate id {0}", id));
1779 else
1780 entities.Add(id, entity);
1781
1782 entity.Id = id;
1783 }
1784
1785 private T GetEntity<T>(string id) where T : Entity
1786 {
1787 Entity result;
1788
1789 if (entities == null
1790 || string.IsNullOrEmpty(id)
1791 || !entities.TryGetValue(id, out result))
1792 return null;
1793
1794 return result as T;
1795 }
1796
1797 private void BindUrlAttribute<T>(string name, Action<T> action)
1798 where T : Entity
1799 {
1800 BindUrl(xml.GetAttribute(name), action);
1801 }
1802
1803 private void BindUrl<T>(string url, Action<T> action)
1804 where T : Entity
1805 {
1806 if (string.IsNullOrEmpty(url))
1807 return;
1808
1809 if (url[0] != '#')
1810 throw new NotSupportedException(string.Format("External reference '{0}' is not supported", url));
1811
1812 BindId<T>(url.Substring(1), action);
1813 }
1814
1815 private void BindId<T>(string id, Action<T> action)
1816 where T : Entity
1817 {
1818 if (string.IsNullOrEmpty(id))
1819 return;
1820
1821 var entity = GetEntity<T>(id);
1822
1823 if (entity != null)
1824 {
1825 action(entity);
1826 return;
1827 }
1828
1829 delayedBindActions.Add(delegate {
1830 var entity2 = GetEntity<T>(id);
1831
1832 if (entity2 != null)
1833 action(entity2);
1834 });
1835 }
1836
1837 private void BindAnimationSampler(string sourceId, string targetPath)
1838 {
1839 var path = TargetPath.Parse(targetPath);
1840
1841 BindUrlAttribute<Sampler>("source", sampler => {
1842 var output = sampler.Inputs.Find(i => i.Semantic == Semantic.Output);
1843
1844 if (output == null || output.Source == null)
1845 return;
1846
1847 int stride = output.Source.Stride;
1848
1849 if (stride != 1)
1850 {
1851 for (int offset = 0; offset < stride; offset++)
1852 {
1853 var newSampler = sampler.Split(offset);
1854
1855 BindId<Node>(path.NodeId, node => {
1856 Transform transform = FindTransform(node, path.Path[0]);
1857
1858 if (transform != null)
1859 transform.BindAnimation(string.Format(CultureInfo.InvariantCulture, "({0})", offset), newSampler);
1860 });
1861 }
1862 }
1863 else
1864 {
1865 BindId<Node>(path.NodeId, node => {
1866 var transform = FindTransform(node, path.Path[0]);
1867
1868 if (transform != null)
1869 transform.BindAnimation(path.Value, sampler);
1870 });
1871 }
1872 });
1873 }
1874
1875 private Transform FindTransform(Node node, string sid)
1876 {
1877 return node.Transforms.Find(t => t.Sid == sid);
1878 }
1879
1880 private void BindNodes(Node node)
1881 {
1882 foreach (var instance in node.Instances.OfType<NodeInstance>().ToList())
1883 {
1884 node.Instances.Remove(instance);
1885
1886 if (instance.Target != node)
1887 node.Nodes.Add(instance.Target);
1888 }
1889
1890 foreach (var child in node.Nodes)
1891 BindNodes(child);
1892 }
1893 }
1894}
Note: See TracBrowser for help on using the repository browser.