Exemple #1
0
        // [TODO] projection pass
        //   - only project vertices modified by smooth pass?
        //   - and/or verts in set of modified edges?
        protected virtual void TrackedFaceProjectionPass()
        {
            var normalTarget = ProjectionTarget as IOrientedProjectionTarget;

            if (normalTarget == null)
            {
                throw new Exception("RemesherPro.TrackedFaceProjectionPass: projection target does not have normals!");
            }

            InitializeBuffersForFacePass();

            var buffer_lock = new SpinLock();

            // this function computes rotated position of triangle, such that it
            // aligns with face normal on target surface. We accumulate weighted-average
            // of vertex positions, which we will then use further down where possible.
            Action <int> process_triangle = (tid) =>
            {
                Vector3d normal; double area; Vector3d centroid;
                mesh.GetTriInfo(tid, out normal, out area, out centroid);

                Vector3d projNormal;
                Vector3d projPos = normalTarget.Project(centroid, out projNormal);

                Index3i  tv = mesh.GetTriangle(tid);
                Vector3d v0 = mesh.GetVertex(tv.a), v1 = mesh.GetVertex(tv.b), v2 = mesh.GetVertex(tv.c);

                // ugh could probably do this more efficiently...
                var triF = new Frame3f(centroid, normal);
                v0 = triF.ToFrameP(ref v0); v1 = triF.ToFrameP(ref v1); v2 = triF.ToFrameP(ref v2);
                triF.AlignAxis(2, (Vector3f)projNormal);
                triF.Origin = (Vector3f)projPos;
                v0          = triF.FromFrameP(ref v0); v1 = triF.FromFrameP(ref v1); v2 = triF.FromFrameP(ref v2);

                double dot = normal.Dot(projNormal);
                dot = MathUtil.Clamp(dot, 0, 1.0);
                double w = area * (dot * dot * dot);

                bool taken = false;
                buffer_lock.Enter(ref taken);
                vBufferV[tv.a] += w * v0; vBufferVWeights[tv.a] += w;
                vBufferV[tv.b] += w * v1; vBufferVWeights[tv.b] += w;
                vBufferV[tv.c] += w * v2; vBufferVWeights[tv.c] += w;
                buffer_lock.Exit();
            };

            // compute face-aligned vertex positions
            gParallel.ForEach(mesh.TriangleIndices(), process_triangle);


            // ok now we filter out all the positions we can't change, as well as vertices that
            // did not actually move. We also queue any edges that moved far enough to fall
            // under min/max edge length thresholds
            gParallel.ForEach(mesh.VertexIndices(), (vID) =>
            {
                vModifiedV[vID] = false;
                if (vBufferVWeights[vID] < MathUtil.ZeroTolerance)
                {
                    return;
                }

                if (vertex_is_constrained(vID))
                {
                    return;
                }

                if (VertexControlF != null && (VertexControlF(vID) & VertexControl.NoProject) != 0)
                {
                    return;
                }

                Vector3d curpos  = mesh.GetVertex(vID);
                Vector3d projPos = vBufferV[vID] / vBufferVWeights[vID];
                if (curpos.EpsilonEqual(projPos, MathUtil.ZeroTolerancef))
                {
                    return;
                }

                vModifiedV[vID] = true;
                vBufferV[vID]   = projPos;

                foreach (int eid in mesh.VtxEdgesItr(vID))
                {
                    Index2i ev      = Mesh.GetEdgeV(eid);
                    int othervid    = (ev.a == vID) ? ev.b : ev.a;
                    Vector3d otherv = mesh.GetVertex(othervid);
                    double old_len  = curpos.Distance(otherv);
                    double new_len  = projPos.Distance(otherv);
                    if (new_len < MinEdgeLength || new_len > MaxEdgeLength)
                    {
                        queue_edge_safe(eid);
                    }
                }
            });


            // update vertices
            ApplyVertexBuffer(true);
        }
Exemple #2
0
        unsafe IOReadResult BuildMeshes_ByMaterial(ReadOptions options, IMeshBuilder builder)
        {
            if (vPositions.Length == 0)
            {
                return(new IOReadResult(IOCode.GarbageDataError, "No vertices in file"));
            }
            if (vTriangles.Length == 0)
            {
                return(new IOReadResult(IOCode.GarbageDataError, "No triangles in file"));
            }

            bool bHaveNormals = (vNormals.Length > 0);
            bool bHaveColors  = (vColors.Length > 0);
            bool bHaveUVs     = (vUVs.Length > 0);

            List <int> usedMaterialIDs = new List <int>(UsedMaterials.Keys);

            usedMaterialIDs.Add(Triangle.InvalidMaterialID);
            foreach (int material_id in usedMaterialIDs)
            {
                int matID = Triangle.InvalidMaterialID;
                if (material_id != Triangle.InvalidMaterialID)
                {
                    string      sMatName = UsedMaterials[material_id];
                    OBJMaterial useMat   = Materials[sMatName];
                    matID = builder.BuildMaterial(useMat);
                }
                bool bMatHaveUVs = (material_id == Triangle.InvalidMaterialID) ? false : bHaveUVs;

                // don't append mesh until we actually see triangles
                int meshID = -1;

                Dictionary <Index3i, int> mapV = new Dictionary <Index3i, int>();

                for (int k = 0; k < vTriangles.Length; ++k)
                {
                    Triangle t = vTriangles[k];
                    if (t.nMaterialID == material_id)
                    {
                        if (meshID == -1)
                        {
                            meshID = builder.AppendNewMesh(bHaveNormals, bHaveColors, bMatHaveUVs, false);
                        }

                        Triangle t2 = new Triangle();
                        for (int j = 0; j < 3; ++j)
                        {
                            Index3i vk = new Index3i(
                                t.vIndices[j] - 1, t.vNormals[j] - 1, t.vUVs[j] - 1);

                            int use_vtx = -1;
                            if (mapV.ContainsKey(vk) == false)
                            {
                                use_vtx  = append_vertex(builder, vk, bHaveNormals, bHaveColors, bMatHaveUVs);
                                mapV[vk] = use_vtx;
                            }
                            else
                            {
                                use_vtx = mapV[vk];
                            }

                            t2.vIndices[j] = use_vtx;
                        }
                        append_triangle(builder, t2);
                    }
                }

                if (matID != Triangle.InvalidMaterialID)
                {
                    builder.AssignMaterial(matID, meshID);
                }
            }

            return(new IOReadResult(IOCode.Ok, ""));
        }
