Ejemplo n.º 1
0
 /// <summary>
 /// Clear and refresh mesh in case of failure to create a shape.
 /// </summary>
 /// <param name="mesh"></param>
 internal static void ClearAndRefreshMesh(this ProBuilderMesh mesh)
 {
     mesh.Clear();
     mesh.ToMesh();
     mesh.Refresh();
 }
Ejemplo n.º 2
0
        public static void ImportMesh(ProBuilderMesh mesh)
        {
            MeshFilter filter = mesh.GetComponent <MeshFilter>();

            ImportMesh(filter, mesh, Vector2.one);
        }
Ejemplo n.º 3
0
 internal override MeshAndElementSelection GetElementSelection(ProBuilderMesh mesh, PivotPoint pivot, HandleOrientation orientation)
 {
     return(new MeshAndTextures(mesh, pivot, orientation));
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Ensure that this object has a valid mesh reference, and the geometry is current. If it is not valid, this function will attempt to repair the sync state.
        /// </summary>
        /// <param name="mesh">The component to test.</param>
        /// <seealso cref="ProBuilderMesh.meshSyncState"/>
        public static void SynchronizeWithMeshFilter(ProBuilderMesh mesh)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            mesh.EnsureMeshFilterIsAssigned();
            mesh.EnsureMeshColliderIsAssigned();
            MeshSyncState state           = mesh.meshSyncState;
            bool          meshesAreAssets = Experimental.meshesAreAssets;

            if (state != MeshSyncState.InSync)
            {
                Mesh oldMesh;

                if (state == MeshSyncState.Null)
                {
                    var versionID = mesh.versionID;
                    mesh.Rebuild();
                    mesh.Optimize();
                    mesh.versionID = versionID;
                }
                else
                // If the mesh ID doesn't match the gameObject Id, it could mean two things:
                //   1. The object was just duplicated, and then made unique
                //   2. The scene was reloaded, and gameObject ids were recalculated.
                // If (2) we need to clean up the old mesh. If the (1) the old mesh needs to *not* be destroyed.
                if ((oldMesh = mesh.mesh) != null)
                {
                    int meshNo = -1;
                    int.TryParse(oldMesh.name.Replace("pb_Mesh", ""), out meshNo);

                    UnityEngine.Object dup = UnityEditor.EditorUtility.InstanceIDToObject(meshNo);
                    GameObject         go  = dup as GameObject;

                    // Scene reload, just rename the mesh to the correct ID
                    if (go == null)
                    {
                        mesh.mesh.name = "pb_Mesh" + mesh.id;
                    }
                    else
                    {
                        // Mesh was duplicated, need to instantiate a unique mesh asset
                        if (!meshesAreAssets || !(IsPrefabAsset(mesh.gameObject) || IsPrefabInstance(mesh.gameObject)))
                        {
                            // deep copy arrays & ToMesh/Refresh
                            mesh.MakeUnique();
                            mesh.Optimize();
                        }
                    }
                }
                else
                {
                    // old mesh didn't exist, so this is probably a prefab being instanced
                    if (IsPrefabAsset(mesh.gameObject))
                    {
                        mesh.mesh.hideFlags = (HideFlags)(1 | 2 | 4 | 8);
                    }

                    mesh.Optimize();
                }
            }
            else
            {
                if (meshesAreAssets)
                {
                    EditorMeshUtility.TryCacheMesh(mesh);
                }
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Reverses the orientation of the middle edge in a quad.
        /// <![CDATA[
        /// ```
        /// .  _____        _____
        /// . |\    |      |    /|
        /// . |  \  |  =>  |  /  |
        /// . |____\|      |/____|
        /// ```
        /// ]]>
        ///
        /// This is the equivalent to the [Flip Face Edge](../manual/Face_FlipTri.html) action.
        /// </summary>
        /// <param name="mesh">The mesh that face belongs to.</param>
        /// <param name="face">The target face.</param>
        /// <returns>True if successful; false if not. This operation will fail if the face does not contain two triangles with exactly two shared vertices.</returns>
        public static bool FlipEdge(this ProBuilderMesh mesh, Face face)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

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

            int[] indexes = face.indexesInternal;

            if (indexes.Length != 6)
            {
                return(false);
            }

            int[] mode = ArrayUtility.Fill <int>(1, indexes.Length);

            for (int x = 0; x < indexes.Length - 1; x++)
            {
                for (int y = x + 1; y < indexes.Length; y++)
                {
                    if (indexes[x] == indexes[y])
                    {
                        mode[x]++;
                        mode[y]++;
                    }
                }
            }

            if (mode[0] + mode[1] + mode[2] != 5 ||
                mode[3] + mode[4] + mode[5] != 5)
            {
                return(false);
            }

            int i0 = indexes[mode[0] == 1 ? 0 : mode[1] == 1 ? 1 : 2];
            int i1 = indexes[mode[3] == 1 ? 3 : mode[4] == 1 ? 4 : 5];

            int used = -1;

            if (mode[0] == 2)
            {
                used       = indexes[0];
                indexes[0] = i1;
            }
            else if (mode[1] == 2)
            {
                used       = indexes[1];
                indexes[1] = i1;
            }
            else if (mode[2] == 2)
            {
                used       = indexes[2];
                indexes[2] = i1;
            }

            if (mode[3] == 2 && indexes[3] != used)
            {
                indexes[3] = i0;
            }
            else if (mode[4] == 2 && indexes[4] != used)
            {
                indexes[4] = i0;
            }
            else if (mode[5] == 2 && indexes[5] != used)
            {
                indexes[5] = i0;
            }

            face.InvalidateCache();

            return(true);
        }
        public static List <ElementGroup> GetElementGroups(ProBuilderMesh mesh, PivotPoint pivot, HandleOrientation orientation, bool collectCoincident)
        {
            var groups     = new List <ElementGroup>();
            var trs        = mesh.transform.localToWorldMatrix;
            var selectMode = ProBuilderEditor.selectMode;

            switch (pivot)
            {
            case PivotPoint.IndividualOrigins:
            {
                if (selectMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex))
                {
                    foreach (var list in GetVertexSelectionGroups(mesh, collectCoincident))
                    {
                        var bounds = Math.GetBounds(mesh.positionsInternal, list);
                        var rot    = UnityEngine.ProBuilder.HandleUtility.GetVertexRotation(mesh, orientation, list);
                        groups.Add(new ElementGroup(list, trs.MultiplyPoint3x4(bounds.center), rot));
                    }
                }
                else if (selectMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
                {
                    foreach (var list in GetEdgeSelectionGroups(mesh))
                    {
                        var bounds = Math.GetBounds(mesh.positionsInternal, list);
                        var rot    = UnityEngine.ProBuilder.HandleUtility.GetEdgeRotation(mesh, orientation, list);

                        List <int> indices;

                        if (collectCoincident)
                        {
                            indices = new List <int>();
                            mesh.GetCoincidentVertices(list, indices);
                        }
                        else
                        {
                            indices = list.SelectMany(x => new int[] { x.a, x.b }).ToList();
                        }

                        groups.Add(new ElementGroup(indices, trs.MultiplyPoint3x4(bounds.center), rot));
                    }
                }
                else if (selectMode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace))
                {
                    foreach (var list in GetFaceSelectionGroups(mesh))
                    {
                        var        bounds = Math.GetBounds(mesh.positionsInternal, list);
                        var        rot    = UnityEngine.ProBuilder.HandleUtility.GetFaceRotation(mesh, orientation, list);
                        List <int> indices;

                        if (collectCoincident)
                        {
                            indices = new List <int>();
                            mesh.GetCoincidentVertices(list, indices);
                        }
                        else
                        {
                            indices = list.SelectMany(x => x.distinctIndexesInternal).ToList();
                        }

                        groups.Add(new ElementGroup(indices, trs.MultiplyPoint3x4(bounds.center), rot));
                    }
                }
                break;
            }

            case PivotPoint.ActiveElement:
            {
                var indices  = GetSelectedIndicesForSelectMode(mesh, selectMode, collectCoincident);
                var position = mesh.transform.position;
                var rotation = mesh.transform.rotation;

                if (selectMode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace))
                {
                    var face = mesh.GetActiveFace();

                    if (face != null)
                    {
                        position = trs.MultiplyPoint3x4(Math.GetBounds(mesh.positionsInternal, face.distinctIndexesInternal).center);
                        rotation = UnityEngine.ProBuilder.HandleUtility.GetFaceRotation(mesh, orientation, new Face[] { face });
                    }
                }
                else if (selectMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
                {
                    var edge = mesh.GetActiveEdge();

                    if (edge != Edge.Empty)
                    {
                        position = trs.MultiplyPoint3x4(Math.GetBounds(mesh.positionsInternal, new int [] { edge.a, edge.b }).center);
                        rotation = UnityEngine.ProBuilder.HandleUtility.GetEdgeRotation(mesh, orientation, new Edge[] { edge });
                    }
                }
                else if (selectMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex))
                {
                    var vertex = mesh.GetActiveVertex();

                    if (vertex > -1)
                    {
                        position = trs.MultiplyPoint3x4(mesh.positionsInternal[vertex]);
                        rotation = UnityEngine.ProBuilder.HandleUtility.GetVertexRotation(mesh, orientation, new int[] { vertex });
                    }
                }

                groups.Add(new ElementGroup(indices, position, rotation));
                break;
            }

            default:
            {
                var indices  = GetSelectedIndicesForSelectMode(mesh, selectMode, collectCoincident);
                var position = MeshSelection.bounds.center;
                var rotation = Quaternion.identity;

                if (selectMode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace))
                {
                    var face = mesh.GetActiveFace();

                    if (face != null)
                    {
                        rotation = UnityEngine.ProBuilder.HandleUtility.GetFaceRotation(mesh, orientation, new Face[] { face });
                    }
                }
                else if (selectMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
                {
                    var edge = mesh.GetActiveEdge();

                    if (edge != Edge.Empty)
                    {
                        rotation = UnityEngine.ProBuilder.HandleUtility.GetEdgeRotation(mesh, orientation, new Edge[] { edge });
                    }
                }
                else if (selectMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex))
                {
                    var vertex = mesh.GetActiveVertex();

                    if (vertex > -1)
                    {
                        rotation = UnityEngine.ProBuilder.HandleUtility.GetVertexRotation(mesh, orientation, new int[] { vertex });
                    }
                }

                groups.Add(new ElementGroup(indices, position, rotation));
                break;
            }
            }

            return(groups);
        }
        static IEnumerable <List <Edge> > GetEdgeSelectionGroups(ProBuilderMesh mesh)
        {
            var edges  = EdgeLookup.GetEdgeLookup(mesh.selectedEdgesInternal, mesh.sharedVertexLookup);
            var groups = new List <SimpleTuple <HashSet <int>, List <Edge> > >();

            foreach (var edge in edges)
            {
                var foundMatch = false;

                foreach (var kvp in groups)
                {
                    if (kvp.item1.Contains(edge.common.a) || kvp.item1.Contains(edge.common.b))
                    {
                        kvp.item1.Add(edge.common.a);
                        kvp.item1.Add(edge.common.b);
                        kvp.item2.Add(edge.local);
                        foundMatch = true;
                        break;
                    }
                }

                if (!foundMatch)
                {
                    groups.Add(new SimpleTuple <HashSet <int>, List <Edge> >(
                                   new HashSet <int>()
                    {
                        edge.common.a, edge.common.b
                    },
                                   new List <Edge>()
                    {
                        edge.local
                    }));
                }
            }

            // collect overlapping groups (happens in cases where selection order begins as two separate groups but
            // becomes one)
            var res     = new List <List <Edge> >();
            var overlap = new HashSet <int>();

            for (int i = 0, c = groups.Count; i < c; i++)
            {
                if (overlap.Contains(i))
                {
                    continue;
                }

                List <Edge> grp = groups[i].item2;

                for (int n = i + 1; n < c; n++)
                {
                    if (groups[i].item1.Overlaps(groups[n].item1))
                    {
                        overlap.Add(n);
                        grp.AddRange(groups[n].item2);
                    }
                }

                res.Add(grp);
            }

            return(res);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Insert a number of new points to each edge. Points are evenly spaced out along the edge.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="edges">The edges to split with points.</param>
        /// <param name="count">The number of new points to insert. Must be greater than 0.</param>
        /// <returns>The new edges created by inserting points.</returns>
        public static List <Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, IList <Edge> edges, int count)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

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

            if (count < 1 || count > 512)
            {
                Log.Error("New edge vertex count is less than 1 or greater than 512.");
                return(null);
            }

            List <Vertex>         vertices        = new List <Vertex>(mesh.GetVertices());
            Dictionary <int, int> lookup          = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV        = mesh.sharedTextureLookup;
            List <int>            indexesToDelete = new List <int>();
            IEnumerable <Edge>    commonEdges     = EdgeUtility.GetSharedVertexHandleEdges(mesh, edges);
            List <Edge>           distinctEdges   = commonEdges.Distinct().ToList();

            Dictionary <Face, FaceRebuildData> modifiedFaces = new Dictionary <Face, FaceRebuildData>();

            int originalSharedIndexesCount = lookup.Count();
            int sharedIndexesCount         = originalSharedIndexesCount;

            foreach (Edge edge in distinctEdges)
            {
                Edge localEdge = EdgeUtility.GetEdgeWithSharedVertexHandles(mesh, edge);

                // Generate the new vertices that will be inserted on this edge
                List <Vertex> verticesToAppend = new List <Vertex>(count);

                for (int i = 0; i < count; i++)
                {
                    verticesToAppend.Add(Vertex.Mix(vertices[localEdge.a], vertices[localEdge.b], (i + 1) / ((float)count + 1)));
                }

                List <SimpleTuple <Face, Edge> > adjacentFaces = ElementSelection.GetNeighborFaces(mesh, localEdge);

                // foreach face attached to common edge, append vertices
                foreach (SimpleTuple <Face, Edge> tup in adjacentFaces)
                {
                    Face face = tup.item1;

                    FaceRebuildData data;

                    if (!modifiedFaces.TryGetValue(face, out data))
                    {
                        data                 = new FaceRebuildData();
                        data.face            = new Face(new int[0], face.submeshIndex, new AutoUnwrapSettings(face.uv), face.smoothingGroup, face.textureGroup, -1, face.manualUV);
                        data.vertices        = new List <Vertex>(ArrayUtility.ValuesWithIndexes(vertices, face.distinctIndexesInternal));
                        data.sharedIndexes   = new List <int>();
                        data.sharedIndexesUV = new List <int>();

                        foreach (int i in face.distinctIndexesInternal)
                        {
                            int shared;

                            if (lookup.TryGetValue(i, out shared))
                            {
                                data.sharedIndexes.Add(shared);
                            }

                            if (lookupUV.TryGetValue(i, out shared))
                            {
                                data.sharedIndexesUV.Add(shared);
                            }
                        }

                        indexesToDelete.AddRange(face.distinctIndexesInternal);

                        modifiedFaces.Add(face, data);
                    }

                    data.vertices.AddRange(verticesToAppend);

                    for (int i = 0; i < count; i++)
                    {
                        data.sharedIndexes.Add(sharedIndexesCount + i);
                        data.sharedIndexesUV.Add(-1);
                    }
                }

                sharedIndexesCount += count;
            }

            // now apply the changes
            List <Face>            dic_face      = modifiedFaces.Keys.ToList();
            List <FaceRebuildData> dic_data      = modifiedFaces.Values.ToList();
            List <EdgeLookup>      appendedEdges = new List <EdgeLookup>();

            for (int i = 0; i < dic_face.Count; i++)
            {
                Face            face = dic_face[i];
                FaceRebuildData data = dic_data[i];

                Vector3   nrm        = Math.Normal(mesh, face);
                Vector2[] projection = Projection.PlanarProject(data.vertices.Select(x => x.position).ToArray(), null, nrm);

                int vertexCount = vertices.Count;

                // triangulate and set new face indexes to end of current vertex list
                List <int> indexes;

                if (Triangulation.SortAndTriangulate(projection, out indexes))
                {
                    data.face.indexesInternal = indexes.ToArray();
                }
                else
                {
                    continue;
                }

                data.face.ShiftIndexes(vertexCount);
                face.CopyFrom(data.face);

                for (int n = 0; n < data.vertices.Count; n++)
                {
                    lookup.Add(vertexCount + n, data.sharedIndexes[n]);
                }

                if (data.sharedIndexesUV.Count == data.vertices.Count)
                {
                    for (int n = 0; n < data.vertices.Count; n++)
                    {
                        lookupUV.Add(vertexCount + n, data.sharedIndexesUV[n]);
                    }
                }

                vertices.AddRange(data.vertices);

                foreach (Edge e in face.edgesInternal)
                {
                    EdgeLookup el = new EdgeLookup(new Edge(lookup[e.a], lookup[e.b]), e);

                    if (el.common.a >= originalSharedIndexesCount || el.common.b >= originalSharedIndexesCount)
                    {
                        appendedEdges.Add(el);
                    }
                }
            }

            indexesToDelete = indexesToDelete.Distinct().ToList();
            int delCount = indexesToDelete.Count;

            var newEdges = appendedEdges.Distinct().Select(x => x.local - delCount).ToList();

            mesh.SetVertices(vertices);
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);
            mesh.DeleteVertices(indexesToDelete);

            return(newEdges);
        }
        /// <summary>
        /// Iterates through face edges and builds a list using the opposite edge.
        /// </summary>
        /// <param name="pb"></param>
        /// <param name="edges"></param>
        /// <returns></returns>
        internal static IEnumerable <Edge> GetEdgeRing(ProBuilderMesh pb, IEnumerable <Edge> edges)
        {
            List <WingedEdge> wings      = WingedEdge.GetWingedEdges(pb);
            List <EdgeLookup> edgeLookup = EdgeLookup.GetEdgeLookup(edges, pb.sharedVertexLookup).ToList();

            edgeLookup = edgeLookup.Distinct().ToList();

            Dictionary <Edge, WingedEdge> wings_dic = new Dictionary <Edge, WingedEdge>();

            for (int i = 0; i < wings.Count; i++)
            {
                if (!wings_dic.ContainsKey(wings[i].edge.common))
                {
                    wings_dic.Add(wings[i].edge.common, wings[i]);
                }
            }

            HashSet <EdgeLookup> used = new HashSet <EdgeLookup>();

            for (int i = 0, c = edgeLookup.Count; i < c; i++)
            {
                WingedEdge we;

                if (!wings_dic.TryGetValue(edgeLookup[i].common, out we) || used.Contains(we.edge))
                {
                    continue;
                }

                WingedEdge cur = we;

                while (cur != null)
                {
                    if (!used.Add(cur.edge))
                    {
                        break;
                    }
                    cur = EdgeRingNext(cur);
                    if (cur != null && cur.opposite != null)
                    {
                        cur = cur.opposite;
                    }
                }

                cur = EdgeRingNext(we.opposite);
                if (cur != null && cur.opposite != null)
                {
                    cur = cur.opposite;
                }

                // run in both directions
                while (cur != null)
                {
                    if (!used.Add(cur.edge))
                    {
                        break;
                    }
                    cur = EdgeRingNext(cur);
                    if (cur != null && cur.opposite != null)
                    {
                        cur = cur.opposite;
                    }
                }
            }

            return(used.Select(x => x.local));
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Add a set of points to a face and retriangulate. Points are added to the nearest edge.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="face">The face to append points to.</param>
        /// <param name="points">Points to added to the face.</param>
        /// <returns>The face created by appending the points.</returns>
        public static Face AppendVerticesToFace(this ProBuilderMesh mesh, Face face, Vector3[] points)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

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

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

            List <Vertex>         vertices = mesh.GetVertices().ToList();
            List <Face>           faces    = new List <Face>(mesh.facesInternal);
            Dictionary <int, int> lookup   = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV = null;

            if (mesh.sharedTextures != null)
            {
                lookupUV = new Dictionary <int, int>();
                SharedVertex.GetSharedVertexLookup(mesh.sharedTextures, lookupUV);
            }

            List <Edge> wound = WingedEdge.SortEdgesByAdjacency(face);

            List <Vertex> n_vertices = new List <Vertex>();
            List <int>    n_shared   = new List <int>();
            List <int>    n_sharedUV = lookupUV != null ? new List <int>() : null;

            for (int i = 0; i < wound.Count; i++)
            {
                n_vertices.Add(vertices[wound[i].a]);
                n_shared.Add(lookup[wound[i].a]);

                if (lookupUV != null)
                {
                    int uv;

                    if (lookupUV.TryGetValue(wound[i].a, out uv))
                    {
                        n_sharedUV.Add(uv);
                    }
                    else
                    {
                        n_sharedUV.Add(-1);
                    }
                }
            }

            // now insert the new points on the nearest edge
            for (int i = 0; i < points.Length; i++)
            {
                int     index = -1;
                float   best  = Mathf.Infinity;
                Vector3 p     = points[i];
                int     vc    = n_vertices.Count;

                for (int n = 0; n < vc; n++)
                {
                    Vector3 v = n_vertices[n].position;
                    Vector3 w = n_vertices[(n + 1) % vc].position;

                    float dist = Math.DistancePointLineSegment(p, v, w);

                    if (dist < best)
                    {
                        best  = dist;
                        index = n;
                    }
                }

                Vertex left = n_vertices[index], right = n_vertices[(index + 1) % vc];

                float x = (p - left.position).sqrMagnitude;
                float y = (p - right.position).sqrMagnitude;

                Vertex insert = Vertex.Mix(left, right, x / (x + y));

                n_vertices.Insert((index + 1) % vc, insert);
                n_shared.Insert((index + 1) % vc, -1);
                if (n_sharedUV != null)
                {
                    n_sharedUV.Insert((index + 1) % vc, -1);
                }
            }

            List <int> triangles;

            try
            {
                Triangulation.TriangulateVertices(n_vertices, out triangles, false);
            }
            catch
            {
                Debug.Log("Failed triangulating face after appending vertices.");
                return(null);
            }

            FaceRebuildData data = new FaceRebuildData();

            data.face            = new Face(triangles.ToArray(), face.submeshIndex, new AutoUnwrapSettings(face.uv), face.smoothingGroup, face.textureGroup, -1, face.manualUV);
            data.vertices        = n_vertices;
            data.sharedIndexes   = n_shared;
            data.sharedIndexesUV = n_sharedUV;

            FaceRebuildData.Apply(new List <FaceRebuildData>()
            {
                data
            },
                                  vertices,
                                  faces,
                                  lookup,
                                  lookupUV);

            var newFace = data.face;

            mesh.SetVertices(vertices);
            mesh.faces = faces;
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);

            // check old normal and make sure this new face is pointing the same direction
            Vector3 oldNrm = Math.Normal(mesh, face);
            Vector3 newNrm = Math.Normal(mesh, newFace);

            if (Vector3.Dot(oldNrm, newNrm) < 0)
            {
                newFace.Reverse();
            }

            mesh.DeleteFace(face);

            return(newFace);
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Insert a number of new points to an edge. Points are evenly spaced out along the edge.
 /// </summary>
 /// <param name="mesh">The source mesh.</param>
 /// <param name="edge">The edge to split with points.</param>
 /// <param name="count">The number of new points to insert. Must be greater than 0.</param>
 /// <returns>The new edges created by inserting points.</returns>
 public static List <Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, Edge edge, int count)
 {
     return(AppendVerticesToEdge(mesh, new Edge[] { edge }, count));
 }
