コード例 #1
0
        public static void test_remesh_constraints_fixedverts()
        {
            int    Slices = 128;
            DMesh3 mesh   = TestUtil.MakeCappedCylinder(false, Slices);

            MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1));
            mesh.CheckValidity();
            AxisAlignedBox3d bounds = mesh.CachedBounds;

            // construct mesh projection target
            DMesh3 meshCopy = new DMesh3(mesh);

            meshCopy.CheckValidity();
            DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy);

            tree.Build();
            MeshProjectionTarget target = new MeshProjectionTarget()
            {
                Mesh = meshCopy, Spatial = tree
            };

            if (WriteDebugMeshes)
            {
                TestUtil.WriteDebugMesh(mesh, "remesh_fixed_constraints_test_before.obj");
            }

            // construct constraint set
            MeshConstraints cons = new MeshConstraints();

            //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse;
            EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip;

            foreach (int eid in mesh.EdgeIndices())
            {
                double fAngle = MeshUtil.OpeningAngleD(mesh, eid);
                if (fAngle > 30.0f)
                {
                    cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags));
                    Index2i ev      = mesh.GetEdgeV(eid);
                    int     nSetID0 = (mesh.GetVertex(ev[0]).y > bounds.Center.y) ? 1 : 2;
                    int     nSetID1 = (mesh.GetVertex(ev[1]).y > bounds.Center.y) ? 1 : 2;
                    cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(true, nSetID0));
                    cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(true, nSetID1));
                }
            }

            Remesher r = new Remesher(mesh);

            r.Precompute();
            r.SetExternalConstraints(cons);
            r.SetProjectionTarget(target);

            var stopwatch = Stopwatch.StartNew();

            //double fResScale = 1.0f;
            double fResScale = 0.5f;

            r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
            r.MinEdgeLength   = 0.1f * fResScale;
            r.MaxEdgeLength   = 0.2f * fResScale;
            r.EnableSmoothing = true;
            r.SmoothSpeedT    = 0.5f;

            try {
                for (int k = 0; k < 20; ++k)
                {
                    r.BasicRemeshPass();
                    mesh.CheckValidity();
                }
            } catch {
                // ignore
            }

            stopwatch.Stop();
            System.Console.WriteLine("Second Pass Timing: " + stopwatch.Elapsed);

            if (WriteDebugMeshes)
            {
                TestUtil.WriteDebugMesh(mesh, "remesh_fixed_constraints_test_after.obj");
            }
        }
コード例 #2
0
ファイル: MinimalHoleFill.cs プロジェクト: CallAndGus/ViRGIS
        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);
        }
コード例 #3
0
ファイル: MinimalHoleFill.cs プロジェクト: CallAndGus/ViRGIS
 double get_tri_area(DMesh3 mesh, ref Index3i tri)
 {
     return(MathUtil.Area(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c)));
 }
コード例 #4
0
  public void fpMeshPoints()
  {
      List <Vector3d> points  = new List <Vector3d>();
      List <int>      tris    = new List <int>();
      List <Vector3d> normals = new List <Vector3d>();
      float           normal  = .01f;

//string toPull = ReadString();
//string[] linesInFile = toPull.Split('\n');

      foreach (var pointCloud in ARpoints.trackables)
      {
//Debug.Log ("Lit" +pointCloud.transform.localPosition);
          int counter   = 0;
          var visualize = GameObject.FindWithTag("allPoints").GetComponent <UnityEngine.XR.ARFoundation.ARAllPointCloudPointsParticleVisualizer>();
          foreach (var kvp in visualize.m_Points)
          {
              counter++;
              points.Add(kvp.Value);
              tris.Add(counter);
              tris.Add(counter);
              tris.Add(counter);

              normals.Add(new Vector3d(normal, normal, normal));
          }
      }



      DMesh3         pointSet = DMesh3Builder.Build(points, tris, normals);
      PointAABBTree3 bvtree   = new PointAABBTree3(pointSet, true);

      bvtree.FastWindingNumber(Vector3d.Zero);
// estimate point area based on nearest-neighbour distance
      double[] areas = new double[pointSet.MaxVertexID];
      foreach (int vid in pointSet.VertexIndices())
      {
          bvtree.PointFilterF = (i) => { return(i != vid); }; // otherwise vid is nearest to vid!
          int    near_vid = bvtree.FindNearestPoint(pointSet.GetVertex(vid));
          double dist     = pointSet.GetVertex(vid).Distance(pointSet.GetVertex(near_vid));
          areas[vid] = Circle2d.RadiusArea(dist);
      }
      bvtree.FWNAreaEstimateF = (vid) => {
          return(areas[vid]);
      };
      MarchingCubes mc = new MarchingCubes();

      mc.Implicit = new PWNImplicit()
      {
          Spatial = bvtree
      };
      mc.IsoValue = 0.0;
      mc.CubeSize = bvtree.Bounds.MaxDim / 10;
      mc.Bounds   = bvtree.Bounds.Expanded(mc.CubeSize * 3);
      mc.RootMode = MarchingCubes.RootfindingModes.Bisection;
      mc.Generate();
      DMesh3 resultMesh = mc.Mesh;

      g3UnityUtils.SetGOMesh(transform.gameObject, resultMesh);

      /*  MarchingCubes mc = new MarchingCubes();
       *   mc.Implicit = new PWNImplicit() { Spatial =  bvtree };
       *   mc.IsoValue = 0.0;
       *   mc.CubeSize = bvtree.Bounds.MaxDim / 10;
       *   mc.Bounds =  bvtree.Bounds.Expanded(mc.CubeSize * 3);
       *   mc.RootMode = MarchingCubes.RootfindingModes.Bisection;
       *   mc.Generate();
       *   DMesh3 resultMesh = mc.Mesh;
       *   g3UnityUtils.SetGOMesh(transform.gameObject, resultMesh);
       */
  }
コード例 #5
0
        public static UnityMesh ToUnityMesh(this DMesh3 dMesh,
                                            bool bNorm = true,
                                            bool bUV   = false,
                                            bool bCol  = false)
        {
            bNorm &= dMesh.HasVertexNormals;
            bUV   &= dMesh.HasVertexUVs;
            bCol  &= dMesh.HasVertexColors;

            int[] vertexMap   = new int[dMesh.VerticesBuffer.Length];
            int[] triangleMap = new int[dMesh.TrianglesBuffer.Length];
            int[] triangles   = new int[dMesh.TriangleCount * 3];

            List <Vector3d> vertices       = new List <Vector3d>();
            List <Vector3f> normals        = new List <Vector3f>();
            List <Vector2f> uv             = new List <Vector2f>();
            List <Colorf>   colors         = new List <Colorf>();
            List <int>      vertexUseCount = new List <int>();

            NewVertexInfo vInfo = new NewVertexInfo(new Vector3d(),
                                                    new Vector3f(),
                                                    new Vector3f(),
                                                    new Vector2f());

            IEnumerator e  = dMesh.TrianglesRefCounts.GetEnumerator();
            int         ti = 0;

            while (e.MoveNext())
            {
                int     iRef     = (int)e.Current;
                Index3i triangle = dMesh.GetTriangle(iRef);
                triangleMap[iRef] = ti;

                for (int i = 0; i < 3; i++)
                {
                    int vertIndex = triangle[i];
                    if (vertexMap[vertIndex] == 0)
                    {
                        vertexUseCount.Add(1);
                        dMesh.GetVertex(vertIndex, ref vInfo, bNorm, bCol, bUV);
                        vertices.Add(new Vector3f((float)vInfo.v.x,
                                                  (float)vInfo.v.y,
                                                  (float)vInfo.v.z));
                        vertexMap[vertIndex] = vertices.Count - 1;

                        if (bNorm)
                        {
                            normals.Add(vInfo.n);
                        }
                        if (bUV)
                        {
                            uv.Add(vInfo.uv);
                        }
                        if (bCol)
                        {
                            colors.Add(new Colorf(vInfo.c.x, vInfo.c.y, vInfo.c.z));
                        }
                    }
                    else
                    {
                        vertexUseCount[vertexMap[vertIndex]]++;
                    }

                    triangles[ti * 3 + i] = vertexMap[vertIndex];
                }
                ti++;
            }

            UnityMesh uMesh = new UnityMesh(vertexUseCount.ToArray(),
                                            triangles,
                                            vertices,
                                            normals,
                                            uv,
                                            colors);

            // Triangle normals and neighbors.
            e = dMesh.TrianglesRefCounts.GetEnumerator();
            while (e.MoveNext())
            {
                int   iRef      = (int)e.Current;
                int[] nb        = dMesh.GetTriNeighbourTris(iRef).array;
                int[] neighbors = new int[3];

                for (int i = 0; i < 3; i++)
                {
                    neighbors[i] = (nb[i] != -1) ? triangleMap[nb[i]] : -1;
                }
                uMesh.AddTriangleInfo(triangleMap[iRef],
                                      (Vector3f)dMesh.GetTriNormal(iRef),
                                      neighbors);
            }

            return(uMesh);
        }