Exemple #3
0
        public virtual bool Apply()
        {
            DMesh3 testAgainstMesh = Mesh;

            if (InsideMode == CalculationMode.RayParity)
            {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(testAgainstMesh);
                if (loops.Count > 0)
                {
                    testAgainstMesh = new DMesh3(Mesh);
                    foreach (var loop in loops)
                    {
                        if (Cancelled())
                        {
                            return(false);
                        }
                        SimpleHoleFiller filler = new SimpleHoleFiller(testAgainstMesh, loop);
                        filler.Fill();
                    }
                }
            }

            DMeshAABBTreePro spatial = (Spatial != null && testAgainstMesh == Mesh) ?
                                       Spatial : new DMeshAABBTreePro(testAgainstMesh, true);

            if (InsideMode == CalculationMode.AnalyticWindingNumber)
            {
                spatial.WindingNumber(Vector3d.Zero);
            }
            else if (InsideMode == CalculationMode.FastWindingNumber)
            {
                spatial.FastWindingNumber(Vector3d.Zero);
            }

            if (Cancelled())
            {
                return(false);
            }

            // ray directions
            List <Vector3d> ray_dirs = null; int NR = 0;

            if (InsideMode == CalculationMode.SimpleOcclusionTest)
            {
                ray_dirs = new List <Vector3d>();
                ray_dirs.Add(Vector3d.AxisX); ray_dirs.Add(-Vector3d.AxisX);
                ray_dirs.Add(Vector3d.AxisY); ray_dirs.Add(-Vector3d.AxisY);
                ray_dirs.Add(Vector3d.AxisZ); ray_dirs.Add(-Vector3d.AxisZ);
                NR = ray_dirs.Count;
            }

            Func <Vector3d, bool> isOccludedF = (pt) => {
                if (InsideMode == CalculationMode.RayParity)
                {
                    return(spatial.IsInside(pt));
                }
                else if (InsideMode == CalculationMode.AnalyticWindingNumber)
                {
                    return(spatial.WindingNumber(pt) > WindingIsoValue);
                }
                else if (InsideMode == CalculationMode.FastWindingNumber)
                {
                    return(spatial.FastWindingNumber(pt) > WindingIsoValue);
                }
                else
                {
                    for (int k = 0; k < NR; ++k)
                    {
                        int hit_tid = spatial.FindNearestHitTriangle(new Ray3d(pt, ray_dirs[k]));
                        if (hit_tid == DMesh3.InvalidID)
                        {
                            return(false);
                        }
                    }
                    return(true);
                }
            };

            bool cancel = false;

            BitArray vertices = null;

            if (PerVertex)
            {
                vertices = new BitArray(Mesh.MaxVertexID);

                MeshNormals normals = null;
                if (Mesh.HasVertexNormals == false)
                {
                    normals = new MeshNormals(Mesh);
                    normals.Compute();
                }

                gParallel.ForEach(Mesh.VertexIndices(), (vid) => {
                    if (cancel)
                    {
                        return;
                    }
                    if (vid % 10 == 0)
                    {
                        cancel = Cancelled();
                    }

                    Vector3d c    = Mesh.GetVertex(vid);
                    Vector3d n    = (normals == null) ? Mesh.GetVertexNormal(vid) : normals[vid];
                    c            += n * NormalOffset;
                    vertices[vid] = isOccludedF(c);
                });
            }
            if (Cancelled())
            {
                return(false);
            }

            RemovedT = new List <int>();
            SpinLock removeLock = new SpinLock();

            gParallel.ForEach(Mesh.TriangleIndices(), (tid) => {
                if (cancel)
                {
                    return;
                }
                if (tid % 10 == 0)
                {
                    cancel = Cancelled();
                }

                bool inside = false;
                if (PerVertex)
                {
                    Index3i tri = Mesh.GetTriangle(tid);
                    inside      = vertices[tri.a] || vertices[tri.b] || vertices[tri.c];
                }
                else
                {
                    Vector3d c = Mesh.GetTriCentroid(tid);
                    Vector3d n = Mesh.GetTriNormal(tid);
                    c         += n * NormalOffset;
                    inside     = isOccludedF(c);
                }

                if (inside)
                {
                    bool taken = false;
                    removeLock.Enter(ref taken);
                    RemovedT.Add(tid);
                    removeLock.Exit();
                }
            });

            if (Cancelled())
            {
                return(false);
            }

            if (RemovedT.Count > 0)
            {
                MeshEditor editor = new MeshEditor(Mesh);
                bool       bOK    = editor.RemoveTriangles(RemovedT, true);
                RemoveFailed = (bOK == false);
            }

            return(true);
        }
Exemple #4
0
        public void BuildLinear()
        {
            int NV = mesh.MaxVertexID;

            if (TrackVertexMapping)
            {
                mapTo = new Index2i[NV];
                for (int i = 0; i < NV; ++i)
                {
                    mapTo[i] = Index2i.Zero;
                }
                mapToMulti = new DVector <int>();
            }

            // temporary map from orig vertices to submesh vertices
            int[] mapToCur = new int[NV];
            Array.Clear(mapToCur, 0, mapToCur.Length);

            int nT = mesh.MaxTriangleID;

            // depending on the mesh, either the vertex count or triangle count
            // could hit the upper bound first. For connected meshes we have
            // NT =~ 2*NV by eulers formula, but eg for a pathological triangle
            // soup mesh, we might have NV = 3*NT

            int[] cur_subt = new int[MaxComponentSize];     // accumulated tris for this component
            int   subti    = 0;                             // index into cur_subt
            int   subi     = 1;                             // component index

            // also need to make sure we don't get too many verts. To do this
            // we have to keep count of how many unique verts we have accumulated.
            int[]    cur_subv  = new int[MaxComponentSize];      // temp buffer in add_component
            BitArray vert_bits = new BitArray(mesh.MaxVertexID); // which verts have we seen for this component
            int      subvcount = 0;                              // accumulated vert count for this component


            Action add_component = () => {
                Index2i   mapRange;
                int       max_subv;
                Component new_comp = Extract_submesh(subi++, cur_subt, subti, mapToCur, cur_subv, out mapRange, out max_subv);

                // [TODO] perhaps manager can request smaller chunks?
                Manager.AddComponent(new_comp);

                Array.Clear(cur_subt, 0, subti);
                subti = 0;
                Array.Clear(mapToCur, mapRange.a, mapRange.b - mapRange.a + 1);
                Array.Clear(cur_subv, 0, max_subv);
                subvcount = 0;
                vert_bits.SetAll(false);
            };



            int[] tri_order = Get_tri_order_by_axis_sort();
            int   tri_count = tri_order.Length;

            for (int ii = 0; ii < tri_count; ++ii)
            {
                int ti = tri_order[ii];

                Index3i tri = mesh.GetTriangle(ti);
                if (vert_bits[tri.a] == false)
                {
                    vert_bits[tri.a] = true; subvcount++;
                }
                if (vert_bits[tri.b] == false)
                {
                    vert_bits[tri.b] = true; subvcount++;
                }
                if (vert_bits[tri.c] == false)
                {
                    vert_bits[tri.c] = true; subvcount++;
                }

                cur_subt[subti++] = ti;

                if (subti == MaxComponentSize || subvcount > MaxComponentSize - 3)
                {
                    add_component();
                }
            }
            if (subti > 0)
            {
                add_component();
            }
        }