Ejemplo n.º 12
0
        /// <summary>
        /// Insert a face between two edges.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="a">First edge.</param>
        /// <param name="b">Second edge</param>
        /// <param name="allowNonManifoldGeometry">If true, this function will allow edges to be bridged that create overlapping (non-manifold) faces.</param>
        /// <returns>The new face, or null of the action failed.</returns>
        public static Face Bridge(this ProBuilderMesh mesh, Edge a, Edge b, bool allowNonManifoldGeometry = false)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            SharedVertex[]        sharedVertices = mesh.sharedVerticesInternal;
            Dictionary <int, int> lookup         = mesh.sharedVertexLookup;

            // Check to see if a face already exists
            if (!allowNonManifoldGeometry)
            {
                if (ElementSelection.GetNeighborFaces(mesh, a).Count > 1 || ElementSelection.GetNeighborFaces(mesh, b).Count > 1)
                {
                    return(null);
                }
            }

            foreach (Face face in mesh.facesInternal)
            {
                if (mesh.IndexOf(face.edgesInternal, a) >= 0 && mesh.IndexOf(face.edgesInternal, b) >= 0)
                {
                    Log.Warning("Face already exists between these two edges!");
                    return(null);
                }
            }

            Vector3[] positions = mesh.positionsInternal;
            bool      hasColors = mesh.HasArrays(MeshArrays.Color);

            Color[] colors = hasColors ? mesh.colorsInternal : null;

            Vector3[]          v;
            Color[]            c;
            int[]              s;
            AutoUnwrapSettings uvs = AutoUnwrapSettings.tile;
            int submeshIndex       = 0;

            // Get material and UV stuff from the first edge face
            SimpleTuple <Face, Edge> faceAndEdge;

            if (EdgeUtility.ValidateEdge(mesh, a, out faceAndEdge) || EdgeUtility.ValidateEdge(mesh, b, out faceAndEdge))
            {
                uvs          = new AutoUnwrapSettings(faceAndEdge.item1.uv);
                submeshIndex = faceAndEdge.item1.submeshIndex;
            }

            // Bridge will form a triangle
            if (a.Contains(b.a, lookup) || a.Contains(b.b, lookup))
            {
                v = new Vector3[3];
                c = new Color[3];
                s = new int[3];

                bool axbx = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.a)].arrayInternal, b.a) > -1;
                bool axby = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.a)].arrayInternal, b.b) > -1;

                bool aybx = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.b)].arrayInternal, b.a) > -1;
                bool ayby = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.b)].arrayInternal, b.b) > -1;

                if (axbx)
                {
                    v[0] = positions[a.a];
                    if (hasColors)
                    {
                        c[0] = colors[a.a];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.a);
                    v[1] = positions[a.b];
                    if (hasColors)
                    {
                        c[1] = colors[a.b];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.b);
                    v[2] = positions[b.b];
                    if (hasColors)
                    {
                        c[2] = colors[b.b];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.b);
                }
                else if (axby)
                {
                    v[0] = positions[a.a];
                    if (hasColors)
                    {
                        c[0] = colors[a.a];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.a);
                    v[1] = positions[a.b];
                    if (hasColors)
                    {
                        c[1] = colors[a.b];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.b);
                    v[2] = positions[b.a];
                    if (hasColors)
                    {
                        c[2] = colors[b.a];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.a);
                }
                else if (aybx)
                {
                    v[0] = positions[a.b];
                    if (hasColors)
                    {
                        c[0] = colors[a.b];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.b);
                    v[1] = positions[a.a];
                    if (hasColors)
                    {
                        c[1] = colors[a.a];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.a);
                    v[2] = positions[b.b];
                    if (hasColors)
                    {
                        c[2] = colors[b.b];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.b);
                }
                else if (ayby)
                {
                    v[0] = positions[a.b];
                    if (hasColors)
                    {
                        c[0] = colors[a.b];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.b);
                    v[1] = positions[a.a];
                    if (hasColors)
                    {
                        c[1] = colors[a.a];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.a);
                    v[2] = positions[b.a];
                    if (hasColors)
                    {
                        c[2] = colors[b.a];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.a);
                }

                return(mesh.AppendFace(
                           v,
                           hasColors ? c : null,
                           new Vector2[v.Length],
                           new Face(axbx || axby ? new int[3] {
                    2, 1, 0
                } : new int[3] {
                    0, 1, 2
                }, submeshIndex, uvs, 0, -1, -1, false),
                           s));
            }

            // Else, bridge will form a quad

            v = new Vector3[4];
            c = new Color[4];
            s = new int[4]; // shared indexes index to add to

            v[0] = positions[a.a];
            if (hasColors)
            {
                c[0] = mesh.colorsInternal[a.a];
            }
            s[0] = mesh.GetSharedVertexHandle(a.a);
            v[1] = positions[a.b];
            if (hasColors)
            {
                c[1] = mesh.colorsInternal[a.b];
            }
            s[1] = mesh.GetSharedVertexHandle(a.b);

            Vector3 nrm = Vector3.Cross(positions[b.a] - positions[a.a], positions[a.b] - positions[a.a]).normalized;

            Vector2[] planed = Projection.PlanarProject(new Vector3[4] {
                positions[a.a], positions[a.b], positions[b.a], positions[b.b]
            }, null, nrm);

            Vector2 ipoint     = Vector2.zero;
            bool    intersects = Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint);

            if (!intersects)
            {
                v[2] = positions[b.a];
                if (hasColors)
                {
                    c[2] = mesh.colorsInternal[b.a];
                }
                s[2] = mesh.GetSharedVertexHandle(b.a);
                v[3] = positions[b.b];
                if (hasColors)
                {
                    c[3] = mesh.colorsInternal[b.b];
                }
                s[3] = mesh.GetSharedVertexHandle(b.b);
            }
            else
            {
                v[2] = positions[b.b];
                if (hasColors)
                {
                    c[2] = mesh.colorsInternal[b.b];
                }
                s[2] = mesh.GetSharedVertexHandle(b.b);
                v[3] = positions[b.a];
                if (hasColors)
                {
                    c[3] = mesh.colorsInternal[b.a];
                }
                s[3] = mesh.GetSharedVertexHandle(b.a);
            }

            return(mesh.AppendFace(
                       v,
                       hasColors ? c : null,
                       new Vector2[v.Length],
                       new Face(new int[6] {
                2, 1, 0, 2, 3, 1
            }, submeshIndex, uvs, 0, -1, -1, false),
                       s));
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Rebuild a mesh from an ordered set of points.
        /// </summary>
        /// <param name="mesh">The target mesh. The mesh values will be cleared and repopulated with the shape extruded from points.</param>
        /// <param name="points">A path of points to triangulate and extrude.</param>
        /// <param name="extrude">The distance to extrude.</param>
        /// <param name="flipNormals">If true the faces will be inverted at creation.</param>
        /// <param name="holePoints">Holes in the polygon. If null this will be ignored.</param>
        /// <returns>An ActionResult with the status of the operation.</returns>
        public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList <Vector3> points, float extrude, bool flipNormals, IList <IList <Vector3> > holePoints)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (points == null || points.Count < 3)
            {
                ClearAndRefreshMesh(mesh);
                return(new ActionResult(ActionResult.Status.NoChange, "Too Few Points"));
            }

            Vector3[] vertices = points.ToArray();

            Vector3[][] holeVertices = null;
            if (holePoints != null && holePoints.Count > 0)
            {
                holeVertices = new Vector3[holePoints.Count][];
                for (int i = 0; i < holePoints.Count; i++)
                {
                    if (holePoints[i] == null || holePoints[i].Count < 3)
                    {
                        ClearAndRefreshMesh(mesh);
                        return(new ActionResult(ActionResult.Status.NoChange, "Too Few Points in hole " + i));
                    }
                    holeVertices[i] = holePoints[i].ToArray();
                }
            }

            List <int> triangles;

            Log.PushLogLevel(LogLevel.Error);

            if (Triangulation.TriangulateVertices(vertices, out triangles, holeVertices))
            {
                Vector3[] combinedVertices = null;
                if (holeVertices != null)
                {
                    combinedVertices = new Vector3[vertices.Length + holeVertices.Sum(arr => arr.Length)];
                    Array.Copy(vertices, combinedVertices, vertices.Length);
                    int destinationIndex = vertices.Length;
                    foreach (var hole in holeVertices)
                    {
                        Array.ConstrainedCopy(hole, 0, combinedVertices, destinationIndex, hole.Length);
                        destinationIndex += hole.Length;
                    }
                }
                else
                {
                    combinedVertices = vertices;
                }
                int[] indexes = triangles.ToArray();

                if (Math.PolygonArea(combinedVertices, indexes) < Mathf.Epsilon)
                {
                    ClearAndRefreshMesh(mesh);
                    Log.PopLogLevel();
                    return(new ActionResult(ActionResult.Status.Failure, "Polygon Area < Epsilon"));
                }

                mesh.Clear();

                mesh.positionsInternal = combinedVertices;
                var newFace = new Face(indexes);
                mesh.facesInternal          = new[] { newFace };
                mesh.sharedVerticesInternal = SharedVertex.GetSharedVerticesWithPositions(combinedVertices);
                mesh.InvalidateCaches();

                // check that all points are represented in the triangulation
                if (newFace.distinctIndexesInternal.Length != combinedVertices.Length)
                {
                    ClearAndRefreshMesh(mesh);
                    Log.PopLogLevel();
                    return(new ActionResult(ActionResult.Status.Failure, "Triangulation missing points"));
                }

                Vector3 nrm = Math.Normal(mesh, mesh.facesInternal[0]);
                nrm = mesh.gameObject.transform.TransformDirection(nrm);
                if ((flipNormals ? Vector3.Dot(mesh.gameObject.transform.up, nrm) > 0f : Vector3.Dot(mesh.gameObject.transform.up, nrm) < 0f))
                {
                    mesh.facesInternal[0].Reverse();
                }

                if (extrude != 0.0f)
                {
                    mesh.DuplicateAndFlip(mesh.facesInternal);

                    mesh.Extrude(new Face[] { (flipNormals ? mesh.facesInternal[1] : mesh.facesInternal[0]) }, ExtrudeMethod.IndividualFaces, extrude);

                    if ((extrude < 0f && !flipNormals) || (extrude > 0f && flipNormals))
                    {
                        foreach (var face in mesh.facesInternal)
                        {
                            face.Reverse();
                        }
                    }
                }

                mesh.ToMesh();
                mesh.Refresh();
            }
            else
            {
                // clear mesh instead of showing an invalid one
                ClearAndRefreshMesh(mesh);
                Log.PopLogLevel();
                return(new ActionResult(ActionResult.Status.Failure, "Failed Triangulating Points"));
            }

            Log.PopLogLevel();

            return(new ActionResult(ActionResult.Status.Success, "Create Polygon Shape"));
        }
