Exemplo n.º 1
0
    public void MeshValidation_IsSplitFace_DetectsDisconnectedTriangles()
    {
        var cube = TestUtility.CreateCubeWithNonContiguousMergedFace();

        Assume.That(cube.item1.faceCount, Is.EqualTo(5));
        Assert.That(MeshValidation.ContainsNonContiguousTriangles(cube.item1, cube.item2), Is.True);
    }
Exemplo n.º 2
0
    public void MeshValidation_IsSplitFace_ConfirmsValidFaces([ValueSource("k_IndexCounts")] int indexCount)
    {
        var points  = new Vector3[indexCount];
        var indices = new int[(indexCount - 1) * 3];

        for (int i = 0; i < indexCount; i++)
        {
            float travel = ((i + 1) / (float)indexCount) * Mathf.PI * 2f;
            points[i] = new Vector3(Mathf.Cos(travel), 0f, Mathf.Sin(travel));
        }

        for (int i = 1; i < indexCount - 1; i++)
        {
            indices[(i - 1) * 3 + 0] = 0;
            indices[(i - 1) * 3 + 1] = i;
            indices[(i - 1) * 3 + 2] = (i + 1) % indexCount;
        }

        var shape = ProBuilderMesh.Create(points, new Face[] { new Face(indices) });

        Assume.That(shape, Is.Not.Null);
        var face = shape.faces.First();

        Assume.That(face, Is.Not.Null);
        Assume.That(face.edgesInternal, Has.Length.EqualTo(indexCount));
        Assert.That(MeshValidation.ContainsNonContiguousTriangles(shape, face), Is.False);
    }
Exemplo n.º 3
0
        /// <summary>
        /// Condense co-incident vertex positions per-face. vertices must already be marked as shared in the sharedIndexes
        /// array to be considered. This method is really only useful after merging faces.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="faces"></param>
        internal static void CollapseCoincidentVertices(ProBuilderMesh mesh, IEnumerable <Face> faces)
        {
            Dictionary <int, int> lookup = new Dictionary <int, int>();

            SharedVertex.GetSharedVertexLookup(mesh.sharedVertices, lookup);
            Dictionary <int, int> matches = new Dictionary <int, int>();

            foreach (Face face in faces)
            {
                matches.Clear();

                int[] indexes = face.indexes.ToArray();
                for (int i = 0; i < indexes.Length; i++)
                {
                    int common = lookup[face.indexes[i]];

                    if (matches.ContainsKey(common))
                    {
                        indexes[i] = matches[common];
                    }
                    else
                    {
                        matches.Add(common, indexes[i]);
                    }
                }
                face.SetIndexes(indexes);

                face.Reverse();
                face.Reverse();
            }

            MeshValidation.RemoveUnusedVertices(mesh);
            //mesh.RemoveUnusedVertices();
        }
