public static List <Face> ToQuads(this ProBuilderMesh mesh, IList <Face> faces, bool smoothing = true)
        {
            HashSet <Face> processed = new HashSet <Face>();

            List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh, faces, true);

            // build a lookup of the strength of edge connections between triangle faces
            Dictionary <EdgeLookup, float> connections = new Dictionary <EdgeLookup, float>();

            for (int i = 0; i < wings.Count; i++)
            {
                using (var it = new WingedEdgeEnumerator(wings[i]))
                {
                    while (it.MoveNext())
                    {
                        var border = it.Current;

                        if (border.opposite != null && !connections.ContainsKey(border.edge))
                        {
                            float score = mesh.GetQuadScore(border, border.opposite);
                            connections.Add(border.edge, score);
                        }
                    }
                }
            }

            List <SimpleTuple <Face, Face> > quads = new List <SimpleTuple <Face, Face> >();

            // move through each face and find it's best quad neighbor
            foreach (WingedEdge face in wings)
            {
                if (!processed.Add(face.face))
                {
                    continue;
                }

                float bestScore = 0f;
                Face  buddy     = null;

                using (var it = new WingedEdgeEnumerator(face))
                {
                    while (it.MoveNext())
                    {
                        var border = it.Current;

                        if (border.opposite != null && processed.Contains(border.opposite.face))
                        {
                            continue;
                        }

                        float borderScore;

                        // only add it if the opposite face's best score is also this face
                        if (connections.TryGetValue(border.edge, out borderScore) &&
                            borderScore > bestScore &&
                            face.face == GetBestQuadConnection(border.opposite, connections))
                        {
                            bestScore = borderScore;
                            buddy     = border.opposite.face;
                        }
                    }
                }

                if (buddy != null)
                {
                    processed.Add(buddy);
                    quads.Add(new SimpleTuple <Face, Face>(face.face, buddy));
                }
            }

            // don't collapse coincident vertices if smoothing is enabled, we need the original normals intact
            return(MergeElements.MergePairs(mesh, quads, smoothing));
        }
        /// <summary>
        /// Imports mesh data from a GameObject's <see cref="UnityEngine.MeshFilter.sharedMesh"/> and
        /// <see cref="UnityEngine.Renderer.sharedMaterials"/> properties.
        /// </summary>
        /// <param name="importSettings">Optional import customization settings.</param>
        /// <exception cref="NotSupportedException">Import only supports triangle and quad mesh topologies.</exception>
        public void Import(MeshImportSettings importSettings = null)
        {
            if (importSettings == null)
            {
                importSettings = k_DefaultImportSettings;
            }

            // When importing the mesh is always split into triangles with no vertices shared
            // between faces. In a later step co-incident vertices are collapsed (eg, before
            // leaving the Import function).
            Vertex[]      sourceVertices = m_SourceMesh.GetVertices();
            List <Vertex> splitVertices  = new List <Vertex>();
            List <Face>   faces          = new List <Face>();

            // Fill in Faces array with just the position indexes. In the next step we'll
            // figure out smoothing groups & merging
            int vertexIndex   = 0;
            int materialCount = m_SourceMaterials != null ? m_SourceMaterials.Length : 0;

            for (int submeshIndex = 0; submeshIndex < m_SourceMesh.subMeshCount; submeshIndex++)
            {
                switch (m_SourceMesh.GetTopology(submeshIndex))
                {
                case MeshTopology.Triangles:
                {
                    int[] indexes = m_SourceMesh.GetIndices(submeshIndex);

                    for (int tri = 0; tri < indexes.Length; tri += 3)
                    {
                        faces.Add(new Face(
                                      new int[] { vertexIndex, vertexIndex + 1, vertexIndex + 2 },
                                      Math.Clamp(submeshIndex, 0, materialCount - 1),
                                      AutoUnwrapSettings.tile,
                                      Smoothing.smoothingGroupNone,
                                      -1,
                                      -1,
                                      true));

                        splitVertices.Add(sourceVertices[indexes[tri]]);
                        splitVertices.Add(sourceVertices[indexes[tri + 1]]);
                        splitVertices.Add(sourceVertices[indexes[tri + 2]]);

                        vertexIndex += 3;
                    }
                }
                break;

                case MeshTopology.Quads:
                {
                    int[] indexes = m_SourceMesh.GetIndices(submeshIndex);

                    for (int quad = 0; quad < indexes.Length; quad += 4)
                    {
                        faces.Add(new Face(new int[]
                            {
                                vertexIndex, vertexIndex + 1, vertexIndex + 2,
                                vertexIndex + 2, vertexIndex + 3, vertexIndex + 0
                            },
                                           Math.Clamp(submeshIndex, 0, materialCount - 1),
                                           AutoUnwrapSettings.tile,
                                           Smoothing.smoothingGroupNone,
                                           -1,
                                           -1,
                                           true));

                        splitVertices.Add(sourceVertices[indexes[quad]]);
                        splitVertices.Add(sourceVertices[indexes[quad + 1]]);
                        splitVertices.Add(sourceVertices[indexes[quad + 2]]);
                        splitVertices.Add(sourceVertices[indexes[quad + 3]]);

                        vertexIndex += 4;
                    }
                }
                break;

                default:
                    throw new NotSupportedException("ProBuilder only supports importing triangle and quad meshes.");
                }
            }

            m_Vertices = splitVertices.ToArray();

            m_Destination.Clear();
            m_Destination.SetVertices(m_Vertices);
            m_Destination.faces          = faces;
            m_Destination.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(m_Destination.positionsInternal);
            m_Destination.sharedTextures = new SharedVertex[0];

            if (importSettings.quads)
            {
                var newFaces = m_Destination.ToQuads(m_Destination.facesInternal, !importSettings.smoothing);
            }

            if (importSettings.smoothing)
            {
                Smoothing.ApplySmoothingGroups(m_Destination, m_Destination.facesInternal, importSettings.smoothingAngle, m_Vertices.Select(x => x.normal).ToArray());
                // After smoothing has been applied go back and weld coincident vertices created by MergePairs.
                MergeElements.CollapseCoincidentVertices(m_Destination, m_Destination.facesInternal);
            }
        }
