/// <summary> /// Remeshes specified mesh. /// </summary> /// <param name="mesh">The mesh to remesh</param> /// <param name="toRemove">The number of triangles to remove.</param> /// <param name="progress">The progress listener.</param> private void Remesh(ref Mesh mesh, int toRemove, IProgressListener progress = null) { if (!(toRemove > mesh.Triangles.Count && mesh.Triangles.Count > 10)) { throw new ArgumentOutOfRangeException("toRemove", toRemove, "Invalid triangle count"); } progress.OnStart("Remeshing using AngleSum Error Metric"); var triangles = mesh.Triangles; var vertices = mesh.Vertices; if (triangles.Count <= toRemove) { progress.OnComplete("Failed. Too many triangles to remove"); } //int toRemove = (int)((m_Removed) * triangles.Count); int triangleLimit = triangles.Count - toRemove; foreach (var v in vertices) { UpdateVertexCost(v); } #if false // Adding mesh border penalty. foreach (var v in vertices) { // Border vertices has no more that 5 neighbors if (v.Neighbors.Count <= 4) { v.Cost += 1.0; } } #endif int index = 0; while (triangles.Count > triangleLimit) { var min = mesh.Vertices.Min(); if (mesh.JoinToNearestByCost(min, Mesh.JoinPositionType.Source)) { UpdateVertexCost(min); UpdateVertexNeighborsCost(min); progress.OnProgress(vertices.Count, triangles.Count); index += 2; } } progress.OnComplete("End of remeshing using AngleSum Error Metric"); }
/// <summary> /// Updates vertex normals and face planes. /// </summary> public void Invalidate(IProgressListener listener = null, InvalidateFlags invalidate = InvalidateFlags.All) { if (invalidate.HasFlag(InvalidateFlags.Faces)) { if (listener != null) { listener.OnStarted("Invalidating mesh faces"); } foreach (var face in m_Faces) { var v0 = m_Vertices[face[0]].Position; var v1 = m_Vertices[face[1]].Position; var v2 = m_Vertices[face[2]].Position; face.Plane = new Plane(v0, v1, v2); } if (listener != null) { listener.OnComplete("Invalidating mesh faces"); } } if (invalidate.HasFlag(InvalidateFlags.Vertices)) { if (listener != null) { listener.OnStarted("Invalidating mesh vertices"); } int i = 0; foreach (var vertex in m_Vertices) { foreach (var face in m_Faces) { if (face.HasVertex(vertex.Key)) { vertex.Value.Normal += face.Plane.Normal; } } if (listener != null) { listener.OnStep(++i, m_Vertices.Count); } vertex.Value.Normal.Normalize(); } if (listener != null) { listener.OnComplete("Invalidating mesh vertices"); } } }
private void Remesh(ref Mesh mesh, int toRemove, IProgressListener progress = null) { if (!(toRemove > mesh.Triangles.Count && mesh.Triangles.Count > 10)) { throw new ArgumentOutOfRangeException("toRemove", toRemove, "Invalid triangle count"); } var vertices = mesh.Vertices; progress.OnStart("Started remeshing"); int totalVertices = mesh.Vertices.Count; Debug.Assert(toRemove > 0); Random random = new Random(); while (toRemove > 0) { //vertices.ran var element = vertices.ElementAt(random.Next(0, vertices.Count - 1)); mesh.JoinToNearestByDistance(element); toRemove -= 2; progress.OnProgress(toRemove, totalVertices); } progress.OnComplete("Ended remeshing"); }
public void WriteMesh(ref Mesh mesh, IProgressListener listener = null) { long current = 0; long total = mesh.Vertices.Count + mesh.Faces.Count; listener.OnStarted("Mesh write"); foreach (var vertex in mesh.Vertices) { m_Writer.WriteLine(string.Format( CultureInfo.InvariantCulture, "v {0} {1} {2}", vertex.Value.Position.X.ToString("0.000000", CultureInfo.InvariantCulture), vertex.Value.Position.Y.ToString("0.000000", CultureInfo.InvariantCulture), vertex.Value.Position.Z.ToString("0.000000", CultureInfo.InvariantCulture))); ++current; if (listener != null) { listener.OnStep(current, total); } } int count = 0; foreach (var face in mesh.Faces) { m_Writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "f {0} {1} {2}", face[0], face[1], face[2])); ++count; ++current; if (listener != null) { listener.OnStep(current, total); } } if (listener != null) { listener.OnComplete("Mesh write"); } m_Writer.Close(); }
/// <summary> /// Reads raw mesh. /// </summary> /// <param name="vertices">The list of the vertices.</param> /// <param name="indices">The list of the indices.</param> /// <param name="progress">The optional progress listener.</param> public void ReadMesh(out List <Vertex> vertices, out List <int> indices, IProgressListener progress = null) { vertices = new List <Vertex>(); indices = new List <int>(); Regex r = new Regex(@" |//"); NumberStyles numberStyle = NumberStyles.Float; IFormatProvider numberFormatProvider = CultureInfo.InvariantCulture; progress.OnStart("Loading mesh started..."); while (inputReader.Peek() >= 0) { string line = inputReader.ReadLine(); String[] elements = r.Split(line); // List<Vertex> normals; switch (elements[0]) { case "v": { float x, y, z; if ( float.TryParse(elements[1], numberStyle, numberFormatProvider, out x) && float.TryParse(elements[2], numberStyle, numberFormatProvider, out y) && float.TryParse(elements[3], numberStyle, numberFormatProvider, out z) ) { Vertex w = new Vertex(new Vector3(x, y, z)); vertices.Add(w); } else { Trace.WriteLine(line); } break; } case "f": { int f1, f2, f3; int f1Index = 1; int f2Index = 2; int f3Index = 3; switch (elements.Length) { case 7: { //normal indexes in elements[2],elements[4] and elements[6] are ignored; f1Index = 1; f2Index = 3; f3Index = 5; goto case 4; } case 4: { if ( int.TryParse(elements[f1Index], numberStyle, numberFormatProvider, out f1) && int.TryParse(elements[f2Index], numberStyle, numberFormatProvider, out f2) && int.TryParse(elements[f3Index], numberStyle, numberFormatProvider, out f3) ) { indices.Add(f1 - 1); indices.Add(f2 - 1); indices.Add(f3 - 1); } else { Trace.WriteLine(line); throw new NotImplementedException("Only triangles are Implemented. Faces in file are not a triangles.That is bad:("); } } break; default: Trace.WriteLine("Invalid number of components"); break; } break; } case "vn": { break; } default: Trace.WriteLine("Unknown obj specifier."); break; } if (progress != null) { progress.OnProgress( inputReader.BaseStream.Position, inputReader.BaseStream.Length); } } if (progress != null) { progress.OnComplete("Mesh loaded successfully"); } }
/// <summary> /// Reads raw mesh. /// </summary> /// <param name="vertices">The list of the vertices.</param> /// <param name="indices">The list of the indices.</param> /// <param name="progress">The optional progress listener.</param> public void ReadMesh(out List<Vertex> vertices, out List<int> indices, IProgressListener progress = null) { vertices = new List<Vertex>(); indices = new List<int>(); Regex r = new Regex(@" |//"); NumberStyles numberStyle = NumberStyles.Float; IFormatProvider numberFormatProvider = CultureInfo.InvariantCulture; progress.OnStart("Loading mesh started..."); while (inputReader.Peek() >= 0) { string line = inputReader.ReadLine(); String[] elements = r.Split(line); // List<Vertex> normals; switch (elements[0]) { case "v": { float x, y, z; if ( float.TryParse(elements[1], numberStyle, numberFormatProvider, out x) && float.TryParse(elements[2], numberStyle, numberFormatProvider, out y) && float.TryParse(elements[3], numberStyle, numberFormatProvider, out z) ) { Vertex w = new Vertex(new Vector3(x, y, z)); vertices.Add(w); } else { Trace.WriteLine(line); } break; } case "f": { int f1, f2, f3; int f1Index = 1; int f2Index = 2; int f3Index = 3; switch (elements.Length) { case 7: { //normal indexes in elements[2],elements[4] and elements[6] are ignored; f1Index = 1; f2Index = 3; f3Index = 5; goto case 4; } case 4: { if ( int.TryParse(elements[f1Index], numberStyle, numberFormatProvider, out f1) && int.TryParse(elements[f2Index], numberStyle, numberFormatProvider, out f2) && int.TryParse(elements[f3Index], numberStyle, numberFormatProvider, out f3) ) { indices.Add(f1 -1); indices.Add(f2 -1); indices.Add(f3 -1); } else { Trace.WriteLine(line); throw new NotImplementedException("Only triangles are Implemented. Faces in file are not a triangles.That is bad:("); } } break; default: Trace.WriteLine("Invalid number of components"); break; } break; } case "vn": { break; } default: Trace.WriteLine("Unknown obj specifier."); break; } if (progress != null) { progress.OnProgress( inputReader.BaseStream.Position, inputReader.BaseStream.Length); } } if (progress != null) { progress.OnComplete("Mesh loaded successfully"); } }
/// <summary> /// Writes raw mesh to stream. /// </summary> /// <param name="vertices">The list of the vertices.</param> /// <param name="indices">The list of the indices.</param> /// <param name="progress">The optional progress listener.</param> public void WriteMesh(ref List<Vertex> vertices, ref List<int> indices, IProgressListener progress = null) { long current = 0; long total = vertices.Count + indices.Count; progress.OnStart("Mesh writing started..."); foreach (var vertex in vertices) { m_Writer.WriteLine(string.Format( CultureInfo.InvariantCulture, "v {0} {1} {2}", vertex.Position.X.ToString("0.000000", CultureInfo.InvariantCulture), vertex.Position.Y.ToString("0.000000", CultureInfo.InvariantCulture), vertex.Position.Z.ToString("0.000000", CultureInfo.InvariantCulture))); ++current; if (progress != null) { progress.OnProgress(current, total); } } int count = 0; foreach (var index in indices) { switch (count % 3) { case 0: { m_Writer.Write(string.Format(CultureInfo.InvariantCulture, "f {0} ", index + 1)); break; } case 1: { m_Writer.Write(string.Format(CultureInfo.InvariantCulture, "{0} ", index + 1)); break; } case 2: { m_Writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", index + 1)); break; } default: { Trace.WriteLine("Not supported"); break; } } ++count; ++current; if (progress != null) { progress.OnProgress(current, total); } } if (progress != null) { progress.OnComplete("Mesh writing complete"); } m_Writer.Close(); }
public virtual void Read(out Mesh mesh, IProgressListener listener = null) { mesh = new Mesh(); Regex r = new Regex(@" |//"); NumberStyles numberStyle = NumberStyles.Float; IFormatProvider numberFormatProvider = CultureInfo.InvariantCulture; if (listener != null) { listener.OnStarted("Load mesh"); } int indicesCount = 0; while (m_Reader.Peek() >= 0) { string line = m_Reader.ReadLine(); String[] elements = r.Split(line); // List<Vertex> normals; switch (elements[0]) { case "v": { float x, y, z; if ( float.TryParse(elements[1], numberStyle, numberFormatProvider, out x) && float.TryParse(elements[2], numberStyle, numberFormatProvider, out y) && float.TryParse(elements[3], numberStyle, numberFormatProvider, out z) ) { mesh.Vertices.Add(++indicesCount, new Vertex(new Vector3(x, y, z))); } else { Trace.WriteLine(line); } break; } case "f": { int f1, f2, f3; int f1Index = 1; int f2Index = 2; int f3Index = 3; switch (elements.Length) { case 7: { //normal indexes in elements[2],elements[4] and elements[6] are ignored; f1Index = 1; f2Index = 3; f3Index = 5; goto case 4; } case 4: { if ( int.TryParse(elements[f1Index], numberStyle, numberFormatProvider, out f1) && int.TryParse(elements[f2Index], numberStyle, numberFormatProvider, out f2) && int.TryParse(elements[f3Index], numberStyle, numberFormatProvider, out f3) ) { mesh.Faces.Add(new Triangle(f1, f2, f3)); } else { Trace.WriteLine(line); throw new NotImplementedException("Only triangles are Implemented. Faces in file are not a triangles.That is bad:("); } } break; default: Trace.WriteLine("Invalid number of components"); break; } break; } case "vn": { break; } default: Trace.WriteLine("Unknown obj specifier."); break; } if (listener != null) { listener.OnStep( m_Reader.BaseStream.Position, m_Reader.BaseStream.Length); } } if (listener != null) { listener.OnComplete("Mesh loaded successfully"); } mesh.Invalidate(listener, Mesh.InvalidateFlags.Faces); }
/// <summary> /// Writes raw mesh to stream. /// </summary> /// <param name="vertices">The list of the vertices.</param> /// <param name="indices">The list of the indices.</param> /// <param name="progress">The optional progress listener.</param> public void WriteMesh(ref List <Vertex> vertices, ref List <int> indices, IProgressListener progress = null) { long current = 0; long total = vertices.Count + indices.Count; progress.OnStart("Mesh writing started..."); foreach (var vertex in vertices) { m_Writer.WriteLine(string.Format( CultureInfo.InvariantCulture, "v {0} {1} {2}", vertex.Position.X.ToString("0.000000", CultureInfo.InvariantCulture), vertex.Position.Y.ToString("0.000000", CultureInfo.InvariantCulture), vertex.Position.Z.ToString("0.000000", CultureInfo.InvariantCulture))); ++current; if (progress != null) { progress.OnProgress(current, total); } } int count = 0; foreach (var index in indices) { switch (count % 3) { case 0: { m_Writer.Write(string.Format(CultureInfo.InvariantCulture, "f {0} ", index + 1)); break; } case 1: { m_Writer.Write(string.Format(CultureInfo.InvariantCulture, "{0} ", index + 1)); break; } case 2: { m_Writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", index + 1)); break; } default: { Trace.WriteLine("Not supported"); break; } } ++count; ++current; if (progress != null) { progress.OnProgress(current, total); } } if (progress != null) { progress.OnComplete("Mesh writing complete"); } m_Writer.Close(); }
private void Contract(int targetTriangles, IProgressListener listener = null) { SelectValidPairs(); if (listener != null) { listener.OnStarted("Compacting"); } int totalTriangles = m_Faces.Count - targetTriangles; int currentTriangle = 0; while (m_Faces.Count > targetTriangles) { if (listener != null) { listener.OnStep(currentTriangle, totalTriangles); } ++currentTriangle; double minError = (double)int.MaxValue; //KeyValuePair<VertexPair, double> min; VertexPair minPair = new VertexPair() { First = 0, Second = 0 }; foreach (KeyValuePair<VertexPair, double> e in m_Errors) { if (e.Value < minError) { minError = e.Value; minPair = e.Key; } } Vector3 error; ComputeError(minPair.First, minPair.Second, out error); #if false VertexSplit split = new VertexSplit(); //var fv split.First.Position = m_Vertices[minPair.First].Position; split.Second.Position = m_Vertices[minPair.Second].Position; split.Target.Position = error; m_Splits.Add(split); #endif var vertex = m_Vertices[minPair.First]; vertex.Position = error; m_Quadrics[minPair.First] = m_Quadrics[minPair.First] + m_Quadrics[minPair.Second]; for (int i = m_Faces.Count - 1; i != 0; ) { var face = m_Faces[i]; for (int j = 0; j < 3; ++j) { if (face[j] == minPair.Second) { if (face[0] == minPair.First || face[1] == minPair.First || face[2] == minPair.First) { m_Faces.Remove(face); } else { face[j] = minPair.First; } --i; break; } else if (j == 2) { --i; } } } m_Vertices.Remove(minPair.Second); KeyValuePair<VertexPair, double> pair; #if false for (int iter = m_Errors.Count - 1; iter != 0; ) { pair = m_Errors.ElementAt(iter); if (pair.Key.First == minPair.Second && pair.Key.Second != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(iter).Key); m_Errors.Add( new VertexPair() { First = Math.Min(minPair.First, pair.Key.Second), Second = Math.Max(minPair.First, pair.Key.Second) }, 0.0); --iter; } else if (pair.Key.Second == minPair.Second && pair.Key.First != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(iter).Key); m_Errors.Add( new VertexPair() { First = Math.Min(minPair.First, pair.Key.First), Second = Math.Max(minPair.First, pair.Key.First) }, 0.0); --iter; } else { --iter; } } #else for (int it = 0; it < m_Errors.Count; ++it) { pair = m_Errors.ElementAt(it); if (pair.Key.First == minPair.Second && pair.Key.Second != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(it).Key); var key = new VertexPair() { First = Math.Min(minPair.First, pair.Key.Second), Second = Math.Max(minPair.First, pair.Key.Second) }; if (!m_Errors.ContainsKey(key)) { m_Errors.Add( key, 0.0); } } else if (pair.Key.Second == minPair.Second && pair.Key.First != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(it).Key); var key = new VertexPair() { First = Math.Min(minPair.First, pair.Key.First), Second = Math.Max(minPair.First, pair.Key.First) }; if (!m_Errors.ContainsKey(key)) { m_Errors.Add( key, 0.0); } } } #endif m_Errors.Remove(minPair); for (int it = 0; it < m_Errors.Count; ++it) { var key = m_Errors.ElementAt(it).Key; if (key.First == minPair.First) { m_Errors[key] = ComputeError(minPair.First, key.Second); } if (key.Second == minPair.First) { m_Errors[key] = ComputeError(minPair.First, key.First); } } /*foreach (var e in m_Errors) { var p = e.Key; if (p.First == minPair.First) { m_Errors[p] = ComputeError(minPair.First, p.Second); } if (p.Second == minPair.First) { m_Errors[p] = ComputeError(minPair.First, p.First); } }*/ } if (listener != null) { listener.OnComplete("Compacting"); } }
private void Contract(int targetTriangles, IProgressListener listener = null) { SelectValidPairs(); if (listener != null) { listener.OnStarted("Compacting"); } int totalTriangles = m_Faces.Count - targetTriangles; int currentTriangle = 0; while (m_Faces.Count > targetTriangles) { if (listener != null) { listener.OnStep(currentTriangle, totalTriangles); } ++currentTriangle; double minError = (double)int.MaxValue; //KeyValuePair<VertexPair, double> min; VertexPair minPair = new VertexPair() { First = 0, Second = 0 }; foreach (KeyValuePair <VertexPair, double> e in m_Errors) { if (e.Value < minError) { minError = e.Value; minPair = e.Key; } } Vector3 error; ComputeError(minPair.First, minPair.Second, out error); #if false VertexSplit split = new VertexSplit(); //var fv split.First.Position = m_Vertices[minPair.First].Position; split.Second.Position = m_Vertices[minPair.Second].Position; split.Target.Position = error; m_Splits.Add(split); #endif var vertex = m_Vertices[minPair.First]; vertex.Position = error; m_Quadrics[minPair.First] = m_Quadrics[minPair.First] + m_Quadrics[minPair.Second]; for (int i = m_Faces.Count - 1; i != 0;) { var face = m_Faces[i]; for (int j = 0; j < 3; ++j) { if (face[j] == minPair.Second) { if (face[0] == minPair.First || face[1] == minPair.First || face[2] == minPair.First) { m_Faces.Remove(face); } else { face[j] = minPair.First; } --i; break; } else if (j == 2) { --i; } } } m_Vertices.Remove(minPair.Second); KeyValuePair <VertexPair, double> pair; #if false for (int iter = m_Errors.Count - 1; iter != 0;) { pair = m_Errors.ElementAt(iter); if (pair.Key.First == minPair.Second && pair.Key.Second != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(iter).Key); m_Errors.Add( new VertexPair() { First = Math.Min(minPair.First, pair.Key.Second), Second = Math.Max(minPair.First, pair.Key.Second) }, 0.0); --iter; } else if (pair.Key.Second == minPair.Second && pair.Key.First != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(iter).Key); m_Errors.Add( new VertexPair() { First = Math.Min(minPair.First, pair.Key.First), Second = Math.Max(minPair.First, pair.Key.First) }, 0.0); --iter; } else { --iter; } } #else for (int it = 0; it < m_Errors.Count; ++it) { pair = m_Errors.ElementAt(it); if (pair.Key.First == minPair.Second && pair.Key.Second != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(it).Key); var key = new VertexPair() { First = Math.Min(minPair.First, pair.Key.Second), Second = Math.Max(minPair.First, pair.Key.Second) }; if (!m_Errors.ContainsKey(key)) { m_Errors.Add( key, 0.0); } } else if (pair.Key.Second == minPair.Second && pair.Key.First != minPair.First) { m_Errors.Remove(m_Errors.ElementAt(it).Key); var key = new VertexPair() { First = Math.Min(minPair.First, pair.Key.First), Second = Math.Max(minPair.First, pair.Key.First) }; if (!m_Errors.ContainsKey(key)) { m_Errors.Add( key, 0.0); } } } #endif m_Errors.Remove(minPair); for (int it = 0; it < m_Errors.Count; ++it) { var key = m_Errors.ElementAt(it).Key; if (key.First == minPair.First) { m_Errors[key] = ComputeError(minPair.First, key.Second); } if (key.Second == minPair.First) { m_Errors[key] = ComputeError(minPair.First, key.First); } } /*foreach (var e in m_Errors) * { * var p = e.Key; * if (p.First == minPair.First) * { * m_Errors[p] = ComputeError(minPair.First, p.Second); * } * * if (p.Second == minPair.First) * { * m_Errors[p] = ComputeError(minPair.First, p.First); * } * }*/ } if (listener != null) { listener.OnComplete("Compacting"); } }