Ejemplo n.º 14
0
 public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList <Vector3> points, float extrude, bool flipNormals, Vector3 cameraLookAt, IList <IList <Vector3> > holePoints = null)
 {
     return(CreateShapeFromPolygon(mesh, points, extrude, flipNormals, null));
 }
Ejemplo n.º 15
0
        static string ExportPrefab(string path, ProBuilderMesh pb, bool replaceOriginal, bool batchExport)
        {
            string name     = Path.GetFileNameWithoutExtension(path);
            string basePath = AssetPathFromFullPath(path, null);

            pb.ToMesh();
            pb.Refresh();
            pb.Optimize();

            string meshPath   = string.Format("{0}.asset", basePath);
            string prefabPath = string.Format("{0}.prefab", basePath);

            if (batchExport)
            {
                // Never overwrite during batch export.
                meshPath   = AssetDatabase.GenerateUniqueAssetPath(meshPath);
                prefabPath = AssetDatabase.GenerateUniqueAssetPath(prefabPath);
            }
            else
            {
                var existingMesh = AssetDatabase.LoadAssetAtPath <Mesh>(meshPath);
                if (existingMesh)
                {
                    // Overwriting mesh.
                    var existingPrefab = AssetDatabase.LoadAssetAtPath <GameObject>(prefabPath);
                    if (existingPrefab)
                    {
                        // Overwriting prefab as well.
                        var meshFilter = existingPrefab.GetComponent <MeshFilter>();
                        if (!meshFilter || meshFilter.sharedMesh != existingMesh)
                        {
                            // Prefab and mesh being overwritten are not related, pick different path.
                            meshPath = PickDifferentAssetPath(meshPath);
                        }
                        // Else allow overwrite both as they are related.
                    }
                    else
                    {
                        // Unrelated mesh is being overriden, pick different path.
                        meshPath = PickDifferentAssetPath(meshPath);
                    }
                }
            }

            Mesh meshAsset = pb.mesh;

            meshAsset.name = name;
            meshAsset      = CreateOrReplaceAsset(meshAsset, meshPath);

            var go = replaceOriginal ? pb.gameObject : Object.Instantiate(pb.gameObject);

            var component = go.GetComponent <ProBuilderMesh>();

            Undo.RecordObject(component, "Export ProBuilderMesh as Replacement");
            StripProBuilderScripts.DestroyProBuilderMeshAndDependencies(go, component, true, true);

            go.GetComponent <MeshFilter>().sharedMesh = meshAsset;
            var meshCollider = go.GetComponent <MeshCollider>();

            if (meshCollider)
            {
                meshCollider.sharedMesh = meshAsset;
            }

            if (replaceOriginal)
            {
                PrefabUtility.SaveAsPrefabAssetAndConnect(go, prefabPath, InteractionMode.UserAction);
            }
            else
            {
                // if we're about to overwrite the prefab asset of the source mesh, first disconnect it so that we're not
                // overwriting the instance
                if (PrefabUtility.IsPartOfAnyPrefab(pb) &&
                    PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(pb) == prefabPath)
                {
                    PrefabUtility.UnpackPrefabInstance(pb.gameObject, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
                }
                PrefabUtility.SaveAsPrefabAsset(go, prefabPath);
            }

            if (!replaceOriginal)
            {
                pb.mesh = null;
                EditorUtility.SynchronizeWithMeshFilter(pb);
                Object.DestroyImmediate(go);
            }

            return(meshPath);
        }
        void OnGUI()
        {
            DoContextMenu();

            GUILayout.BeginHorizontal(EditorStyles.toolbar);

            EditorGUI.BeginChangeCheck();

            s_ShowSettings.value = GUILayout.Toggle(s_ShowSettings.value, g_Settings, EditorStyles.toolbarButton);
            s_ShowPreview.value  = GUILayout.Toggle(s_ShowPreview.value, g_Preview, EditorStyles.toolbarButton);
            s_ShowNormals.value  = GUILayout.Toggle(s_ShowNormals.value, g_Normals, EditorStyles.toolbarButton);

            if (s_ShowNormals)
            {
                EditorGUI.BeginChangeCheck();
                s_NormalsSize.value = GUILayout.HorizontalSlider(
                    s_NormalsSize,
                    .001f,
                    1f,
                    GUILayout.MinWidth(30f),
                    GUILayout.MaxWidth(100f));
                if (EditorGUI.EndChangeCheck())
                {
                    foreach (var kvp in m_SmoothGroups)
                    {
                        kvp.Value.RebuildNormalsMesh(kvp.Key);
                    }
                }
            }

            if (EditorGUI.EndChangeCheck())
            {
                ProBuilderSettings.Save();
                SceneView.RepaintAll();
            }

            GUILayout.FlexibleSpace();

            if (GUILayout.Button(m_HelpIcon, UI.EditorStyles.toolbarHelpIcon))
            {
                s_ShowHelp.SetValue(!s_ShowHelp, true);
            }
            GUILayout.EndHorizontal();

            if (s_ShowSettings)
            {
                GUILayout.BeginVertical(UI.EditorStyles.settingsGroup);

                EditorGUIUtility.labelWidth = 100;

                EditorGUI.BeginChangeCheck();

                s_PreviewOpacity.value = EditorGUILayout.Slider(g_PreviewOpacity, s_PreviewOpacity, .001f, 1f);
                s_PreviewDither.value  = EditorGUILayout.Toggle(g_PreviewDither, s_PreviewDither);

                if (EditorGUI.EndChangeCheck())
                {
                    ProBuilderSettings.Save();
                    smoothPreviewMaterial.SetFloat("_Opacity", s_PreviewOpacity);
                    smoothPreviewMaterial.SetFloat("_Dither", s_PreviewDither ? 1f : 0f);
                    SceneView.RepaintAll();
                }

                EditorGUIUtility.labelWidth = 0;

                GUILayout.EndVertical();
            }

            m_Scroll = EditorGUILayout.BeginScrollView(m_Scroll);

            if (s_ShowHelp)
            {
                GUILayout.BeginVertical(UI.EditorStyles.settingsGroup);

                GUILayout.Label("Create and Clear Smoothing Groups", EditorStyles.boldLabel);

                GUILayout.Label("Adjacent faces with the same smoothing group will appear to have a soft adjoining edge.", wordWrappedRichText);
                GUILayout.Space(2);
                GUILayout.Label("<b>To smooth</b> a selected group of faces, click one of the Smooth Group buttons.", wordWrappedRichText);
                GUILayout.Label("<b>To clear</b> selected faces of their smooth group, click the [Break] icon.", wordWrappedRichText);
                GUILayout.Label("<b>To select</b> all faces in a group, Right+Click or Alt+Click a smooth group button.", wordWrappedRichText);
                GUILayout.Space(2);

                UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonStyle);
                GUILayout.Label("An unused smooth group", wordWrappedRichText);
                UI.EditorGUILayout.EndRow();

                UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonInUseStyle);
                GUILayout.Label("A smooth group that is in use, but not in the current selection", wordWrappedRichText);
                UI.EditorGUILayout.EndRow();

                UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonSelectedStyle);
                GUILayout.Label("A smooth group that is currently selected", wordWrappedRichText);
                UI.EditorGUILayout.EndRow();

                UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonMixedSelectionStyle);
                GUI.backgroundColor = Color.white;
                GUILayout.Label("A smooth group is selected, but the selection also contains non-grouped faces", wordWrappedRichText);
                UI.EditorGUILayout.EndRow();

                if (GUILayout.Button("Open Documentation"))
                {
                    Application.OpenURL("https://docs.unity3d.com/Packages/com.unity.probuilder@latest/index.html?subfolder=/manual/workflow-edit-smoothing.html");
                }

                GUILayout.EndVertical();
            }

            // border style is 4 margin, 4 pad, 1px content. inner is accounted for by btn size + btn margin.
            float area    = (position.width - 10);
            float margin  = Mathf.Max(groupButtonStyle.margin.left, groupButtonStyle.margin.right);
            int   columns = (int)(area / (groupButtonStyle.CalcSize(m_GroupKeyContent).x + margin)) - 1;

            if (m_SmoothGroups.Count < 1)
            {
                GUILayout.BeginVertical();
                GUILayout.FlexibleSpace();
                GUILayout.Label("Select a ProBuilder Mesh", UI.EditorGUIUtility.CenteredGreyMiniLabel);
                GUILayout.FlexibleSpace();
                GUILayout.EndVertical();
            }
            else
            {
                foreach (var mesh in m_SmoothGroups)
                {
                    ProBuilderMesh  pb   = mesh.Key;
                    SmoothGroupData data = mesh.Value;

                    GUILayout.BeginVertical(UI.EditorStyles.settingsGroup);

                    GUILayout.BeginHorizontal();

                    if (GUILayout.Button(pb.name, UI.EditorStyles.headerLabel))
                    {
                        data.isVisible = !data.isVisible;
                    }

                    GUILayout.FlexibleSpace();

                    if (GUILayout.Button(m_SelectFacesWithSmoothGroupSelectionContent,
                                         UI.EditorStyles.buttonStyle))
                    {
                        SelectGroups(pb, new HashSet <int>(pb.selectedFacesInternal.Select(x => x.smoothingGroup)));
                    }

                    if (GUILayout.Button(m_BreakSmoothingContent,
                                         UI.EditorStyles.buttonStyle))
                    {
                        SetGroup(pb, Smoothing.smoothingGroupNone);
                    }

                    GUILayout.EndHorizontal();

                    bool isMixedSelection = data.selected.Contains(Smoothing.smoothingGroupNone);

                    if (data.isVisible)
                    {
                        int  column          = 0;
                        bool anySmoothGroups = data.groups.Any(x => x.Key > Smoothing.smoothingGroupNone);

                        GUILayout.BeginHorizontal();

                        for (int i = 1; i < Smoothing.smoothRangeMax; i++)
                        {
                            bool isSelected = data.selected.Contains(i);

                            GUIStyle stateStyle = isSelected ?
                                                  (isMixedSelection ? groupButtonMixedSelectionStyle : groupButtonSelectedStyle) :
                                                  data.groups.ContainsKey(i) ? groupButtonInUseStyle : groupButtonStyle;

                            if (s_ShowPreview && anySmoothGroups)
                            {
                                GUILayout.BeginVertical(GUILayout.MaxWidth(IconWidth));
                            }

                            m_GroupKeyContent.text = i.ToString();

                            if (GUILayout.Button(m_GroupKeyContent, stateStyle))
                            {
                                // if right click or alt click select the faces instead of setting a group
                                if ((Event.current.modifiers & EventModifiers.Alt) == EventModifiers.Alt ||
                                    Event.current.button != 0)
                                {
                                    SelectGroups(pb, new HashSet <int>()
                                    {
                                        i
                                    });
                                }
                                else
                                {
                                    SetGroup(pb, i);
                                }
                            }

                            if (s_ShowPreview && anySmoothGroups)
                            {
                                GUI.backgroundColor = data.groupColors.ContainsKey(i) ? data.groupColors[i] : Color.clear;
                                GUILayout.Label("", colorKeyStyle);
                                GUILayout.EndVertical();
                                GUI.backgroundColor = Color.white;
                            }

                            if (++column > columns)
                            {
                                column = 0;
                                GUILayout.EndHorizontal();
                                GUILayout.BeginHorizontal();
                            }
                        }

                        GUILayout.EndHorizontal();
                    }

                    GUILayout.EndVertical();
                }
            }

            EditorGUILayout.EndScrollView();
        }
