/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> private static void QuadSplitGeometry <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { // create face vertices foreach (var f in mesh.Faces) { var v = mesh.AddVertex(); if (!f.IsUnused) { position.Set(v, f.Vertices.Mean(position.Get)); } } // create edge vertices foreach (var he in mesh.Edges) { var v = mesh.AddVertex(); if (!he.IsUnused) { var p = (position.Get(he.Start) + position.Get(he.End)) * 0.5; position.Set(v, p); } } }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> /// <param name="boundaryType"></param> private static void CatmullClarkGeometry <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, SmoothBoundaryType boundaryType) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var verts = mesh.Vertices; int fv0 = verts.Count; // index of first face vertex // create face vertices foreach (var f in mesh.Faces) { var v = mesh.AddVertex(); if (!f.IsUnused) { position.Set(v, f.Vertices.Mean(position.Get)); } } // create edge vertices foreach (var he0 in mesh.Edges) { var v = mesh.AddVertex(); if (he0.IsUnused) { continue; } if (he0.IsBoundary) { var p = (position.Get(he0.Start) + position.Get(he0.End)) * 0.5; position.Set(v, p); continue; } var he1 = he0.Twin; var p0 = position.Get(he0.Start); var p1 = position.Get(he1.Start); var p2 = position.Get(verts[he0.Face.Index + fv0]); var p3 = position.Get(verts[he1.Face.Index + fv0]); position.Set(v, (p0 + p1 + p2 + p3) * 0.25); } // smooth old vertices CatmullClarkSmooth(mesh, position, boundaryType); }
/// <summary> /// Implementation currently ignores texture coordinates and normals. /// </summary> /// <typeparam name="TV"></typeparam> /// <typeparam name="TE"></typeparam> /// <typeparam name="TF"></typeparam> /// <param name="path"></param> /// <param name="mesh"></param> /// <param name="setPosition"></param> public static void ReadFromObj <TV, TE, TF>(string path, HeMesh <TV, TE, TF> mesh, Action <TV, Vec3d> setPosition) where TV : HeMesh <TV, TE, TF> .Vertex where TE : HeMesh <TV, TE, TF> .Halfedge where TF : HeMesh <TV, TE, TF> .Face { var verts = mesh.Vertices; var faces = mesh.Faces; var face = new List <int>(); using (var reader = new StreamReader(path)) { string line; while ((line = reader.ReadLine()) != null) { // skip empty lines and comments if (line.Length == 0 || line[0] == '#') { continue; } // check the first character var segments = line.Split(ObjUtil.Separators, StringSplitOptions.RemoveEmptyEntries); switch (segments[0]) { case "v": { // parse vertex double x = double.Parse(segments[1]); double y = double.Parse(segments[2]); double z = double.Parse(segments[3]); var v = mesh.AddVertex(); setPosition(v, new Vec3d(x, y, z)); break; } case "f": { // parse face for (int i = 1; i < segments.Length; i++) { var ids = segments[i].Split(ObjUtil.FaceSeparators); face.Add(int.Parse(ids[0]) - 1); } mesh.AddFace(face); face.Clear(); break; } } } } }
/* * /// <summary> * /// Applies a single iteration of Catmull-Clark subdivision to the given mesh. * /// http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface * /// http://w3.impa.br/~lcruz/courses/cma/surfaces.html * /// </summary> * /// <typeparam name="V"></typeparam> * /// <typeparam name="E"></typeparam> * /// <typeparam name="F"></typeparam> * /// <param name="mesh"></param> * /// <param name="position"></param> * /// <param name="boundaryType"></param> * public static void CatmullClark<V, E, F>(HeMesh<V, E, F> mesh, Property<V, Vec3d> position, SmoothBoundaryType boundaryType, bool parallel) * where V : HeMeshBase<V, E, F>.Vertex * where E : HeMeshBase<V, E, F>.Halfedge * where F : HeMeshBase<V, E, F>.Face * { * CatmullClarkGeometry(mesh, position, boundaryType, parallel); * QuadSplitTopology(mesh); * } * * * /// <summary> * /// * /// </summary> * private static void CatmullClarkGeometry<V, E, F>(HeMesh<V, E, F> mesh, Property<V, Vec3d> position, SmoothBoundaryType boundaryType, bool parallel) * where V : HeMeshBase<V, E, F>.Vertex * where E : HeMeshBase<V, E, F>.Halfedge * where F : HeMeshBase<V, E, F>.Face * { * var verts = mesh.Vertices; * var edges = mesh.Edges; * var faces = mesh.Faces; * * int fv0 = verts.Count; // index of first face vertex * int ev0 = verts.Count + faces.Count; * * // add all new vertices * mesh.AddVertices(faces.Count); * mesh.AddVertices(edges.Count); * * // set attributes of face vertices * Action<Tuple<int, int>> setFaceVerts = range => * { * for (int i = range.Item1; i < range.Item2; i++) * { * var f = faces[i]; * * if (!f.IsUnused) * position.Set(verts[i + fv0], f.Vertices.Mean(position.Get)); * } * }; * * // set attributes of edge vertices * Action<Tuple<int, int>> setEdgeVerts = range => * { * for (int i = range.Item1; i < range.Item2; i++) * { * var he0 = edges[i]; * if (he0.IsUnused) continue; * * if (he0.IsBoundary) * { * position.Set(verts[i + ev0], he0.Lerp(position.Get, 0.5)); * continue; * } * * var he1 = he0.Twin; * var p0 = position.Get(he0.Start); * var p1 = position.Get(he1.Start); * var p2 = position.Get(verts[he0.Face.Index + fv0]); * var p3 = position.Get(verts[he1.Face.Index + fv0]); * position.Set(verts[i + ev0], (p0 + p1 + p2 + p3) * 0.25); * } * }; * * // set attributes of old vertices * //CatmullClarkSmooth(mesh, position, boundaryType); * Action<Tuple<int, int>> setOldVerts = range => * { * for (int i = range.Item1; i < range.Item2; i++) * { * var v = verts[i]; * if (v.IsUnused) continue; * * if (v.IsBoundary) * { * var he0 = v.FirstOut; * var he1 = he0.PrevInFace; * var p0 = position.Get(verts[(he0.Index >> 1) + ev0]); * var p1 = position.Get(verts[(he1.Index >> 1) + ev0]); * position.Set(v, position.Get(v) * 0.5 + (p0 + p1) * 0.25); * } * else * { * Vec3d fsum = new Vec3d(); * Vec3d esum = new Vec3d(); * int n = 0; * * foreach (var he in v.OutgoingHalfedges) * { * fsum += position.Get(verts[he.Face.Index + fv0]); * esum += position.Get(verts[(he.Index >> 1) + ev0]); * n++; * } * * double t = 1.0 / n; * position.Set(v, (position.Get(v) * (n - 3) + fsum * t + 2 * esum * t) * t); * } * } * }; * * * if (parallel) * { * Parallel.ForEach(Partitioner.Create(0, faces.Count), setFaceVerts); * Parallel.ForEach(Partitioner.Create(0, edges.Count), setEdgeVerts); * Parallel.ForEach(Partitioner.Create(0, verts.Count), setOldVerts); * } * else * { * setFaceVerts(Tuple.Create(0, faces.Count)); * setEdgeVerts(Tuple.Create(0, edges.Count)); * setOldVerts(Tuple.Create(0, verts.Count)); * } * } */ #endregion /// <summary> /// If using external buffers to store vertex attributes, the number of vertices after subdivision equals the sum of the number of vertices and faces in the initial mesh. /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> /// <param name="skipBoundary"></param> public static void Diagonalize <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, bool skipBoundary) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var edges = mesh.Edges; var faces = mesh.Faces; int ne = edges.Count; int nf = faces.Count; // stellate faces for (int i = 0; i < nf; i++) { var f = faces[i]; if (f.IsUnused) { continue; } var v = mesh.AddVertex(); position.Set(v, f.GetBarycenter(position.Get)); mesh.PokeFaceImpl(f.First, v); } // merge faces if (skipBoundary) { for (int i = 0; i < ne; i++) { var he = edges[i]; if (he.IsUnused || he.IsBoundary) { continue; } mesh.MergeFaces(he); } } else { for (int i = 0; i < ne; i++) { var he = edges[i]; if (he.IsUnused) { continue; } mesh.MergeFaces(he); } } }
/// <summary> /// Reads this buffer to the given mesh. /// </summary> public void ReadTo <V, E, F>(HeMesh <V, E, F> mesh, Action <V, VA> setVertexAttributes = null, Action <E, EA> setHedgeAttributes = null, Action <F, FA> setFaceAttributes = null) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var verts = mesh.Vertices; var hedges = mesh.Halfedges; var faces = mesh.Faces; int nv = verts.Count; int nhe = hedges.Count; int nf = faces.Count; // add new vertices for (int i = 0; i < _vertexRefs.Length; i++) { mesh.AddVertex(); } // add new halfedges for (int i = 0; i < _hedgeRefs.Length; i += 2) { mesh.AddEdge(); } // add new faces for (int i = 0; i < _faceRefs.Length; i++) { mesh.AddFace(); } // link up vertices for (int i = 0; i < _vertexRefs.Length; i++) { var v = verts[i + nv]; var first = _vertexRefs[i]; if (first != -1) { v.First = hedges[first + nhe]; } } // link up halfedges for (int i = 0; i < _hedgeRefs.Length; i++) { var he = hedges[i + nhe]; var refs = _hedgeRefs[i]; var prev = refs[0]; if (prev != -1) { he.Previous = hedges[prev + nhe]; } he.Next = hedges[refs[1] + nhe]; he.Start = verts[refs[2] + nv]; var face = refs[3]; if (face != -1) { he.Face = faces[face + nf]; } } // link up faces for (int i = 0; i < _faceRefs.Length; i++) { var f = faces[i + nf]; var first = _faceRefs[i]; if (first != -1) { f.First = hedges[first + nhe]; } } // TODO validate topology? // set vertex attributes if (setVertexAttributes != null) { for (int i = 0; i < _vertexAttributes.Length; i++) { setVertexAttributes(verts[i + nv], _vertexAttributes[i]); } } // set vertex attributes if (setHedgeAttributes != null) { for (int i = 0; i < _hedgeAttributes.Length; i++) { setHedgeAttributes(hedges[i + nhe], _hedgeAttributes[i]); } } // set vertex attributes if (setFaceAttributes != null) { for (int i = 0; i < _faceAttributes.Length; i++) { setFaceAttributes(faces[i + nf], _faceAttributes[i]); } } }