Exemplo n.º 4
0
        void DoSceneViewOverlay()
        {
            Handles.BeginGUI();
            GUILayout.Space(4);
            GUILayout.BeginHorizontal();
            GUILayout.Space(4);
            GUILayout.BeginVertical(EditorStyles.helpBox);
            if (m_Meshes.Length > 0)
            {
                for (int i = 0, c = m_Meshes.Length; i < c; ++i)
                {
                    GUILayout.BeginHorizontal();
                    if (m_Invalid[i].Count > 0 && GUILayout.Button("Fix"))
                    {
                        MeshValidation.EnsureMeshIsValid(m_Meshes[i], out var removedVertexCount);
                        Debug.Log($"Successfully repaired {m_Meshes[i].name}. Removed {removedVertexCount} problem vertices.");
                    }
                    GUILayout.Label($"Mesh \"{m_Meshes[i].name}\" found {m_Invalid[i].Count} problems");
                    GUILayout.EndHorizontal();
                }
            }
            else
            {
                GUILayout.Label("Select a ProBuilder Mesh");
            }

            GUILayout.EndVertical();
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
            Handles.EndGUI();
        }
        public static void MenuRemoveDegenerateTriangles()
        {
            int count = 0;

            foreach (ProBuilderMesh pb in InternalUtility.GetComponents <ProBuilderMesh>(Selection.transforms))
            {
                int removedVertexCount;

                if (!MeshValidation.EnsureMeshIsValid(pb, out removedVertexCount))
                {
                    pb.Rebuild();
                    pb.Optimize();
                    count += removedVertexCount;
                }
            }

            EditorUtility.ShowNotification("Removed " + count + " vertices \nbelonging to degenerate triangles.");
        }
        protected override ActionResult PerformActionImplementation()
        {
            if (MeshSelection.selectedObjectCount < 1)
            {
                return(ActionResult.NoSelection);
            }

            ActionResult res = ActionResult.NoSelection;

            UndoUtility.RecordSelection("Weld Vertices");

            int weldCount = 0;

            foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
            {
                weldCount += mesh.sharedVerticesInternal.Length;

                if (mesh.selectedIndexesInternal.Length > 1)
                {
                    mesh.ToMesh();

                    var   selectedVertices = mesh.GetCoincidentVertices(mesh.selectedVertices);
                    int[] welds            = mesh.WeldVertices(mesh.selectedIndexesInternal, m_WeldDistance);
                    res = welds != null ? new ActionResult(ActionResult.Status.Success, "Weld Vertices") : new ActionResult(ActionResult.Status.Failure, "Failed Weld Vertices");

                    if (res)
                    {
                        var newSelection = welds ?? new int[0] {
                        };

                        if (MeshValidation.ContainsDegenerateTriangles(mesh))
                        {
                            List <int> removedIndices = new List <int>();
                            var        vertexCount    = mesh.vertexCount;

                            if (MeshValidation.RemoveDegenerateTriangles(mesh, removedIndices))
                            {
                                if (removedIndices.Count < vertexCount)
                                {
                                    var newlySelectedVertices = new List <int>();
                                    selectedVertices.Sort();
                                    removedIndices.Sort();

                                    int count = 0;

                                    for (int i = 0; i < selectedVertices.Count; i++)
                                    {
                                        if (count >= removedIndices.Count || selectedVertices[i] != removedIndices[count])
                                        {
                                            newlySelectedVertices.Add(selectedVertices[i] - UnityEngine.ProBuilder.ArrayUtility.NearestIndexPriorToValue(removedIndices, selectedVertices[i]) - 1);
                                        }
                                        else
                                        {
                                            ++count;
                                        }
                                    }

                                    newSelection = newlySelectedVertices.ToArray();
                                }
                                else
                                {
                                    newSelection = new int[0];
                                }
                            }
                            mesh.ToMesh();
                        }
                        mesh.SetSelectedVertices(newSelection);
                    }

                    mesh.Refresh();
                    mesh.Optimize();
                }

                weldCount -= mesh.sharedVerticesInternal.Length;
            }

            ProBuilderEditor.Refresh();

            if (res && weldCount > 0)
            {
                return(new ActionResult(ActionResult.Status.Success, "Weld " + weldCount + (weldCount > 1 ? " Vertices" : " Vertex")));
            }

            return(new ActionResult(ActionResult.Status.Failure, "Nothing to Weld"));
        }
Exemplo n.º 7
0
        public virtual ValidationStatus Validate()
        {
            ValidationStatus loopStatus = MeshValidation.IsBoundaryLoop(Mesh, Loop);

            return(loopStatus);
        }