Ejemplo n.º 17
0
        public override MeshSelection Select(Camera camera, Rect rect, Rect uiRootRect, GameObject[] gameObjects, bool depthTest, MeshEditorSelectionMode mode)
        {
            MeshSelection selection = new MeshSelection();

            m_faceSelection.BeginChange();

            Dictionary <ProBuilderMesh, HashSet <Face> > result = PBUtility.PickFaces(camera, rect, uiRootRect, gameObjects, depthTest);

            if (mode == MeshEditorSelectionMode.Add)
            {
                foreach (KeyValuePair <ProBuilderMesh, HashSet <Face> > kvp in result)
                {
                    ProBuilderMesh mesh        = kvp.Key;
                    IList <Face>   faces       = mesh.faces;
                    IList <int>    faceIndices = kvp.Value.Select(f => faces.IndexOf(f)).ToArray();
                    IList <int>    notSelected = faceIndices.Where(f => !m_faceSelection.IsSelected(mesh, f)).ToArray();
                    foreach (int face in notSelected)
                    {
                        m_faceSelection.Add(mesh, face);
                    }

                    selection.SelectedFaces.Add(mesh, notSelected);
                }
            }
            else if (mode == MeshEditorSelectionMode.Substract)
            {
                foreach (KeyValuePair <ProBuilderMesh, HashSet <Face> > kvp in result)
                {
                    ProBuilderMesh mesh        = kvp.Key;
                    IList <Face>   faces       = mesh.faces;
                    IList <int>    faceIndices = kvp.Value.Select(f => faces.IndexOf(f)).ToArray();
                    IList <int>    selected    = faceIndices.Where(f => m_faceSelection.IsSelected(mesh, f)).ToArray();
                    foreach (int face in selected)
                    {
                        m_faceSelection.Remove(mesh, face);
                    }
                    selection.UnselectedFaces.Add(mesh, selected);
                }
            }
            else if (mode == MeshEditorSelectionMode.Difference)
            {
                foreach (KeyValuePair <ProBuilderMesh, HashSet <Face> > kvp in result)
                {
                    ProBuilderMesh mesh        = kvp.Key;
                    IList <Face>   faces       = mesh.faces;
                    IList <int>    faceIndices = kvp.Value.Select(f => faces.IndexOf(f)).ToArray();

                    IList <int> selected    = faceIndices.Where(f => m_faceSelection.IsSelected(mesh, f)).ToArray();
                    IList <int> notSelected = faceIndices.Where(f => !m_faceSelection.IsSelected(mesh, f)).ToArray();

                    foreach (int face in selected)
                    {
                        m_faceSelection.Remove(mesh, face);
                    }

                    foreach (int face in notSelected)
                    {
                        m_faceSelection.Add(mesh, face);
                    }

                    selection.UnselectedFaces.Add(mesh, selected);
                    selection.SelectedFaces.Add(mesh, notSelected);
                }
            }

            m_faceSelection.EndChange();

            if (selection.SelectedFaces.Count == 0 && selection.UnselectedFaces.Count == 0)
            {
                selection = null;
            }

            return(selection);
        }
