Example #1
0
        /// <summary>
        /// Rebuild the mesh positions and submeshes. If vertex count matches new positions array the existing attributes are kept, otherwise the mesh is cleared. UV2 is the exception, it is always cleared.
        /// </summary>
        /// <param name="preferredTopology">Triangles and Quads are supported.</param>
        public void ToMesh(MeshTopology preferredTopology = MeshTopology.Triangles)
        {
            // if the mesh vertex count hasn't been modified, we can keep most of the mesh elements around
            if (mesh == null)
            {
#if ENABLE_DRIVEN_PROPERTIES
                SerializationUtility.RegisterDrivenProperty(this, this, "m_Mesh");
#endif
                mesh = new Mesh();
            }
            else if (mesh.vertexCount != vertexCount)
            {
                mesh.Clear();
            }

            mesh.indexFormat = vertexCount > ushort.MaxValue ? Rendering.IndexFormat.UInt32 : Rendering.IndexFormat.UInt16;
            mesh.vertices    = m_Positions;
            mesh.uv2         = null;

            if (m_MeshFormatVersion < k_MeshFormatVersion)
            {
                if (m_MeshFormatVersion < k_MeshFormatVersionSubmeshMaterialRefactor)
                {
                    Submesh.MapFaceMaterialsToSubmeshIndex(this);
                }
                if (m_MeshFormatVersion < k_MeshFormatVersionAutoUVScaleOffset)
                {
                    UvUnwrapping.UpgradeAutoUVScaleOffset(this);
                }
                m_MeshFormatVersion = k_MeshFormatVersion;
            }

            m_MeshFormatVersion = k_MeshFormatVersion;

            int materialCount = MaterialUtility.GetMaterialCount(renderer);

            Submesh[] submeshes = Submesh.GetSubmeshes(facesInternal, materialCount, preferredTopology);

            mesh.subMeshCount = materialCount;

            for (int i = 0; i < mesh.subMeshCount; i++)
            {
#if DEVELOPER_MODE
                if (i >= materialCount)
                {
                    Log.Warning("Submesh index " + i + " is out of bounds of the MeshRenderer materials array.");
                }
                if (submeshes[i] == null)
                {
                    throw new Exception("Attempting to assign a null submesh. " + i + "/" + materialCount);
                }
#endif
                mesh.SetIndices(submeshes[i].m_Indexes, submeshes[i].m_Topology, i, false);
            }

            mesh.name = string.Format("pb_Mesh{0}", id);

            EnsureMeshFilterIsAssigned();
        }
        /// <summary>
        /// Rebuild the mesh positions and submeshes. If vertex count matches new positions array the existing attributes are kept, otherwise the mesh is cleared. UV2 is the exception, it is always cleared.
        /// </summary>
        /// <param name="preferredTopology">Triangles and Quads are supported.</param>
        public void ToMesh(MeshTopology preferredTopology = MeshTopology.Triangles)
        {
            Mesh m = mesh;

            // if the mesh vertex count hasn't been modified, we can keep most of the mesh elements around
            if (m != null && m.vertexCount == m_Positions.Length)
            {
                m = mesh;
            }
            else if (m == null)
            {
                m = new Mesh();
            }
            else
            {
                m.Clear();
            }

            m.indexFormat = vertexCount > ushort.MaxValue ? Rendering.IndexFormat.UInt32 : Rendering.IndexFormat.UInt16;
            m.vertices    = m_Positions;
            m.uv2         = null;

            if (m_MeshFormatVersion < k_MeshFormatVersion)
            {
                if (m_MeshFormatVersion < k_MeshFormatVersionSubmeshMaterialRefactor)
                {
                    Submesh.MapFaceMaterialsToSubmeshIndex(this);
                }

                m_MeshFormatVersion = k_MeshFormatVersion;
            }

            m_MeshFormatVersion = k_MeshFormatVersion;

            int materialCount = MaterialUtility.GetMaterialCount(renderer);

            Submesh[] submeshes = Submesh.GetSubmeshes(facesInternal, materialCount, preferredTopology);

            m.subMeshCount = materialCount;

            for (int i = 0; i < m.subMeshCount; i++)
            {
#if DEVELOPER_MODE
                if (i >= materialCount)
                {
                    Log.Warning("Submesh index " + i + " is out of bounds of the MeshRenderer materials array.");
                }
                if (submeshes[i] == null)
                {
                    throw new Exception("Attempting to assign a null submesh. " + i + "/" + materialCount);
                }
#endif
                m.SetIndices(submeshes[i].m_Indexes, submeshes[i].m_Topology, i, false);
            }

            m.name            = string.Format("pb_Mesh{0}", id);
            filter.sharedMesh = m;
        }
        /// <summary>
        /// Compile a UnityEngine.Mesh from a ProBuilderMesh.
        /// </summary>
        /// <param name="probuilderMesh">The mesh source.</param>
        /// <param name="targetMesh">Destination UnityEngine.Mesh.</param>
        /// <param name="preferredTopology">If specified, the function will try to create topology matching the reqested format (and falling back on triangles where necessary).</param>
        /// <returns>The resulting material array from the compiled faces array. This is suitable to assign to the MeshRenderer.sharedMaterials property.</returns>
        public static void Compile(ProBuilderMesh probuilderMesh, Mesh targetMesh, MeshTopology preferredTopology = MeshTopology.Triangles)
        {
            if (probuilderMesh == null)
            {
                throw new ArgumentNullException("probuilderMesh");
            }

            if (targetMesh == null)
            {
                throw new ArgumentNullException("targetMesh");
            }

            targetMesh.Clear();

            targetMesh.vertices = probuilderMesh.positionsInternal;
            targetMesh.uv       = probuilderMesh.texturesInternal;

            if (probuilderMesh.HasArrays(MeshArrays.Texture2))
            {
                List <Vector4> uvChannel = new List <Vector4>();
                probuilderMesh.GetUVs(2, uvChannel);
                targetMesh.SetUVs(2, uvChannel);
            }

            if (probuilderMesh.HasArrays(MeshArrays.Texture3))
            {
                List <Vector4> uvChannel = new List <Vector4>();
                probuilderMesh.GetUVs(3, uvChannel);
                targetMesh.SetUVs(3, uvChannel);
            }

            targetMesh.normals  = probuilderMesh.GetNormals();
            targetMesh.tangents = probuilderMesh.GetTangents();

            if (probuilderMesh.HasArrays(MeshArrays.Color))
            {
                targetMesh.colors = probuilderMesh.colorsInternal;
            }

            var materialCount = probuilderMesh.GetComponent <Renderer>().sharedMaterials.Length;
            var submeshes     = Submesh.GetSubmeshes(probuilderMesh.facesInternal, materialCount, preferredTopology);

            targetMesh.subMeshCount = submeshes.Length;

            for (int i = 0; i < targetMesh.subMeshCount; i++)
            {
                targetMesh.SetIndices(submeshes[i].m_Indexes, submeshes[i].m_Topology, i, false);
            }

            targetMesh.name = string.Format("pb_Mesh{0}", probuilderMesh.id);
        }