Exemplo n.º 8
0
        public void Close_Flat()
        {
            double minlen, maxlen, avglen;

            MeshQueries.EdgeLengthStats(Mesh, out minlen, out maxlen, out avglen, 1000);
            double target_edge_len = (TargetEdgeLen <= 0) ? avglen : TargetEdgeLen;

            // massage around boundary loop
            cleanup_boundary(Mesh, InitialBorderLoop, avglen, 3);

            // find new border loop
            // [TODO] this just assumes there is only one!!
            MeshBoundaryLoops loops     = new MeshBoundaryLoops(Mesh);
            EdgeLoop          fill_loop = loops.Loops[0];

            int extrude_group = (ExtrudeGroup == -1) ? Mesh.AllocateTriangleGroup() : ExtrudeGroup;
            int fill_group    = (FillGroup == -1) ? Mesh.AllocateTriangleGroup() : FillGroup;

            // decide on projection plane
            //AxisAlignedBox3d loopbox = fill_loop.GetBounds();
            //Vector3d topPt = loopbox.Center;
            //if ( bIsUpper ) {
            //    topPt.y = loopbox.Max.y + 0.25 * dims.y;
            //} else {
            //    topPt.y = loopbox.Min.y - 0.25 * dims.y;
            //}
            //Frame3f plane = new Frame3f((Vector3f)topPt);

            // extrude loop to this plane
            MeshExtrusion extrude = new MeshExtrusion(Mesh, fill_loop);

            extrude.PositionF = (v, n, i) => {
                return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1));
            };
            extrude.Extrude(extrude_group);
            MeshValidation.IsBoundaryLoop(Mesh, extrude.NewLoop);

            Debug.Assert(Mesh.CheckValidity());

            // smooth the extrude loop
            MeshLoopSmooth loop_smooth = new MeshLoopSmooth(Mesh, extrude.NewLoop);

            loop_smooth.ProjectF = (v, i) => {
                return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1));
            };
            loop_smooth.Alpha  = 0.5f;
            loop_smooth.Rounds = 100;
            loop_smooth.Smooth();

            Debug.Assert(Mesh.CheckValidity());

            // fill result
            SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, extrude.NewLoop);

            filler.Fill(fill_group);

            Debug.Assert(Mesh.CheckValidity());

            // make selection for remesh region
            MeshFaceSelection remesh_roi = new MeshFaceSelection(Mesh);

            remesh_roi.Select(extrude.NewTriangles);
            remesh_roi.Select(filler.NewTriangles);
            remesh_roi.ExpandToOneRingNeighbours();
            remesh_roi.ExpandToOneRingNeighbours();
            remesh_roi.LocalOptimize(true, true);
            int[] new_roi = remesh_roi.ToArray();

            // get rid of extrude group
            FaceGroupUtil.SetGroupToGroup(Mesh, extrude_group, 0);

            /*  clean up via remesh
             *     - constrain loop we filled to itself
             */

            RegionRemesher r = new RegionRemesher(Mesh, new_roi);

            DCurve3 top_curve = mesh.MeshUtil.ExtractLoopV(Mesh, extrude.NewLoop.Vertices);
            DCurveProjectionTarget curve_target = new DCurveProjectionTarget(top_curve);

            int[] top_loop = (int[])extrude.NewLoop.Vertices.Clone();
            r.Region.MapVerticesToSubmesh(top_loop);
            MeshConstraintUtil.ConstrainVtxLoopTo(r.Constraints, r.Mesh, top_loop, curve_target);

            DMeshAABBTree3 spatial = new DMeshAABBTree3(Mesh);

            spatial.Build();
            MeshProjectionTarget target = new MeshProjectionTarget(Mesh, spatial);

            r.SetProjectionTarget(target);

            bool bRemesh = true;

            if (bRemesh)
            {
                r.Precompute();
                r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
                r.MinEdgeLength   = target_edge_len;
                r.MaxEdgeLength   = 2 * target_edge_len;
                r.EnableSmoothing = true;
                r.SmoothSpeedT    = 1.0f;
                for (int k = 0; k < 40; ++k)
                {
                    r.BasicRemeshPass();
                }
                r.SetProjectionTarget(null);
                r.SmoothSpeedT = 0.25f;
                for (int k = 0; k < 10; ++k)
                {
                    r.BasicRemeshPass();
                }
                Debug.Assert(Mesh.CheckValidity());

                r.BackPropropagate();
            }

            // smooth around the join region to clean up ugliness
            smooth_region(Mesh, r.Region.BaseBorderV, 3);
        }