/// <summary> /// Loads an OBJ file from a stream. /// </summary> /// <param name="stream">A stream with OBJ file data.</param> private static TriMesh LoadObjStream(Stream stream) { TriMesh mesh = new TriMesh(); mesh.Traits.HasFaceVertexNormals = true; mesh.Traits.HasTextureCoordinates = true; StreamReader sr = new StreamReader(stream); ObjFileProcessorState state = new ObjFileProcessorState(); string line; while ((line = sr.ReadLine()) != null) { ProcessObjLine(mesh, line, state); } if (state.VertexTextureCoords.Count == mesh.Vertices.Count) { for (int i = 0; i < mesh.Vertices.Count; i++) { mesh.Vertices[i].Traits.UV = state.VertexTextureCoords[i]; } } mesh.TrimExcess(); return mesh; }
/// <summary> /// Loads an OBJ file from a stream. /// </summary> /// <param name="stream">A stream with OBJ file data.</param> private static TriMesh LoadObjStream(Stream stream) { TriMesh mesh = new TriMesh(); mesh.Traits.HasFaceVertexNormals = true; mesh.Traits.HasTextureCoordinates = true; StreamReader sr = new StreamReader(stream); ObjFileProcessorState state = new ObjFileProcessorState(); string line; while ((line = sr.ReadLine()) != null) { ProcessObjLine(mesh, line, state); } if (state.VertexTextureCoords.Count == mesh.Vertices.Count) { for (int i = 0; i < mesh.Vertices.Count; i++) { mesh.Vertices[i].Traits.UV = state.VertexTextureCoords[i]; } } mesh.TrimExcess(); return(mesh); }
/// <summary> /// Loads an OBJ file from a stream. /// </summary> /// <param name="stream">A stream with OBJ file data.</param> protected void LoadObjStream(Stream stream) { Traits.HasFaceVertexNormals = true; Traits.HasTextureCoordinates = true; StreamReader sr = new StreamReader(stream); ObjFileProcessorState state = new ObjFileProcessorState(); string line; while ((line = sr.ReadLine()) != null) { ProcessObjLine(line, state); } TrimExcess(); }
/// <summary> /// Loads an OBJ file from a stream. /// </summary> /// <param name="stream">A stream with OBJ file data.</param> protected void LoadObjStream(Stream stream, bool hasVertexNormals = true, bool hasTextureCoords = false) { Traits.HasFaceVertexNormals = hasVertexNormals; Traits.HasTextureCoordinates = hasTextureCoords; StreamReader sr = new StreamReader(stream); ObjFileProcessorState state = new ObjFileProcessorState(); string line; while ((line = sr.ReadLine()) != null) { ProcessObjLine(line, state); } // add per-vertex properties //if (state.VertexNormals.Count == this.Vertices.Count) //{ // for (int i = 0; i < this.Vertices.Count; i++) // { // this.Vertices[i].Traits.Normal = state.VertexNormals[i]; // } // this.Traits.HasFaceVertexNormals = true; //} //else //{ // this.Traits.HasFaceVertexNormals = false; //} //if (state.VertexTextureCoords.Count == this.Vertices.Count) //{ // for (int i = 0; i < this.Vertices.Count; i++) // { // this.Vertices[i].Traits.TextureCoordinate = state.VertexTextureCoords[i]; // } // this.Traits.HasTextureCoordinates = true; //} //else //{ // this.Traits.HasTextureCoordinates = false; //} TrimExcess(); }
/// <summary> /// Processes a line from an OBJ file. /// </summary> /// <param name="line">A line from an OBJ file.</param> /// <param name="state">An object that manages state between calls.</param> private static void ProcessObjLine(TriMesh mesh, string line, ObjFileProcessorState state) { // Trim out comments (allow comments trailing on a line) int commentStart = line.IndexOf('#'); if (commentStart != -1) { line = line.Substring(0, commentStart); } // Tokenize line string[] tokens = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); // Process line based on the keyword used if (tokens.Length > 0) { int? v; float x, y, z; TriMesh.Vertex[] faceVertices; int?[] vt, vn; switch (tokens[0]) { case "v": // Vertex if (tokens.Length != 4) { throw new IOException("Vertices in the OBJ file must have 3 coordinates."); } x = Single.Parse(tokens[1]); y = Single.Parse(tokens[2]); z = Single.Parse(tokens[3]); mesh.Vertices.Add(new VertexTraits(x, y, z)); break; case "vt": // Vertex texture if (tokens.Length != 3 && tokens.Length != 4) { throw new IOException("Texture coordinates in the OBJ file must have 2 or 3 coordinates."); } x = Single.Parse(tokens[1]); y = Single.Parse(tokens[2]); state.VertexTextureCoords.Add(new Vector2D(x, y)); break; case "vn": // Vertex normal if (tokens.Length != 4) { throw new IOException("Vertex normals in the OBJ file must have 3 coordinates."); } x = Single.Parse(tokens[1]); y = Single.Parse(tokens[2]); z = Single.Parse(tokens[3]); state.VertexNormals.Add(new Vector3D(x, y, z)); break; case "f": // Face faceVertices = new TriMesh.Vertex[tokens.Length - 1]; vt = new int?[tokens.Length - 1]; vn = new int?[tokens.Length - 1]; // Parse vertex/texture coordinate/normal indices for (int i = 0; i < faceVertices.Length; ++i) { string[] vertexTokens = tokens[i + 1].Split("/".ToCharArray()); v = Int32.Parse(vertexTokens[0]); if (vertexTokens.Length > 1 && vertexTokens[1].Length > 0) { vt[i] = Int32.Parse(vertexTokens[1]); } else { mesh.Traits.HasTextureCoordinates = false; } if (vertexTokens.Length > 2 && vertexTokens[2].Length > 0) { vn[i] = Int32.Parse(vertexTokens[2]); } else { mesh.Traits.HasFaceVertexNormals = false; } faceVertices[i] = mesh.Vertices[v.Value - 1]; } try { TriMesh.Face[] addedFaces = mesh.Faces.AddTriangles(faceVertices); // Set texture coordinates and normals if any are given for (int i = 0; i < faceVertices.Length; ++i) { TriMesh.HalfEdge faceVertex; if (vt[i].HasValue || vn[i].HasValue) { foreach (TriMesh.Face f in addedFaces) { faceVertex = f.FindHalfedgeTo(faceVertices[i]); if (faceVertex != null) // Make sure vertex belongs to face if triangularization is on { if (vt[i].HasValue) { faceVertex.Traits.TextureCoordinate = state.VertexTextureCoords[vt[i].Value - 1]; } if (vn[i].HasValue) { faceVertex.Traits.Normal = state.VertexNormals[vn[i].Value - 1]; } } } } } } catch { } break; } } }
/// <summary> /// Processes a line from an OBJ file. /// </summary> /// <param name="line">A line from an OBJ file.</param> /// <param name="state">An object that manages state between calls.</param> private void ProcessObjLine(string line, ObjFileProcessorState state) { // Trim out comments (allow comments trailing on a line) int commentStart = line.IndexOf('#'); if (commentStart != -1) { line = line.Substring(0, commentStart); } // Tokenize line string[] tokens = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); // Process line based on the keyword used if (tokens.Length > 0) { int? v; float x, y, z; Vertex[] faceVertices; int?[] vt, vn; switch (tokens[0]) { case "v": // Vertex if (tokens.Length != 4) { throw new IOException("Vertices in the OBJ file must have 3 coordinates."); } x = Single.Parse(tokens[1], CultureInfo.InvariantCulture); y = Single.Parse(tokens[2], CultureInfo.InvariantCulture); z = Single.Parse(tokens[3], CultureInfo.InvariantCulture); Vertices.Add(new VertexTraits(x, y, z)); break; case "vt": // Vertex texture if (tokens.Length != 3 && tokens.Length != 4) { throw new IOException("Texture coordinates in the OBJ file must have 2 or 3 coordinates."); } x = Single.Parse(tokens[1], CultureInfo.InvariantCulture); y = Single.Parse(tokens[2], CultureInfo.InvariantCulture); state.VertexTextureCoords.Add(new Point(x, y)); break; case "vn": // Vertex normal if (tokens.Length != 4) { throw new IOException("Vertex normals in the OBJ file must have 3 coordinates."); } x = Single.Parse(tokens[1], CultureInfo.InvariantCulture); y = Single.Parse(tokens[2], CultureInfo.InvariantCulture); z = Single.Parse(tokens[3], CultureInfo.InvariantCulture); state.VertexNormals.Add(new Vector3D(x, y, z)); break; case "f": // Face faceVertices = new Vertex[tokens.Length - 1]; vt = new int?[tokens.Length - 1]; vn = new int?[tokens.Length - 1]; // Parse vertex/texture coordinate/normal indices for (int i = 0; i < faceVertices.Length; ++i) { string[] vertexTokens = tokens[i + 1].Split("/".ToCharArray()); v = Int32.Parse(vertexTokens[0]); if (vertexTokens.Length > 1 && vertexTokens[1].Length > 0) { vt[i] = Int32.Parse(vertexTokens[1]); } else { Traits.HasTextureCoordinates = false; } if (vertexTokens.Length > 2 && vertexTokens[2].Length > 0) { vn[i] = Int32.Parse(vertexTokens[2]); } else { Traits.HasFaceVertexNormals = false; } faceVertices[i] = Vertices[v.Value - 1]; } Face[] addedFaces = Faces.AddTriangles(faceVertices); // Set texture coordinates and normals if any are given for (int i = 0; i < faceVertices.Length; ++i) { Halfedge faceVertex; if (vt[i].HasValue || vn[i].HasValue) { foreach (Face f in addedFaces) { faceVertex = f.FindHalfedgeTo(faceVertices[i]); if (faceVertex != null) // Make sure vertex belongs to face if triangularization is on { if (vt[i].HasValue) { faceVertex.Traits.TextureCoordinate = state.VertexTextureCoords[vt[i].Value - 1]; } if (vn[i].HasValue) { faceVertex.Traits.Normal = state.VertexNormals[vn[i].Value - 1]; } } } } } break; } } }