Ejemplo n.º 18
0
 static void ElementSelectionChanged(ProBuilderMesh mesh)
 {
     InvalidateElementSelection();
 }
 public MeshAndElementSelection(ProBuilderMesh mesh, PivotPoint pivot, HandleOrientation orientation, bool collectCoincidentIndices)
 {
     m_Mesh          = mesh;
     m_ElementGroups = ElementGroup.GetElementGroups(mesh, pivot, orientation, collectCoincidentIndices);
 }
Ejemplo n.º 20
0
 internal static bool Contains(ProBuilderMesh mesh)
 {
     return(s_TopSelection.Contains(mesh));
 }
Ejemplo n.º 21
0
 internal static bool IsPrefab(ProBuilderMesh mesh)
 {
     return(PrefabUtility.GetPrefabAssetType(mesh.gameObject) != PrefabAssetType.NotAPrefab);
 }
Ejemplo n.º 22
0
        /// <summary>
        /// move the UVs to where the edges passed meet
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="faceToMove"></param>
        /// <param name="edgeToAlignTo"></param>
        /// <param name="edgeToBeAligned"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        static bool AlignEdges(ProBuilderMesh mesh, Face faceToMove, Edge edgeToAlignTo, Edge edgeToBeAligned, int channel)
        {
            Vector2[]      uvs           = GetUVs(mesh, channel);
            SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;

            // Match each edge vertex to the other
            int[] matchX = new int[2] {
                edgeToAlignTo.a, -1
            };
            int[] matchY = new int[2] {
                edgeToAlignTo.b, -1
            };

            int siIndex = mesh.GetSharedVertexHandle(edgeToAlignTo.a);

            if (siIndex < 0)
            {
                return(false);
            }

            if (sharedIndexes[siIndex].Contains(edgeToBeAligned.a))
            {
                matchX[1] = edgeToBeAligned.a;
                matchY[1] = edgeToBeAligned.b;
            }
            else
            {
                matchX[1] = edgeToBeAligned.b;
                matchY[1] = edgeToBeAligned.a;
            }

            // scale face 2 to match the edge size of f1
            float dist_e1 = Vector2.Distance(uvs[edgeToAlignTo.a], uvs[edgeToAlignTo.b]);
            float dist_e2 = Vector2.Distance(uvs[edgeToBeAligned.a], uvs[edgeToBeAligned.b]);

            float scale = dist_e1 / dist_e2;

            // doesn't matter what point we scale around because we'll move it in the next step anyways
            foreach (int i in faceToMove.distinctIndexesInternal)
            {
                uvs[i] = uvs[i].ScaleAroundPoint(Vector2.zero, Vector2.one * scale);
            }

            /**
             * Figure out where the center of each edge is so that we can move the f2 edge to match f1's origin
             */
            Vector2 f1_center = (uvs[edgeToAlignTo.a] + uvs[edgeToAlignTo.b]) / 2f;
            Vector2 f2_center = (uvs[edgeToBeAligned.a] + uvs[edgeToBeAligned.b]) / 2f;

            Vector2 diff = f1_center - f2_center;

            /**
             * Move f2 face to where it's matching edge center is on top of f1's center
             */
            foreach (int i in faceToMove.distinctIndexesInternal)
            {
                uvs[i] += diff;
            }

            /**
             * Now that the edge's centers are matching, rotate f2 to match f1's angle
             */
            Vector2 angle1 = uvs[matchY[0]] - uvs[matchX[0]];
            Vector2 angle2 = uvs[matchY[1]] - uvs[matchX[1]];

            float angle = Vector2.Angle(angle1, angle2);

            if (Vector3.Cross(angle1, angle2).z < 0)
            {
                angle = 360f - angle;
            }

            foreach (int i in faceToMove.distinctIndexesInternal)
            {
                uvs[i] = Math.RotateAroundPoint(uvs[i], f1_center, angle);
            }

            float error = Mathf.Abs(Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]])) + Mathf.Abs(Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]));

            // now check that the matched UVs are on top of one another if the error allowance is greater than some small value
            if (error > .02f)
            {
                // first try rotating 180 degrees
                foreach (int i in faceToMove.distinctIndexesInternal)
                {
                    uvs[i] = Math.RotateAroundPoint(uvs[i], f1_center, 180f);
                }

                float e2 = Mathf.Abs(Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]])) + Mathf.Abs(Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]));
                if (e2 < error)
                {
                    error = e2;
                }
                else
                {
                    // flip 'em back around
                    foreach (int i in faceToMove.distinctIndexesInternal)
                    {
                        uvs[i] = Math.RotateAroundPoint(uvs[i], f1_center, 180f);
                    }
                }
            }

            // If successfully aligned, merge the sharedIndexesUV
            SplitUVs(mesh, faceToMove.distinctIndexesInternal);

            mesh.SetTexturesCoincident(matchX);
            mesh.SetTexturesCoincident(matchY);
            ApplyUVs(mesh, uvs, channel);

            return(true);
        }