コード例 #6
0
        public static void performance_grinder()
        {
            LocalProfiler p = new LocalProfiler();

            DateTime start = DateTime.Now;

            //p.Start("Meshgen");
            //for (int k = 0; k < 100; ++k) {
            //    Sphere3Generator_NormalizedCube tmpgen = new Sphere3Generator_NormalizedCube();
            //    tmpgen.EdgeVertices = 100;
            //    tmpgen.Generate();
            //    DMesh3 tmp = tmpgen.MakeDMesh();
            //}
            //p.StopAndAccumulate("Meshgen");

            //System.Console.WriteLine("done meshgen");

            Sphere3Generator_NormalizedCube meshgen = new Sphere3Generator_NormalizedCube()
            {
                EdgeVertices = 100
            };

            meshgen.Generate();
            DMesh3 sphereMesh = meshgen.MakeDMesh();


            //p.Start("Spatial");
            //for (int k = 0; k < 100; ++k) {
            //    DMeshAABBTree3 tmpspatial = new DMeshAABBTree3(sphereMesh);
            //    tmpspatial.Build();
            //}
            //p.StopAndAccumulate("Spatial");

            //System.Console.WriteLine("done spatial");

            meshgen.EdgeVertices = 5;
            meshgen.Generate();
            sphereMesh = meshgen.MakeDMesh();
            double remesh_len = (2.0 / 5.0) * 0.025;   // takes ~220s
            //double remesh_len = (2.0 / 5.0) * 0.05;

            long     max_mem  = 0;
            Remesher remesher = new Remesher(sphereMesh);

            for (int k = 0; k < 10; ++k)
            {
                System.Console.WriteLine("{0}", k);
                p.Start("Remesh");
                remesher.SetTargetEdgeLength(remesh_len);
                remesher.SmoothSpeedT = 0.5f;
                for (int j = 0; j < 20; ++j)
                {
                    remesher.BasicRemeshPass();
                    foreach (int vid in sphereMesh.VertexIndices())
                    {
                        Vector3d v = sphereMesh.GetVertex(vid);
                        v.Normalize();
                        sphereMesh.SetVertex(vid, v);
                    }
                }
                p.StopAndAccumulate("Remesh");

                //System.Console.WriteLine(sphereMesh.MeshInfoString());

                System.Console.WriteLine(" {0}", k);

                p.Start("Reduce");
                remesher.SetTargetEdgeLength(remesh_len * 10);
                for (int j = 0; j < 20; ++j)
                {
                    remesher.BasicRemeshPass();
                    foreach (int vid in sphereMesh.VertexIndices())
                    {
                        Vector3d v = sphereMesh.GetVertex(vid);
                        v.Normalize();
                        sphereMesh.SetVertex(vid, v);
                    }
                }
                p.StopAndAccumulate("Reduce");
            }

            DateTime end = DateTime.Now;

            System.Console.WriteLine("done remesh");
            System.Console.WriteLine("Time {0} MaxMem {1}", (end - start).TotalSeconds, max_mem / (1024 * 1024));
            System.Console.WriteLine(p.AllAccumulatedTimes("Accumulated: "));
        }
コード例 #7
0
  public void fpMeshPointsfromTextFileWithaSecondPoints()
  {
//this is used in the fast points winding scene to optimize algorithm and make sure its working well
//it reads points via text files and then meshes them
      List <Vector3d> points  = new List <Vector3d>();
      List <int>      tris    = new List <int>();
      List <Vector3d> normals = new List <Vector3d>();
      float           normal  = .01f;

      string toPull = ReadString();

      string[] linesInFile = toPull.Split('\n');

      int counter = 0;

      foreach (string line in linesInFile)
      {
          counter++;
//Debug.Log(line);

          if (!string.IsNullOrEmpty(line))
          {
              points.Add(StringToVector3(line));
              tris.Add(counter);
              tris.Add(counter);
              tris.Add(counter);

              normals.Add(new Vector3d(normal, normal, normal));
          }
      }

      DMesh3         pointSet = DMesh3Builder.Build(points, tris, normals);
      PointAABBTree3 bvtree   = new PointAABBTree3(pointSet, true);

      bvtree.FastWindingNumber(Vector3d.Zero);
// estimate point area based on nearest-neighbour distance
      double[] areas = new double[pointSet.MaxVertexID];
      foreach (int vid in pointSet.VertexIndices())
      {
          bvtree.PointFilterF = (i) => { return(i != vid); }; // otherwise vid is nearest to vid!
          int    near_vid = bvtree.FindNearestPoint(pointSet.GetVertex(vid));
          double dist     = pointSet.GetVertex(vid).Distance(pointSet.GetVertex(near_vid));
          areas[vid] = Circle2d.RadiusArea(dist);
      }
      bvtree.FWNAreaEstimateF = (vid) => {
          return(areas[vid]);
      };
      MarchingCubes mc = new MarchingCubes();

      mc.Implicit = new PWNImplicit()
      {
          Spatial = bvtree
      };
      mc.IsoValue = 0.0;
      mc.CubeSize = bvtree.Bounds.MaxDim / 50;
      mc.Bounds   = bvtree.Bounds.Expanded(mc.CubeSize * 3);
      mc.RootMode = MarchingCubes.RootfindingModes.Bisection;
      mc.Generate();
      DMesh3 resultMesh = mc.Mesh;

      g3UnityUtils.SetGOMesh(transform.gameObject, resultMesh);

      /*  MarchingCubes mc = new MarchingCubes();
       *   mc.Implicit = new PWNImplicit() { Spatial =  bvtree };
       *   mc.IsoValue = 0.0;
       *   mc.CubeSize = bvtree.Bounds.MaxDim / 10;
       *   mc.Bounds =  bvtree.Bounds.Expanded(mc.CubeSize * 3);
       *   mc.RootMode = MarchingCubes.RootfindingModes.Bisection;
       *   mc.Generate();
       *   DMesh3 resultMesh = mc.Mesh;
       *   g3UnityUtils.SetGOMesh(transform.gameObject, resultMesh);
       */
//ok now that we meshed the first point set, lets try to take in a second frame of points and then add to orginal dmesh
      points  = new List <Vector3d>();
      tris    = new List <int>();
      normals = new List <Vector3d>();


      toPull      = ReadString1();
      linesInFile = toPull.Split('\n');

      counter = 0;
      foreach (string line in linesInFile)
      {
          counter++;
          Debug.Log(line);

          if (!string.IsNullOrEmpty(line))
          {
              points.Add(StringToVector3(line));
              tris.Add(counter);
              tris.Add(counter);
              tris.Add(counter);

              normals.Add(new Vector3d(normal, normal, normal));
          }
      }
      pointSet = DMesh3Builder.Build(points, tris, normals);
      bvtree   = new PointAABBTree3(pointSet, true);
      bvtree.FastWindingNumber(Vector3d.Zero);
// estimate point area based on nearest-neighbour distance
      areas = new double[pointSet.MaxVertexID];
      foreach (int vid in pointSet.VertexIndices())
      {
          bvtree.PointFilterF = (i) => { return(i != vid); }; // otherwise vid is nearest to vid!
          int    near_vid = bvtree.FindNearestPoint(pointSet.GetVertex(vid));
          double dist     = pointSet.GetVertex(vid).Distance(pointSet.GetVertex(near_vid));
          areas[vid] = Circle2d.RadiusArea(dist);
      }
      bvtree.FWNAreaEstimateF = (vid) => {
          return(areas[vid]);
      };
      mc          = new MarchingCubes();
      mc.Implicit = new PWNImplicit()
      {
          Spatial = bvtree
      };
      mc.IsoValue = 0.0;
      mc.CubeSize = bvtree.Bounds.MaxDim / 50;
      mc.Bounds   = bvtree.Bounds.Expanded(mc.CubeSize * 3);
      mc.RootMode = MarchingCubes.RootfindingModes.Bisection;
      mc.Generate();
      DMesh3     resultMesh1 = mc.Mesh;
      MeshEditor editor      = new MeshEditor(resultMesh);

      editor.AppendMesh(resultMesh1, resultMesh.AllocateTriangleGroup());

      g3UnityUtils.SetGOMesh(transform.gameObject, resultMesh1);

      //  g3UnityUtils.SetGOMesh(transform.gameObject, resultMesh);
  }
コード例 #8
0
        DMesh3 ComputeInflation(DMesh3 planarMesh)
        {
            DMesh3 mesh = new DMesh3(planarMesh);

            DijkstraGraphDistance dist = new DijkstraGraphDistance(
                mesh.MaxVertexID, false,
                (vid) => { return(mesh.IsVertex(vid)); },
                (a, b) => { return((float)mesh.GetVertex(a).Distance(mesh.GetVertex(b))); },
                mesh.VtxVerticesItr);

            foreach (int vid in MeshIterators.BoundaryVertices(mesh))
            {
                dist.AddSeed(vid, 0);
            }

            dist.Compute();
            float max_dist = dist.MaxDistance;

            float[] distances = new float[mesh.MaxVertexID];
            foreach (int vid in mesh.VertexIndices())
            {
                distances[vid] = dist.GetDistance(vid);
            }


            List <int> local_maxima = new List <int>();

            foreach (int vid in MeshIterators.InteriorVertices(mesh))
            {
                float d         = distances[vid];
                bool  is_maxima = true;
                foreach (int nbrid in mesh.VtxVerticesItr(vid))
                {
                    if (distances[nbrid] > d)
                    {
                        is_maxima = false;
                    }
                }
                if (is_maxima)
                {
                    local_maxima.Add(vid);
                }
            }

            // smooth distances   (really should use cotan here!!)
            float smooth_alpha  = 0.1f;
            int   smooth_rounds = 5;

            foreach (int ri in Interval1i.Range(smooth_rounds))
            {
                foreach (int vid in mesh.VertexIndices())
                {
                    float cur       = distances[vid];
                    float centroid  = 0;
                    int   nbr_count = 0;
                    foreach (int nbrid in mesh.VtxVerticesItr(vid))
                    {
                        centroid += distances[nbrid];
                        nbr_count++;
                    }
                    centroid      /= nbr_count;
                    distances[vid] = (1 - smooth_alpha) * cur + (smooth_alpha) * centroid;
                }
            }

            Vector3d normal = Vector3d.AxisZ;

            foreach (int vid in mesh.VertexIndices())
            {
                if (dist.IsSeed(vid))
                {
                    continue;
                }
                float h = distances[vid];

                // [RMS] there are different options here...
                h = 2 * (float)Math.Sqrt(h);

                float    offset = h;
                Vector3d d      = mesh.GetVertex(vid);
                mesh.SetVertex(vid, d + (Vector3d)(offset * normal));
            }


            DMesh3 compacted   = new DMesh3();
            var    compactInfo = compacted.CompactCopy(mesh);

            IndexUtil.Apply(local_maxima, compactInfo.MapV);
            mesh = compacted;
            MeshVertexSelection boundary_sel = new MeshVertexSelection(mesh);
            HashSet <int>       boundaryV    = new HashSet <int>(MeshIterators.BoundaryVertices(mesh));

            boundary_sel.Select(boundaryV);
            boundary_sel.ExpandToOneRingNeighbours();

            LaplacianMeshSmoother smoother = new LaplacianMeshSmoother(mesh);

            foreach (int vid in boundary_sel)
            {
                if (boundaryV.Contains(vid))
                {
                    smoother.SetConstraint(vid, mesh.GetVertex(vid), 100.0f, true);
                }
                else
                {
                    smoother.SetConstraint(vid, mesh.GetVertex(vid), 10.0f, false);
                }
            }
            foreach (int vid in local_maxima)
            {
                smoother.SetConstraint(vid, mesh.GetVertex(vid), 50, false);
            }

            bool ok = smoother.SolveAndUpdateMesh();

            Util.gDevAssert(ok);


            List <int>          intVerts = new List <int>(MeshIterators.InteriorVertices(mesh));
            MeshIterativeSmooth smooth   = new MeshIterativeSmooth(mesh, intVerts.ToArray(), true);

            smooth.SmoothType = MeshIterativeSmooth.SmoothTypes.Cotan;
            smooth.Rounds     = 10;
            smooth.Alpha      = 0.1f;
            smooth.Smooth();

            return(mesh);
        }