Exemple #3
0
        /// <summary>
        /// Import mesh data from a GameObject's MeshFilter.sharedMesh and MeshRenderer.sharedMaterials.
        /// </summary>
        /// <param name="originalMesh">The UnityEngine.Mesh to extract attributes from.</param>
        /// <param name="materials">The materials array corresponding to the originalMesh submeshes.</param>
        /// <param name="importSettings">Optional settings parameter defines import customization properties.</param>
        /// <exception cref="NotSupportedException">Import only supports triangle and quad mesh topologies.</exception>
        public void Import(MeshImportSettings importSettings = null)
        {
            if (importSettings == null)
            {
                importSettings = k_DefaultImportSettings;
            }

            // When importing the mesh is always split into triangles with no vertices shared
            // between faces. In a later step co-incident vertices are collapsed (eg, before
            // leaving the Import function).
            Vertex[]      sourceVertices = m_SourceMesh.GetVertices();
            List <Vertex> splitVertices  = new List <Vertex>();
            List <Face>   faces          = new List <Face>();

            // Fill in Faces array with just the position indexes. In the next step we'll
            // figure out smoothing groups & merging
            int vertexIndex   = 0;
            int materialCount = m_SourceMaterials != null ? m_SourceMaterials.Length : 0;

            for (int submeshIndex = 0; submeshIndex < m_SourceMesh.subMeshCount; submeshIndex++)
            {
                switch (m_SourceMesh.GetTopology(submeshIndex))
                {
                case MeshTopology.Triangles:
                {
                    int[] indexes = m_SourceMesh.GetIndices(submeshIndex);

                    for (int tri = 0; tri < indexes.Length; tri += 3)
                    {
                        faces.Add(new Face(
                                      new int[] { vertexIndex, vertexIndex + 1, vertexIndex + 2 },
                                      Math.Clamp(submeshIndex, 0, materialCount - 1),
                                      AutoUnwrapSettings.tile,
                                      Smoothing.smoothingGroupNone,
                                      -1,
                                      -1,
                                      true));

                        splitVertices.Add(sourceVertices[indexes[tri]]);
                        splitVertices.Add(sourceVertices[indexes[tri + 1]]);
                        splitVertices.Add(sourceVertices[indexes[tri + 2]]);

                        vertexIndex += 3;
                    }
                }
                break;

                case MeshTopology.Quads:
                {
                    int[] indexes = m_SourceMesh.GetIndices(submeshIndex);

                    for (int quad = 0; quad < indexes.Length; quad += 4)
                    {
                        faces.Add(new Face(new int[]
                            {
                                vertexIndex, vertexIndex + 1, vertexIndex + 2,
                                vertexIndex + 2, vertexIndex + 3, vertexIndex + 0
                            },
                                           Math.Clamp(submeshIndex, 0, materialCount - 1),
                                           AutoUnwrapSettings.tile,
                                           Smoothing.smoothingGroupNone,
                                           -1,
                                           -1,
                                           true));

                        splitVertices.Add(sourceVertices[indexes[quad]]);
                        splitVertices.Add(sourceVertices[indexes[quad + 1]]);
                        splitVertices.Add(sourceVertices[indexes[quad + 2]]);
                        splitVertices.Add(sourceVertices[indexes[quad + 3]]);

                        vertexIndex += 4;
                    }
                }
                break;

                default:
                    throw new NotSupportedException("ProBuilder only supports importing triangle and quad meshes.");
                }
            }

            m_Vertices = splitVertices.ToArray();

            m_Destination.Clear();
            m_Destination.SetVertices(m_Vertices);
            m_Destination.faces          = faces;
            m_Destination.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(m_Destination.positionsInternal);
            m_Destination.sharedTextures = new SharedVertex[0];

            HashSet <Face> processed = new HashSet <Face>();

            if (importSettings.quads)
            {
                List <WingedEdge> wings = WingedEdge.GetWingedEdges(m_Destination, m_Destination.facesInternal, true);

                // build a lookup of the strength of edge connections between triangle faces
                Dictionary <EdgeLookup, float> connections = new Dictionary <EdgeLookup, float>();

                for (int i = 0; i < wings.Count; i++)
                {
                    using (var it = new WingedEdgeEnumerator(wings[i]))
                    {
                        while (it.MoveNext())
                        {
                            var border = it.Current;

                            if (border.opposite != null && !connections.ContainsKey(border.edge))
                            {
                                float score = GetQuadScore(border, border.opposite);
                                connections.Add(border.edge, score);
                            }
                        }
                    }
                }

                List <SimpleTuple <Face, Face> > quads = new List <SimpleTuple <Face, Face> >();

                // move through each face and find it's best quad neighbor
                foreach (WingedEdge face in wings)
                {
                    if (!processed.Add(face.face))
                    {
                        continue;
                    }

                    float bestScore = 0f;
                    Face  buddy     = null;

                    using (var it = new WingedEdgeEnumerator(face))
                    {
                        while (it.MoveNext())
                        {
                            var border = it.Current;

                            if (border.opposite != null && processed.Contains(border.opposite.face))
                            {
                                continue;
                            }

                            float borderScore;

                            // only add it if the opposite face's best score is also this face
                            if (connections.TryGetValue(border.edge, out borderScore) &&
                                borderScore > bestScore &&
                                face.face == GetBestQuadConnection(border.opposite, connections))
                            {
                                bestScore = borderScore;
                                buddy     = border.opposite.face;
                            }
                        }
                    }

                    if (buddy != null)
                    {
                        processed.Add(buddy);
                        quads.Add(new SimpleTuple <Face, Face>(face.face, buddy));
                    }
                }

                // don't collapse coincident vertices if smoothing is enabled, we need the original normals intact
                MergeElements.MergePairs(m_Destination, quads, !importSettings.smoothing);
            }

            if (importSettings.smoothing)
            {
                Smoothing.ApplySmoothingGroups(m_Destination, m_Destination.facesInternal, importSettings.smoothingAngle, m_Vertices.Select(x => x.normal).ToArray());
                // After smoothing has been applied go back and weld coincident vertices created by MergePairs.
                MergeElements.CollapseCoincidentVertices(m_Destination, m_Destination.facesInternal);
            }
        }