public static void ApplySmoothingGroup(PBMesh pbMesh, MeshSelection selection, float angleThreshold) { selection = selection.ToFaces(false); ProBuilderMesh mesh = pbMesh.ProBuilderMesh; IList <Face> faces = new List <Face>(); mesh.GetFaces(selection.GetFaces(pbMesh).ToArray(), faces); Smoothing.ApplySmoothingGroups(mesh, faces, angleThreshold); }
public void CreateBoundary() { var boundaryPoints = Config.BoundaryGeoData.GetPathInStreamingAssets().GetBoundaryPoints().AddTileIntersectionPoints(); // Create vertices var wallVertices = new List <Vector3>(); for (int p = 0; p < boundaryPoints.Length - 1; p++) { var point0 = boundaryPoints[p]; var point1 = boundaryPoints[p + 1]; wallVertices.Add(point0); wallVertices.Add(point1); wallVertices.Add(new Vector3(point0.x, point0.y + Config.BoundaryHeight, point0.z)); wallVertices.Add(new Vector3(point1.x, point1.y + Config.BoundaryHeight, point1.z)); } // Create faces var faces = new List <Face>(); for (int f = 0; f < wallVertices.Count - 3; f += 4) { var faceVertices = new int[] { f, f + 1, f + 2, f + 1, f + 3, f + 2 }; faces.Add(new Face(faceVertices)); } var wall = ProBuilderMesh.Create(wallVertices, faces); Normals.CalculateNormals(wall); Normals.CalculateTangents(wall); Smoothing.ApplySmoothingGroups(wall, faces, 30); wall.ToMesh(); wall.Refresh(); EditorMeshUtility.Optimize(wall); var meshRenderer = wall.GetComponent <MeshRenderer>(); meshRenderer.material = Config.BoundaryMaterial; meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; meshRenderer.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off; wall.gameObject.name = wall.name = Path.GetFileNameWithoutExtension(Config.BoundaryGeoData).Replace('_', ' '); wall.transform.SetParent(null, true); }
/// <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); } }
/// <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); } }