Exemple #5
0
        /// <summary>
        /// Assumption is that input graph is a polygon with inserted ray-spans. We want to
        /// find a set of paths (ie no junctions) that cover all the spans, and travel between
        /// adjacent spans along edges of the input polygon.
        /// </summary>
        protected DGraph2 BuildPathGraph(DGraph2 input)
        {
            int NV = input.MaxVertexID;

            /*
             * OK, as input we have a graph of our original polygon and a bunch of inserted
             * segments ("spans"). Orig polygon segments have gid < 0, and span segments >= 0.
             * However between polygon/span junctions, we have an arbitrary # of polygon edges.
             * So first step is to simplify these to single-edge "connectors", in new graph MinGraph.
             * the [connector-edge, path] mappings (if pathlen > 1) are stored in MinEdgePaths
             * We also store a weight for each connector edge in EdgeWeights (just distance for now)
             */

            DGraph2 MinGraph = new DGraph2();
            Dictionary <int, List <int> > MinEdgePaths = new Dictionary <int, List <int> >();
            DVector <double> EdgeWeights = new DVector <double>(); EdgeWeights.resize(NV);
            BitArray         done_edge   = new BitArray(input.MaxEdgeID); // we should see each edge twice, this avoids repetition

            // vertex map from input graph to MinGraph
            int[] MapV = new int[NV];
            for (int i = 0; i < NV; ++i)
            {
                MapV[i] = -1;
            }

            for (int a = 0; a < NV; ++a)
            {
                if (input.IsVertex(a) == false || input.IsJunctionVertex(a) == false)
                {
                    continue;
                }

                if (MapV[a] == -1)
                {
                    MapV[a] = MinGraph.AppendVertex(input.GetVertex(a));
                }

                foreach (int eid in input.VtxEdgesItr(a))
                {
                    if (done_edge[eid])
                    {
                        continue;
                    }

                    Index2i ev = input.GetEdgeV(eid);
                    int     b  = (ev.a == a) ? ev.b : ev.a;

                    if (input.IsJunctionVertex(b))
                    {
                        // if we have junction/juntion connection, we can just copy this edge to MinGraph

                        if (MapV[b] == -1)
                        {
                            MapV[b] = MinGraph.AppendVertex(input.GetVertex(b));
                        }

                        int gid      = input.GetEdgeGroup(eid);
                        int existing = MinGraph.FindEdge(MapV[a], MapV[b]);
                        if (existing == DMesh3.InvalidID)
                        {
                            int    new_eid  = MinGraph.AppendEdge(MapV[a], MapV[b], gid);
                            double path_len = input.GetEdgeSegment(eid).Length;
                            EdgeWeights.insertAt(path_len, new_eid);
                        }
                        else
                        {
                            // we may have inserted this edge already in the simplify branch, this happens eg at the
                            // edge of a circle where the minimal path is between the same vertices as the segment.
                            // But if this is also a fill edge, we want to treat it that way (determind via positive gid)
                            if (gid >= 0)
                            {
                                MinGraph.SetEdgeGroup(existing, gid);
                            }
                        }
                    }
                    else
                    {
                        // not a junction - walk until we find other vtx, and add single edge to MinGraph
                        List <int> path = DGraph2Util.WalkToNextNonRegularVtx(input, a, eid);
                        if (path == null || path.Count < 2)
                        {
                            throw new Exception("build_min_graph: invalid walk!");
                        }

                        int c = path[path.Count - 1];

                        // it is somehow possible to get loops...
                        if (c == a)
                        {
                            goto skip_this_edge;
                        }

                        if (MapV[c] == -1)
                        {
                            MapV[c] = MinGraph.AppendVertex(input.GetVertex(c));
                        }

                        if (MinGraph.FindEdge(MapV[a], MapV[c]) == DMesh3.InvalidID)
                        {
                            int new_eid = MinGraph.AppendEdge(MapV[a], MapV[c], -2);
                            path.Add(MapV[a]); path.Add(MapV[c]);
                            MinEdgePaths[new_eid] = path;
                            double path_len = DGraph2Util.PathLength(input, path);
                            EdgeWeights.insertAt(path_len, new_eid);
                        }
                    }

skip_this_edge:
                    done_edge[eid] = true;
                }
            }


            // [TODO] filter MinGraph to remove invalid connectors
            //    - can a connector between two connectors happen? that would be bad.
            ///   - connector that is too close to paths should be ignored (ie avoid collisions)


            /*
             * Now that we have MinGraph, we can easily walk between the spans because
             * they are connected by at most one edge. To find a sequence of spans, we
             * pick one to start, then walk along connectors, discarding as we go,
             * so that we don't pass through these vertices again. Repeat until
             * there are no remaining spans.
             */

            // [TODO]
            //  do we actually have to delete from MinGraph? this prevents us from doing
            //  certain things, like trying different options. Maybe could use a hash for
            //  remaining vertices and edges instead?

            DGraph2  PathGraph = new DGraph2();
            Vector2d sortAxis  = Vector2d.FromAngleDeg(AngleDeg).Perp;

            while (true)
            {
                // find most extreme edge to start at
                // [TODO] could use segment gid here as we set them based on insertion span!
                // [TODO] could use a smarter metric? like, closest to previous last endpoint? Using
                //   extrema like this tends to produce longest spans, though...
                double min_dot   = double.MaxValue;
                int    start_eid = -1;
                foreach (int eid in MinGraph.EdgeIndices())
                {
                    Index3i evg = MinGraph.GetEdge(eid);
                    if (evg.c >= 0)
                    {
                        double dot = MinGraph.GetVertex(evg.a).Dot(sortAxis);
                        if (dot < min_dot)
                        {
                            min_dot   = dot;
                            start_eid = eid;
                        }
                    }
                }
                if (start_eid == -1)
                {
                    break;   // if we could not find a start edge, we must be done!
                }
                // ok now walk forward through connectors and spans. We do this in
                // connector/span pairs - we are always at an end-of-span point, and
                // we pick a next-connector and then a next-span.
                // We need to keep track of vertices in both the pathgraph and mingraph,
                // these are the "new" and "old" vertices
                Index3i start_evg = MinGraph.GetEdge(start_eid);
                int     new_start = PathGraph.AppendVertex(MinGraph.GetVertex(start_evg.a));
                int     new_prev  = PathGraph.AppendVertex(MinGraph.GetVertex(start_evg.b));
                int     old_prev  = start_evg.b;
                PathGraph.AppendEdge(new_start, new_prev, start_evg.c);
                MinGraph.RemoveVertex(start_evg.a, true);
                while (true)
                {
                    // choose next connector edge, outgoing from current vtx
                    int connector_e = -1;
                    foreach (int eid in MinGraph.VtxEdgesItr(old_prev))
                    {
                        Index3i evg = MinGraph.GetEdge(eid);
                        if (evg.c >= 0)
                        {
                            continue;  // what??
                        }
                        if (connector_e == -1 || EdgeWeights[connector_e] > EdgeWeights[eid])
                        {
                            connector_e = eid;
                        }
                    }
                    if (connector_e == -1)
                    {
                        break;
                    }

                    // find the vertex at end of connector
                    Index3i conn_evg   = MinGraph.GetEdge(connector_e);
                    int     old_conn_v = (conn_evg.a == old_prev) ? conn_evg.b : conn_evg.a;

                    // can never look at prev vertex again, or any edges connected to it
                    // [TODO] are we sure none of these edges are unused spans?!?
                    MinGraph.RemoveVertex(old_prev, true);

                    // now find outgoing span edge
                    int span_e = -1;
                    foreach (int eid in MinGraph.VtxEdgesItr(old_conn_v))
                    {
                        Index3i evg = MinGraph.GetEdge(eid);
                        if (evg.c >= 0)
                        {
                            span_e = eid;
                            break;
                        }
                    }
                    if (span_e == -1)
                    {
                        break;   // disaster!
                    }
                    // find vertex at far end of span
                    Index3i span_evg   = MinGraph.GetEdge(span_e);
                    int     old_span_v = (span_evg.a == old_conn_v) ? span_evg.b : span_evg.a;

                    // ok we want to insert the connectr to the path graph, however the
                    // connector might actually have come from a more complex path in the input graph.
                    int new_conn_next = -1;
                    if (MinEdgePaths.ContainsKey(connector_e))
                    {
                        // complex path case. Note that the order [old_prev, old_conn_v] may be the opposite
                        // of the order in the pathv. But above, we appended the [a,b] edge order to the pathv.
                        // So we can check if we need to flip, but this means we need to be a bit clever w/ indices...
                        List <int> pathv     = MinEdgePaths[connector_e];
                        int        N         = pathv.Count;
                        int        path_prev = new_prev;
                        int        k         = 1;
                        if (pathv[N - 2] != old_prev)     // case where order flipped
                        {
                            pathv.Reverse();
                            k = 3;
                        }
                        else
                        {
                            N = N - 2;
                        }
                        while (k < N)
                        {
                            int path_next = PathGraph.AppendVertex(input.GetVertex(pathv[k]));
                            PathGraph.AppendEdge(path_prev, path_next);
                            path_prev = path_next;
                            k++;
                        }
                        new_conn_next = path_prev;
                    }
                    else
                    {
                        new_conn_next = PathGraph.AppendVertex(MinGraph.GetVertex(old_conn_v));
                        PathGraph.AppendEdge(new_prev, new_conn_next, conn_evg.c);
                    }

                    // add span to path
                    int new_fill_next = PathGraph.AppendVertex(MinGraph.GetVertex(old_span_v));
                    PathGraph.AppendEdge(new_conn_next, new_fill_next, span_evg.c);

                    // remove the connector vertex
                    MinGraph.RemoveVertex(old_conn_v, true);

                    // next iter starts at far end of span
                    new_prev = new_fill_next;
                    old_prev = old_span_v;
                }

                sortAxis = -sortAxis;
            }


            // for testing/debugging
            //SVGWriter writer = new SVGWriter();
            ////writer.AddGraph(input, SVGWriter.Style.Outline("blue", 0.1f));
            //writer.AddGraph(MinGraph, SVGWriter.Style.Outline("red", 0.1f));
            ////foreach ( int eid in MinGraph.EdgeIndices() ) {
            ////    if ( MinGraph.GetEdgeGroup(eid) >= 0 )  writer.AddLine(MinGraph.GetEdgeSegment(eid), SVGWriter.Style.Outline("green", 0.07f));
            ////}
            ////writer.AddGraph(MinGraph, SVGWriter.Style.Outline("black", 0.03f));
            //writer.AddGraph(PathGraph, SVGWriter.Style.Outline("black", 0.03f));
            //foreach (int vid in PathGraph.VertexIndices()) {
            //    if (PathGraph.IsBoundaryVertex(vid))
            //        writer.AddCircle(new Circle2d(PathGraph.GetVertex(vid), 0.5f), SVGWriter.Style.Outline("blue", 0.03f));
            //}
            ////writer.AddGraph(IntervalGraph, SVGWriter.Style.Outline("black", 0.03f));
            //writer.Write("c:\\scratch\\MIN_GRAPH.svg");


            return(PathGraph);
        }
    public override void Selected(SelectionType button)
    {
        nullifyHitPos = true;
        transform.parent.SendMessage("Selected", button, SendMessageOptions.DontRequireReceiver);
        if (button == SelectionType.SELECTALL)
        {
            BlockMove = true;
        }
        else
        {
            MeshFilter mf   = GetComponent <MeshFilter>();
            Mesh       mesh = mf.sharedMesh;
            currentHit = transform.InverseTransformPoint(AppState.instance.lastHitPosition);
            Vector3d      target = currentHit;
            System.Random rand   = new System.Random();
            int           count  = 0;
            //
            // The algorithm will find local optima - repeat until you get the tru optima
            // but limit the interation using a count
            //
            Int32 current = 0;
            while (count < dmesh.VertexCount)
            {
                count++;
                //
                // choose a random starting point
                //
                current = rand.Next(0, dmesh.VertexCount);
                Vector3d vtx         = dmesh.GetVertex(current);
                double   currentDist = vtx.DistanceSquared(target);
                //
                // find the ring of triangles around the current point
                //
                int iter = 0;
                while (true)
                {
                    iter++;
                    //    throw new InvalidOperationException("Meh One Ring operation invalid : " + res.ToString());
                    //
                    // Interate through the vertices in the one Ring and find the clost to the target point
                    //
                    bool flag = false;
                    foreach (Int32 v in dmesh.VtxVerticesItr(current))
                    {
                        Vector3d thisVtx  = dmesh.GetVertex(v);
                        double   thisDist = thisVtx.DistanceSquared(target);
                        if (thisDist < currentDist)
                        {
                            flag        = true;
                            current     = v;
                            vtx         = thisVtx;
                            currentDist = thisDist;
                        }
                    }
                    //
                    // if the current point as closest - then  have a local optima
                    //
                    if (!flag)
                    {
                        break;
                    }
                }
                //
                // we now have a local optima
                // to check for a global optima look to see if hit is contained by one of the triangles in the one ring
                //
                bool f2 = false;
                foreach (Int32 t in dmesh.VtxTrianglesItr(current))
                {
                    Index3i    tri      = dmesh.GetTriangle(t);
                    Triangle3d triangle = new Triangle3d(
                        dmesh.GetVertex(tri.a),
                        dmesh.GetVertex(tri.b),
                        dmesh.GetVertex(tri.c)
                        );
                    double[] xs = new double[3] {
                        triangle.V0.x, triangle.V1.x, triangle.V2.x
                    };
                    double[] ys = new double[3] {
                        triangle.V0.y, triangle.V1.y, triangle.V2.y
                    };
                    double[] zs = new double[3] {
                        triangle.V0.z, triangle.V1.z, triangle.V2.z
                    };

                    if (
                        target.x >= xs.Min() &&
                        target.x <= xs.Max() &&
                        target.y >= ys.Min() &&
                        target.y <= ys.Max() &&
                        target.z >= zs.Min() &&
                        target.z <= zs.Max()
                        )
                    {
                        f2            = true;
                        currentHitTri = tri;
                    }
                }
                //
                // if we found on triamgle that contain the current hit then we have finished
                //
                if (f2)
                {
                    break;
                }
            }
            if (count >= dmesh.VertexCount)
            {
                //
                // This is the unoptimized verion but it is guaranteed to find a solution if one exits
                //

                current = -1;
                float currentDist = float.MaxValue;
                if (currentHit != null)
                {
                    for (int i = 0; i < mesh.vertices.Length; i++)
                    {
                        Vector3 vtx  = mesh.vertices[i];
                        float   dist = (currentHit - vtx).sqrMagnitude;
                        if (dist < currentDist)
                        {
                            current     = i;
                            currentDist = dist;
                        }
                    }
                }
                //
                // Check that the closet vertex to the point is an actual solution
                //
                bool f2 = false;
                foreach (Int32 t in dmesh.VtxTrianglesItr(current))
                {
                    Index3i    tri      = dmesh.GetTriangle(t);
                    Triangle3d triangle = new Triangle3d(
                        dmesh.GetVertex(tri.a),
                        dmesh.GetVertex(tri.b),
                        dmesh.GetVertex(tri.c)
                        );
                    double[] xs = new double[3] {
                        triangle.V0.x, triangle.V1.x, triangle.V2.x
                    };
                    double[] ys = new double[3] {
                        triangle.V0.y, triangle.V1.y, triangle.V2.y
                    };
                    double[] zs = new double[3] {
                        triangle.V0.z, triangle.V1.z, triangle.V2.z
                    };

                    if (
                        target.x >= xs.Min() &&
                        target.x <= xs.Max() &&
                        target.y >= ys.Min() &&
                        target.y <= ys.Max() &&
                        target.z >= zs.Min() &&
                        target.z <= zs.Max()
                        )
                    {
                        f2            = true;
                        currentHitTri = tri;
                    }
                }
                //
                // if we found on triamgle that contain the current hit then we have finished
                //
                if (!f2)
                {
                    Debug.LogError(" Mesh Vertex Search : No Solution Found");
                    return;
                }
            }
            selectedVertex              = current;
            sphere                      = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            sphere.transform.position   = transform.TransformPoint(mesh.vertices[current]);
            sphere.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
            sphere.transform.parent     = transform;
        }
    }