コード例 #9
0
        public static void test_arrangement_demo()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("spheres_and_planes.obj");

            MeshTransforms.Scale(mesh, 8);
            AxisAlignedBox3d meshBounds      = mesh.CachedBounds;
            Vector3d         origin          = meshBounds.Center;
            double           simplify_thresh = 5.0;

            Frame3f      plane = new Frame3f(origin, Vector3d.AxisY);
            MeshPlaneCut cut   = new MeshPlaneCut(mesh, plane.Origin, plane.Z);

            cut.Cut();

            Arrangement2d builder = new Arrangement2d(new AxisAlignedBox2d(1024.0));

            // insert all cut edges
            HashSet <Vector2d> srcpts = new HashSet <Vector2d>();

            foreach (EdgeLoop loop in cut.CutLoops)
            {
                Polygon2d poly = new Polygon2d();
                foreach (int vid in loop.Vertices)
                {
                    poly.AppendVertex(mesh.GetVertex(vid).xz);
                }

                poly.Simplify(simplify_thresh, 0.01, true);
                foreach (Vector2d v in poly.Vertices)
                {
                    srcpts.Add(v);
                }

                builder.Insert(poly);
            }
            foreach (EdgeSpan span in cut.CutSpans)
            {
                PolyLine2d pline = new PolyLine2d();
                foreach (int vid in span.Vertices)
                {
                    pline.AppendVertex(mesh.GetVertex(vid).xz);
                }
                pline.Simplify(simplify_thresh, 0.01, true);
                foreach (Vector2d v in pline)
                {
                    srcpts.Add(v);
                }
                builder.Insert(pline);
            }

            SVGWriter svg = new SVGWriter();

            svg.AddGraph(builder.Graph);

            var vtx_style = SVGWriter.Style.Outline("red", 1.0f);

            foreach (int vid in builder.Graph.VertexIndices())
            {
                Vector2d v = builder.Graph.GetVertex(vid);
                if (srcpts.Contains(v) == false)
                {
                    svg.AddCircle(new Circle2d(v, 2), vtx_style);
                }
            }

            svg.Write(TestUtil.GetTestOutputPath("arrangement.svg"));
        }
コード例 #10
0
ファイル: EditableMesh.cs プロジェクト: ViRGIS-Team/ViRGiS_v2
    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;
        }
    }
コード例 #11
0
        protected void do_flatten(DMesh3 mesh)
        {
            double   BAND_HEIGHT = flatten_band_height;
            Vector3d down_axis   = -Vector3d.AxisY;
            double   dot_thresh  = 0.2;

            AxisAlignedBox3d bounds  = mesh.CachedBounds;
            DMeshAABBTree3   spatial = new DMeshAABBTree3(mesh, true);

            Ray3d   ray     = new Ray3d(bounds.Center - 2 * bounds.Height * Vector3d.AxisY, Vector3d.AxisY);
            int     hit_tid = spatial.FindNearestHitTriangle(ray);
            Frame3f hitF;

            MeshQueries.RayHitPointFrame(mesh, spatial, ray, out hitF);
            Vector3d basePt = hitF.Origin;

            Frame3f basePlane = new Frame3f(basePt, Vector3f.AxisY);

            MeshConnectedComponents components = new MeshConnectedComponents(mesh)
            {
                FilterF = (tid) => {
                    if (mesh.GetTriangleGroup(tid) != LastExtrudeOuterGroupID)
                    {
                        return(false);
                    }
                    Vector3d n, c; double a;
                    mesh.GetTriInfo(tid, out n, out a, out c);
                    double h = Math.Abs(c.y - basePt.y);
                    if (h > BAND_HEIGHT)
                    {
                        return(false);
                    }
                    if (n.Dot(down_axis) < dot_thresh)
                    {
                        return(false);
                    }
                    return(true);
                },
                SeedFilterF = (tid) => {
                    return(tid == hit_tid);
                }
            };

            components.FindConnectedT();

            MeshFaceSelection all_faces = new MeshFaceSelection(mesh);

            foreach (var comp in components)
            {
                MeshVertexSelection vertices = new MeshVertexSelection(mesh);
                vertices.SelectTriangleVertices(comp.Indices);
                foreach (int vid in vertices)
                {
                    Vector3d v = mesh.GetVertex(vid);
                    v = basePlane.ProjectToPlane((Vector3f)v, 2);
                    mesh.SetVertex(vid, v);
                }
                all_faces.SelectVertexOneRings(vertices);
            }

            all_faces.ExpandToOneRingNeighbours(3, (tid) => {
                return(mesh.GetTriangleGroup(tid) == LastExtrudeOuterGroupID);
            });

            RegionRemesher r = new RegionRemesher(mesh, all_faces);

            r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh));
            r.SetTargetEdgeLength(2.0f);
            r.SmoothSpeedT = 1.0f;
            for (int k = 0; k < 10; ++k)
            {
                r.BasicRemeshPass();
            }
            r.SetProjectionTarget(null);
            r.SmoothSpeedT = 1.0f;
            for (int k = 0; k < 10; ++k)
            {
                r.BasicRemeshPass();
            }
            r.BackPropropagate();
        }
コード例 #12
0
ファイル: EditableMesh.cs プロジェクト: ViRGIS-Team/ViRGiS_v2
 public override void MoveTo(MoveArgs args)
 {
     if (BlockMove)
     {
         if (args.translate != Vector3.zero)
         {
             transform.Translate(args.translate, Space.World);
             transform.parent.SendMessage("Translate", args);
         }
     }
     else
     {
         if (args.translate != Vector3.zero)
         {
             MeshFilter mf             = GetComponent <MeshFilter>();
             Mesh       mesh           = mf.sharedMesh;
             Vector3    localTranslate = transform.InverseTransformVector(args.translate);
             Vector3d   target         = mesh.vertices[selectedVertex] + localTranslate;
             if (AppState.instance.editScale > 2)
             {
                 if (n != AppState.instance.editScale)
                 {
                     n = AppState.instance.editScale;
                     //
                     // first we need to find the n-ring of vertices
                     //
                     // first get 1-ring
                     nRing = new List <int>();
                     List <Int32> inside = new List <int>();
                     nRing.Add(selectedVertex);
                     for (int i = 0; i < n; i++)
                     {
                         int[] working = nRing.ToArray();
                         nRing.Clear();
                         foreach (int v in working)
                         {
                             if (!inside.Contains(v))
                             {
                                 foreach (int vring in dmesh.VtxVerticesItr(v))
                                 {
                                     if (!inside.Contains(vring))
                                     {
                                         nRing.Add(vring);
                                     }
                                 }
                             }
                             inside.Add(v);
                         }
                     }
                 }
                 //
                 // create the deformer
                 // set the constraint that the selected vertex is moved to position
                 // set the contraint that the n-ring remains stationary
                 //
                 LaplacianMeshDeformer deform = new LaplacianMeshDeformer(dmesh);
                 deform.SetConstraint(selectedVertex, target, 1, false);
                 foreach (int v in nRing)
                 {
                     deform.SetConstraint(v, dmesh.GetVertex(v), 10, false);
                 }
                 deform.SolveAndUpdateMesh();
                 newDmesh = deform.Mesh;
             }
             else
             {
                 dmesh.SetVertex(selectedVertex, target);
             }
             //
             // reset the Unity meh
             //
             if (sphere != null)
             {
                 sphere.transform.localPosition = (Vector3)dmesh.GetVertex(selectedVertex);
             }
             List <Vector3> vtxs = new List <Vector3>();
             foreach (int v in newDmesh.VertexIndices())
             {
                 vtxs.Add((Vector3)newDmesh.GetVertex(v));
             }
             mesh.vertices = vtxs.ToArray();
             mesh.RecalculateBounds();
             mesh.RecalculateNormals();
         }
     }
 }