Ejemplo n.º 23
0
 internal override MeshAndElementSelection GetElementSelection(ProBuilderMesh mesh, PivotPoint pivot)
 {
     return(new TranslateTextureSelection(mesh, pivot));
 }
Ejemplo n.º 24
0
        /// <inheritdoc/>
        public override Bounds RebuildMesh(ProBuilderMesh mesh, Vector3 size, Quaternion rotation)
        {
            var upDir      = Vector3.Scale(rotation * Vector3.up, size);
            var rightDir   = Vector3.Scale(rotation * Vector3.right, size);
            var forwardDir = Vector3.Scale(rotation * Vector3.forward, size);

            var xRadius = rightDir.magnitude / 2f;
            var yRadius = upDir.magnitude;
            var depth   = forwardDir.magnitude / 2f;

            var radialCuts  = m_NumberOfSides + 1;
            var angle       = m_ArchDegrees;
            var templateOut = new Vector2[radialCuts];
            var templateIn  = new Vector2[radialCuts];

            if (angle < 90f)
            {
                xRadius *= 2f;
            }
            else if (angle < 180f)
            {
                xRadius *= 1f + Mathf.Lerp(1f, 0f, Mathf.Abs(Mathf.Cos(angle * Mathf.Deg2Rad)));
            }
            else if (angle > 180f)
            {
                yRadius /= 1f + Mathf.Lerp(0f, 1f, (angle - 180f) / 90f);
            }

            for (int i = 0; i < radialCuts; i++)
            {
                var     currentAngle = i * (angle / (radialCuts - 1));
                Vector2 tangent;
                templateOut[i] = Math.PointInEllipseCircumference(xRadius, yRadius, currentAngle, Vector2.zero, out tangent);
                templateIn[i]  = Math.PointInEllipseCircumference(xRadius - m_Thickness, yRadius - m_Thickness, currentAngle, Vector2.zero, out tangent);
            }

            List <Vector3> v = new List <Vector3>();

            Vector2 tmp, tmp2, tmp3, tmp4;

            float y = -depth;
            int   smoothedFaceCount = 0;

            for (int n = 0; n < radialCuts - 1; n++)
            {
                // outside faces
                tmp  = templateOut[n];
                tmp2 = n < (radialCuts - 1) ? templateOut[n + 1] : templateOut[n];

                Vector3[] qvo = GetFace(tmp, tmp2, -depth);

                // inside faces
                tmp  = templateIn[n];
                tmp2 = n < (radialCuts - 1) ? templateIn[n + 1] : templateIn[n];

                Vector3[] qvi = GetFace(tmp2, tmp, -depth);

                // left side bottom face
                if (angle < 360f && m_EndCaps)
                {
                    if (n == 0)
                    {
                        v.AddRange(GetFace(templateOut[n], templateIn[n], depth));
                    }
                }

                v.AddRange(qvo);
                v.AddRange(qvi);
                smoothedFaceCount += 2;

                if (angle < 360f && m_EndCaps)
                {
                    // right side bottom face
                    if (n == radialCuts - 2)
                    {
                        v.AddRange(GetFace(templateIn[n + 1], templateOut[n + 1], depth));
                    }
                }
            }

            // build front and back faces
            for (int i = 0; i < radialCuts - 1; i++)
            {
                tmp  = templateOut[i];
                tmp2 = (i < radialCuts - 1) ? templateOut[i + 1] : templateOut[i];
                tmp3 = templateIn[i];
                tmp4 = (i < radialCuts - 1) ? templateIn[i + 1] : templateIn[i];

                // front
                Vector3[] tpb = new Vector3[4]
                {
                    new Vector3(tmp.x, tmp.y, depth),
                    new Vector3(tmp2.x, tmp2.y, depth),
                    new Vector3(tmp3.x, tmp3.y, depth),
                    new Vector3(tmp4.x, tmp4.y, depth),
                };

                // back
                Vector3[] tpt = new Vector3[4]
                {
                    new Vector3(tmp2.x, tmp2.y, y),
                    new Vector3(tmp.x, tmp.y, y),
                    new Vector3(tmp4.x, tmp4.y, y),
                    new Vector3(tmp3.x, tmp3.y, y)
                };

                v.AddRange(tpb);
                v.AddRange(tpt);
            }

            var sizeSigns = Math.Sign(size);

            for (int i = 0; i < v.Count; i++)
            {
                v[i] = Vector3.Scale(rotation * v[i], sizeSigns);
            }

            mesh.GeometryWithPoints(v.ToArray());

            if (m_Smooth)
            {
                for (int i = (angle < 360f && m_EndCaps) ? 1 : 0; i < smoothedFaceCount; i++)
                {
                    mesh.facesInternal[i].smoothingGroup = 1;
                }
            }

            var sizeSign = sizeSigns.x * sizeSigns.y * sizeSigns.z;

            if (sizeSign < 0)
            {
                var faces = mesh.facesInternal;
                foreach (var face in faces)
                {
                    face.Reverse();
                }
            }

            mesh.TranslateVerticesInWorldSpace(mesh.mesh.triangles, mesh.transform.TransformDirection(-mesh.mesh.bounds.center));
            mesh.Refresh();

            return(UpdateBounds(mesh, size, rotation, new Bounds()));
        }
