source: OniSplit/Motoko/GeometryDaeReader.cs@ 1138

Last change on this file since 1138 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: 9.7 KB
Line 
1using System;
2using System.Collections.Generic;
3
4namespace Oni.Motoko
5{
6 internal static class GeometryDaeReader
7 {
8 #region private struct Vertex
9
10 private struct Vertex : IEquatable<Vertex>
11 {
12 public readonly int PositionIndex;
13 public readonly int TexcoordIndex;
14 public readonly int NormalIndex;
15
16 public Vertex(int pointIndex, int uvIndex, int normalIndex)
17 {
18 PositionIndex = pointIndex;
19 TexcoordIndex = uvIndex;
20 NormalIndex = normalIndex;
21 }
22
23 public static bool operator ==(Vertex v1, Vertex v2) => v1.Equals(v2);
24
25 public static bool operator !=(Vertex v1, Vertex v2) => !v1.Equals(v2);
26
27 public bool Equals(Vertex v) => PositionIndex == v.PositionIndex && TexcoordIndex == v.TexcoordIndex && NormalIndex == v.NormalIndex;
28
29 public override bool Equals(object obj) => obj is Vertex && Equals((Vertex)obj);
30
31 public override int GetHashCode() => PositionIndex ^ TexcoordIndex ^ NormalIndex;
32 }
33
34 #endregion
35
36 public static Geometry Read(Dae.Geometry daeGeometry)
37 {
38 return Read(daeGeometry, false, false, 0.0f);
39 }
40
41 public static IEnumerable<Geometry> Read(Dae.Node node, TextureImporter3 textureImporter)
42 {
43 Dae.FaceConverter.Triangulate(node);
44
45 foreach (var daeGeometryInstance in node.GeometryInstances)
46 {
47 var daeGeometry = daeGeometryInstance.Target;
48
49 var geometry = Read(daeGeometry, false, false, 0.0f);
50 geometry.Name = node.Name;
51
52 if (textureImporter != null && daeGeometryInstance.Materials.Count > 0)
53 geometry.TextureName = textureImporter.AddMaterial(daeGeometryInstance.Materials[0].Target);
54
55 yield return geometry;
56 }
57 }
58
59 public static Geometry Read(Dae.Geometry daeGeometry, bool generateNormals, bool flatNormals, float shellOffset)
60 {
61 if (daeGeometry.Primitives.Count > 1)
62 throw new NotSupportedException(string.Format("Geometry {0}: Multiple primitive groups per mesh are not supported", daeGeometry.Name));
63
64 var primitives = daeGeometry.Primitives[0];
65
66 if (primitives.PrimitiveType == Dae.MeshPrimitiveType.Lines || primitives.PrimitiveType == Dae.MeshPrimitiveType.LineStrips)
67 throw new NotSupportedException(string.Format("Geometry {0}: Line primitives are not supported", daeGeometry.Name));
68
69 var positionIndex = new Dictionary<Vector3, int>();
70 var positions = new List<Vector3>();
71 int[] positionIndices = null;
72
73 var normalIndex = new Dictionary<Vector3, int>();
74 var normals = new List<Vector3>();
75 int[] normalIndices = null;
76
77 var texCoordIndex = new Dictionary<Vector2, int>();
78 var texCoords = new List<Vector2>();
79 int[] texCoordIndices = null;
80
81 foreach (var input in primitives.Inputs)
82 {
83 switch (input.Semantic)
84 {
85 case Dae.Semantic.Position:
86 positionIndices = RemoveDuplicates(input, positions, positionIndex, Dae.Source.ReadVector3);
87 break;
88
89 case Dae.Semantic.Normal:
90 if (!generateNormals)
91 normalIndices = RemoveDuplicates(input, normals, normalIndex, Dae.Source.ReadVector3);
92 break;
93
94 case Dae.Semantic.TexCoord:
95 texCoordIndices = RemoveDuplicates(input, texCoords, texCoordIndex, Dae.Source.ReadTexCoord);
96 break;
97 }
98 }
99
100 if (texCoordIndices == null)
101 Console.WriteLine("Geometry {0} does not have texture coordinates", daeGeometry.Name);
102
103 if (normalIndices == null)
104 generateNormals = true;
105
106 Vector3[] generatedNormals = null;
107
108 if (generateNormals || shellOffset != 0.0f)
109 generatedNormals = GenerateNormals(positions, positionIndices, flatNormals);
110
111 if (generateNormals)
112 {
113 normals = new List<Vector3>(generatedNormals);
114 normalIndices = positionIndices;
115 }
116
117 int[] shellIndices = null;
118
119 if (shellOffset != 0.0f)
120 {
121 var shellNormals = generatedNormals;
122
123 if (flatNormals)
124 shellNormals = GenerateNormals(positions, positionIndices, false);
125
126 shellIndices = GenerateShell(positions, positionIndices, shellNormals, shellOffset);
127 }
128
129 var triangles = new int[(shellIndices == null) ? positionIndices.Length : positionIndices.Length + shellIndices.Length];
130 var vertices = new List<Vertex>();
131 var vertexIndex = new Dictionary<Vertex, int>();
132
133 for (int i = 0; i < positionIndices.Length; i++)
134 {
135 var vertex = new Vertex(
136 positionIndices[i],
137 (texCoordIndices != null) ? texCoordIndices[i] : -1,
138 (normalIndices != null) ? normalIndices[i] : -1);
139
140 if (!vertexIndex.TryGetValue(vertex, out triangles[i]))
141 {
142 triangles[i] = vertices.Count;
143 vertices.Add(vertex);
144 vertexIndex.Add(vertex, triangles[i]);
145 }
146 }
147
148 if (shellIndices != null)
149 {
150 for (int i = 0; i < shellIndices.Length; i++)
151 {
152 var vertex = new Vertex(shellIndices[i], -1, -1);
153 int j = i + positionIndices.Length;
154
155 if (!vertexIndex.TryGetValue(vertex, out triangles[j]))
156 {
157 triangles[j] = vertices.Count;
158 vertices.Add(vertex);
159 vertexIndex.Add(vertex, triangles[j]);
160 }
161 }
162 }
163
164 if (vertices.Count > 2048)
165 Console.Error.WriteLine("Warning: Geometry {0} has too many vertices ({1})", daeGeometry.Name, vertices.Count);
166
167 var geometry = new Geometry
168 {
169 Points = new Vector3[vertices.Count],
170 Normals = new Vector3[vertices.Count],
171 TexCoords = new Vector2[vertices.Count],
172 Triangles = triangles
173 };
174
175 for (int i = 0; i < vertices.Count; i++)
176 {
177 geometry.Points[i] = positions[vertices[i].PositionIndex];
178
179 if (vertices[i].NormalIndex != -1)
180 geometry.Normals[i] = normals[vertices[i].NormalIndex];
181
182 if (vertices[i].TexcoordIndex != -1)
183 geometry.TexCoords[i] = texCoords[vertices[i].TexcoordIndex];
184 }
185
186 return geometry;
187 }
188
189 private static int[] RemoveDuplicates<T>(
190 Dae.IndexedInput input,
191 List<T> list,
192 Dictionary<T, int> index,
193 Func<Dae.Source, int, T> elementReader)
194 {
195 var indices = new int[input.Indices.Count];
196
197 for (int i = 0; i < indices.Length; i++)
198 {
199 var v = elementReader(input.Source, input.Indices[i]);
200
201 if (!index.TryGetValue(v, out indices[i]))
202 {
203 indices[i] = list.Count;
204 list.Add(v);
205 index.Add(v, indices[i]);
206 }
207 }
208
209 return indices;
210 }
211
212 private static Vector3[] GenerateNormals(List<Vector3> positions, int[] triangleList, bool flatNormals)
213 {
214 var autoNormals = new Vector3[positions.Count];
215
216 if (!flatNormals)
217 {
218 for (int i = 0; i < triangleList.Length; i += 3)
219 {
220 Vector3 p0 = positions[triangleList[i + 0]];
221 Vector3 p1 = positions[triangleList[i + 1]];
222 Vector3 p2 = positions[triangleList[i + 2]];
223
224 Vector3 e1 = p1 - p0;
225 Vector3 e2 = p2 - p0;
226
227 Vector3 faceNormal = Vector3.Cross(e1, e2);
228 float weight = FMath.Atan2(faceNormal.Length(), Vector3.Dot(e1, e2));
229 faceNormal = Vector3.Normalize(faceNormal) * weight;
230
231 for (int j = 0; j < 3; j++)
232 autoNormals[triangleList[i + j]] += faceNormal;
233 }
234
235 for (int i = 0; i < autoNormals.Length; i++)
236 autoNormals[i].Normalize();
237 }
238
239 return autoNormals;
240 }
241
242 private static int[] GenerateShell(List<Vector3> positions, int[] positionIndices, Vector3[] normals, float offset)
243 {
244 int positionCount = positions.Count;
245
246 for (int i = 0; i < positionCount; i++)
247 positions.Add(positions[i] + normals[i] * offset);
248
249 var shellIndices = new int[positionIndices.Length];
250
251 for (int i = 0; i < positionIndices.Length; i += 3)
252 {
253 shellIndices[i + 0] = positionIndices[i + 2] + positionCount;
254 shellIndices[i + 1] = positionIndices[i + 1] + positionCount;
255 shellIndices[i + 2] = positionIndices[i + 0] + positionCount;
256 }
257
258 return shellIndices;
259 }
260 }
261}
Note: See TracBrowser for help on using the repository browser.