source: OniSplit/Dae/IO/DaeWriter.cs@ 1193

Last change on this file since 1193 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: 38.5 KB
RevLine 
[1114]1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Globalization;
5using System.Text;
6using System.Xml;
7
8namespace Oni.Dae.IO
9{
10 internal class DaeWriter
11 {
12 #region Private data
13 private XmlWriter xml;
14 private Scene mainScene;
15 private WriteVisitor visitor;
16 private Dictionary<Source, string> writtenSources = new Dictionary<Source, string>();
17 #endregion
18
19 #region private class Animation
20
21 private class Animation : Entity
22 {
23 public readonly List<Source> Sources = new List<Source>();
24 public readonly List<Sampler> Samplers = new List<Sampler>();
25 public readonly List<AnimationChannel> Channels = new List<AnimationChannel>();
26 }
27
28 #endregion
29 #region private class AnimationChannel
30
31 private class AnimationChannel
32 {
33 public readonly Sampler Sampler;
34 public readonly string TargetPath;
35
36 public AnimationChannel(Sampler sampler, string targetPath)
37 {
38 this.Sampler = sampler;
39 this.TargetPath = targetPath;
40 }
41 }
42
43 #endregion
44 #region private class AnimationSource
45
46 private class AnimationSource : Source
47 {
48 public readonly string[] Parameters;
49
50 public AnimationSource(float[] data, string[] parameters)
51 : base(data, parameters.Length)
52 {
53 this.Parameters = parameters;
54 }
55
56 public AnimationSource(string[] data, string[] parameters)
57 : base(data, parameters.Length)
58 {
59 this.Parameters = parameters;
60 }
61 }
62
63 #endregion
64
65 #region private class WriteVisitor
66
67 private class WriteVisitor : Visitor
68 {
69 private readonly Dictionary<Entity, string> entities = new Dictionary<Entity, string>();
70 private readonly Dictionary<string, Entity> ids = new Dictionary<string, Entity>(StringComparer.Ordinal);
71 private readonly Dictionary<string, Sampler> samplers = new Dictionary<string, Sampler>(StringComparer.Ordinal);
72 private readonly Dictionary<string, Source> sources = new Dictionary<string, Source>(StringComparer.Ordinal);
73 private int uniqueEntityId = 1;
74
75 public readonly List<Image> Images = new List<Image>();
76 public readonly List<Effect> Effects = new List<Effect>();
77 public readonly List<Material> Materials = new List<Material>();
78 public readonly List<Geometry> Geometries = new List<Geometry>();
79 public readonly List<Scene> Scenes = new List<Scene>();
80 public readonly List<Animation> Animations = new List<Animation>();
81 public readonly List<Camera> Cameras = new List<Camera>();
82
83 public override void VisitScene(Scene scene)
84 {
85 AddEntity(scene);
86
87 base.VisitScene(scene);
88 }
89
90 public override void VisitNode(Node node)
91 {
92 EnsureId(node);
93
94 foreach (var transform in node.Transforms.Where(t => t.HasAnimations))
95 {
96 for (int i = 0; i < transform.Animations.Length; i++)
97 {
98 var sampler = transform.Animations[i];
99
100 if (sampler != null)
101 AddAnimationChannel(sampler, node, transform, i);
102 }
103 }
104
105 base.VisitNode(node);
106 }
107
108 public override void VisitGeometry(Geometry geometry)
109 {
110 AddEntity(geometry);
111
112 string baseId = IdOf(geometry);
113
114 if (baseId.EndsWith("_geometry", StringComparison.Ordinal))
115 baseId = baseId.Substring(0, baseId.Length - "_geometry".Length);
116
117 foreach (var input in geometry.Vertices)
118 EnsureId(input.Source, string.Format(CultureInfo.InvariantCulture, "{0}_{1}", baseId, input.Semantic.ToString().ToLowerInvariant()));
119
120 foreach (var input in geometry.Primitives.SelectMany(p => p.Inputs))
121 EnsureId(input.Source, string.Format(CultureInfo.InvariantCulture, "{0}_{1}", baseId, input.Semantic.ToString().ToLowerInvariant()));
122
123 base.VisitGeometry(geometry);
124 }
125
126 public override void VisitMaterial(Material material)
127 {
128 AddEntity(material);
129
130 base.VisitMaterial(material);
131 }
132
133 public override void VisitEffect(Effect effect)
134 {
135 AddEntity(effect);
136
137 base.VisitEffect(effect);
138 }
139
140 public override void VisitImage(Image image)
141 {
142 AddEntity(image);
143
144 base.VisitImage(image);
145 }
146
147 public override void VisitCamera(Camera camera)
148 {
149 AddEntity(camera);
150
151 base.VisitCamera(camera);
152 }
153
154 private void AddEntity(Scene scene)
155 {
156 AddEntity(scene, Scenes);
157 }
158
159 private void AddEntity(Image image)
160 {
161 AddEntity(image, Images);
162 }
163
164 private void AddEntity(Effect effect)
165 {
166 AddEntity(effect, Effects);
167 }
168
169 private void AddEntity(Material material)
170 {
171 AddEntity(material, Materials);
172 }
173
174 private void AddEntity(Geometry geometry)
175 {
176 AddEntity(geometry, Geometries);
177 }
178
179 private void AddEntity(Animation animation)
180 {
181 AddEntity(animation, Animations);
182 }
183
184 private void AddEntity(Camera camera)
185 {
186 AddEntity(camera, Cameras);
187 }
188
189 private void AddEntity<T>(T entity, ICollection<T> entityCollection)
190 where T : Entity
191 {
192 if (EnsureId(entity))
193 entityCollection.Add(entity);
194 }
195
196 private bool EnsureId(Entity entity)
197 {
198 if (entities.ContainsKey(entity))
199 return false;
200
201 string name = entity.Name;
202 string id;
203
204 if (string.IsNullOrEmpty(name))
205 {
206 do
207 {
208 id = string.Format(CultureInfo.InvariantCulture,
209 "unique_{0}", uniqueEntityId++, entity.GetType().Name.ToLowerInvariant());
210 }
211 while (ids.ContainsKey(id));
212 }
213 else
214 {
215 if (!ids.ContainsKey(name))
216 {
217 id = name;
218 }
219 else
220 {
221 id = string.Format(CultureInfo.InvariantCulture,
222 "{0}_{1}", name, entity.GetType().Name.ToLowerInvariant());
223
224 while (ids.ContainsKey(id))
225 {
226 id = string.Format(CultureInfo.InvariantCulture,
227 "{0}_{1}_{2}", name, uniqueEntityId++, entity.GetType().Name.ToLowerInvariant());
228 }
229 }
230 }
231
232 entities.Add(entity, id);
233 ids.Add(id, entity);
234
235 return true;
236 }
237
238 private bool EnsureId(Entity entity, string id)
239 {
240 if (entities.ContainsKey(entity))
241 return false;
242
243 entities.Add(entity, id);
244 ids.Add(id, entity);
245
246 return true;
247 }
248
249 public string IdOf(Entity entity)
250 {
251 string id;
252 entities.TryGetValue(entity, out id);
253 return id;
254 }
255
256 public string UrlOf(Entity entity)
257 {
258 return string.Format("#{0}", IdOf(entity));
259 }
260
261 private void AddAnimationChannel(Sampler sampler, Node node, Transform transform, int valueIndex)
262 {
263 Animation animation;
264
265 if (Animations.Count == 0)
266 {
267 animation = new Animation();
268 Animations.Add(animation);
269 }
270 else
271 {
272 animation = Animations[0];
273 }
274
275 EnsureId(sampler);
276
277 string nodeId = IdOf(node);
278 string samplerId = IdOf(sampler);
279 string valueName = transform.ValueIndexToValueName(valueIndex);
280 Sampler valueSampler;
281
282 if (!samplers.TryGetValue(samplerId + valueName, out valueSampler))
283 {
284 valueSampler = new Sampler();
285 EnsureId(valueSampler, string.Format("{0}_{1}_{2}", IdOf(node), transform.Sid, valueName));
286 animation.Samplers.Add(valueSampler);
287
288 foreach (var input in sampler.Inputs)
289 {
290 var source = input.Source;
291 EnsureId(source);
292
293 string sourceId = IdOf(source) + (input.Semantic == Semantic.Output ? valueName : "");
294
295 if (!sources.TryGetValue(sourceId, out source))
296 {
297 source = input.Source;
298
299 switch (input.Semantic)
300 {
301 case Semantic.Input:
302 source = new AnimationSource(source.FloatData, new[] { "TIME" });
303 break;
304 case Semantic.Output:
305 source = new AnimationSource(source.FloatData, new[] { valueName });
306 break;
307 case Semantic.Interpolation:
308 source = new AnimationSource(source.NameData, new[] { "INTERPOLATION" });
309 break;
310 case Semantic.OutTangent:
311 case Semantic.InTangent:
312 source = new AnimationSource(source.FloatData, new[] { "X", "Y" });
313 break;
314 default:
315 throw new NotSupportedException(string.Format("Invalid semantic {0} for animation input", input.Semantic));
316 }
317
318 sources.Add(sourceId, source);
319
320 EnsureId(source, string.Format(CultureInfo.InvariantCulture, "{0}_{1}", IdOf(valueSampler), input.Semantic.ToString().ToLowerInvariant()));
321 animation.Sources.Add(source);
322 }
323
324 valueSampler.Inputs.Add(new Input(input.Semantic, source));
325 }
326 }
327
328 animation.Channels.Add(new AnimationChannel(
329 valueSampler,
330 string.Format(CultureInfo.InvariantCulture, "{0}/{1}.{2}", IdOf(node), transform.Sid, valueName)));
331 }
332 }
333
334 #endregion
335
336 public static void WriteFile(string filePath, Scene scene)
337 {
338 var writer = new DaeWriter();
339 writer.visitor = new WriteVisitor();
340 writer.visitor.VisitScene(scene);
341 writer.mainScene = scene;
342
343 var settings = new XmlWriterSettings {
344 CloseOutput = true,
345 ConformanceLevel = ConformanceLevel.Document,
346 Encoding = Encoding.UTF8,
347 Indent = true,
348 IndentChars = "\t"
349 };
350
351 //AxisConverter.Convert(scene, Axis.Y, Axis.Z);
352
353 using (var stream = File.Create(filePath))
354 using (writer.xml = XmlWriter.Create(stream, settings))
355 writer.WriteRoot();
356 }
357
358 private void WriteRoot()
359 {
360 WriteCollada();
361
362 WriteLibrary("library_cameras", visitor.Cameras, WriteCamera);
363 WriteLibrary("library_images", visitor.Images, WriteImage);
364 WriteLibrary("library_effects", visitor.Effects, WriteEffect);
365 WriteLibrary("library_materials", visitor.Materials, WriteMaterial);
366 WriteLibrary("library_geometries", visitor.Geometries, WriteGeometry);
367 WriteLibrary("library_visual_scenes", visitor.Scenes, WriteScene);
368 WriteLibrary("library_animations", visitor.Animations, WriteAnimation);
369
370 WriteScene();
371 }
372
373 private void WriteCollada()
374 {
375 xml.WriteStartDocument();
376 xml.WriteStartElement("COLLADA", "http://www.collada.org/2005/11/COLLADASchema");
377 xml.WriteAttributeString("version", "1.4.0");
378
379 xml.WriteStartElement("asset");
380 xml.WriteStartElement("contributor");
381 //xml.WriteElementString("author", "OniSplit");
382 xml.WriteElementString("authoring_tool", string.Format(CultureInfo.InvariantCulture, "OniSplit v{0}", Utils.Version));
383 xml.WriteEndElement();
384
385 xml.WriteStartElement("unit");
386 xml.WriteAttributeString("meter", "0.1");
387 xml.WriteAttributeString("name", "decimeter");
388 xml.WriteEndElement();
389 xml.WriteElementString("up_axis", "Y_UP");
390 xml.WriteEndElement();
391 }
392
393 private void WriteLibrary<T>(string name, ICollection<T> library, Action<T> entityWriter)
394 {
395 if (library.Count == 0)
396 return;
397
398 xml.WriteStartElement(name);
399
400 foreach (T entity in library)
401 entityWriter(entity);
402
403 xml.WriteEndElement();
404 }
405
406 private void WriteScene()
407 {
408 xml.WriteStartElement("scene");
409 xml.WriteStartElement("instance_visual_scene");
410 xml.WriteAttributeString("url", visitor.UrlOf(mainScene));
411 xml.WriteEndElement();
412 xml.WriteEndElement();
413 }
414
415 private void WriteImage(Image image)
416 {
417 BeginEntity("image", image);
418
419 string imageUrl;
420
421 if (Path.IsPathRooted(image.FilePath))
422 imageUrl = "file:///" + image.FilePath.Replace('\\', '/');
423 else
424 imageUrl = image.FilePath.Replace('\\', '/');
425
426 xml.WriteElementString("init_from", imageUrl);
427
428 EndEntity();
429 }
430
431 private void WriteEffect(Effect effect)
432 {
433 BeginEntity("effect", effect);
434 WriteEffectCommonProfile(effect);
435 EndEntity();
436 }
437
438 private void WriteEffectCommonProfile(Effect effect)
439 {
440 xml.WriteStartElement("profile_COMMON");
441
442 foreach (var parameter in effect.Parameters)
443 WriteEffectParameter(parameter);
444
445 WriteEffectTechnique(effect);
446
447 xml.WriteEndElement();
448 }
449
450 private void WriteEffectParameter(EffectParameter parameter)
451 {
452 xml.WriteStartElement("newparam");
453 xml.WriteAttributeString("sid", parameter.Sid);
454
455 if (!string.IsNullOrEmpty(parameter.Semantic))
456 {
457 xml.WriteStartElement("semantic");
458 xml.WriteString(parameter.Semantic);
459 xml.WriteEndElement();
460 }
461
462 if (parameter.Value is float)
463 {
464 float value = (float)parameter.Value;
465 xml.WriteElementString("float",
466 XmlConvert.ToString(value));
467 }
468 else if (parameter.Value is Vector2)
469 {
470 var value = (Vector2)parameter.Value;
471 xml.WriteElementString("float2", string.Format("{0} {1}",
472 XmlConvert.ToString(value.X), XmlConvert.ToString(value.Y)));
473 }
474 else if (parameter.Value is Vector3)
475 {
476 var value = (Vector3)parameter.Value;
477 xml.WriteElementString("float3", string.Format("{0} {1} {3}",
478 XmlConvert.ToString(value.X), XmlConvert.ToString(value.Y), XmlConvert.ToString(value.Z)));
479 }
480 else if (parameter.Value is EffectSurface)
481 {
482 var surface = (EffectSurface)parameter.Value;
483 xml.WriteStartElement("surface");
484 xml.WriteAttributeString("type", "2D");
485 xml.WriteElementString("init_from", visitor.IdOf(surface.InitFrom));
486 xml.WriteEndElement();
487 }
488 else if (parameter.Value is EffectSampler)
489 {
490 var sampler = (EffectSampler)parameter.Value;
491 xml.WriteStartElement("sampler2D");
492 xml.WriteStartElement("source");
493 xml.WriteString(sampler.Surface.DeclaringParameter.Sid);
494 xml.WriteEndElement();
495
496 if (sampler.MinFilter != EffectSamplerFilter.None)
497 xml.WriteElementString("minfilter", sampler.MinFilter.ToString().ToUpperInvariant());
498
499 if (sampler.MagFilter != EffectSamplerFilter.None)
500 xml.WriteElementString("magfilter", sampler.MagFilter.ToString().ToUpperInvariant());
501
502 if (sampler.MipFilter != EffectSamplerFilter.None)
503 xml.WriteElementString("mipfilter", sampler.MipFilter.ToString().ToUpperInvariant());
504
505 xml.WriteEndElement();
506 }
507
508 xml.WriteEndElement();
509 }
510
511 private void WriteEffectTechnique(Effect effect)
512 {
513 xml.WriteStartElement("technique");
514 xml.WriteStartElement("phong");
515
516 //WriteEffectTechniqueProperty("emission", effect.Emission);
517
518 WriteEffectTechniqueProperty("ambient", effect.Ambient);
519 WriteEffectTechniqueProperty("diffuse", effect.Diffuse);
520 WriteEffectTechniqueProperty("specular", effect.Specular);
521
522 //WriteEffectTechniqueProperty("shininess", effect.Shininess);
523 //WriteEffectTechniqueProperty("reflective", effect.Reflective);
524 //WriteEffectTechniqueProperty("reflectivity", effect.Reflectivity);
525
526 WriteEffectTechniqueProperty("transparent", effect.Transparent);
527
528 //WriteEffectTechniqueProperty("transparency", effect.Transparency);
529 //WriteEffectTechniqueProperty("index_of_refraction", effect.IndexOfRefraction);
530
531 xml.WriteEndElement();
532 xml.WriteEndElement();
533 }
534
535 private void WriteEffectTechniqueProperty(string name, EffectParameter value)
536 {
537 bool isTransparent = name == "transparent";
538
539 if (isTransparent && value.Value == null)
540 return;
541
542 xml.WriteStartElement(name);
543
544 if (isTransparent)
545 xml.WriteAttributeString("opaque", "A_ONE");
546
547 if (value.Reference != null)
548 {
549 xml.WriteStartElement("param");
550 xml.WriteString(value.Reference);
551 xml.WriteEndElement();
552 }
553 else if (value.Value is float)
554 {
555 float flt = (float)value.Value;
556 xml.WriteStartElement("float");
557 xml.WriteAttributeString("sid", value.Sid);
558 xml.WriteString(XmlConvert.ToString(flt));
559 xml.WriteEndElement();
560 }
561 else if (value.Value is Vector4)
562 {
563 var color = (Vector4)value.Value;
564 xml.WriteStartElement("color");
565 xml.WriteAttributeString("sid", value.Sid);
566 xml.WriteString(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}",
567 XmlConvert.ToString(color.X), XmlConvert.ToString(color.Y), XmlConvert.ToString(color.Z), XmlConvert.ToString(color.W)));
568 xml.WriteEndElement();
569 }
570 else if (value.Value is EffectTexture)
571 {
572 var texture = (EffectTexture)value.Value;
573 xml.WriteStartElement("texture");
574 xml.WriteAttributeString("texture", texture.Sampler.Owner.Sid);
575 xml.WriteAttributeString("texcoord", texture.TexCoordSemantic);
576 xml.WriteEndElement();
577 }
578
579 xml.WriteEndElement();
580 }
581
582 private void WriteMaterial(Material matrial)
583 {
584 BeginEntity("material", matrial);
585
586 xml.WriteStartElement("instance_effect");
587 xml.WriteAttributeString("url", visitor.UrlOf(matrial.Effect));
588 xml.WriteEndElement();
589
590 EndEntity();
591 }
592
593 private void WriteGeometry(Geometry geometry)
594 {
595 BeginEntity("geometry", geometry);
596
597 xml.WriteStartElement("mesh");
598
599 WriteGeometrySources(geometry);
600 WriteGeometryVertices(geometry);
601
602 foreach (var primitives in geometry.Primitives)
603 WriteGeometryPrimitives(geometry, primitives);
604
605 xml.WriteEndElement();
606
607 EndEntity();
608 }
609
610 private void WriteGeometrySources(Geometry geometry)
611 {
612 var sources = new Dictionary<Source, List<Semantic>>();
613
614 foreach (var primitives in geometry.Primitives)
615 {
616 foreach (var input in primitives.Inputs)
617 {
618 List<Semantic> uses;
619
620 if (!sources.TryGetValue(input.Source, out uses))
621 {
622 uses = new List<Semantic>();
623 sources.Add(input.Source, uses);
624 }
625
626 if (!uses.Contains(input.Semantic))
627 uses.Add(input.Semantic);
628 }
629 }
630
631 foreach (var pair in sources)
632 {
633 foreach (var semantic in pair.Value)
634 WriteSource(pair.Key, semantic);
635 }
636 }
637
638 private void WriteGeometryVertices(Geometry geometry)
639 {
640 string baseId = visitor.IdOf(geometry);
641
642 if (baseId.EndsWith("_geometry", StringComparison.Ordinal))
643 baseId = baseId.Substring(0, baseId.Length - "_geometry".Length);
644
645 xml.WriteStartElement("vertices");
646 xml.WriteAttributeString("id", baseId + "_vertices");
647
648 foreach (var input in geometry.Vertices)
649 {
650 xml.WriteStartElement("input");
651 WriteSemanticAttribute("semantic", input.Semantic);
652 xml.WriteAttributeString("source", visitor.UrlOf(input.Source));
653 xml.WriteEndElement();
654 }
655
656 xml.WriteEndElement();
657 }
658
659 private void WriteGeometryPrimitives(Geometry geometry, MeshPrimitives primitives)
660 {
661 switch (primitives.PrimitiveType)
662 {
663 case MeshPrimitiveType.Lines:
664 case MeshPrimitiveType.LineStrips:
665 case MeshPrimitiveType.TriangleFans:
666 case MeshPrimitiveType.TriangleStrips:
667 throw new NotSupportedException(string.Format("Writing {0} is not supported", primitives.PrimitiveType));
668 }
669
670 bool trianglesOnly = !primitives.VertexCounts.Exists(x => x != 3);
671
672 if (!trianglesOnly)
673 xml.WriteStartElement("polylist");
674 else
675 xml.WriteStartElement("triangles");
676
677 xml.WriteAttributeString("count", XmlConvert.ToString(primitives.VertexCounts.Count));
678
679 if (!string.IsNullOrEmpty(primitives.MaterialSymbol))
680 xml.WriteAttributeString("material", primitives.MaterialSymbol);
681
682 int offset = 0;
683 bool vertexInputWritten = false;
684
685 var inputs = new List<IndexedInput>();
686
687 string baseUrl = visitor.UrlOf(geometry);
688
689 if (baseUrl.EndsWith("_geometry", StringComparison.Ordinal))
690 baseUrl = baseUrl.Substring(0, baseUrl.Length - "_geometry".Length);
691
692 foreach (var input in primitives.Inputs)
693 {
694 if (geometry.Vertices.Any(x => x.Source == input.Source))
695 {
696 if (!vertexInputWritten)
697 {
698 inputs.Add(input);
699
700 xml.WriteStartElement("input");
701 xml.WriteAttributeString("semantic", "VERTEX");
702 xml.WriteAttributeString("source", baseUrl + "_vertices");
703 xml.WriteAttributeString("offset", XmlConvert.ToString(offset++));
704 xml.WriteEndElement();
705 }
706
707 vertexInputWritten = true;
708 }
709 else
710 {
711 inputs.Add(input);
712
713 xml.WriteStartElement("input");
714 WriteSemanticAttribute("semantic", input.Semantic);
715 xml.WriteAttributeString("source", visitor.UrlOf(input.Source));
716 xml.WriteAttributeString("offset", XmlConvert.ToString(offset++));
717
718 if (input.Set != 0)
719 xml.WriteAttributeString("set", XmlConvert.ToString(input.Set));
720
721 xml.WriteEndElement();
722 }
723 }
724
725 if (!trianglesOnly)
726 {
727 xml.WriteStartElement("vcount");
728 xml.WriteWhitespace("\n");
729
730 int vertexCount = 0;
731 int c = 0;
732
733 foreach (int i in primitives.VertexCounts)
734 {
735 xml.WriteString(XmlConvert.ToString(i) + " ");
736 vertexCount += i;
737
738 c++;
739
740 if (c == 32)
741 {
742 xml.WriteWhitespace("\n");
743 c = 0;
744 }
745 }
746
747 xml.WriteEndElement();
748 }
749
750 xml.WriteStartElement("p");
751 xml.WriteWhitespace("\n");
752
753 int polygonStartIndex = 0;
754
755 foreach (int vertexCount in primitives.VertexCounts)
756 {
757 for (int index = 0; index < vertexCount; index++)
758 {
759 foreach (var input in inputs)
760 {
761 xml.WriteString(XmlConvert.ToString(input.Indices[polygonStartIndex + index]));
762
763 if (input != inputs.Last() || index != vertexCount - 1)
764 xml.WriteWhitespace(" ");
765 }
766 }
767
768 xml.WriteWhitespace("\n");
769 polygonStartIndex += vertexCount;
770 }
771
772 xml.WriteEndElement();
773
774 xml.WriteEndElement();
775 }
776
777 private void WriteScene(Scene scene)
778 {
779 BeginEntity("visual_scene", scene);
780
781 foreach (var node in scene.Nodes)
782 WriteSceneNode(node);
783
784 EndEntity();
785 }
786
787 private void WriteSceneNode(Node node)
788 {
789 BeginEntity("node", node);
790
791 foreach (var transform in node.Transforms)
792 WriteNodeTransform(transform);
793
794 foreach (var instance in node.Instances)
795 {
796 if (instance is GeometryInstance)
797 WriteGeometryInstance((GeometryInstance)instance);
798 else if (instance is CameraInstance)
799 WriteCameraInstance((CameraInstance)instance);
800 }
801
802 foreach (var child in node.Nodes)
803 WriteSceneNode(child);
804
805 EndEntity();
806 }
807
808 private void WriteNodeTransform(Transform transform)
809 {
810 string type;
811
812 if (transform is TransformTranslate)
813 type = "translate";
814 else if (transform is TransformRotate)
815 type = "rotate";
816 else if (transform is TransformScale)
817 type = "scale";
818 else
819 type = "matrix";
820
821 xml.WriteStartElement(type);
822
823 if (!string.IsNullOrEmpty(transform.Sid))
824 xml.WriteAttributeString("sid", transform.Sid);
825
826 var values = new StringBuilder(transform.Values.Length * 16);
827
828 foreach (float value in transform.Values)
829 values.AppendFormat(CultureInfo.InvariantCulture, "{0:f6} ", value);
830
831 if (values.Length > 0)
832 values.Length--;
833
834 xml.WriteValue(values.ToString());
835
836 xml.WriteEndElement();
837 }
838
839 private void WriteCameraInstance(CameraInstance instance)
840 {
841 xml.WriteStartElement("instance_camera");
842 xml.WriteAttributeString("url", visitor.UrlOf(instance.Target));
843 xml.WriteEndElement();
844 }
845
846 private void WriteGeometryInstance(GeometryInstance instance)
847 {
848 xml.WriteStartElement("instance_geometry");
849 xml.WriteAttributeString("url", visitor.UrlOf(instance.Target));
850
851 if (instance.Materials.Count > 0)
852 {
853 xml.WriteStartElement("bind_material");
854 xml.WriteStartElement("technique_common");
855
856 foreach (var matInstance in instance.Materials)
857 WriteMaterialInstance(matInstance);
858
859 xml.WriteEndElement();
860 xml.WriteEndElement();
861 }
862
863 xml.WriteEndElement();
864 }
865
866 private void WriteMaterialInstance(MaterialInstance matInstance)
867 {
868 xml.WriteStartElement("instance_material");
869 xml.WriteAttributeString("symbol", matInstance.Symbol);
870 xml.WriteAttributeString("target", visitor.UrlOf(matInstance.Target));
871
872 foreach (var binding in matInstance.Bindings)
873 {
874 xml.WriteStartElement("bind_vertex_input");
875 xml.WriteAttributeString("semantic", binding.Semantic);
876 WriteSemanticAttribute("input_semantic", binding.VertexInput.Semantic);
877 xml.WriteAttributeString("input_set", XmlConvert.ToString(binding.VertexInput.Set));
878 xml.WriteEndElement();
879 }
880
881 xml.WriteEndElement();
882 }
883
884 private void WriteAnimation(Animation animation)
885 {
886 BeginEntity("animation", animation);
887
888 foreach (var source in animation.Sources)
889 WriteSource(source, Semantic.None);
890
891 foreach (var sampler in animation.Samplers)
892 WriteAnimationSampler(animation, sampler);
893
894 foreach (var channel in animation.Channels)
895 WriteAnimationChannel(channel);
896
897 EndEntity();
898 }
899
900 private void WriteAnimationSampler(Animation animation, Sampler sampler)
901 {
902 xml.WriteStartElement("sampler");
903 xml.WriteAttributeString("id", visitor.IdOf(sampler));
904
905 foreach (var input in sampler.Inputs)
906 {
907 xml.WriteStartElement("input");
908 WriteSemanticAttribute("semantic", input.Semantic);
909 xml.WriteAttributeString("source", visitor.UrlOf(input.Source));
910 xml.WriteEndElement();
911 }
912
913 xml.WriteEndElement();
914 }
915
916 private void WriteAnimationChannel(AnimationChannel channel)
917 {
918 xml.WriteStartElement("channel");
919 xml.WriteAttributeString("source", visitor.UrlOf(channel.Sampler));
920 xml.WriteAttributeString("target", channel.TargetPath);
921 xml.WriteEndElement();
922 }
923
924 private void BeginEntity(string name, Entity entity)
925 {
926 xml.WriteStartElement(name);
927
928 string id = visitor.IdOf(entity);
929
930 if (!string.IsNullOrEmpty(id))
931 xml.WriteAttributeString("id", id);
932
933 //if (!String.IsNullOrEmpty(entity.Name))
934 // xml.WriteAttributeString("name", entity.Name);
935 }
936
937 private void EndEntity()
938 {
939 xml.WriteEndElement();
940 }
941
942 private void WriteSource(Source source, Semantic semantic)
943 {
944 if (writtenSources.ContainsKey(source))
945 return;
946
947 string sourceId = visitor.IdOf(source);
948
949 writtenSources.Add(source, sourceId);
950
951 var animationSource = source as AnimationSource;
952
953 if (animationSource != null)
954 {
955 if (source.FloatData != null)
956 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, animationSource.Parameters);
957 else
958 WriteSource(sourceId, source.NameData, x => x, source.Stride, animationSource.Parameters);
959
960 return;
961 }
962
963 switch (semantic)
964 {
965 case Semantic.Position:
966 case Semantic.Normal:
967 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, new[] { "X", "Y", "Z" });
968 break;
969
970 case Semantic.TexCoord:
971 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, new[] { "S", "T" });
972 break;
973
974 case Semantic.Color:
975 if (source.Stride == 4)
976 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, new[] { "R", "G", "B", "A" });
977 else
978 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, new[] { "R", "G", "B" });
979 break;
980
981 case Semantic.Input:
982 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, new[] { "TIME" });
983 return;
984
985 case Semantic.Output:
986 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, new[] { "VALUE" });
987 return;
988
989 case Semantic.Interpolation:
990 WriteSource(sourceId, source.NameData, x => x, source.Stride, new[] { "INTERPOLATION" });
991 return;
992
993 case Semantic.InTangent:
994 case Semantic.OutTangent:
995 WriteSource(sourceId, source.FloatData, XmlConvert.ToString, source.Stride, new[] { "X", "Y" });
996 break;
997
998 default:
999 throw new NotSupportedException(string.Format("Sources with semantic {0} are not supported", semantic));
1000 }
1001 }
1002
1003 private void WriteSource<T>(string sourceId, T[] data, Func<T, string> toString, int stride, string[] paramNames)
1004 {
1005 string arrayId = sourceId + "_array";
1006 string type = null;
1007
1008 if (typeof(T) == typeof(float))
1009 type = "float";
1010 else if (typeof(T) == typeof(string))
1011 type = "Name";
1012
1013 xml.WriteStartElement("source");
1014 xml.WriteAttributeString("id", sourceId);
1015
1016 xml.WriteStartElement(type + "_array");
1017 xml.WriteAttributeString("id", arrayId);
1018 xml.WriteAttributeString("count", XmlConvert.ToString(data.Length));
1019 xml.WriteWhitespace("\n");
1020
1021 int valuesPerLine = (stride == 1) ? 10 : stride;
1022
1023 for (int i = 0; i < data.Length; i++)
1024 {
1025 xml.WriteString(toString(data[i]));
1026
1027 if (i != data.Length - 1)
1028 {
1029 if (i % valuesPerLine == valuesPerLine - 1)
1030 xml.WriteWhitespace("\n");
1031 else
1032 xml.WriteWhitespace(" ");
1033 }
1034 }
1035
1036 xml.WriteEndElement();
1037
1038 xml.WriteStartElement("technique_common");
1039 WriteSourceAccessor<T>(arrayId, data.Length / stride, stride, type, paramNames);
1040 xml.WriteEndElement();
1041
1042 xml.WriteEndElement();
1043 }
1044
1045 private void WriteSourceAccessor<T>(string arrayId, int count, int stride, string type, string[] paramNames)
1046 {
1047 xml.WriteStartElement("accessor");
1048 xml.WriteAttributeString("source", "#" + arrayId);
1049 xml.WriteAttributeString("count", XmlConvert.ToString(count));
1050 xml.WriteAttributeString("stride", XmlConvert.ToString(stride));
1051
1052 for (int i = 0; i < stride; i++)
1053 {
1054 xml.WriteStartElement("param");
1055 xml.WriteAttributeString("type", type);
1056 xml.WriteAttributeString("name", paramNames[i]);
1057 xml.WriteEndElement();
1058 }
1059
1060 xml.WriteEndElement();
1061 }
1062
1063 private void WriteSemanticAttribute(string name, Semantic semantic)
1064 {
1065 xml.WriteAttributeString(name, semantic.ToString().ToUpperInvariant());
1066 }
1067
1068 private void WriteCamera(Camera camera)
1069 {
1070 BeginEntity("camera", camera);
1071
1072 xml.WriteStartElement("optics");
1073 xml.WriteStartElement("technique_common");
1074 xml.WriteStartElement("perspective");
1075 xml.WriteElementString("xfov", XmlConvert.ToString(camera.XFov));
1076 xml.WriteElementString("aspect_ratio", XmlConvert.ToString(camera.AspectRatio));
1077 xml.WriteElementString("znear", XmlConvert.ToString(camera.ZNear));
1078 xml.WriteElementString("zfar", XmlConvert.ToString(camera.ZFar));
1079 xml.WriteEndElement();
1080 xml.WriteEndElement();
1081 xml.WriteEndElement();
1082
1083 EndEntity();
1084 }
1085 }
1086}
Note: See TracBrowser for help on using the repository browser.