コード例 #13
0
        protected virtual DMesh3 compute_bounded_distance()
        {
            DMesh3   sourceMesh   = MeshSource.GetDMeshUnsafe();
            ISpatial inputSpatial = MeshSource.GetSpatial();

            DMesh3   targetMesh    = TargetSource.GetDMeshUnsafe();
            ISpatial targetSpatial = TargetSource.GetSpatial();

            double max_dist = (TargetMaxDistance == double.MaxValue) ? double.MaxValue : TargetMaxDistance;

            DMesh3 meshIn = new DMesh3(sourceMesh);

            bool target_closed           = targetMesh.IsClosed();
            MeshVertexSelection roiV     = new MeshVertexSelection(meshIn);
            SpinLock            roi_lock = new SpinLock();

            gParallel.ForEach(meshIn.VertexIndices(), (vid) => {
                Vector3d pos       = meshIn.GetVertex(vid);
                Vector3d posTarget = TransformToTarget.TransformP(pos);
                double dist        = MeshQueries.NearestPointDistance(targetMesh, targetSpatial, posTarget, max_dist);
                bool inside        = (target_closed && targetSpatial.IsInside(posTarget));
                if (dist < max_dist || inside)
                {
                    bool taken = false;
                    roi_lock.Enter(ref taken);
                    roiV.Select(vid);
                    roi_lock.Exit();
                }
            });
            if (is_invalidated())
            {
                return(null);
            }

            MeshFaceSelection roi_faces = new MeshFaceSelection(meshIn, roiV, 1);

            roi_faces.ExpandToOneRingNeighbours(3);
            roi_faces.LocalOptimize();
            if (is_invalidated())
            {
                return(null);
            }

            RegionOperator op      = new RegionOperator(meshIn, roi_faces);
            DMesh3         meshROI = op.Region.SubMesh;

            if (is_invalidated())
            {
                return(null);
            }

            RemesherPro remesher = new RemesherPro(meshROI);

            remesher.SetTargetEdgeLength(TargetEdgeLength);
            remesher.PreventNormalFlips = this.PreventNormalFlips;
            remesher.EnableFlips        = this.EnableFlips;
            remesher.EnableSplits       = this.EnableSplits;
            remesher.EnableCollapses    = this.EnableCollapses;
            remesher.EnableSmoothing    = this.EnableSmoothing;
            remesher.SmoothSpeedT       = this.SmoothingSpeed;

            BoundedProjectionTarget target = new BoundedProjectionTarget()
            {
                Source = sourceMesh, SourceSpatial = inputSpatial,
                Target = targetMesh, TargetSpatial = targetSpatial,
                SourceToTargetXForm = source_to_target,
                TargetToSourceXForm = target_to_source,
                MaxDistance         = max_dist,
                Smoothness          = transition_smoothness
            };

            remesher.SetProjectionTarget(target);

            if (remesher.Constraints == null)
            {
                remesher.SetExternalConstraints(new MeshConstraints());
            }
            MeshConstraintUtil.FixAllBoundaryEdges(remesher.Constraints, meshROI);
            if (is_invalidated())
            {
                return(null);
            }

            remesher.Progress = new ProgressCancel(is_invalidated);
            remesher.FastestRemesh(RemeshRounds);
            if (is_invalidated())
            {
                return(null);
            }

            op.BackPropropagate();

            return(meshIn);
        }
コード例 #14
0
        public static void test_remesh_constraints_vertcurves()
        {
            int    Slices = 16;
            DMesh3 mesh   = TestUtil.MakeCappedCylinder(false, Slices);

            MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1));
            //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(0.25);
            //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(1.0);
            mesh.CheckValidity();
            AxisAlignedBox3d bounds = mesh.CachedBounds;

            // construct mesh projection target
            DMesh3 meshCopy = new DMesh3(mesh);

            meshCopy.CheckValidity();
            DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy);

            tree.Build();
            MeshProjectionTarget mesh_target = new MeshProjectionTarget()
            {
                Mesh = meshCopy, Spatial = tree
            };

            // cylinder projection target
            CylinderProjectionTarget cyl_target = new CylinderProjectionTarget()
            {
                Cylinder = new Cylinder3d(new Vector3d(0, 1, 0), Vector3d.AxisY, 1, 2)
            };

            //IProjectionTarget target = mesh_target;
            IProjectionTarget target = cyl_target;

            // construct projection target circles
            CircleProjectionTarget bottomCons = new CircleProjectionTarget()
            {
                Circle = new Circle3d(bounds.Center, 1.0)
            };

            bottomCons.Circle.Center.y = bounds.Min.y;
            CircleProjectionTarget topCons = new CircleProjectionTarget()
            {
                Circle = new Circle3d(bounds.Center, 1.0)
            };

            topCons.Circle.Center.y = bounds.Max.y;


            if (WriteDebugMeshes)
            {
                TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_before.obj");
            }

            // construct constraint set
            MeshConstraints cons = new MeshConstraints();

            //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse;
            EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip;

            bool bConstrainVertices = true;

            foreach (int eid in mesh.EdgeIndices())
            {
                double fAngle = MeshUtil.OpeningAngleD(mesh, eid);
                if (fAngle > 30.0f)
                {
                    Index2i  ev  = mesh.GetEdgeV(eid);
                    Vector3d ev0 = mesh.GetVertex(ev[0]);
                    Vector3d ev1 = mesh.GetVertex(ev[1]);
                    CircleProjectionTarget loopTarget = null;
                    if (ev0.y > bounds.Center.y && ev1.y > bounds.Center.y)
                    {
                        loopTarget = topCons;
                    }
                    else if (ev0.y < bounds.Center.y && ev1.y < bounds.Center.y)
                    {
                        loopTarget = bottomCons;
                    }

                    cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags, loopTarget));
                    if (bConstrainVertices && loopTarget != null)
                    {
                        cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(loopTarget));
                        cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(loopTarget));
                    }
                }
            }


            Remesher r = new Remesher(mesh);

            //r.SetExternalConstraints(cons);
            r.SetProjectionTarget(target);
            r.Precompute();
            r.ENABLE_PROFILING = true;

            var stopwatch = Stopwatch.StartNew();

            //double fResScale = 1.0f;
            double fResScale = 0.5f;

            r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
            r.MinEdgeLength   = 0.1f * fResScale;
            r.MaxEdgeLength   = 0.2f * fResScale;
            r.EnableSmoothing = true;
            r.SmoothSpeedT    = 1.0f;

            try {
                for (int k = 0; k < 20; ++k)
                {
                    r.BasicRemeshPass();
                    mesh.CheckValidity();
                }
            } catch {
                // continue;
            }
            stopwatch.Stop();
            System.Console.WriteLine("Second Pass Timing: " + stopwatch.Elapsed);

            if (WriteDebugMeshes)
            {
                TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_after.obj");
            }
        }
コード例 #15
0
        public virtual ExportStatus Export(FScene scene, string filename)
        {
            int[]            vertexMap = new int[2048]; // temp
            List <WriteMesh> vMeshes   = new List <WriteMesh>();

            if (WriteFaceGroups)
            {
                throw new Exception("SceneMeshExporter.Export: writing face groups has not yet been implemented!");
            }

            // extract all the mesh data we want to export
            foreach (SceneObject so in scene.SceneObjects)
            {
                if (so.IsTemporary || so.IsSurface == false || SceneUtil.IsVisible(so) == false)
                {
                    continue;
                }
                if (SOFilterF != null && SOFilterF(so) == false)
                {
                    continue;
                }

                // if this SO has an internal mesh we can just copy, use it
                if (so is DMeshSO)
                {
                    DMeshSO meshSO = so as DMeshSO;

                    // todo: flags

                    // make a copy of mesh
                    DMesh3 m = new DMesh3(meshSO.Mesh, true);

                    // transform to scene coords and swap left/right
                    foreach (int vid in m.VertexIndices())
                    {
                        Vector3f v = (Vector3f)m.GetVertex(vid);
                        v = SceneTransforms.ObjectToScene(meshSO, v);
                        v = UnityUtil.SwapLeftRight(v);
                        m.SetVertex(vid, v);
                    }
                    m.ReverseOrientation();

                    vMeshes.Add(new WriteMesh(m, so.Name));
                }


                // Look for lower-level fGameObject items to export. By default
                // this is anything with a MeshFilter, override CollectGOChildren
                // or use GOFilterF to add restrictions
                List <fGameObject> vExports = CollectGOChildren(so);
                if (vExports.Count > 0)
                {
                    SimpleMesh m = new SimpleMesh();
                    m.Initialize(WriteNormals, WriteVertexColors, WriteUVs, WriteFaceGroups);
                    int groupCounter = 1;

                    foreach (fGameObject childgo in vExports)
                    {
                        if (GOFilterF != null && GOFilterF(so, childgo) == false)
                        {
                            continue;
                        }

                        if (AppendGOMesh(childgo, m, vertexMap, scene, groupCounter))
                        {
                            groupCounter++;
                        }
                    }

                    vMeshes.Add(new WriteMesh(m, so.Name));
                }
            }


            // ok, we are independent of Scene now and can write in bg thread
            if (WriteInBackgroundThreads)
            {
                ExportStatus status = new ExportStatus()
                {
                    Exporter = this, IsComputing = true
                };
                WriteOptions useOptions = Options;
                useOptions.ProgressFunc = (cur, max) => {
                    status.Progress    = cur;
                    status.MaxProgress = max;
                };
                BackgroundWriteThread t = new BackgroundWriteThread()
                {
                    Meshes      = vMeshes, options = useOptions, Filename = filename,
                    CompletionF = (result) => {
                        LastWriteStatus         = result.code;
                        LastErrorMessage        = result.message;
                        status.LastErrorMessage = result.message;
                        status.Ok          = (result.code == IOCode.Ok);
                        status.IsComputing = false;
                        if (BackgroundWriteCompleteF != null)
                        {
                            BackgroundWriteCompleteF(this, status);
                        }
                    }
                };
                t.Start();
                return(status);
            }
            else
            {
                IOWriteResult result = StandardMeshWriter.WriteFile(filename, vMeshes, Options);
                LastWriteStatus  = result.code;
                LastErrorMessage = result.message;
                return(new ExportStatus()
                {
                    Exporter = this, IsComputing = false,
                    Ok = (result.code == IOCode.Ok),
                    LastErrorMessage = result.message
                });
            }
        }