Ejemplo n.º 25
0
 /// <summary>
 /// Returns the [winding order](../manual/gloss.html#winding) for a face.
 /// </summary>
 /// <param name="mesh">The source mesh.</param>
 /// <param name="face">The face to test.</param>
 /// <returns>The winding order if successful; <see cref="WindingOrder.Unknown"/> if not.</returns>
 public static WindingOrder GetWindingOrder(this ProBuilderMesh mesh, Face face)
 {
     Vector2[] p = Projection.PlanarProject(mesh.positionsInternal, face.distinctIndexesInternal);
     return(GetWindingOrder(p));
 }
Ejemplo n.º 26
0
 public abstract Bounds RebuildMesh(ProBuilderMesh mesh, Vector3 size, Quaternion rotation);
Ejemplo n.º 27
0
        private static void ImportMesh(ProBuilderMesh mesh, Vector2 uvScale)
        {
            MeshFilter filter = mesh.GetComponent <MeshFilter>();

            ImportMesh(filter, mesh, uvScale);
        }
Ejemplo n.º 28
0
 public virtual Bounds UpdateBounds(ProBuilderMesh mesh, Vector3 size, Quaternion rotation, Bounds bounds)
 {
     return(mesh.mesh.bounds);
 }
Ejemplo n.º 29
0
        /// <summary>
        /// Merge a collection of <see cref="ProBuilderMesh"/> objects to as few meshes as possible. It will re-use the meshTarget object as the first
        /// destination for the first <see cref="ProBuilderMesh.maxVertexCount"/> -1 vertices. If the sum of vertices is above <see cref="ProBuilderMesh.maxVertexCount"/> - 1
        /// it will generate new meshes unless there is a single mesh left in which case it will append it to the return list.
        /// </summary>
        /// <param name="meshes">A collection of meshes to be merged. Note: it is expected that meshes includes meshTarget.</param>
        /// <param name="meshTarget">A mesh which will be used as the starting point for merging and which will be kept as reference/target.</param>
        /// <returns>
        /// A list of merged meshes. In most cases this will be a single mesh corresponding to meshTarget. However it can be multiple in cases
        /// where the resulting vertex count exceeds the maximum allowable value.
        /// </returns>
        public static List <ProBuilderMesh> Combine(IEnumerable <ProBuilderMesh> meshes, ProBuilderMesh meshTarget)
        {
            if (meshes == null)
            {
                throw new ArgumentNullException("meshes");
            }

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

            if (!meshes.Any() || meshes.Count() < 2)
            {
                return(null);
            }

            if (!meshes.Contains(meshTarget))
            {
                return(null);
            }

            var vertices        = new List <Vertex>(meshTarget.GetVertices());
            var faces           = new List <Face>(meshTarget.facesInternal);
            var sharedVertices  = new List <SharedVertex>(meshTarget.sharedVertices);
            var sharedTextures  = new List <SharedVertex>(meshTarget.sharedTextures);
            int offset          = meshTarget.vertexCount;
            var materialMap     = new List <Material>(meshTarget.renderer.sharedMaterials);
            var targetTransform = meshTarget.transform;

            var firstMeshContributors     = new List <ProBuilderMesh>();
            var remainderMeshContributors = new List <ProBuilderMesh>();

            var currentMeshVertexCount = offset;

            foreach (var mesh in meshes)
            {
                if (mesh != meshTarget)
                {
                    if (currentMeshVertexCount + mesh.vertexCount < ProBuilderMesh.maxVertexCount)
                    {
                        currentMeshVertexCount += mesh.vertexCount;
                        firstMeshContributors.Add(mesh);
                    }
                    else
                    {
                        remainderMeshContributors.Add(mesh);
                    }
                }
            }

            var autoUvFaces = new List <Face>();

            AccumulateMeshesInfo(
                firstMeshContributors,
                offset,
                ref vertices,
                ref faces,
                ref autoUvFaces,
                ref sharedVertices,
                ref sharedTextures,
                ref materialMap,
                targetTransform
                );

            meshTarget.SetVertices(vertices);
            meshTarget.faces          = faces;
            meshTarget.sharedVertices = sharedVertices;
            meshTarget.sharedVertices = sharedTextures != null?sharedTextures.ToArray() : null;

            meshTarget.renderer.sharedMaterials = materialMap.ToArray();
            meshTarget.ToMesh();
            meshTarget.Refresh();
            UVEditing.SetAutoAndAlignUnwrapParamsToUVs(meshTarget, autoUvFaces);

            var returnedMesh = new List <ProBuilderMesh>()
            {
                meshTarget
            };

            if (remainderMeshContributors.Count > 1)
            {
                var newMeshes = CombineToNewMeshes(remainderMeshContributors);
                foreach (var mesh in newMeshes)
                {
                    returnedMesh.Add(mesh);
                }
            }
            else if (remainderMeshContributors.Count == 1)
            {
                returnedMesh.Add(remainderMeshContributors[0]);
            }

            return(returnedMesh);
        }