Exemple #7
0
        public IOWriteResult Write(TextWriter writer, List <WriteMesh> vMeshes, WriteOptions options)
        {
            int N = vMeshes.Count;

            writer.WriteLine("OFF");

            string three_floats = Util.MakeVec3FormatString(0, 1, 2, options.RealPrecisionDigits);

            int nTotalV = 0, nTotalT = 0, nTotalE = 0;

            // OFF only supports one mesh, so have to collapse all input meshes
            // into a single list, with mapping for triangles
            // [TODO] can skip this if input is a single mesh!
            int[][] mapV = new int[N][];
            for (int mi = 0; mi < N; ++mi)
            {
                nTotalV += vMeshes[mi].Mesh.VertexCount;
                nTotalT += vMeshes[mi].Mesh.TriangleCount;
                nTotalE += 0;
                mapV[mi] = new int[vMeshes[mi].Mesh.MaxVertexID];
            }
            writer.WriteLine(string.Format("{0} {1} {2}", nTotalV, nTotalT, nTotalE));


            // write all vertices, and construct vertex re-map
            int vi = 0;

            for (int mi = 0; mi < N; ++mi)
            {
                IMesh mesh = vMeshes[mi].Mesh;
                if (options.ProgressFunc != null)
                {
                    options.ProgressFunc(mi, 2 * (N - 1));
                }
                foreach (int vid in mesh.VertexIndices())
                {
                    Vector3D v = mesh.GetVertex(vid);
                    writer.WriteLine(three_floats, v.x, v.y, v.z);
                    mapV[mi][vid] = vi;
                    vi++;
                }
            }

            // write all triangles
            for (int mi = 0; mi < N; ++mi)
            {
                IMesh mesh = vMeshes[mi].Mesh;
                if (options.ProgressFunc != null)
                {
                    options.ProgressFunc(N + mi, 2 * (N - 1));
                }

                foreach (int ti in mesh.TriangleIndices())
                {
                    Index3i t = mesh.GetTriangle(ti);
                    t[0] = mapV[mi][t[0]];
                    t[1] = mapV[mi][t[1]];
                    t[2] = mapV[mi][t[2]];
                    writer.WriteLine(string.Format("3 {0} {1} {2}", t[0], t[1], t[2]));
                }
            }

            return(new IOWriteResult(IOCode.Ok, ""));
        }