コード例 #16
0
        public static void quick_test_2()
        {
            DMesh3         target        = TestUtil.LoadTestInputMesh("cylinder_orig.obj");
            DMeshAABBTree3 targetSpatial = new DMeshAABBTree3(target, true);

            DMesh3         mesh        = TestUtil.LoadTestInputMesh("cylinder_approx.obj");
            DMeshAABBTree3 meshSpatial = new DMeshAABBTree3(mesh, true);

            double search_dist = 10.0;

            MeshTopology topo = new MeshTopology(target);

            topo.Compute();

            RemesherPro r = new RemesherPro(mesh);

            r.SetTargetEdgeLength(2.0);
            r.SmoothSpeedT = 0.5;
            r.SetProjectionTarget(MeshProjectionTarget.Auto(target));
            MeshConstraints cons = new MeshConstraints();

            r.SetExternalConstraints(cons);


            int set_id = 1;

            foreach (var loop in topo.Loops)
            {
                DCurveProjectionTarget curveTarget = new DCurveProjectionTarget(loop.ToCurve(target));
                set_id++;

                // pick a set of points we will find paths between. We will chain
                // up those paths and constrain them to target loops.
                // (this part is the hack!)
                List <int> target_verts = new List <int>();
                List <int> mesh_verts   = new List <int>();
                for (int k = 0; k < loop.VertexCount; k += 5)
                {
                    target_verts.Add(loop.Vertices[k]);

                    Vector3d vCurve   = target.GetVertex(loop.Vertices[k]);
                    int      mesh_vid = meshSpatial.FindNearestVertex(vCurve, search_dist);
                    mesh_verts.Add(mesh_vid);
                }
                int NT = target_verts.Count;

                // find the paths to assemble the edge chain
                // [TODO] need to filter out junction vertices? or will they just handle themselves
                //   because they can be collapsed away?
                List <int> vert_seq = new List <int>();
                for (int k = 0; k < NT; k++)
                {
                    EdgeSpan e = find_edge_path(mesh, mesh_verts[k], mesh_verts[(k + 1) % NT]);
                    int      n = e.Vertices.Length;
                    for (int i = 0; i < n - 1; ++i)
                    {
                        vert_seq.Add(e.Vertices[i]);
                    }
                }

                // now it's easy, just add the loop constraint
                EdgeLoop full_loop = EdgeLoop.FromVertices(mesh, vert_seq);
                MeshConstraintUtil.ConstrainVtxLoopTo(cons, mesh, full_loop.Vertices, curveTarget, set_id);
            }


            r.FastestRemesh();

            TestUtil.WriteTestOutputMesh(mesh, "curves_test_out.obj");
        }
コード例 #17
0
        public bool Insert()
        {
            Func <int, bool> is_contained_v = (vid) => {
                Vector3d v   = Mesh.GetVertex(vid);
                Vector2f vf2 = ProjectFrame.ToPlaneUV((Vector3f)v, 2);
                return(Polygon.Contains(vf2));
            };

            MeshVertexSelection vertexROI = new MeshVertexSelection(Mesh);
            Index3i             seedT     = Mesh.GetTriangle(SeedTriangle);

            // if a seed vert of seed triangle is containd in polygon, we will
            // flood-fill out from there, this gives a better ROI.
            // If not, we will try flood-fill from the seed triangles.
            List <int> seed_verts = new List <int>();

            for (int j = 0; j < 3; ++j)
            {
                if (is_contained_v(seedT[j]))
                {
                    seed_verts.Add(seedT[j]);
                }
            }
            if (seed_verts.Count == 0)
            {
                seed_verts.Add(seedT.a);
                seed_verts.Add(seedT.b);
                seed_verts.Add(seedT.c);
            }

            // flood-fill out from seed vertices until we have found all vertices
            // contained in polygon
            vertexROI.FloodFill(seed_verts.ToArray(), is_contained_v);

            // convert vertex ROI to face ROI
            MeshFaceSelection faceROI = new MeshFaceSelection(Mesh, vertexROI, 1);

            faceROI.ExpandToOneRingNeighbours();
            faceROI.FillEars(true);    // this might be a good idea...

            // construct submesh
            RegionOperator regionOp   = new RegionOperator(Mesh, faceROI);
            DSubmesh3      roiSubmesh = regionOp.Region;
            DMesh3         roiMesh    = roiSubmesh.SubMesh;

            // save 3D positions of unmodified mesh
            Vector3d[] initialPositions = new Vector3d[roiMesh.MaxVertexID];

            // map roi mesh to plane
            MeshTransforms.PerVertexTransform(roiMesh, roiMesh.VertexIndices(), (v, vid) => {
                Vector2f uv           = ProjectFrame.ToPlaneUV((Vector3f)v, 2);
                initialPositions[vid] = v;
                return(new Vector3d(uv.x, uv.y, 0));
            });

            // save a copy of 2D mesh and construct bvtree. we will use
            // this later to project back to 3d
            // [TODO] can we use a better spatial DS here, that takes advantage of 2D?
            DMesh3         projectMesh = new DMesh3(roiMesh);
            DMeshAABBTree3 projecter   = new DMeshAABBTree3(projectMesh, true);

            MeshInsertUVPolyCurve insertUV = new MeshInsertUVPolyCurve(roiMesh, Polygon);
            //insertUV.Validate()
            bool bOK = insertUV.Apply();

            if (!bOK)
            {
                throw new Exception("insertUV.Apply() failed");
            }

            if (SimplifyInsertion)
            {
                insertUV.Simplify();
            }

            int[] insertedPolyVerts = insertUV.CurveVertices;

            // grab inserted loop, assuming it worked
            EdgeLoop insertedLoop = null;

            if (insertUV.Loops.Count == 1)
            {
                insertedLoop = insertUV.Loops[0];
            }

            // find interior triangles
            List <int> interiorT = new List <int>();

            foreach (int tid in roiMesh.TriangleIndices())
            {
                Vector3d centroid = roiMesh.GetTriCentroid(tid);
                if (Polygon.Contains(centroid.xy))
                {
                    interiorT.Add(tid);
                }
            }
            if (RemovePolygonInterior)
            {
                MeshEditor editor = new MeshEditor(roiMesh);
                editor.RemoveTriangles(interiorT, true);
                InteriorTriangles = null;
            }
            else
            {
                InteriorTriangles = interiorT.ToArray();
            }


            // map back to 3d
            Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero;

            foreach (int vid in roiMesh.VertexIndices())
            {
                // [TODO] somehow re-use exact positions from regionOp maps?

                // construct new 3D pos w/ barycentric interpolation
                Vector3d v   = roiMesh.GetVertex(vid);
                int      tid = projecter.FindNearestTriangle(v);
                Index3i  tri = projectMesh.GetTriangle(tid);
                projectMesh.GetTriVertices(tid, ref a, ref b, ref c);
                Vector3d bary = MathUtil.BarycentricCoords(ref v, ref a, ref b, ref c);
                Vector3d pos  = bary.x * initialPositions[tri.a] + bary.y * initialPositions[tri.b] + bary.z * initialPositions[tri.c];

                roiMesh.SetVertex(vid, pos);
            }

            bOK = BackPropagate(regionOp, insertedPolyVerts, insertedLoop);

            return(bOK);
        }
コード例 #18
0
        void extract_topology()
        {
            var graph = new DGraph3();

            // add vertices to graph, and store mappings
            int[] mapV     = new int[Mesh.MaxVertexID];
            int[] mapVFrom = new int[AllVertices.Count];
            foreach (int vid in AllVertices)
            {
                int new_vid = graph.AppendVertex(Mesh.GetVertex(vid));
                mapV[vid]         = new_vid;
                mapVFrom[new_vid] = vid;
            }

            // add edges to graph. graph-to-mesh eid mapping is stored via graph edge-group-id
            int[] mapE = new int[Mesh.MaxEdgeID];
            foreach (int eid in AllEdges)
            {
                Index2i ev      = Mesh.GetEdgeV(eid);
                int     new_a   = mapV[ev.a];
                int     new_b   = mapV[ev.b];
                int     new_eid = graph.AppendEdge(new_a, new_b, eid);
                mapE[eid] = new_eid;
            }

            // extract the graph topology
            DGraph3Util.Curves curves = DGraph3Util.ExtractCurves(graph, true);

            // reconstruct mesh spans / curves / junctions from graph topology

            int NP = curves.PathEdges.Count;

            Spans = new EdgeSpan[NP];
            for (int pi = 0; pi < NP; ++pi)
            {
                List <int> pathE = curves.PathEdges[pi];
                for (int k = 0; k < pathE.Count; ++k)
                {
                    pathE[k] = graph.GetEdgeGroup(pathE[k]);
                }
                Spans[pi] = EdgeSpan.FromEdges(Mesh, pathE);
            }

            int NL = curves.LoopEdges.Count;

            Loops = new EdgeLoop[NL];
            for (int li = 0; li < NL; ++li)
            {
                List <int> loopE = curves.LoopEdges[li];
                for (int k = 0; k < loopE.Count; ++k)
                {
                    loopE[k] = graph.GetEdgeGroup(loopE[k]);
                }
                Loops[li] = EdgeLoop.FromEdges(Mesh, loopE);
            }

            JunctionVertices = new HashSet <int>();
            foreach (int gvid in curves.JunctionV)
            {
                JunctionVertices.Add(mapVFrom[gvid]);
            }
        }
