using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.ProBuilder; namespace UnityEditor.ProBuilder { /// /// Represents the state of a ProBuilderMesh and it's selected elements. /// abstract class MeshAndElementSelection { ProBuilderMesh m_Mesh; List m_ElementGroups; public ProBuilderMesh mesh { get { return m_Mesh; } } public List elementGroups { get { return m_ElementGroups; } } public MeshAndElementSelection(ProBuilderMesh mesh, PivotPoint pivot, HandleOrientation orientation, bool collectCoincidentIndices) { m_Mesh = mesh; m_ElementGroups = ElementGroup.GetElementGroups(mesh, pivot, orientation, collectCoincidentIndices); } } class ElementGroup { List m_Indices; Vector3 m_Position; Quaternion m_Rotation; /// /// Center of this selection in world space. /// public Vector3 position { get { return m_Position; } } /// /// Rotation of this selection in world space. /// public Quaternion rotation { get { return m_Rotation; } } public IEnumerable indices { get { return m_Indices; } } public ElementGroup(List indices, Vector3 position, Quaternion rotation) { m_Indices = indices; m_Position = position; m_Rotation = rotation; } internal static List GetSelectedIndicesForSelectMode(ProBuilderMesh mesh, SelectMode mode, bool collectCoincident) { if (mode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace)) { List indices = new List(); if (collectCoincident) mesh.GetCoincidentVertices(mesh.selectedFacesInternal, indices); else Face.GetDistinctIndices(mesh.selectedFacesInternal, indices); return indices; } else if(mode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge)) { List indices = new List(); if (collectCoincident) mesh.GetCoincidentVertices(mesh.selectedEdgesInternal, indices); else Edge.GetIndices(mesh.selectedEdgesInternal, indices); return indices; } return collectCoincident ? mesh.GetCoincidentVertices(mesh.selectedIndexesInternal) : new List(mesh.selectedIndexesInternal); } public static List GetElementGroups(ProBuilderMesh mesh, PivotPoint pivot, HandleOrientation orientation, bool collectCoincident) { var groups = new List(); 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 indices; if (collectCoincident) { indices = new List(); 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 indices; if (collectCoincident) { indices = new List(); 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> GetFaceSelectionGroups(ProBuilderMesh mesh) { var wings = WingedEdge.GetWingedEdges(mesh, mesh.selectedFacesInternal, true); var filter = new HashSet(); var groups = new List>(); foreach (var wing in wings) { var group = new List() {}; CollectAdjacentFaces(wing, filter, group); if (group.Count > 0) groups.Add(group); } return groups; } static IEnumerable> GetVertexSelectionGroups(ProBuilderMesh mesh, bool collectCoincident) { if (!collectCoincident) return mesh.selectedIndexesInternal.Select(x => new List() { x }).ToList(); var shared = mesh.selectedSharedVertices; var groups = new List>(); foreach (var index in shared) { var coincident = new List(); mesh.GetCoincidentVertices(mesh.sharedVerticesInternal[index][0], coincident); groups.Add(coincident); } return groups; } static IEnumerable> GetEdgeSelectionGroups(ProBuilderMesh mesh) { var edges = EdgeLookup.GetEdgeLookup(mesh.selectedEdgesInternal, mesh.sharedVertexLookup); var groups = new List, List>>(); 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, List>( new HashSet() { edge.common.a, edge.common.b }, new List() { edge.local })); } // collect overlapping groups (happens in cases where selection order begins as two separate groups but // becomes one) var res = new List>(); var overlap = new HashSet(); for(int i = 0, c = groups.Count; i < c; i++) { if (overlap.Contains(i)) continue; List 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; } static void CollectAdjacentFaces(WingedEdge wing, HashSet filter, List group) { if (!filter.Add(wing.face)) return; group.Add(wing.face); var enumerator = new WingedEdgeEnumerator(wing); while (enumerator.MoveNext()) { var opposite = enumerator.Current.opposite; if (opposite == null) continue; CollectAdjacentFaces(opposite, filter, group); } } } }