[1114] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using System.Globalization;
|
---|
| 4 | using System.IO;
|
---|
| 5 |
|
---|
| 6 | namespace Oni.Dae.IO
|
---|
| 7 | {
|
---|
| 8 | internal class ObjReader
|
---|
| 9 | {
|
---|
| 10 | private struct ObjVertex : IEquatable<ObjVertex>
|
---|
| 11 | {
|
---|
| 12 | public int PointIndex;
|
---|
| 13 | public int TexCoordIndex;
|
---|
| 14 | public int NormalIndex;
|
---|
| 15 |
|
---|
| 16 | public ObjVertex(int pointIndex, int uvIndex, int normalIndex)
|
---|
| 17 | {
|
---|
| 18 | PointIndex = pointIndex;
|
---|
| 19 | TexCoordIndex = uvIndex;
|
---|
| 20 | NormalIndex = normalIndex;
|
---|
| 21 | }
|
---|
| 22 |
|
---|
| 23 | public static bool operator ==(ObjVertex v1, ObjVertex v2) =>
|
---|
| 24 | v1.PointIndex == v2.PointIndex
|
---|
| 25 | && v1.TexCoordIndex == v2.TexCoordIndex
|
---|
| 26 | && v1.NormalIndex == v2.NormalIndex;
|
---|
| 27 |
|
---|
| 28 | public static bool operator !=(ObjVertex v1, ObjVertex v2) =>
|
---|
| 29 | v1.PointIndex != v2.PointIndex
|
---|
| 30 | || v1.TexCoordIndex != v2.TexCoordIndex
|
---|
| 31 | || v1.NormalIndex != v2.NormalIndex;
|
---|
| 32 |
|
---|
| 33 | public bool Equals(ObjVertex v) => this == v;
|
---|
| 34 | public override bool Equals(object obj) => obj is ObjVertex && Equals((ObjVertex)obj);
|
---|
| 35 | public override int GetHashCode() => PointIndex ^ TexCoordIndex ^ NormalIndex;
|
---|
| 36 | }
|
---|
| 37 |
|
---|
| 38 | private class ObjFace
|
---|
| 39 | {
|
---|
| 40 | public string ObjectName;
|
---|
| 41 | public string[] GroupsNames;
|
---|
| 42 | public ObjVertex[] Vertices;
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | private class ObjMaterial
|
---|
| 46 | {
|
---|
| 47 | private readonly string name;
|
---|
| 48 | private string textureFilePath;
|
---|
| 49 | private Material material;
|
---|
| 50 |
|
---|
| 51 | public ObjMaterial(string name)
|
---|
| 52 | {
|
---|
| 53 | this.name = name;
|
---|
| 54 | }
|
---|
| 55 |
|
---|
| 56 | public string Name => name;
|
---|
| 57 |
|
---|
| 58 | public string TextureFilePath
|
---|
| 59 | {
|
---|
| 60 | get { return textureFilePath; }
|
---|
| 61 | set { textureFilePath = value; }
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 | public Material Material
|
---|
| 65 | {
|
---|
| 66 | get
|
---|
| 67 | {
|
---|
| 68 | if (material == null && TextureFilePath != null)
|
---|
| 69 | CreateMaterial();
|
---|
| 70 |
|
---|
| 71 | return material;
|
---|
| 72 | }
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | private void CreateMaterial()
|
---|
| 76 | {
|
---|
| 77 | var image = new Image
|
---|
| 78 | {
|
---|
| 79 | FilePath = TextureFilePath,
|
---|
| 80 | Name = name + "_img"
|
---|
| 81 | };
|
---|
| 82 |
|
---|
| 83 | var effectSurface = new EffectSurface(image);
|
---|
| 84 | var effectSampler = new EffectSampler(effectSurface);
|
---|
| 85 | var effectTexture = new EffectTexture
|
---|
| 86 | {
|
---|
| 87 | Sampler = effectSampler,
|
---|
| 88 | Channel = EffectTextureChannel.Diffuse,
|
---|
| 89 | TexCoordSemantic = "diffuse_TEXCOORD"
|
---|
| 90 | };
|
---|
| 91 |
|
---|
| 92 | material = new Material
|
---|
| 93 | {
|
---|
| 94 | Id = name,
|
---|
| 95 | Name = name,
|
---|
| 96 | Effect = new Effect
|
---|
| 97 | {
|
---|
| 98 | Id = name + "_fx",
|
---|
| 99 | DiffuseValue = effectTexture,
|
---|
| 100 | Parameters = {
|
---|
| 101 | new EffectParameter("surface", effectSurface),
|
---|
| 102 | new EffectParameter("sampler", effectSampler)
|
---|
| 103 | }
|
---|
| 104 | }
|
---|
| 105 | };
|
---|
| 106 | }
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 | private class ObjPrimitives
|
---|
| 110 | {
|
---|
| 111 | public ObjMaterial Material;
|
---|
| 112 | public readonly List<ObjFace> Faces = new List<ObjFace>(4);
|
---|
| 113 | }
|
---|
| 114 |
|
---|
| 115 | #region Private data
|
---|
| 116 | private static readonly string[] emptyStrings = new string[0];
|
---|
| 117 | private static readonly char[] whiteSpaceChars = new char[] { ' ', '\t' };
|
---|
| 118 | private static readonly char[] vertexSeparator = new char[] { '/' };
|
---|
| 119 |
|
---|
| 120 | private Scene mainScene;
|
---|
| 121 |
|
---|
| 122 | private readonly List<Vector3> points = new List<Vector3>();
|
---|
| 123 | private readonly List<Vector2> texCoords = new List<Vector2>();
|
---|
| 124 | private readonly List<Vector3> normals = new List<Vector3>();
|
---|
| 125 |
|
---|
| 126 | private int pointCount;
|
---|
| 127 | private int normalCount;
|
---|
| 128 | private int texCoordCount;
|
---|
| 129 |
|
---|
| 130 | private readonly Dictionary<Vector3, int> pointIndex = new Dictionary<Vector3, int>();
|
---|
| 131 | private readonly Dictionary<Vector3, int> normalIndex = new Dictionary<Vector3, int>();
|
---|
| 132 | private readonly Dictionary<Vector2, int> texCoordIndex = new Dictionary<Vector2, int>();
|
---|
| 133 | private readonly List<int> pointRemap = new List<int>();
|
---|
| 134 | private readonly List<int> normalRemap = new List<int>();
|
---|
| 135 | private readonly List<int> texCoordRemap = new List<int>();
|
---|
| 136 |
|
---|
| 137 | private readonly Dictionary<string, ObjMaterial> materials = new Dictionary<string, ObjMaterial>(StringComparer.Ordinal);
|
---|
| 138 |
|
---|
| 139 | private string currentObjectName;
|
---|
| 140 | private string[] currentGroupNames;
|
---|
| 141 | private readonly List<ObjPrimitives> primitives = new List<ObjPrimitives>();
|
---|
| 142 | private ObjPrimitives currentPrimitives;
|
---|
| 143 | #endregion
|
---|
| 144 |
|
---|
| 145 | public static Scene ReadFile(string filePath)
|
---|
| 146 | {
|
---|
| 147 | var reader = new ObjReader();
|
---|
| 148 | reader.ReadObjFile(filePath);
|
---|
| 149 | reader.ImportObjects();
|
---|
| 150 | return reader.mainScene;
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | private void ReadObjFile(string filePath)
|
---|
| 154 | {
|
---|
| 155 | mainScene = new Scene();
|
---|
| 156 |
|
---|
| 157 | foreach (string line in ReadLines(filePath))
|
---|
| 158 | {
|
---|
| 159 | var tokens = line.Split(whiteSpaceChars, StringSplitOptions.RemoveEmptyEntries);
|
---|
| 160 |
|
---|
| 161 | switch (tokens[0])
|
---|
| 162 | {
|
---|
| 163 | case "o":
|
---|
| 164 | ReadObject(tokens);
|
---|
| 165 | break;
|
---|
| 166 |
|
---|
| 167 | case "g":
|
---|
| 168 | ReadGroup(tokens);
|
---|
| 169 | break;
|
---|
| 170 |
|
---|
| 171 | case "v":
|
---|
| 172 | ReadPoint(tokens);
|
---|
| 173 | break;
|
---|
| 174 |
|
---|
| 175 | case "vn":
|
---|
| 176 | ReadNormal(tokens);
|
---|
| 177 | break;
|
---|
| 178 |
|
---|
| 179 | case "vt":
|
---|
| 180 | ReadTexCoord(tokens);
|
---|
| 181 | break;
|
---|
| 182 |
|
---|
| 183 | case "f":
|
---|
| 184 | case "fo":
|
---|
| 185 | ReadFace(tokens);
|
---|
| 186 | break;
|
---|
| 187 |
|
---|
| 188 | case "mtllib":
|
---|
| 189 | ReadMtlLib(filePath, tokens);
|
---|
| 190 | break;
|
---|
| 191 |
|
---|
| 192 | case "usemtl":
|
---|
| 193 | ReadUseMtl(tokens);
|
---|
| 194 | break;
|
---|
| 195 | }
|
---|
| 196 | }
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 | private void ReadPoint(string[] tokens)
|
---|
| 200 | {
|
---|
| 201 | var point = new Vector3(
|
---|
| 202 | float.Parse(tokens[1], CultureInfo.InvariantCulture),
|
---|
| 203 | float.Parse(tokens[2], CultureInfo.InvariantCulture),
|
---|
| 204 | float.Parse(tokens[3], CultureInfo.InvariantCulture));
|
---|
| 205 |
|
---|
| 206 | AddPoint(point);
|
---|
| 207 |
|
---|
| 208 | pointCount++;
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 | private void AddPoint(Vector3 point)
|
---|
| 212 | {
|
---|
| 213 | int newIndex;
|
---|
| 214 |
|
---|
| 215 | if (pointIndex.TryGetValue(point, out newIndex))
|
---|
| 216 | {
|
---|
| 217 | pointRemap.Add(newIndex);
|
---|
| 218 | }
|
---|
| 219 | else
|
---|
| 220 | {
|
---|
| 221 | pointRemap.Add(points.Count);
|
---|
| 222 | pointIndex.Add(point, points.Count);
|
---|
| 223 | points.Add(point);
|
---|
| 224 | }
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | private void ReadNormal(string[] tokens)
|
---|
| 228 | {
|
---|
| 229 | var normal = new Vector3(
|
---|
| 230 | float.Parse(tokens[1], CultureInfo.InvariantCulture),
|
---|
| 231 | float.Parse(tokens[2], CultureInfo.InvariantCulture),
|
---|
| 232 | float.Parse(tokens[3], CultureInfo.InvariantCulture));
|
---|
| 233 |
|
---|
| 234 | AddNormal(normal);
|
---|
| 235 |
|
---|
| 236 | normalCount++;
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | private void AddNormal(Vector3 normal)
|
---|
| 240 | {
|
---|
| 241 | int newIndex;
|
---|
| 242 |
|
---|
| 243 | if (normalIndex.TryGetValue(normal, out newIndex))
|
---|
| 244 | {
|
---|
| 245 | normalRemap.Add(newIndex);
|
---|
| 246 | }
|
---|
| 247 | else
|
---|
| 248 | {
|
---|
| 249 | normalRemap.Add(normals.Count);
|
---|
| 250 | normalIndex.Add(normal, normals.Count);
|
---|
| 251 | normals.Add(normal);
|
---|
| 252 | }
|
---|
| 253 | }
|
---|
| 254 |
|
---|
| 255 | private void ReadTexCoord(string[] tokens)
|
---|
| 256 | {
|
---|
| 257 | var texCoord = new Vector2(
|
---|
| 258 | float.Parse(tokens[1], CultureInfo.InvariantCulture),
|
---|
| 259 | 1.0f - float.Parse(tokens[2], CultureInfo.InvariantCulture));
|
---|
| 260 |
|
---|
| 261 | AddTexCoord(texCoord);
|
---|
| 262 |
|
---|
| 263 | texCoordCount++;
|
---|
| 264 | }
|
---|
| 265 |
|
---|
| 266 | private void AddTexCoord(Vector2 texCoord)
|
---|
| 267 | {
|
---|
| 268 | int newIndex;
|
---|
| 269 |
|
---|
| 270 | if (texCoordIndex.TryGetValue(texCoord, out newIndex))
|
---|
| 271 | {
|
---|
| 272 | texCoordRemap.Add(newIndex);
|
---|
| 273 | }
|
---|
| 274 | else
|
---|
| 275 | {
|
---|
| 276 | texCoordRemap.Add(texCoords.Count);
|
---|
| 277 | texCoordIndex.Add(texCoord, texCoords.Count);
|
---|
| 278 | texCoords.Add(texCoord);
|
---|
| 279 | }
|
---|
| 280 | }
|
---|
| 281 |
|
---|
| 282 | private void ReadFace(string[] tokens)
|
---|
| 283 | {
|
---|
| 284 | var faceVertices = ReadVertices(tokens);
|
---|
| 285 |
|
---|
| 286 | if (currentPrimitives == null)
|
---|
| 287 | ReadUseMtl(emptyStrings);
|
---|
| 288 |
|
---|
| 289 | currentPrimitives.Faces.Add(new ObjFace
|
---|
| 290 | {
|
---|
| 291 | ObjectName = currentObjectName,
|
---|
| 292 | GroupsNames = currentGroupNames,
|
---|
| 293 | Vertices = faceVertices
|
---|
| 294 | });
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | private ObjVertex[] ReadVertices(string[] tokens)
|
---|
| 298 | {
|
---|
| 299 | var vertices = new ObjVertex[tokens.Length - 1];
|
---|
| 300 |
|
---|
| 301 | for (int i = 0; i < vertices.Length; i++)
|
---|
| 302 | {
|
---|
| 303 | //
|
---|
| 304 | // Read a point/texture/normal index pair
|
---|
| 305 | //
|
---|
| 306 |
|
---|
| 307 | var indices = tokens[i + 1].Split(vertexSeparator);
|
---|
| 308 |
|
---|
| 309 | if (indices.Length == 0 || indices.Length > 3)
|
---|
| 310 | throw new InvalidDataException();
|
---|
| 311 |
|
---|
| 312 | //
|
---|
| 313 | // Extract indices from file: 0 means "not specified"
|
---|
| 314 | //
|
---|
| 315 |
|
---|
| 316 | int pointIndex = int.Parse(indices[0], CultureInfo.InvariantCulture);
|
---|
| 317 | int texCoordIndex = (indices.Length > 1 && indices[1].Length > 0) ? int.Parse(indices[1], CultureInfo.InvariantCulture) : 0;
|
---|
| 318 | int normalIndex = (indices.Length > 2 && indices[2].Length > 0) ? int.Parse(indices[2], CultureInfo.InvariantCulture) : 0;
|
---|
| 319 |
|
---|
| 320 | //
|
---|
| 321 | // Adjust for negative indices
|
---|
| 322 | //
|
---|
| 323 |
|
---|
| 324 | if (pointIndex < 0)
|
---|
| 325 | pointIndex = pointCount + pointIndex + 1;
|
---|
| 326 |
|
---|
| 327 | if (texCoordIndex < 0)
|
---|
| 328 | texCoordIndex = texCoordCount + texCoordIndex + 1;
|
---|
| 329 |
|
---|
| 330 | if (normalIndex < 0)
|
---|
| 331 | normalIndex = normalCount + normalIndex + 1;
|
---|
| 332 |
|
---|
| 333 | //
|
---|
| 334 | // Convert indices to internal representation: range 0..n and -1 means "not specified".
|
---|
| 335 | //
|
---|
| 336 |
|
---|
| 337 | pointIndex = pointIndex - 1;
|
---|
| 338 | texCoordIndex = texCoordIndex - 1;
|
---|
| 339 | normalIndex = normalIndex - 1;
|
---|
| 340 |
|
---|
| 341 | //
|
---|
| 342 | // Remap indices
|
---|
| 343 | //
|
---|
| 344 |
|
---|
| 345 | pointIndex = pointRemap[pointIndex];
|
---|
| 346 |
|
---|
| 347 | if (texCoordIndex < 0 || texCoordRemap.Count <= texCoordIndex)
|
---|
| 348 | texCoordIndex = -1;
|
---|
| 349 | else
|
---|
| 350 | texCoordIndex = texCoordRemap[texCoordIndex];
|
---|
| 351 |
|
---|
| 352 | if (normalIndex < 0 || normalRemap.Count <= normalIndex)
|
---|
| 353 | normalIndex = -1;
|
---|
| 354 | else
|
---|
| 355 | normalIndex = normalRemap[normalIndex];
|
---|
| 356 |
|
---|
| 357 | vertices[i] = new ObjVertex
|
---|
| 358 | {
|
---|
| 359 | PointIndex = pointIndex,
|
---|
| 360 | TexCoordIndex = texCoordIndex,
|
---|
| 361 | NormalIndex = normalIndex
|
---|
| 362 | };
|
---|
| 363 | }
|
---|
| 364 |
|
---|
| 365 | return vertices;
|
---|
| 366 | }
|
---|
| 367 |
|
---|
| 368 | private void ReadObject(string[] tokens)
|
---|
| 369 | {
|
---|
| 370 | currentObjectName = tokens[1];
|
---|
| 371 | }
|
---|
| 372 |
|
---|
| 373 | private void ReadGroup(string[] tokens)
|
---|
| 374 | {
|
---|
| 375 | currentGroupNames = tokens;
|
---|
| 376 | }
|
---|
| 377 |
|
---|
| 378 | private void ReadUseMtl(string[] tokens)
|
---|
| 379 | {
|
---|
| 380 | currentPrimitives = new ObjPrimitives();
|
---|
| 381 |
|
---|
| 382 | if (tokens.Length > 0)
|
---|
| 383 | materials.TryGetValue(tokens[1], out currentPrimitives.Material);
|
---|
| 384 |
|
---|
| 385 | primitives.Add(currentPrimitives);
|
---|
| 386 | }
|
---|
| 387 |
|
---|
| 388 | private void ReadMtlLib(string objFilePath, string[] tokens)
|
---|
| 389 | {
|
---|
| 390 | string materialLibraryFilePath = tokens[1];
|
---|
| 391 |
|
---|
| 392 | if (Path.GetExtension(materialLibraryFilePath).Length == 0)
|
---|
| 393 | materialLibraryFilePath += ".mtl";
|
---|
| 394 |
|
---|
| 395 | var dirPath = Path.GetDirectoryName(objFilePath);
|
---|
| 396 | var mtlFilePath = Path.Combine(dirPath, materialLibraryFilePath);
|
---|
| 397 |
|
---|
| 398 | if (!File.Exists(mtlFilePath))
|
---|
| 399 | {
|
---|
| 400 | Console.Error.WriteLine("Material file {0} does not exist", mtlFilePath);
|
---|
| 401 | return;
|
---|
| 402 | }
|
---|
| 403 |
|
---|
| 404 | ReadMtlFile(mtlFilePath);
|
---|
| 405 | }
|
---|
| 406 |
|
---|
| 407 | private void ReadMtlFile(string filePath)
|
---|
| 408 | {
|
---|
| 409 | var dirPath = Path.GetDirectoryName(filePath);
|
---|
| 410 |
|
---|
| 411 | ObjMaterial currentMaterial = null;
|
---|
| 412 |
|
---|
| 413 | foreach (string line in ReadLines(filePath))
|
---|
| 414 | {
|
---|
| 415 | var tokens = line.Split(whiteSpaceChars, StringSplitOptions.RemoveEmptyEntries);
|
---|
| 416 |
|
---|
| 417 | switch (tokens[0])
|
---|
| 418 | {
|
---|
| 419 | case "newmtl":
|
---|
| 420 | currentMaterial = new ObjMaterial(tokens[1]);
|
---|
| 421 | materials[currentMaterial.Name] = currentMaterial;
|
---|
| 422 | break;
|
---|
| 423 |
|
---|
| 424 | case "map_Kd":
|
---|
| 425 | string textureFilePath = Path.GetFullPath(Path.Combine(dirPath, tokens[1]));
|
---|
| 426 |
|
---|
| 427 | if (File.Exists(textureFilePath))
|
---|
| 428 | currentMaterial.TextureFilePath = textureFilePath;
|
---|
| 429 |
|
---|
| 430 | break;
|
---|
| 431 | }
|
---|
| 432 | }
|
---|
| 433 | }
|
---|
| 434 |
|
---|
| 435 | private void ImportObjects()
|
---|
| 436 | {
|
---|
| 437 | var inputs = new List<IndexedInput>();
|
---|
| 438 | IndexedInput positionInput, texCoordInput, normalInput;
|
---|
| 439 |
|
---|
| 440 | positionInput = new IndexedInput(Semantic.Position, new Source(points));
|
---|
| 441 | inputs.Add(positionInput);
|
---|
| 442 |
|
---|
| 443 | if (texCoords.Count > 0)
|
---|
| 444 | {
|
---|
| 445 | texCoordInput = new IndexedInput(Semantic.TexCoord, new Source(texCoords));
|
---|
| 446 | inputs.Add(texCoordInput);
|
---|
| 447 | }
|
---|
| 448 | else
|
---|
| 449 | {
|
---|
| 450 | texCoordInput = null;
|
---|
| 451 | }
|
---|
| 452 |
|
---|
| 453 | if (normals.Count > 0)
|
---|
| 454 | {
|
---|
| 455 | normalInput = new IndexedInput(Semantic.Normal, new Source(normals));
|
---|
| 456 | inputs.Add(normalInput);
|
---|
| 457 | }
|
---|
| 458 | else
|
---|
| 459 | {
|
---|
| 460 | normalInput = null;
|
---|
| 461 | }
|
---|
| 462 |
|
---|
| 463 | var geometry = new Geometry
|
---|
| 464 | {
|
---|
| 465 | Vertices = { positionInput }
|
---|
| 466 | };
|
---|
| 467 |
|
---|
| 468 | var geometryInstance = new GeometryInstance
|
---|
| 469 | {
|
---|
| 470 | Target = geometry
|
---|
| 471 | };
|
---|
| 472 |
|
---|
| 473 | foreach (var primitive in primitives.Where(p => p.Faces.Count > 0))
|
---|
| 474 | {
|
---|
| 475 | var meshPrimtives = new MeshPrimitives(MeshPrimitiveType.Polygons, inputs);
|
---|
| 476 |
|
---|
| 477 | foreach (var face in primitive.Faces)
|
---|
| 478 | {
|
---|
| 479 | meshPrimtives.VertexCounts.Add(face.Vertices.Length);
|
---|
| 480 |
|
---|
| 481 | foreach (var vertex in face.Vertices)
|
---|
| 482 | {
|
---|
| 483 | positionInput.Indices.Add(vertex.PointIndex);
|
---|
| 484 |
|
---|
| 485 | if (texCoordInput != null)
|
---|
| 486 | texCoordInput.Indices.Add(vertex.TexCoordIndex);
|
---|
| 487 |
|
---|
| 488 | if (normalInput != null)
|
---|
| 489 | normalInput.Indices.Add(vertex.NormalIndex);
|
---|
| 490 | }
|
---|
| 491 | }
|
---|
| 492 |
|
---|
| 493 | geometry.Primitives.Add(meshPrimtives);
|
---|
| 494 |
|
---|
| 495 | if (primitive.Material != null && primitive.Material.Material != null)
|
---|
| 496 | {
|
---|
| 497 | meshPrimtives.MaterialSymbol = "mat" + geometryInstance.Materials.Count;
|
---|
| 498 |
|
---|
| 499 | geometryInstance.Materials.Add(new MaterialInstance
|
---|
| 500 | {
|
---|
| 501 | Symbol = meshPrimtives.MaterialSymbol,
|
---|
| 502 | Target = primitive.Material.Material,
|
---|
| 503 | Bindings = { new MaterialBinding("diffuse_TEXCOORD", texCoordInput) }
|
---|
| 504 | });
|
---|
| 505 | }
|
---|
| 506 | }
|
---|
| 507 |
|
---|
| 508 | mainScene.Nodes.Add(new Node
|
---|
| 509 | {
|
---|
| 510 | Instances = { geometryInstance }
|
---|
| 511 | });
|
---|
| 512 | }
|
---|
| 513 |
|
---|
| 514 | private Vector3 ComputeFaceNormal(ObjVertex[] vertices)
|
---|
| 515 | {
|
---|
| 516 | if (vertices.Length < 3)
|
---|
| 517 | return Vector3.Up;
|
---|
| 518 |
|
---|
| 519 | var v1 = points[vertices[0].PointIndex];
|
---|
| 520 | var v2 = points[vertices[1].PointIndex];
|
---|
| 521 | var v3 = points[vertices[2].PointIndex];
|
---|
| 522 |
|
---|
| 523 | return Vector3.Normalize(Vector3.Cross(v2 - v1, v3 - v1));
|
---|
| 524 | }
|
---|
| 525 |
|
---|
| 526 | private static IEnumerable<string> ReadLines(string filePath)
|
---|
| 527 | {
|
---|
| 528 | using (var reader = File.OpenText(filePath))
|
---|
| 529 | {
|
---|
| 530 | for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
|
---|
| 531 | {
|
---|
| 532 | line = line.Trim();
|
---|
| 533 |
|
---|
| 534 | if (line.Length == 0)
|
---|
| 535 | continue;
|
---|
| 536 |
|
---|
| 537 | int commentStart = line.IndexOf('#');
|
---|
| 538 |
|
---|
| 539 | if (commentStart != -1)
|
---|
| 540 | {
|
---|
| 541 | line = line.Substring(0, commentStart).Trim();
|
---|
| 542 |
|
---|
| 543 | if (line.Length == 0)
|
---|
| 544 | continue;
|
---|
| 545 | }
|
---|
| 546 |
|
---|
| 547 | yield return line;
|
---|
| 548 | }
|
---|
| 549 | }
|
---|
| 550 | }
|
---|
| 551 | }
|
---|
| 552 | }
|
---|