コード例 #19
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();
                    }
                }
            }

            DMeshAABBTree3 spatial = (Spatial != null && testAgainstMesh == Mesh) ?
                                     Spatial : new DMeshAABBTree3(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);
        }
コード例 #20
0
        public static Mesh RemeshTest(Mesh inMesh, double fResScale = 1.0, int iterations = 50)
        {
            inMesh.Faces.ConvertQuadsToTriangles();
            DMesh3 mesh = inMesh.ToDMesh3();

            mesh.CheckValidity();
            AxisAlignedBox3d bounds = mesh.CachedBounds;

            // construct mesh projection target
            DMesh3 meshCopy = new DMesh3(mesh);

            meshCopy.CheckValidity();
            DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy);

            tree.Build();
            MeshProjectionTarget target = new MeshProjectionTarget()
            {
                Mesh    = meshCopy,
                Spatial = tree
            };

            // construct constraint set
            MeshConstraints cons = new MeshConstraints();

            //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse;
            EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip;

            foreach (int eid in mesh.EdgeIndices())
            {
                double fAngle = MeshUtil.OpeningAngleD(mesh, eid);
                if (fAngle > 30.0)
                {
                    cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags));
                    Index2i ev      = mesh.GetEdgeV(eid);
                    int     nSetID0 = (mesh.GetVertex(ev[0]).y > bounds.Center.y) ? 1 : 2;
                    int     nSetID1 = (mesh.GetVertex(ev[1]).y > bounds.Center.y) ? 1 : 2;
                    cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(true, nSetID0));
                    cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(true, nSetID1));
                }
            }

            Remesher r = new Remesher(mesh);

            r.Precompute();
            r.SetExternalConstraints(cons);
            r.SetProjectionTarget(target);

            r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
            r.MinEdgeLength   = 0.5 * fResScale;
            r.MaxEdgeLength   = 1.0 * fResScale;
            r.EnableSmoothing = true;
            r.SmoothSpeedT    = 0.5;

            try
            {
                for (int k = 0; k < iterations; ++k)
                {
                    r.BasicRemeshPass();
                    // mesh.CheckValidity();
                }
            }
            catch
            {
                // ignore
            }

            return(mesh.ToRhinoMesh());
        }
コード例 #21
0
  public void takePointsinandAddtoMesh(List <Vector3d> pointers)
  {
      List <int>      tris    = new List <int>();
      List <Vector3d> normals = new List <Vector3d>();
      float           normal  = .01f;



      if (currentMesh == null)
      {
//ok so first mesh is not been created yet so create it from the first frame
          int counter = 0;
          foreach (Vector3d line in pointers)
          {
              counter++;
//Debug.Log(line);
              tris.Add(counter);
              tris.Add(counter);
              tris.Add(counter);

              normals.Add(new Vector3d(normal, normal, normal));
          }

          DMesh3         pointSet = DMesh3Builder.Build(pointers, tris, normals);
          PointAABBTree3 bvtree   = new PointAABBTree3(pointSet, true);
          bvtree.FastWindingNumber(Vector3d.Zero);
// estimate point area based on nearest-neighbour distance
          double[] areas = new double[pointSet.MaxVertexID];
          foreach (int vid in pointSet.VertexIndices())
          {
              bvtree.PointFilterF = (i) => { return(i != vid); }; // otherwise vid is nearest to vid!
              int    near_vid = bvtree.FindNearestPoint(pointSet.GetVertex(vid));
              double dist     = pointSet.GetVertex(vid).Distance(pointSet.GetVertex(near_vid));
              areas[vid] = Circle2d.RadiusArea(dist);
          }
          bvtree.FWNAreaEstimateF = (vid) => {
              return(areas[vid]);
          };
          MarchingCubes mc = new MarchingCubes();
          mc.Implicit = new PWNImplicit()
          {
              Spatial = bvtree
          };
          mc.IsoValue = 0.0;
          mc.CubeSize = bvtree.Bounds.MaxDim / 10;
          mc.Bounds   = bvtree.Bounds.Expanded(mc.CubeSize * 3);
          mc.RootMode = MarchingCubes.RootfindingModes.Bisection;
          mc.Generate();
          DMesh3 resultMesh = mc.Mesh;
          //  g3UnityUtils.SetGOMesh(transform.gameObject, resultMesh);
          currentMesh = resultMesh;
      }
      else
      {
//ok so this is where we are proscessing second mesh
          int counter = 0;
          foreach (Vector3d line in pointers)
          {
              counter++;
//Debug.Log(line);
              tris.Add(counter);
              tris.Add(counter);
              tris.Add(counter);

              normals.Add(new Vector3d(normal, normal, normal));
          }

          DMesh3         pointSet = DMesh3Builder.Build(pointers, tris, normals);
          PointAABBTree3 bvtree   = new PointAABBTree3(pointSet, true);
          bvtree.FastWindingNumber(Vector3d.Zero);
// estimate point area based on nearest-neighbour distance
          double[] areas = new double[pointSet.MaxVertexID];
          foreach (int vid in pointSet.VertexIndices())
          {
              bvtree.PointFilterF = (i) => { return(i != vid); }; // otherwise vid is nearest to vid!
              int    near_vid = bvtree.FindNearestPoint(pointSet.GetVertex(vid));
              double dist     = pointSet.GetVertex(vid).Distance(pointSet.GetVertex(near_vid));
              areas[vid] = Circle2d.RadiusArea(dist);
          }
          bvtree.FWNAreaEstimateF = (vid) => {
              return(areas[vid]);
          };
          MarchingCubes mc = new MarchingCubes();
          mc.Implicit = new PWNImplicit()
          {
              Spatial = bvtree
          };
          mc.IsoValue = 0.0;
          mc.CubeSize = bvtree.Bounds.MaxDim / 10;
          mc.Bounds   = bvtree.Bounds.Expanded(mc.CubeSize * 3);
          mc.RootMode = MarchingCubes.RootfindingModes.Bisection;
          mc.Generate();
          DMesh3     resultMesh = mc.Mesh;
          MeshEditor editor     = new MeshEditor(currentMesh);

          editor.AppendMesh(resultMesh, currentMesh.AllocateTriangleGroup());
//suspected its crashing after mesh is over 64000 faces,
          faceLog.text = "Vertex Count =  " + transform.gameObject.GetComponent <MeshFilter>().mesh.triangles.Length;

          g3UnityUtils.SetGOMesh(transform.gameObject, currentMesh);
      }
  }
コード例 #22
0
        // [RMS] This doesn't work because I did something stupid when
        //   making unwrap meshes. The set of triangles aren't the same. argh.
        public static DMesh3 ConvertUnwrapToUVs(RemoteControl rc, out DenseMeshUVSet UVSet, string mainName = "main")
        {
            int mainID = rc.FindObjectByName(mainName);

            rc.CompactMesh(mainID);
            DMesh3 mainMesh = ReadMeshFromMM(rc, mainID);

            Debug.Assert(mainMesh.IsCompact);

            List <int> all       = rc.ListObjects();
            List <int> unwrapIDs = new List <int>();

            foreach (int id in all)
            {
                string sName = rc.GetObjectName(id);
                if (sName.StartsWith("unwrap", StringComparison.OrdinalIgnoreCase))
                {
                    unwrapIDs.Add(id);
                }
            }
            int[] AllGroups = FaceGroupUtil.FindAllGroups(mainMesh).ToArray();


            Dictionary <int, DMesh3> Unwraps = new Dictionary <int, DMesh3>();

            foreach (int id in unwrapIDs)
            {
                DMesh3        mesh      = ReadMeshFromMM(rc, id);
                HashSet <int> subGroups = FaceGroupUtil.FindAllGroups(mesh);
                Debug.Assert(subGroups.Count == 1);
                int gid = subGroups.ToArray()[0];
                Unwraps[gid] = mesh;
            }


            UVSet = new DenseMeshUVSet();
            UVSet.TriangleUVs.resize(mainMesh.TriangleCount);
            Dictionary <Index2i, int> UVSetMap = new Dictionary <Index2i, int>();

            Dictionary <int, int> GroupCounters = new Dictionary <int, int>();

            for (int i = 0; i < AllGroups.Length; ++i)
            {
                GroupCounters[AllGroups[i]] = 0;
            }

            for (int ti = 0; ti < mainMesh.TriangleCount; ++ti)
            {
                Index3i main_tri = mainMesh.GetTriangle(ti);
                int     gid      = mainMesh.GetTriangleGroup(ti);
                int     sub_tid  = GroupCounters[gid];
                GroupCounters[gid]++;

                DMesh3  SubMesh = Unwraps[gid];
                Index3i sub_tri = SubMesh.GetTriangle(sub_tid);

                for (int j = 0; j < 3; ++j)
                {
                    int     sub_vid = sub_tri[j];
                    Index2i key     = new Index2i(gid, sub_vid);
                    int     uvset_idx;
                    if (UVSetMap.TryGetValue(key, out uvset_idx) == false)
                    {
                        Vector3d v  = SubMesh.GetVertex(sub_vid);
                        Vector2f uv = new Vector2f((float)v.x, (float)v.z);
                        uvset_idx     = UVSet.AppendUV(uv);
                        UVSetMap[key] = uvset_idx;
                    }
                    sub_tri[j] = uvset_idx;
                }

                UVSet.TriangleUVs[ti] = sub_tri;
            }

            return(mainMesh);
        }