Exemple #8
0
 double get_tri_aspect(DMesh3 mesh, ref Index3i tri)
 {
     return(MathUtil.AspectRatio(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c)));
 }
Exemple #9
0
        /// <summary>
        // This function checks that the mesh is well-formed, ie all internal data
        // structures are consistent
        /// </summary>
        public bool CheckValidity(bool bAllowNonManifoldVertices = false, FailMode eFailMode = FailMode.Throw)
        {
            int[] triToVtxRefs = new int[this.MaxVertexID];

            bool          is_ok        = true;
            Action <bool> CheckOrFailF = (b) => {
                is_ok = is_ok && b;
            };

            if (eFailMode == FailMode.DebugAssert)
            {
                CheckOrFailF = (b) => { Debug.Assert(b);
                                        is_ok = is_ok && b; };
            }
            else if (eFailMode == FailMode.gDevAssert)
            {
                CheckOrFailF = (b) => { Util.gDevAssert(b);
                                        is_ok = is_ok && b; };
            }
            else if (eFailMode == FailMode.Throw)
            {
                CheckOrFailF = (b) => { if (b == false)
                                        {
                                            throw new Exception("DMesh3.CheckValidity: check failed");
                                        }
                };
            }

            if (normals != null)
            {
                CheckOrFailF(normals.Size == vertices.Size);
            }
            if (colors != null)
            {
                CheckOrFailF(colors.Size == vertices.Size);
            }
            if (uv != null)
            {
                CheckOrFailF(uv.Size / 2 == vertices.Size / 3);
            }
            if (triangle_groups != null)
            {
                CheckOrFailF(triangle_groups.Size == triangles.Size / 3);
            }

            foreach (int tID in TriangleIndices())
            {
                CheckOrFailF(IsTriangle(tID));
                CheckOrFailF(triangles_refcount.RefCount(tID) == 1);

                // vertices must exist
                Index3i tv = GetTriangle(tID);
                for (int j = 0; j < 3; ++j)
                {
                    CheckOrFailF(IsVertex(tv[j]));
                    triToVtxRefs[tv[j]] += 1;
                }

                // edges must exist and reference this tri
                Index3i e = new Index3i();
                for (int j = 0; j < 3; ++j)
                {
                    int a = tv[j], b = tv[(j + 1) % 3];
                    e[j] = FindEdge(a, b);
                    CheckOrFailF(e[j] != InvalidID);
                    CheckOrFailF(Edge_has_t(e[j], tID));
                    CheckOrFailF(e[j] == FindEdgeFromTri(a, b, tID));
                }
                CheckOrFailF(e[0] != e[1] && e[0] != e[2] && e[1] != e[2]);

                // tri nbrs must exist and reference this tri, or same edge must be boundary edge
                Index3i te = GetTriEdges(tID);
                for (int j = 0; j < 3; ++j)
                {
                    int eid = te[j];
                    CheckOrFailF(IsEdge(eid));
                    int tOther = Edge_other_t(eid, tID);
                    if (tOther == InvalidID)
                    {
                        CheckOrFailF(Tri_is_boundary(tID));
                        continue;
                    }

                    CheckOrFailF(Tri_has_neighbour_t(tOther, tID) == true);

                    // edge must have same two verts as tri for same index
                    int     a = tv[j], b = tv[(j + 1) % 3];
                    Index2i ev = GetEdgeV(te[j]);
                    CheckOrFailF(IndexUtil.same_pair_unordered(a, b, ev[0], ev[1]));

                    // also check that nbr edge has opposite orientation
                    Index3i othertv = GetTriangle(tOther);
                    int     found   = IndexUtil.find_tri_ordered_edge(b, a, othertv.array);
                    CheckOrFailF(found != InvalidID);
                }
            }


            // edge verts/tris must exist
            foreach (int eID in EdgeIndices())
            {
                CheckOrFailF(IsEdge(eID));
                CheckOrFailF(edges_refcount.RefCount(eID) == 1);
                Index2i ev = GetEdgeV(eID);
                Index2i et = GetEdgeT(eID);
                CheckOrFailF(IsVertex(ev[0]));
                CheckOrFailF(IsVertex(ev[1]));
                CheckOrFailF(et[0] != InvalidID);
                CheckOrFailF(ev[0] < ev[1]);
                CheckOrFailF(IsTriangle(et[0]));
                if (et[1] != InvalidID)
                {
                    CheckOrFailF(IsTriangle(et[1]));
                }
            }

            // verify compact check
            bool is_compact = vertices_refcount.Is_dense;

            if (is_compact)
            {
                for (int vid = 0; vid < vertices.Length / 3; ++vid)
                {
                    CheckOrFailF(vertices_refcount.IsValid(vid));
                }
            }

            // vertex edges must exist and reference this vert
            foreach (int vID in VertexIndices())
            {
                CheckOrFailF(IsVertex(vID));

                Vector3D v = GetVertex(vID);
                CheckOrFailF(double.IsNaN(v.LengthSquared) == false);
                CheckOrFailF(double.IsInfinity(v.LengthSquared) == false);

                List <int> l = vertex_edges[vID];
                foreach (int edgeid in l)
                {
                    CheckOrFailF(IsEdge(edgeid));
                    CheckOrFailF(Edge_has_v(edgeid, vID));

                    int otherV = Edge_other_v(edgeid, vID);
                    int e2     = Find_edge(vID, otherV);
                    CheckOrFailF(e2 != InvalidID);
                    CheckOrFailF(e2 == edgeid);
                    e2 = Find_edge(otherV, vID);
                    CheckOrFailF(e2 != InvalidID);
                    CheckOrFailF(e2 == edgeid);
                }

                List <int> vTris = new List <int>(), vTris2 = new List <int>();
                GetVtxTriangles(vID, vTris, false);
                GetVtxTriangles(vID, vTris2, true);
                CheckOrFailF(vTris.Count == vTris2.Count);
                //System.Console.WriteLine(string.Format("{0} {1} {2}", vID, vTris.Count, GetVtxEdges(vID).Count));
                if (bAllowNonManifoldVertices)
                {
                    CheckOrFailF(vTris.Count <= GetVtxEdges(vID).Count);
                }
                else
                {
                    CheckOrFailF(vTris.Count == GetVtxEdges(vID).Count || vTris.Count == GetVtxEdges(vID).Count - 1);
                }
                CheckOrFailF(vertices_refcount.RefCount(vID) == vTris.Count + 1);
                CheckOrFailF(triToVtxRefs[vID] == vTris.Count);
                foreach (int tID in vTris)
                {
                    CheckOrFailF(Tri_has_v(tID, vID));
                }

                // check that edges around vert only references tris above, and reference all of them!
                List <int> vRemoveTris = new List <int>(vTris);
                foreach (int edgeid in l)
                {
                    Index2i edget = GetEdgeT(edgeid);
                    CheckOrFailF(vTris.Contains(edget[0]));
                    if (edget[1] != InvalidID)
                    {
                        CheckOrFailF(vTris.Contains(edget[1]));
                    }
                    vRemoveTris.Remove(edget[0]);
                    if (edget[1] != InvalidID)
                    {
                        vRemoveTris.Remove(edget[1]);
                    }
                }
                CheckOrFailF(vRemoveTris.Count == 0);
            }

            return(is_ok);
        }
