/// <summary> /// Parses one object from input .obj file. /// </summary> /// <param name="reader">Reader with current position in .obj file.</param> /// <returns> /// Triangle mesh with name of the associated material. /// If material name is an empty string, no material was used. /// </returns> private Tuple <string, FastTriangleMesh> parseObject(StreamReader reader, ref int verticesCount, ref int txtCoordsCount, ref int normalsCount) { SceneBrep scene = new SceneBrep(); string materialName = ""; int v0 = scene.Vertices; int lastVertex = v0 - 1; int faces = 0; List <Vector2> txtCoords = new List <Vector2>(256); List <Vector3> normals = new List <Vector3>(256); int lastTxtCoord = -1; int lastNormal = -1; int[] f = new int[3]; while (!reader.EndOfStream) { // Check if there is defined "o" tag, if yes, it is the end of definition of one object // and return this object. if ((char)reader.Peek() == 'o') { break; } string line = reader.ReadLine(); string[] tokens = line.Split(); if (tokens.Length == 0) { continue; } switch (tokens[0]) { case "v": if (tokens.Length != 4) { throw new Exception("Incorrect .obj file."); } // Add vertex. float x, y, z; if (!float.TryParse(tokens[1], NumberStyles.Float, CultureInfo.InvariantCulture, out x) || !float.TryParse(tokens[2], NumberStyles.Float, CultureInfo.InvariantCulture, out y) || !float.TryParse(tokens[3], NumberStyles.Float, CultureInfo.InvariantCulture, out z)) { throw new Exception("Incorrect .obj file."); } lastVertex = scene.AddVertex(new Vector3(x, y, z)); break; case "vt": if (tokens.Length != 3) { throw new Exception("Incorrect .obj file."); } // Add vertex. float u, v; if (!float.TryParse(tokens[1], NumberStyles.Float, CultureInfo.InvariantCulture, out u) || !float.TryParse(tokens[2], NumberStyles.Float, CultureInfo.InvariantCulture, out v)) { throw new Exception("Incorrect .obj file."); } txtCoords.Add(new Vector2(u, v)); ++lastTxtCoord; break; case "vn": if (tokens.Length != 4) { throw new Exception("Incorrect .obj file."); } // Add vertex. float nx, ny, nz; if (!float.TryParse(tokens[1], NumberStyles.Float, CultureInfo.InvariantCulture, out nx) || !float.TryParse(tokens[2], NumberStyles.Float, CultureInfo.InvariantCulture, out ny) || !float.TryParse(tokens[3], NumberStyles.Float, CultureInfo.InvariantCulture, out nz)) { throw new Exception("Incorrect .obj file."); } normals.Add(new Vector3(nx, ny, nz)); break; case "usemtl": // Set name of the material used for this object. if (tokens.Length != 2) { throw new Exception("Incorrect .obj file."); } materialName = tokens[1]; break; case "f": // Face must be formed by at least three vertices. if (tokens.Length < 4) { continue; } // Number of vertices. int N = tokens.Length - 1; // Reuse same array for each face and resize it if needed. if (f.Length < N) { f = new int[N]; } // Shrink it if less vertices is used. if (N < f.Length) { f = new int[N]; } int i; for (i = 0; i < N; i++) // read indices for one vertex { string[] vt = tokens[i + 1].Split('/'); int ti, ni; ti = ni = 0; // 0 => value not present // 0 .. vertex coord index if (!int.TryParse(vt[0], out f[i]) || f[i] == 0) { break; } if (f[i] > 0) { f[i] = v0 + f[i] - 1 - verticesCount; } else { f[i] = lastVertex + 1 - f[i]; } if (vt.Length > 1) { // 1 .. texture coord index (not yet) if (!int.TryParse(vt[1], out ti)) { ti = 0; } if (vt.Length > 2) { // 2 .. normal vector index if (!int.TryParse(vt[2], out ni)) { ni = 0; } } } // there was a texture coord.. if (ti != 0) { if (ti > 0) { ti = ti - txtCoordsCount - 1; } else { ti = lastTxtCoord + 1 - ti; } if (ti >= 0 && ti < txtCoords.Count) { scene.SetTxtCoord(f[i], txtCoords[ti]); } } // there was a normal.. if (ni != 0) { if (ni > 0) { ni = ni - normalsCount - 1; } else { ni = lastNormal + 1 - ni; } if (ni >= 0 && ni < normals.Count) { scene.SetNormal(f[i], normals[ni]); } } } N = i; for (i = 1; i < N - 1; i++) { scene.AddTriangle(f[0], f[i], f[i + 1]); faces++; } break; } } // Update count of vertices, txt coords and normals. verticesCount += scene.Vertices; txtCoordsCount += txtCoords.Count; normalsCount += normals.Count; scene.BuildCornerTable(); scene.BuildBoundingBox(); return(new Tuple <string, FastTriangleMesh>(materialName, new FastTriangleMesh(scene))); }