コード例 #23
0
        /// <summary>
        /// Cut a "partial" hole, ie we cut the mesh with the polygon once, and then
        /// extrude downwards to a planar version of the cut boundary.
        ///
        /// Currently only supports extruding downwards from topmost intersection.
        ///
        /// </summary>
        protected bool CutPartialHole(DMesh3 mesh, HoleInfo hi, Vector3d translate, bool bUpwards)
        {
            if (hi.IsVertical == false)
            {
                throw new Exception("unsupported!");
            }

            Vector3d basePoint = CombinedBounds.Center - CombinedBounds.Extents.y * Vector3d.AxisY + translate;

            // do we need to compute spatial DS for each hole? not super efficient...
            DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true);

            Vector3d direction = (bUpwards) ? Vector3d.AxisY : -Vector3d.AxisY;
            Vector3d center    = basePoint + new Vector3d(hi.XZOffset.x, 0, hi.XZOffset.y) - 10000 * direction;


            Ray3d ray     = new Ray3d(center, direction);
            int   hit_tid = spatial.FindNearestHitTriangle(ray);

            if (hit_tid == DMesh3.InvalidID)
            {
                return(false);
            }

            IntrRay3Triangle3 intersection = MeshQueries.TriangleIntersection(mesh, hit_tid, ray);
            Vector3d          inter_pos    = ray.PointAt(intersection.RayParameter);

            Frame3f projectFrame = new Frame3f(ray.Origin, ray.Direction);

            int nVerts = 32;

            if (hi.Vertices != 0)
            {
                nVerts = hi.Vertices;
            }
            double    angleShiftRad = hi.AxisAngleD * MathUtil.Deg2Rad;
            Polygon2d circle        = Polygon2d.MakeCircle(hi.Radius, nVerts, angleShiftRad);

            try {
                EdgeLoop loop = null;

                MeshInsertProjectedPolygon insert = new MeshInsertProjectedPolygon(mesh, circle, projectFrame, hit_tid)
                {
                    SimplifyInsertion = false
                };
                if (insert.Insert())
                {
                    loop = insert.InsertedLoop;

                    // [RMS] do we need to simplify for this one?
                    //if (loop.VertexCount > circle.VertexCount)
                    //    loop = simplify_loop(mesh, loop, circle.VertexCount);

                    MeshEditor editor = new MeshEditor(mesh);

                    Vector3d base_pos = inter_pos;
                    base_pos.y = basePoint.y + hi.PartialHoleBaseHeight;

                    int   N       = loop.VertexCount;
                    int[] newLoop = new int[N];
                    for (int k = 0; k < N; ++k)
                    {
                        newLoop[k] = mesh.AppendVertex(mesh, loop.Vertices[k]);
                        Vector3d cur_v = mesh.GetVertex(newLoop[k]);
                        cur_v.y = base_pos.y;
                        mesh.SetVertex(newLoop[k], cur_v);
                    }
                    int   base_vid = mesh.AppendVertex(base_pos);
                    int[] fan_tris = editor.AddTriangleFan_OrderedVertexLoop(base_vid, newLoop);
                    FaceGroupUtil.SetGroupID(mesh, fan_tris, hi.PartialHoleGroupID);
                    int[] stitch_tris = editor.StitchLoop(loop.Vertices, newLoop);

                    // need to remesh fan region because otherwise we get pathological cases
                    RegionRemesher remesh = new RegionRemesher(mesh, fan_tris);
                    remesh.SetTargetEdgeLength(2.0);
                    remesh.SmoothSpeedT       = 1.0;
                    remesh.PreventNormalFlips = true;
                    for (int k = 0; k < 25; ++k)
                    {
                        remesh.BasicRemeshPass();
                    }
                    //remesh.EnableCollapses = remesh.EnableFlips = remesh.EnableSplits = false;
                    //for (int k = 0; k < 20; ++k)
                    //    remesh.BasicRemeshPass();
                    remesh.BackPropropagate();

                    return(true);
                }
                else
                {
                    return(false);
                }
            } catch (Exception e) {
                f3.DebugUtil.Log("partial hole {0} failed!! {1}", hi.nHole, e.Message);
                return(false);
            }
        }
コード例 #24
0
        public static void test_reduce_constraints_fixedverts()
        {
            int    Slices = 128;
            DMesh3 mesh   = TestUtil.MakeCappedCylinder(false, Slices);

            MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1));
            mesh.CheckValidity();
            AxisAlignedBox3d bounds = mesh.CachedBounds;

            // construct mesh projection target
            DMesh3 meshCopy = new DMesh3(mesh);

            meshCopy.CheckValidity();
            DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy);

            tree.Build();
            MeshProjectionTarget target = new MeshProjectionTarget()
            {
                Mesh = meshCopy, Spatial = tree
            };

            if (WriteDebugMeshes)
            {
                TestUtil.WriteTestOutputMesh(mesh, "reduce_fixed_constraints_test_before.obj");
            }

            // construct constraint set
            MeshConstraints cons = new MeshConstraints();

            //EdgeRefineFlags useFlags = EdgeRefineFlags.NoCollapse;
            EdgeRefineFlags useFlags = EdgeRefineFlags.PreserveTopology;

            foreach (int eid in mesh.EdgeIndices())
            {
                double fAngle = MeshUtil.OpeningAngleD(mesh, eid);
                if (fAngle > 30.0f)
                {
                    cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags)
                    {
                        TrackingSetID = 1
                    });
                    Index2i ev      = mesh.GetEdgeV(eid);
                    int     nSetID0 = (mesh.GetVertex(ev[0]).y > bounds.Center.y) ? 1 : 2;
                    int     nSetID1 = (mesh.GetVertex(ev[1]).y > bounds.Center.y) ? 1 : 2;
                    cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(true, nSetID0));
                    cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(true, nSetID1));
                }
            }

            Reducer r = new Reducer(mesh);

            r.SetExternalConstraints(cons);
            r.SetProjectionTarget(target);

            r.ReduceToTriangleCount(50);
            mesh.CheckValidity();

            if (WriteDebugMeshes)
            {
                TestUtil.WriteTestOutputMesh(mesh, "reduce_fixed_constraints_test_after.obj");
            }
        }
コード例 #25
0
        public virtual void Update()
        {
            if (MeshSource == null)
            {
                throw new Exception("PlaneBandExpansionOp: must set valid MeshSource to compute!");
            }

            IMesh imesh = MeshSource.GetIMesh();

            if (imesh.HasVertexNormals == false)
            {
                throw new Exception("PlaneBandExpansionOp: input mesh does not have surface normals...");
            }
            if (imesh is DMesh3 == false)
            {
                throw new Exception("RegionOffsetOp: in current implementation, input mesh must be a DMesh3. Ugh.");
            }
            DMesh3 mesh = imesh as DMesh3;

            IList <int> faces = IndexSource.GetIndices();

            // [RMS] this is all f'n ugly!

            MeshVertexSelection selection = new MeshVertexSelection(mesh);

            selection.SelectTriangleVertices(faces);

            // ugly
            List <Vector2d> seeds = new List <Vector2d>();

            foreach (int vid in selection)
            {
                foreach (int nbrvid in mesh.VtxVerticesItr(vid))
                {
                    if (selection.IsSelected(nbrvid) == false)
                    {
                        seeds.Add(new Vector2d(vid, 0));
                        break;
                    }
                }
            }
            Func <int, int, float> distanceF = (a, b) => { return((float)mesh.GetVertex(a).Distance(mesh.GetVertex(b))); };
            Func <int, bool>       nodeF     = (vid) => { return(selection.IsSelected(vid)); };
            DijkstraGraphDistance  dijkstra  = new DijkstraGraphDistance(mesh.MaxVertexID, true, nodeF, distanceF, mesh.VtxVerticesItr, seeds);

            dijkstra.Compute();
            float maxDist = dijkstra.MaxDistance;


            Displacement.Clear();
            Displacement.Resize(mesh.MaxVertexID);


            // todo: can do this in parallel...
            foreach (int vid in selection)
            {
                //Vector3d v = mesh.GetVertex(vid);

                // [TODO]...
                double dist    = maxDist - dijkstra.GetDistance(vid);
                double falloff = MathUtil.WyvillFalloff(dist, maxDist * 0.0, maxDist);

                Vector3d n = mesh.GetVertexNormal(vid);
                n = n - n.Dot(normal) * normal;
                n.Normalize();

                Displacement[vid] = falloff * offset_distance * n;
            }

            // smooth it?

            result_valid = true;
        }
