| 1 | using System; | 
 
 
 
 
 | 2 | using System.Collections.Generic; | 
 
 
 
 
 | 3 |  | 
 
 
 
 
 | 4 | namespace 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 | } |