Example #4
0
        /// <summary>
        /// Create submeshes from a set of faces. Currently only Quads and Triangles are supported.
        /// </summary>
        /// <param name="faces">The faces to be included in the resulting submeshes. This method handles groups submeshes by comparing the material property of each face.</param>
        /// <param name="submeshCount">How many submeshes to create. Usually you will just want to pass the length of the MeshRenderer.sharedMaterials array.</param>
        /// <param name="preferredTopology">Should the resulting submeshes be in quads or triangles. Note that quads are not guaranteed; ie, some faces may not be able to be represented in quad format and will fall back on triangles.</param>
        /// <returns>An array of Submeshes.</returns>
        /// <exception cref="NotImplementedException">Thrown in the event that a MeshTopology other than Quads or Triangles is passed.</exception>
        public static Submesh[] GetSubmeshes(IEnumerable <Face> faces, int submeshCount, MeshTopology preferredTopology = MeshTopology.Triangles)
        {
            if (preferredTopology != MeshTopology.Triangles && preferredTopology != MeshTopology.Quads)
            {
                throw new System.NotImplementedException("Currently only Quads and Triangles are supported.");
            }

            if (faces == null)
            {
                throw new ArgumentNullException("faces");
            }

            bool wantsQuads = preferredTopology == MeshTopology.Quads;

            List <int>[] quads           = wantsQuads ? new List <int> [submeshCount] : null;
            List <int>[] tris            = new List <int> [submeshCount];
            int          maxSubmeshIndex = submeshCount - 1;

            for (int i = 0; i < submeshCount; i++)
            {
                if (wantsQuads)
                {
                    quads[i] = new List <int>();
                }

                tris[i] = new List <int>();
            }

            foreach (var face in faces)
            {
                if (face.indexesInternal == null || face.indexesInternal.Length < 1)
                {
                    continue;
                }

                int submeshIndex = Math.Clamp(face.submeshIndex, 0, maxSubmeshIndex);

                if (wantsQuads && face.IsQuad())
                {
                    quads[submeshIndex].AddRange(face.ToQuad());
                }
                else
                {
                    tris[submeshIndex].AddRange(face.indexesInternal);
                }
            }

            var submeshes = new Submesh[submeshCount];

            switch (preferredTopology)
            {
            case MeshTopology.Triangles:
            {
                for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++)
                {
                    submeshes[submeshIndex] = new Submesh(submeshIndex, MeshTopology.Triangles, tris[submeshIndex]);
                }
                break;
            }

            case MeshTopology.Quads:
            {
                for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++)
                {
                    // If a submesh is a mix of triangles and quads, fall back to triangles.
                    if (tris[submeshIndex].Count > 0)
                    {
                        var tri  = tris[submeshIndex];
                        var quad = quads[submeshIndex];

                        int triCount  = tri.Count;
                        int quadCount = quad.Count;

                        int[] triangles = new int[triCount + ((quadCount / 4) * 6)];

                        for (int i = 0; i < triCount; i++)
                        {
                            triangles[i] = tri[i];
                        }

                        for (int i = 0, n = triCount; i < quadCount; i += 4, n += 6)
                        {
                            triangles[n + 0] = quad[i + 0];
                            triangles[n + 1] = quad[i + 1];
                            triangles[n + 2] = quad[i + 2];

                            triangles[n + 3] = quad[i + 2];
                            triangles[n + 4] = quad[i + 3];
                            triangles[n + 5] = quad[i + 0];
                        }

                        submeshes[submeshIndex] = new Submesh(submeshIndex, MeshTopology.Triangles, triangles);
                    }
                    else
                    {
                        submeshes[submeshIndex] = new Submesh(submeshIndex, MeshTopology.Quads, quads[submeshIndex]);
                    }
                }
                break;
            }
            }

            return(submeshes);
        }