コード例 #26
0
        public virtual void Update()
        {
            if (MeshSource == null)
            {
                throw new Exception("EnclosedRegionOffsetOp: must set valid MeshSource to compute!");
            }
            if (MeshSource.HasSpatial == false)
            {
                throw new Exception("EnclosedRegionOffsetOp: MeshSource must have spatial data structure!");
            }

            IMesh imesh = MeshSource.GetIMesh();

            if (imesh.HasVertexNormals == false)
            {
                throw new Exception("EnclosedRegionOffsetOp: input mesh does not have surface normals...");
            }
            if (imesh is DMesh3 == false)
            {
                throw new Exception("RegionOffsetOp: in current implementation, input mesh must be a DMesh3. Ugh.");
            }
            DMesh3   mesh    = imesh as DMesh3;
            ISpatial spatial = MeshSource.GetSpatial();

            DCurve3           curve = new DCurve3(CurveSource.GetICurve());
            MeshFacesFromLoop loop  = new MeshFacesFromLoop(mesh, curve, spatial);

            // extract submesh
            RegionOperator op      = new RegionOperator(mesh, loop.InteriorTriangles);
            DMesh3         submesh = op.Region.SubMesh;

            // find boundary verts and nbr ring
            HashSet <int> boundaryV    = new HashSet <int>(MeshIterators.BoundaryEdgeVertices(submesh));
            HashSet <int> boundaryNbrs = new HashSet <int>();

            foreach (int vid in boundaryV)
            {
                foreach (int nbrvid in submesh.VtxVerticesItr(vid))
                {
                    if (boundaryV.Contains(nbrvid) == false)
                    {
                        boundaryNbrs.Add(nbrvid);
                    }
                }
            }

            // [TODO] maybe should be not using vertex normal here?
            // use an averaged normal, or a constant for patch?

            // offset mesh if requested
            if (Math.Abs(offset_distance) > 0.0001)
            {
                foreach (int vid in submesh.VertexIndices())
                {
                    if (boundaryV.Contains(vid))
                    {
                        continue;
                    }
                    // if inner ring is non-zero, then it gets preserved below, and
                    // creates a crease...
                    //double dist = boundaryNbrs.Contains(vid) ? (offset_distance / 2) : offset_distance;
                    double dist = boundaryNbrs.Contains(vid) ? 0 : offset_distance;
                    submesh.SetVertex(vid,
                                      submesh.GetVertex(vid) + (float)dist * submesh.GetVertexNormal(vid));
                }
            }


            //double t = MathUtil.Clamp(1.0 - SmoothAlpha, 0.1, 1.0);
            double t = 1.0 - SmoothAlpha;

            t = t * t;
            double boundary_t = 5.0;
            double ring_t     = 1.0;

            // smooth submesh, with boundary-ring constraints
            LaplacianMeshSmoother smoother = new LaplacianMeshSmoother(submesh);

            foreach (int vid in submesh.VertexIndices())
            {
                if (boundaryV.Contains(vid))
                {
                    smoother.SetConstraint(vid, submesh.GetVertex(vid), boundary_t, true);
                }
                else if (boundaryNbrs.Contains(vid))
                {
                    smoother.SetConstraint(vid, submesh.GetVertex(vid), ring_t);
                }
                else
                {
                    smoother.SetConstraint(vid, submesh.GetVertex(vid), t);
                }
            }
            smoother.SolveAndUpdateMesh();


            // turn into displacement vectors
            Displacement.Clear();
            Displacement.Resize(mesh.MaxVertexID);
            foreach (int subvid in op.Region.SubMesh.VertexIndices())
            {
                Vector3d subv    = op.Region.SubMesh.GetVertex(subvid);
                int      basevid = op.Region.SubToBaseV[subvid];
                Vector3d basev   = op.Region.BaseMesh.GetVertex(basevid);
                Displacement[basevid] = subv - basev;
            }


            result_valid = true;
        }
コード例 #27
0
ファイル: MinimalHoleFill.cs プロジェクト: CallAndGus/ViRGIS
 Vector3d get_tri_normal(DMesh3 mesh, Index3i tri)
 {
     return(MathUtil.Normal(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c)));
 }
コード例 #28
0
        }         // test_basic_fills

        public static void test_plane_cut()
        {
            List <int> tests = new List <int>()
            {
                1
            };

            bool DO_EXHAUSTIVE_TESTS = false;

            foreach (int num_test in tests)
            {
                string name;
                DMesh3 orig_mesh = MakeEditTestMesh(num_test, out name);

                DMesh3 mesh = new DMesh3(orig_mesh);

                AxisAlignedBox3d bounds = mesh.CachedBounds;

                MeshPlaneCut cut = new MeshPlaneCut(mesh, bounds.Center, Vector3d.AxisY);
                Debug.Assert(cut.Validate() == ValidationStatus.Ok);
                bool bCutOK = cut.Cut();

                System.Console.Write("cut: {0}   loops: {1}  spans: {2}", ((bCutOK) ? "Ok" : "Failed"),
                                     cut.CutLoops.Count, cut.CutSpans.Count);

                foreach (var loop in cut.CutLoops)
                {
                    loop.CheckValidity(FailMode.gDevAssert);
                }
                foreach (var span in cut.CutSpans)
                {
                    span.CheckValidity(FailMode.gDevAssert);
                }

                bool bFillOK = cut.FillHoles(-1);
                System.Console.WriteLine("   fill: {0}", ((bFillOK) ? "Ok" : "Failed"));

                TestUtil.WriteTestOutputMesh(mesh, name + "_cut" + ".obj");


                if (DO_EXHAUSTIVE_TESTS == false)
                {
                    continue;
                }


                // grinder: cut through each vtx
                int VertexIncrement = 1;
                for (int vid = 0; vid < orig_mesh.MaxVertexID; vid += VertexIncrement)
                {
                    if (orig_mesh.IsVertex(vid) == false)
                    {
                        continue;
                    }
                    if (vid % 100 == 0)
                    {
                        System.Console.WriteLine("{0} / {1}", vid, orig_mesh.MaxVertexID);
                    }
                    Vector3d v = orig_mesh.GetVertex(vid);
                    mesh   = new DMesh3(orig_mesh);
                    cut    = new MeshPlaneCut(mesh, v, Vector3d.AxisY);
                    bCutOK = cut.Cut();
                    Debug.Assert(bCutOK);
                }
            }             // test_plane_cut
        }
コード例 #29
0
ファイル: MinimalHoleFill.cs プロジェクト: CallAndGus/ViRGIS
 double get_tri_aspect(DMesh3 mesh, ref Index3i tri)
 {
     return(MathUtil.AspectRatio(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c)));
 }
コード例 #30
0
        // [RMS] this only tests some basic cases...
        public static void test_Laplacian()
        {
            // compact version
            DMesh3 mesh = new DMesh3(TestUtil.MakeRemeshedCappedCylinder(1.0), true);

            Debug.Assert(mesh.IsCompact);

            AxisAlignedBox3d bounds = mesh.GetBounds();

            TestUtil.WriteDebugMesh(mesh, "___CG_before.obj");

            List <IMesh> result_meshes = new List <IMesh>();

            // make uniform laplacian matrix
            int N = mesh.VertexCount;
            SymmetricSparseMatrix M = new SymmetricSparseMatrix();

            //DenseMatrix M = new DenseMatrix(N, N);
            double[] Px = new double[N], Py = new double[N], Pz = new double[N];

            int[] nbr_counts = new int[N];
            for (int vid = 0; vid < N; ++vid)
            {
                nbr_counts[vid] = mesh.GetVtxEdgeCount(vid);
            }

            int        ti          = MeshQueries.FindNearestTriangle_LinearSearch(mesh, new Vector3d(2, 5, 2));
            int        v_pin       = mesh.GetTriangle(ti).a;
            List <int> constraints = new List <int>()
            {
                v_pin
            };
            double consW      = 10;
            double consBottom = 10;

            foreach (int vid in constraints)
            {
                result_meshes.Add(TestUtil.MakeMarker(mesh.GetVertex(vid), (vid == 0) ? 0.2f : 0.1f, Colorf.Red));
            }

            for (int vid = 0; vid < N; ++vid)
            {
                int      n = nbr_counts[vid];
                Vector3d v = mesh.GetVertex(vid), c = Vector3d.Zero;

                Px[vid] = v.x; Py[vid] = v.y; Pz[vid] = v.z;

                bool bottom = (v.y - bounds.Min.y) < 0.01f;

                double sum_w = 0;
                foreach (int nbrvid in mesh.VtxVerticesItr(vid))
                {
                    int n2 = nbr_counts[nbrvid];

                    // weight options
                    //double w = -1;
                    double w = -1.0 / Math.Sqrt(n + n2);
                    //double w = -1.0 / n;

                    M.Set(vid, nbrvid, w);

                    c     += w * mesh.GetVertex(nbrvid);
                    sum_w += w;
                }
                sum_w = -sum_w;

                M.Set(vid, vid, sum_w);

                // add soft constraints
                if (constraints.Contains(vid))
                {
                    M.Set(vid, vid, sum_w + consW);
                }
                else if (bottom)
                {
                    M.Set(vid, vid, sum_w + consBottom);
                }
            }

            // compute laplacians
            double[] MLx = new double[N], MLy = new double[N], MLz = new double[N];
            M.Multiply(Px, MLx);
            M.Multiply(Py, MLy);
            M.Multiply(Pz, MLz);


            DiagonalMatrix Preconditioner = new DiagonalMatrix(N);

            for (int i = 0; i < N; i++)
            {
                Preconditioner.Set(i, i, 1.0 / M[i, i]);
            }


            MLy[v_pin] += consW * 0.5f;
            MLx[v_pin] += consW * 0.5f;
            MLz[v_pin] += consW * 0.5f;

            bool useXAsGuess = true;
            // preconditioned
            SparseSymmetricCG SolverX = new SparseSymmetricCG()
            {
                B = MLx, X = Px, MultiplyF = M.Multiply, PreconditionMultiplyF = Preconditioner.Multiply, UseXAsInitialGuess = useXAsGuess
            };
            // initial solution
            SparseSymmetricCG SolverY = new SparseSymmetricCG()
            {
                B = MLy, X = Py, MultiplyF = M.Multiply, UseXAsInitialGuess = useXAsGuess
            };
            // neither of those
            SparseSymmetricCG SolverZ = new SparseSymmetricCG()
            {
                B = MLz, MultiplyF = M.Multiply
            };

            bool bx = SolverX.Solve();
            bool by = SolverY.Solve();
            bool bz = SolverZ.Solve();

            for (int vid = 0; vid < mesh.VertexCount; ++vid)
            {
                Vector3d newV = new Vector3d(SolverX.X[vid], SolverY.X[vid], SolverZ.X[vid]);
                mesh.SetVertex(vid, newV);
            }

            result_meshes.Add(mesh);
            TestUtil.WriteDebugMeshes(result_meshes, "___CG_result.obj");
        }