source: OniSplit/Akira/AkiraDaeReader.cs@ 1124

Last change on this file since 1124 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: 12.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4using Oni.Imaging;
5
6namespace Oni.Akira
7{
8 internal class AkiraDaeReader
9 {
10 #region Private data
11 private readonly PolygonMesh mesh;
12 private readonly List<Vector3> positions;
13 private readonly Dictionary<Vector3, int> uniquePositions;
14 private readonly List<Vector3> normals;
15 private readonly List<Vector2> texCoords;
16 private readonly Dictionary<Dae.Material, Material> materialMap;
17 private readonly Dictionary<string, Material> materialFileMap;
18 private readonly Stack<Matrix> nodeTransformStack;
19
20 private Dae.Scene scene;
21 private Dictionary<string, AkiraDaeNodeProperties> properties;
22 private Matrix nodeTransform;
23 private string nodeName;
24 #endregion
25
26 public static PolygonMesh Read(IEnumerable<string> filePaths)
27 {
28 var reader = new AkiraDaeReader();
29 var properties = new Dictionary<string, AkiraDaeNodeProperties>();
30
31 foreach (var filePath in filePaths)
32 reader.ReadScene(Dae.Reader.ReadFile(filePath), properties);
33
34 return reader.mesh;
35 }
36
37 public AkiraDaeReader()
38 {
39 mesh = new PolygonMesh(new MaterialLibrary());
40
41 positions = mesh.Points;
42 uniquePositions = new Dictionary<Vector3, int>();
43 texCoords = mesh.TexCoords;
44 normals = mesh.Normals;
45
46 materialMap = new Dictionary<Dae.Material, Material>();
47 materialFileMap = new Dictionary<string, Material>(StringComparer.OrdinalIgnoreCase);
48
49 nodeTransformStack = new Stack<Matrix>();
50 nodeTransform = Matrix.Identity;
51 }
52
53 public PolygonMesh Mesh => mesh;
54
55 public void ReadScene(Dae.Scene scene, Dictionary<string, AkiraDaeNodeProperties> properties)
56 {
57 this.scene = scene;
58 this.properties = properties;
59
60 AkiraDaeNodeProperties sceneProperties;
61 properties.TryGetValue(scene.Id, out sceneProperties);
62
63 foreach (var node in scene.Nodes)
64 ReadNode(node, sceneProperties);
65 }
66
67 private void ReadNode(Dae.Node node, AkiraDaeNodeProperties parentNodeProperties)
68 {
69 AkiraDaeNodeProperties nodeProperties;
70
71 if (!properties.TryGetValue(node.Id, out nodeProperties))
72 nodeProperties = parentNodeProperties;
73 else if (nodeProperties.HasPhysics)
74 return;
75
76 nodeTransformStack.Push(nodeTransform);
77
78 foreach (var transform in node.Transforms)
79 nodeTransform = transform.ToMatrix() * nodeTransform;
80
81 nodeName = node.Name;
82
83 foreach (var geometryInstance in node.GeometryInstances)
84 ReadGeometryInstance(node, nodeProperties, geometryInstance);
85
86 foreach (var child in node.Nodes)
87 ReadNode(child, nodeProperties);
88
89 nodeTransform = nodeTransformStack.Pop();
90 }
91
92 private void ReadGeometryInstance(Dae.Node node, AkiraDaeNodeProperties nodeProperties, Dae.GeometryInstance instance)
93 {
94 foreach (var primitives in instance.Target.Primitives)
95 {
96 if (primitives.PrimitiveType != Dae.MeshPrimitiveType.Polygons)
97 {
98 Console.Error.WriteLine("Unsupported primitive type '{0}' found in geometry '{1}', ignoring.", primitives.PrimitiveType, instance.Name);
99 continue;
100 }
101
102 ReadPolygonPrimitives(node, nodeProperties, primitives, instance.Materials.Find(m => m.Symbol == primitives.MaterialSymbol));
103 }
104 }
105
106 private Material ReadMaterial(Dae.Material material)
107 {
108 if (material == null || material.Effect == null)
109 return null;
110
111 Material polygonMaterial;
112
113 if (materialMap.TryGetValue(material, out polygonMaterial))
114 return polygonMaterial;
115
116 Dae.EffectSampler diffuseSampler = null;
117 Dae.EffectSampler transparentSampler = null;
118
119 foreach (var texture in material.Effect.Textures)
120 {
121 if (texture.Channel == Dae.EffectTextureChannel.Diffuse)
122 diffuseSampler = texture.Sampler;
123 else if (texture.Channel == Dae.EffectTextureChannel.Transparent)
124 transparentSampler = texture.Sampler;
125 }
126
127 if (diffuseSampler == null || diffuseSampler.Surface == null || diffuseSampler.Surface.InitFrom == null)
128 {
129 //
130 // this material doesn't have a diffuse texture
131 //
132
133 return null;
134 }
135
136 var image = diffuseSampler.Surface.InitFrom;
137
138 if (materialFileMap.TryGetValue(image.FilePath, out polygonMaterial))
139 return polygonMaterial;
140
141 polygonMaterial = mesh.Materials.GetMaterial(Path.GetFileNameWithoutExtension(image.FilePath));
142 polygonMaterial.ImageFilePath = image.FilePath;
143
144 if (transparentSampler == diffuseSampler)
145 polygonMaterial.Flags |= GunkFlags.Transparent | GunkFlags.NoOcclusion | GunkFlags.TwoSided;
146
147 materialFileMap.Add(image.FilePath, polygonMaterial);
148 materialMap.Add(material, polygonMaterial);
149
150 return polygonMaterial;
151 }
152
153 private void ReadPolygonPrimitives(Dae.Node node, AkiraDaeNodeProperties nodeProperties, Dae.MeshPrimitives primitives, Dae.MaterialInstance materialInstance)
154 {
155 Material material = null;
156
157 if (materialInstance != null)
158 material = ReadMaterial(materialInstance.Target);
159
160 if (material == null)
161 material = mesh.Materials.NotFound;
162
163 int[] positionIndices = null;
164 int[] texCoordIndices = null;
165 int[] normalIndices = null;
166 Color[] colors = null;
167
168 foreach (var input in primitives.Inputs)
169 {
170 switch (input.Semantic)
171 {
172 case Dae.Semantic.Position:
173 positionIndices = ReadInputIndexed(input, positions, uniquePositions, PositionReader);
174 break;
175
176 case Dae.Semantic.TexCoord:
177 texCoordIndices = ReadInputIndexed(input, texCoords, Dae.Source.ReadTexCoord);
178 break;
179
180 case Dae.Semantic.Normal:
181 normalIndices = ReadInputIndexed(input, normals, Dae.Source.ReadVector3);
182 break;
183
184 case Dae.Semantic.Color:
185 colors = ReadInput(input, Dae.Source.ReadColor);
186 break;
187 }
188 }
189
190 if (texCoordIndices == null)
191 Console.Error.WriteLine("Geometry '{0}' does not contain texture coordinates.", nodeName);
192
193 int startIndex = 0;
194 int degeneratePolygonCount = 0;
195
196 foreach (int vertexCount in primitives.VertexCounts)
197 {
198 var polygonPointIndices = new int[vertexCount];
199 Array.Copy(positionIndices, startIndex, polygonPointIndices, 0, vertexCount);
200
201 if (CheckDegenerate(positions, polygonPointIndices))
202 {
203 degeneratePolygonCount++;
204 startIndex += vertexCount;
205 continue;
206 }
207
208 var polygon = new Polygon(mesh, polygonPointIndices)
209 {
210 FileName = node.FileName,
211 ObjectName = node.Name,
212 Material = material
213 };
214
215 if (texCoordIndices != null)
216 {
217 polygon.TexCoordIndices = new int[vertexCount];
218 Array.Copy(texCoordIndices, startIndex, polygon.TexCoordIndices, 0, vertexCount);
219 }
220 else
221 {
222 polygon.TexCoordIndices = new int[vertexCount];
223 }
224
225 if (normalIndices != null)
226 {
227 polygon.NormalIndices = new int[vertexCount];
228 Array.Copy(normalIndices, startIndex, polygon.NormalIndices, 0, vertexCount);
229 }
230
231 if (colors != null)
232 {
233 polygon.Colors = new Color[vertexCount];
234 Array.Copy(colors, startIndex, polygon.Colors, 0, vertexCount);
235 }
236
237 startIndex += vertexCount;
238
239 if (nodeProperties != null)
240 {
241 polygon.ScriptId = nodeProperties.ScriptId;
242 polygon.Flags |= nodeProperties.GunkFlags;
243 }
244
245 if (material == mesh.Materials.Markers.Ghost)
246 mesh.Ghosts.Add(polygon);
247 else if (material == mesh.Materials.Markers.DoorFrame)
248 mesh.Doors.Add(polygon);
249 else if (material.Name.StartsWith("bnv_grid_", StringComparison.Ordinal))
250 mesh.Floors.Add(polygon);
251 else
252 mesh.Polygons.Add(polygon);
253 }
254
255 if (degeneratePolygonCount > 0)
256 {
257 Console.Error.WriteLine("Ignoring {0} degenerate polygons", degeneratePolygonCount);
258 }
259 }
260
261 private static bool CheckDegenerate(List<Vector3> positions, int[] positionIndices)
262 {
263 if (positionIndices.Length < 3)
264 return true;
265
266 var p0 = positions[positionIndices[0]];
267 var p1 = positions[positionIndices[1]];
268
269 for (int i = 2; i < positionIndices.Length; i++)
270 {
271 var p2 = positions[positionIndices[i]];
272
273 var s0 = p0 - p1;
274 var s1 = p2 - p1;
275
276 Vector3 c;
277 Vector3.Cross(ref s0, ref s1, out c);
278
279 if (Math.Abs(c.LengthSquared()) < 0.0001f && Vector3.Dot(s0, s1) > 0.0f)
280 return true;
281
282 p0 = p1;
283 p1 = p2;
284 }
285
286 return false;
287 }
288
289 private static int[] ReadInputIndexed<T>(Dae.IndexedInput input, List<T> list, Func<Dae.Source, int, T> elementReader)
290 where T : struct
291 {
292 var indices = new int[input.Indices.Count];
293
294 for (int i = 0; i < input.Indices.Count; i++)
295 {
296 var v = elementReader(input.Source, input.Indices[i]);
297 indices[i] = list.Count;
298 list.Add(v);
299 }
300
301 return indices;
302 }
303
304 private static int[] ReadInputIndexed<T>(Dae.IndexedInput input, List<T> list, Dictionary<T, int> uniqueList, Func<Dae.Source, int, T> elementReader)
305 where T : struct
306 {
307 var indices = new int[input.Indices.Count];
308
309 for (int i = 0; i < input.Indices.Count; i++)
310 {
311 var v = elementReader(input.Source, input.Indices[i]);
312
313 int index;
314
315 if (!uniqueList.TryGetValue(v, out index))
316 {
317 index = list.Count;
318 list.Add(v);
319 uniqueList.Add(v, index);
320 }
321
322 indices[i] = index;
323 }
324
325 return indices;
326 }
327
328 private static T[] ReadInput<T>(Dae.IndexedInput input, Func<Dae.Source, int, T> elementReader)
329 where T : struct
330 {
331 var values = new T[input.Indices.Count];
332
333 for (int i = 0; i < input.Indices.Count; i++)
334 values[i] = elementReader(input.Source, input.Indices[i]);
335
336 return values;
337 }
338
339 private Vector3 PositionReader(Dae.Source source, int index)
340 {
341 Vector3 p = Dae.Source.ReadVector3(source, index);
342 Vector3 r;
343 Vector3.Transform(ref p, ref nodeTransform, out r);
344 return r;
345 }
346 }
347}
Note: See TracBrowser for help on using the repository browser.