Exemple #10
0
 double get_tri_area(DMesh3 mesh, ref Index3i tri)
 {
     return(MathUtil.Area(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c)));
 }
Exemple #11
0
 Vector3d get_tri_normal(DMesh3 mesh, Index3i tri)
 {
     return(MathUtil.Normal(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c)));
 }
Exemple #12
0
        public bool Apply()
        {
            // do a simple fill
            SimpleHoleFiller simplefill = new SimpleHoleFiller(Mesh, FillLoop);
            int  fill_gid = Mesh.AllocateTriangleGroup();
            bool bOK      = simplefill.Fill(fill_gid);

            if (bOK == false)
            {
                return(false);
            }

            if (FillLoop.Vertices.Length <= 3)
            {
                FillTriangles = simplefill.NewTriangles;
                FillVertices  = new int[0];
                return(true);
            }

            // extract the simple fill mesh as a submesh, via RegionOperator, so we can backsub later
            HashSet <int> intial_fill_tris = new HashSet <int>(simplefill.NewTriangles);

            regionop = new RegionOperator(Mesh, simplefill.NewTriangles,
                                          (submesh) => { submesh.ComputeTriMaps = true; });
            fillmesh = regionop.Region.SubMesh;

            // for each boundary vertex, compute the exterior angle sum
            // we will use this to compute gaussian curvature later
            boundaryv           = new HashSet <int>(MeshIterators.BoundaryEdgeVertices(fillmesh));
            exterior_angle_sums = new Dictionary <int, double>();
            if (IgnoreBoundaryTriangles == false)
            {
                foreach (int sub_vid in boundaryv)
                {
                    double angle_sum = 0;
                    int    base_vid  = regionop.Region.MapVertexToBaseMesh(sub_vid);
                    foreach (int tid in regionop.BaseMesh.VtxTrianglesItr(base_vid))
                    {
                        if (intial_fill_tris.Contains(tid) == false)
                        {
                            Index3i et  = regionop.BaseMesh.GetTriangle(tid);
                            int     idx = IndexUtil.find_tri_index(base_vid, ref et);
                            angle_sum += regionop.BaseMesh.GetTriInternalAngleR(tid, idx);
                        }
                    }
                    exterior_angle_sums[sub_vid] = angle_sum;
                }
            }


            // try to guess a reasonable edge length that will give us enough geometry to work with in simplify pass
            double loop_mine, loop_maxe, loop_avge, fill_mine, fill_maxe, fill_avge;

            MeshQueries.EdgeLengthStatsFromEdges(Mesh, FillLoop.Edges, out loop_mine, out loop_maxe, out loop_avge);
            MeshQueries.EdgeLengthStats(fillmesh, out fill_mine, out fill_maxe, out fill_avge);
            double remesh_target_len = loop_avge;

            if (fill_maxe / remesh_target_len > 10)
            {
                remesh_target_len = fill_maxe / 10;
            }
            //double remesh_target_len = Math.Min(loop_avge, fill_avge / 4);

            // remesh up to target edge length, ideally gives us some triangles to work with
            RemesherPro remesh1 = new RemesherPro(fillmesh);

            remesh1.SmoothSpeedT = 1.0;
            MeshConstraintUtil.FixAllBoundaryEdges(remesh1);
            //remesh1.SetTargetEdgeLength(remesh_target_len / 2);       // would this speed things up? on large regions?
            //remesh1.FastestRemesh();
            remesh1.SetTargetEdgeLength(remesh_target_len);
            remesh1.FastestRemesh();

            /*
             * first round: collapse to minimal mesh, while flipping to try to
             * get to ballpark minimal mesh. We stop these passes as soon as
             * we have done two rounds where we couldn't do another collapse
             *
             * This is the most unstable part of the algorithm because there
             * are strong ordering effects. maybe we could sort the edges somehow??
             */

            int zero_collapse_passes = 0;
            int collapse_passes      = 0;

            while (collapse_passes++ < 20 && zero_collapse_passes < 2)
            {
                // collapse pass
                int NE        = fillmesh.MaxEdgeID;
                int collapses = 0;
                for (int ei = 0; ei < NE; ++ei)
                {
                    if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei))
                    {
                        continue;
                    }
                    Index2i ev = fillmesh.GetEdgeV(ei);
                    bool    a_bdry = boundaryv.Contains(ev.a), b_bdry = boundaryv.Contains(ev.b);
                    if (a_bdry && b_bdry)
                    {
                        continue;
                    }
                    int      keepv  = (a_bdry) ? ev.a : ev.b;
                    int      otherv = (keepv == ev.a) ? ev.b : ev.a;
                    Vector3d newv   = fillmesh.GetVertex(keepv);
                    if (MeshUtil.CheckIfCollapseCreatesFlip(fillmesh, ei, newv))
                    {
                        continue;
                    }
                    DMesh3.EdgeCollapseInfo info;
                    MeshResult result = fillmesh.CollapseEdge(keepv, otherv, out info);
                    if (result == MeshResult.Ok)
                    {
                        collapses++;
                    }
                }
                if (collapses == 0)
                {
                    zero_collapse_passes++;
                }
                else
                {
                    zero_collapse_passes = 0;
                }

                // flip pass. we flip in these cases:
                //  1) if angle between current triangles is too small (slightly more than 90 degrees, currently)
                //  2) if angle between flipped triangles is smaller than between current triangles
                //  3) if flipped edge length is shorter *and* such a flip won't flip the normal
                NE = fillmesh.MaxEdgeID;
                Vector3d n1, n2, on1, on2;
                for (int ei = 0; ei < NE; ++ei)
                {
                    if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei))
                    {
                        continue;
                    }
                    bool do_flip = false;

                    Index2i ev = fillmesh.GetEdgeV(ei);
                    MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2);
                    double dot_cur  = n1.Dot(n2);
                    double dot_flip = on1.Dot(on2);
                    if (n1.Dot(n2) < 0.1 || dot_flip > dot_cur + MathUtil.Epsilonf)
                    {
                        do_flip = true;
                    }

                    if (do_flip == false)
                    {
                        Index2i otherv   = fillmesh.GetEdgeOpposingV(ei);
                        double  len_e    = fillmesh.GetVertex(ev.a).Distance(fillmesh.GetVertex(ev.b));
                        double  len_flip = fillmesh.GetVertex(otherv.a).Distance(fillmesh.GetVertex(otherv.b));
                        if (len_flip < len_e)
                        {
                            if (MeshUtil.CheckIfEdgeFlipCreatesFlip(fillmesh, ei) == false)
                            {
                                do_flip = true;
                            }
                        }
                    }

                    if (do_flip)
                    {
                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                    }
                }
            }

            // Sometimes, for some reason, we have a remaining interior vertex (have only ever seen one?)
            // Try to force removal of such vertices, even if it makes ugly mesh
            remove_remaining_interior_verts();


            // enable/disable passes.
            bool DO_FLATTER_PASS   = true;
            bool DO_CURVATURE_PASS = OptimizeDevelopability && true;
            bool DO_AREA_PASS      = OptimizeDevelopability && OptimizeTriangles && true;


            /*
             * In this pass we repeat the flipping iterations from the previous pass.
             *
             * Note that because of the always-flip-if-dot-is-small case (commented),
             * this pass will frequently not converge, as some number of edges will
             * be able to flip back and forth (because neither has large enough dot).
             * This is not ideal, but also, if we remove this behavior, then we
             * generally get worse fills. This case basically introduces a sort of
             * randomization factor that lets us escape local minima...
             *
             */

            HashSet <int> remaining_edges = new HashSet <int>(fillmesh.EdgeIndices());
            HashSet <int> updated_edges   = new HashSet <int>();

            int flatter_passes    = 0;
            int zero_flips_passes = 0;

            while (flatter_passes++ < 40 && zero_flips_passes < 2 && remaining_edges.Count() > 0 && DO_FLATTER_PASS)
            {
                zero_flips_passes++;
                foreach (int ei in remaining_edges)
                {
                    if (fillmesh.IsBoundaryEdge(ei))
                    {
                        continue;
                    }

                    bool do_flip = false;

                    Index2i  ev = fillmesh.GetEdgeV(ei);
                    Vector3d n1, n2, on1, on2;
                    MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2);
                    double dot_cur  = n1.Dot(n2);
                    double dot_flip = on1.Dot(on2);
                    if (flatter_passes < 20 && dot_cur < 0.1)   // this check causes oscillatory behavior
                    {
                        do_flip = true;
                    }
                    if (dot_flip > dot_cur + MathUtil.Epsilonf)
                    {
                        do_flip = true;
                    }

                    if (do_flip)
                    {
                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                        if (result == MeshResult.Ok)
                        {
                            zero_flips_passes = 0;
                            add_all_edges(ei, updated_edges);
                        }
                    }
                }

                var tmp = remaining_edges;
                remaining_edges = updated_edges;
                updated_edges   = tmp; updated_edges.Clear();
            }


            int curvature_passes = 0;

            if (DO_CURVATURE_PASS)
            {
                curvatures = new double[fillmesh.MaxVertexID];
                foreach (int vid in fillmesh.VertexIndices())
                {
                    update_curvature(vid);
                }

                remaining_edges = new HashSet <int>(fillmesh.EdgeIndices());
                updated_edges   = new HashSet <int>();

                /*
                 *  In this pass we try to minimize gaussian curvature at all the vertices.
                 *  This will recover sharp edges, etc, and do lots of good stuff.
                 *  However, this pass will not make much progress if we are not already
                 *  relatively close to a minimal mesh, so it really relies on the previous
                 *  passes getting us in the ballpark.
                 */
                while (curvature_passes++ < 40 && remaining_edges.Count() > 0 && DO_CURVATURE_PASS)
                {
                    foreach (int ei in remaining_edges)
                    {
                        if (fillmesh.IsBoundaryEdge(ei))
                        {
                            continue;
                        }

                        Index2i ev = fillmesh.GetEdgeV(ei);
                        Index2i ov = fillmesh.GetEdgeOpposingV(ei);

                        int find_other = fillmesh.FindEdge(ov.a, ov.b);
                        if (find_other != DMesh3.InvalidID)
                        {
                            continue;
                        }

                        double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b);
                        if (total_curv_cur < MathUtil.ZeroTolerancef)
                        {
                            continue;
                        }

                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                        if (result != MeshResult.Ok)
                        {
                            continue;
                        }

                        double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b);

                        bool keep_flip = total_curv_flip < total_curv_cur - MathUtil.ZeroTolerancef;
                        if (keep_flip == false)
                        {
                            result = fillmesh.FlipEdge(ei, out info);
                        }
                        else
                        {
                            update_curvature(ev.a); update_curvature(ev.b);
                            update_curvature(ov.a); update_curvature(ov.b);
                            add_all_edges(ei, updated_edges);
                        }
                    }
                    var tmp = remaining_edges;
                    remaining_edges = updated_edges;
                    updated_edges   = tmp; updated_edges.Clear();
                }
            }
            //System.Console.WriteLine("collapse {0}   flatter {1}   curvature {2}", collapse_passes, flatter_passes, curvature_passes);

            /*
             * In this final pass, we try to improve triangle quality. We flip if
             * the flipped triangles have better total aspect ratio, and the
             * curvature doesn't change **too** much. The .DevelopabilityTolerance
             * parameter determines what is "too much" curvature change.
             */
            if (DO_AREA_PASS)
            {
                remaining_edges = new HashSet <int>(fillmesh.EdgeIndices());
                updated_edges   = new HashSet <int>();
                int area_passes = 0;
                while (remaining_edges.Count() > 0 && area_passes < 20)
                {
                    area_passes++;
                    foreach (int ei in remaining_edges)
                    {
                        if (fillmesh.IsBoundaryEdge(ei))
                        {
                            continue;
                        }

                        Index2i ev = fillmesh.GetEdgeV(ei);
                        Index2i ov = fillmesh.GetEdgeOpposingV(ei);

                        int find_other = fillmesh.FindEdge(ov.a, ov.b);
                        if (find_other != DMesh3.InvalidID)
                        {
                            continue;
                        }

                        double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b);

                        double a = aspect_metric(ei);
                        if (a > 1)
                        {
                            continue;
                        }

                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                        if (result != MeshResult.Ok)
                        {
                            continue;
                        }

                        double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b);

                        bool keep_flip = Math.Abs(total_curv_cur - total_curv_flip) < DevelopabilityTolerance;
                        if (keep_flip == false)
                        {
                            result = fillmesh.FlipEdge(ei, out info);
                        }
                        else
                        {
                            update_curvature(ev.a); update_curvature(ev.b);
                            update_curvature(ov.a); update_curvature(ov.b);
                            add_all_edges(ei, updated_edges);
                        }
                    }
                    var tmp = remaining_edges;
                    remaining_edges = updated_edges;
                    updated_edges   = tmp; updated_edges.Clear();
                }
            }


            regionop.BackPropropagate();
            FillTriangles = regionop.CurrentBaseTriangles;
            FillVertices  = regionop.CurrentBaseInteriorVertices().ToArray();

            return(true);
        }