Ejemplo n.º 30
0
        /// <summary>
        /// Append a new face to the ProBuilderMesh.
        /// </summary>
        /// <param name="mesh">The mesh target.</param>
        /// <param name="positions">The new vertex positions to add.</param>
        /// <param name="colors">The new colors to add (must match positions length).</param>
        /// <param name="uvs">The new uvs to add (must match positions length).</param>
        /// <param name="face">A face with the new triangle indexes. The indexes should be 0 indexed.</param>
        /// <param name="common"></param>
        /// <returns>The new face as referenced on the mesh.</returns>
        internal static Face AppendFace(this ProBuilderMesh mesh, Vector3[] positions, Color[] colors, Vector2[] uvs, Face face, int[] common)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

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

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

            int faceVertexCount = positions.Length;

            if (common == null)
            {
                common = new int[faceVertexCount];
                for (int i = 0; i < faceVertexCount; i++)
                {
                    common[i] = -1;
                }
            }

            int vertexCount = mesh.vertexCount;

            var mc = mesh.HasArrays(MeshArrays.Color);
            var fc = colors != null;
            var mt = mesh.HasArrays(MeshArrays.Texture0);
            var ft = uvs != null;

            Vector3[] newPositions = new Vector3[vertexCount + faceVertexCount];
            Color[]   newColors    = (mc || fc) ? new Color[vertexCount + faceVertexCount] : null;
            Vector2[] newTextures  = (mt || ft) ? new Vector2[vertexCount + faceVertexCount] : null;

            List <Face> faces = new List <Face>(mesh.facesInternal);

            Array.Copy(mesh.positionsInternal, 0, newPositions, 0, vertexCount);
            Array.Copy(positions, 0, newPositions, vertexCount, faceVertexCount);

            if (mc || fc)
            {
                Array.Copy(mc ? mesh.colorsInternal : ArrayUtility.Fill(Color.white, vertexCount), 0, newColors, 0, vertexCount);
                Array.Copy(fc ? colors : ArrayUtility.Fill(Color.white, faceVertexCount), 0, newColors, vertexCount, colors.Length);
            }

            if (mt || ft)
            {
                Array.Copy(mt ? mesh.texturesInternal : ArrayUtility.Fill(Vector2.zero, vertexCount), 0, newTextures, 0, vertexCount);
                Array.Copy(ft ? uvs : ArrayUtility.Fill(Vector2.zero, faceVertexCount), 0, newTextures, mesh.texturesInternal.Length, faceVertexCount);
            }

            face.ShiftIndexesToZero();
            face.ShiftIndexes(vertexCount);

            faces.Add(face);

            for (int i = 0; i < common.Length; i++)
            {
                if (common[i] < 0)
                {
                    mesh.AddSharedVertex(new SharedVertex(new int[] { i + vertexCount }));
                }
                else
                {
                    mesh.AddToSharedVertex(common[i], i + vertexCount);
                }
            }

            mesh.positions = newPositions;
            mesh.colors    = newColors;
            mesh.textures  = newTextures;
            mesh.faces     = faces;

            return(face);
        }