Exemple #13
0
        public static fMesh DMeshToUnityMesh(DMesh3 m, bool bSwapLeftRight, bool bAllowLargeMeshes = false)
        {
            if (bSwapLeftRight)
            {
                throw new Exception("[RMSNOTE] I think this conversion is wrong, see MeshTransforms.SwapLeftRight. Just want to know if this code is ever hit.");
            }

            if (bAllowLargeMeshes == false)
            {
                if (m.MaxVertexID > 65000 || m.MaxTriangleID > 65000)
                {
                    Debug.Log("[UnityUtil.DMeshToUnityMesh] attempted to import object larger than 65000 verts/tris, not supported by Unity!");
                    return(null);
                }
            }

            Mesh unityMesh = new Mesh();

            Vector3[] vertices = dvector_to_vector3(m.VerticesBuffer);
            Vector3[] normals  = (m.HasVertexNormals) ? dvector_to_vector3(m.NormalsBuffer) : null;

            unityMesh.vertices = vertices;
            if (m.HasVertexNormals)
            {
                unityMesh.normals = normals;
            }
            if (m.HasVertexColors)
            {
                unityMesh.colors = dvector_to_color(m.ColorsBuffer);
            }
            if (m.HasVertexUVs)
            {
                unityMesh.uv = dvector_to_vector2(m.UVBuffer);
            }

            if (bAllowLargeMeshes && (m.MaxVertexID > 65000 || m.TriangleCount > 65000))
            {
                unityMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            }

            if (m.IsCompactT)
            {
                unityMesh.triangles = dvector_to_int(m.TrianglesBuffer);
            }
            else
            {
                int[] triangles = new int[m.TriangleCount * 3];
                int   ti        = 0;
                for (int k = 0; k < m.MaxTriangleID; ++k)
                {
                    if (m.IsTriangle(k))
                    {
                        Index3i t = m.GetTriangle(k);
                        int     j = 3 * ti;
                        triangles[j]     = t.a;
                        triangles[j + 1] = t.b;
                        triangles[j + 2] = t.c;
                        ti++;
                    }
                }
                unityMesh.triangles = triangles;
            }

            if (m.HasVertexNormals == false)
            {
                unityMesh.RecalculateNormals();
            }

            return(new fMesh(unityMesh));
        }
        }         // Calculate()

        bool is_connection(Index3i flags)
        {
            return((flags.a & (int)TPVertexFlags.IsConnector) != 0);
        }