diff --git a/com.unity.probuilder.tests/Tests/Framework/TestUtility.cs b/com.unity.probuilder.tests/Tests/Framework/TestUtility.cs index 1c6008367..e032ea717 100644 --- a/com.unity.probuilder.tests/Tests/Framework/TestUtility.cs +++ b/com.unity.probuilder.tests/Tests/Framework/TestUtility.cs @@ -12,14 +12,16 @@ namespace UnityEngine.ProBuilder.Tests.Framework { - abstract class TemporaryAssetTest : IPrebuildSetup, IPostBuildCleanup + abstract class TemporaryAssetTest { + [SetUp] public void Setup() { if (!Directory.Exists(TestUtility.TemporarySavedAssetsDirectory)) Directory.CreateDirectory(TestUtility.TemporarySavedAssetsDirectory); } + [TearDown] public void Cleanup() { if (Directory.Exists(TestUtility.TemporarySavedAssetsDirectory)) diff --git a/com.unity.probuilder/CHANGELOG.md b/com.unity.probuilder/CHANGELOG.md index 6eea85945..d49cc792e 100644 --- a/com.unity.probuilder/CHANGELOG.md +++ b/com.unity.probuilder/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [4.0.0-preview.27] - 2018-10-09 +## [4.0.0-preview.28] - 2018-11-05 ### Features @@ -70,11 +70,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Include third party dependencies as source code with assembly definitions instead of pre-compiled DLLs. - Performance optimization for selection changes in editor. -### Changes since 4.0.0-preview.26 +### Changes since 4.0.0-preview.27 -- Fix lightmap unit test instabilities. -- Refactor settings code to be more modular. -- Remove hard-coded `#define PROGRIDS_ENABLED` from ProGrids unit tests. +- Add support for `Pivot` and `Center` handle position toggle. +- Handles now support operating in selection space (Position: Pivot + Orientation: Normal). +- Texture scene tool now supports vertices and edges. +- Improve performance of mesh rebuild functions. +- Improve performance of vertex, edge, and face gizmos. +- Make auto-resizing colliders opt-in instead of on by default. +- Fix tests sometimes not creating temporary directories. +- Fix occasional null reference error in cases where window layout is reloaded. ## [3.0.8] - 2018-05-07 diff --git a/com.unity.probuilder/Content/Shader/FaceHighlight.shader b/com.unity.probuilder/Content/Shader/FaceHighlight.shader index cf2c43aa9..332e4aac0 100644 --- a/com.unity.probuilder/Content/Shader/FaceHighlight.shader +++ b/com.unity.probuilder/Content/Shader/FaceHighlight.shader @@ -4,13 +4,14 @@ Shader "Hidden/ProBuilder/FaceHighlight" { _Color ("Color Tint", Color) = (1,1,1,1) _Dither ("Dithering", float) = 0 + _HandleZTest ("_HandleZTest", Int) = 8 } SubShader { Tags { "IgnoreProjector"="True" "RenderType"="Geometry" } Lighting Off - ZTest LEqual + ZTest [_HandleZTest] ZWrite On Cull Off Blend Off diff --git a/com.unity.probuilder/Content/Shader/LineBillboard.shader b/com.unity.probuilder/Content/Shader/LineBillboard.shader index 3c62e3bae..ca04b06ef 100644 --- a/com.unity.probuilder/Content/Shader/LineBillboard.shader +++ b/com.unity.probuilder/Content/Shader/LineBillboard.shader @@ -4,6 +4,7 @@ { _Color ("Color", Color) = (1,1,1,1) _Scale ("Scale", Range(0, 20)) = 7 + _HandleZTest ("_HandleZTest", Int) = 8 } SubShader @@ -19,7 +20,7 @@ } Lighting Off - ZTest LEqual + ZTest [_HandleZTest] ZWrite On Cull Off Blend Off diff --git a/com.unity.probuilder/Debug/Editor/TempMenuItems.cs b/com.unity.probuilder/Debug/Editor/TempMenuItems.cs index 12a7f8339..a0784fb23 100644 --- a/com.unity.probuilder/Debug/Editor/TempMenuItems.cs +++ b/com.unity.probuilder/Debug/Editor/TempMenuItems.cs @@ -10,13 +10,6 @@ class TempMenuItems : EditorWindow [MenuItem("Tools/Temp Menu Item &d", false, 1000)] static void MenuInit() { - string[] search = new[] - { - "Assets/Temp.cs", - "Assets/*.cs", - "*.cs" - }; - } diff --git a/com.unity.probuilder/Editor/EditorCore/BooleanEditor.cs b/com.unity.probuilder/Editor/EditorCore/BooleanEditor.cs index 9e5c28bb9..dec6afaa9 100644 --- a/com.unity.probuilder/Editor/EditorCore/BooleanEditor.cs +++ b/com.unity.probuilder/Editor/EditorCore/BooleanEditor.cs @@ -1,5 +1,7 @@ using UnityEngine; using UnityEngine.ProBuilder; +using UnityEngine.ProBuilder.Experimental.CSG; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder { @@ -155,15 +157,15 @@ void OnGUI() switch(operation) { case BooleanOp.Union: - MenuCommands.MenuUnion(m_LeftGameObject.GetComponent(), m_RightGameObject.GetComponent()); + MenuUnion(m_LeftGameObject.GetComponent(), m_RightGameObject.GetComponent()); break; case BooleanOp.Intersection: - MenuCommands.MenuIntersect(m_LeftGameObject.GetComponent(), m_RightGameObject.GetComponent()); + MenuIntersect(m_LeftGameObject.GetComponent(), m_RightGameObject.GetComponent()); break; case BooleanOp.Subtraction: - MenuCommands.MenuSubtract(m_LeftGameObject.GetComponent(), m_RightGameObject.GetComponent()); + MenuSubtract(m_LeftGameObject.GetComponent(), m_RightGameObject.GetComponent()); break; } } @@ -291,5 +293,77 @@ bool ListenForDragAndDrop() } return false; } + + enum BooleanOperation + { + Union, + Subtract, + Intersect + } + + static ActionResult MenuBooleanOperation(BooleanOperation operation, ProBuilderMesh lhs, ProBuilderMesh rhs) + { + if(lhs == null || rhs == null) + return new ActionResult(ActionResult.Status.Failure, "Must Select 2 Objects"); + + string op_string = operation == BooleanOperation.Union ? "Union" : (operation == BooleanOperation.Subtract ? "Subtract" : "Intersect"); + + ProBuilderMesh[] sel = new ProBuilderMesh[] { lhs, rhs }; + + UndoUtility.RecordSelection(sel, op_string); + + Mesh c; + + switch(operation) + { + case BooleanOperation.Union: + c = CSG.Union(lhs.gameObject, rhs.gameObject); + break; + + case BooleanOperation.Subtract: + c = CSG.Subtract(lhs.gameObject, rhs.gameObject); + break; + + default: + c = CSG.Intersect(lhs.gameObject, rhs.gameObject); + break; + } + + GameObject go = new GameObject(); + + go.AddComponent().sharedMaterial = EditorUtility.GetUserMaterial(); + go.AddComponent().sharedMesh = c; + + ProBuilderMesh pb = InternalMeshUtility.CreateMeshWithTransform(go.transform, false); + DestroyImmediate(go); + + Selection.objects = new Object[] { pb.gameObject }; + + return new ActionResult(ActionResult.Status.Success, op_string); + } + + /** + * Union operation between two ProBuilder objects. + */ + public static ActionResult MenuUnion(ProBuilderMesh lhs, ProBuilderMesh rhs) + { + return MenuBooleanOperation(BooleanOperation.Union, lhs, rhs); + } + + /** + * Subtract boolean operation between two pb_Objects. + */ + public static ActionResult MenuSubtract(ProBuilderMesh lhs, ProBuilderMesh rhs) + { + return MenuBooleanOperation(BooleanOperation.Subtract, lhs, rhs); + } + + /** + * Intersect boolean operation between two pb_Objects. + */ + public static ActionResult MenuIntersect(ProBuilderMesh lhs, ProBuilderMesh rhs) + { + return MenuBooleanOperation(BooleanOperation.Intersect, lhs, rhs); + } } } diff --git a/com.unity.probuilder/Editor/EditorCore/EditorEnum.cs b/com.unity.probuilder/Editor/EditorCore/EditorEnum.cs index b12f54891..542d42198 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorEnum.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorEnum.cs @@ -16,29 +16,6 @@ enum SceneToolbarLocation BottomRight } - /// - /// How the handle gizmo is oriented with regards to the current element selection. - /// - /// - /// This overrides the Unity Pivot / Global setting when editing vertices, faces, or edges. - /// - /// Editor only. - public enum HandleOrientation - { - /// - /// The gizmo is aligned in world space. - /// - World = 0, - /// - /// The gizmo is aligned relative to the mesh transform. Also called coordinate or model space. - /// - Local = 1, - /// - /// The gizmo is aligned relative to the currently selected face. When editing vertices or edges, this falls back to alignment. - /// - Normal = 2 - } - /// /// When drag selecting mesh elements, this defines how the Shift key will modify the selection. /// diff --git a/com.unity.probuilder/Editor/EditorCore/EditorGUIUtility.cs b/com.unity.probuilder/Editor/EditorCore/EditorGUIUtility.cs index fcca88f47..accec5f26 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorGUIUtility.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorGUIUtility.cs @@ -13,6 +13,43 @@ namespace UnityEditor.ProBuilder.UI /// static class EditorGUIUtility { + static class Styles + { + static bool s_Initialized; + + public static GUIStyle command = "command"; + public static GUIContent[] selectModeIcons; + + public static void Init() + { + if (s_Initialized) + return; + + s_Initialized = true; + + var object_Graphic_off = IconUtility.GetIcon("Modes/Mode_Object"); + var face_Graphic_off = IconUtility.GetIcon("Modes/Mode_Face"); + var vertex_Graphic_off = IconUtility.GetIcon("Modes/Mode_Vertex"); + var edge_Graphic_off = IconUtility.GetIcon("Modes/Mode_Edge"); + + selectModeIcons = new GUIContent[] + { + object_Graphic_off != null + ? new GUIContent(object_Graphic_off, "Object Selection") + : new GUIContent("OBJ", "Object Selection"), + vertex_Graphic_off != null + ? new GUIContent(vertex_Graphic_off, "Vertex Selection") + : new GUIContent("VRT", "Vertex Selection"), + edge_Graphic_off != null + ? new GUIContent(edge_Graphic_off, "Edge Selection") + : new GUIContent("EDG", "Edge Selection"), + face_Graphic_off != null + ? new GUIContent(face_Graphic_off, "Face Selection") + : new GUIContent("FCE", "Face Selection"), + }; + } + } + static readonly Color TOOL_SETTINGS_COLOR = UnityEditor.EditorGUIUtility.isProSkin ? Color.green : new Color(.2f, .2f, .2f, .2f); @@ -379,5 +416,54 @@ public static void SceneLabel(string text, Vector2 position) GUI.Label(sceneLabelRect, gc, sceneBoldLabel); } + + public static SelectMode DoElementModeToolbar(Rect rect, SelectMode mode) + { + Styles.Init(); + + EditorGUI.BeginChangeCheck(); + + var textureMode = mode.ContainsFlag(SelectMode.TextureVertex | SelectMode.TextureEdge | SelectMode.TextureFace); + + int currentSelectionMode = -1; + + switch (mode) + { + case SelectMode.Object: + currentSelectionMode = 0; + break; + case SelectMode.Vertex: + case SelectMode.TextureVertex: + currentSelectionMode = 1; + break; + case SelectMode.Edge: + case SelectMode.TextureEdge: + currentSelectionMode = 2; + break; + case SelectMode.Face: + case SelectMode.TextureFace: + currentSelectionMode = 3; + break; + default: + currentSelectionMode = -1; + break; + } + + currentSelectionMode = GUI.Toolbar(rect, currentSelectionMode, Styles.selectModeIcons, Styles.command); + + if (EditorGUI.EndChangeCheck()) + { + if (currentSelectionMode == 0) + mode = SelectMode.Object; + else if (currentSelectionMode == 1) + mode = textureMode ? SelectMode.TextureVertex : SelectMode.Vertex; + else if (currentSelectionMode == 2) + mode = textureMode ? SelectMode.TextureEdge : SelectMode.Edge; + else if (currentSelectionMode == 3) + mode = textureMode ? SelectMode.TextureFace : SelectMode.Face; + } + + return mode; + } } } diff --git a/com.unity.probuilder/Editor/EditorCore/EditorHandleUtility.cs b/com.unity.probuilder/Editor/EditorCore/EditorHandleUtility.cs index d50e68b40..08d77d9c2 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorHandleUtility.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorHandleUtility.cs @@ -14,6 +14,8 @@ namespace UnityEditor.ProBuilder /// static class EditorHandleUtility { + static Stack s_HandleMatrix = new Stack(); + public static bool SceneViewInUse(Event e) { return e.alt @@ -521,5 +523,50 @@ public static Vector3 ClosestPointToPolyLine(List vertices, out int ind return trs != null ? trs.InverseTransformPoint(p) : p; } + + internal static HandleOrientation ProBuilderHandleOrientation(this PivotRotation rotation) + { + if (rotation == PivotRotation.Global) + return HandleOrientation.World; + + if (rotation == PivotRotation.Local) + return HandleOrientation.Local; + + return HandleOrientation.Normal; + } + + internal static PivotRotation UnityPivotRotation(this HandleOrientation orientation) + { + if (orientation == HandleOrientation.World) + return PivotRotation.Global; + + return PivotRotation.Local; + } + + internal static PivotMode UnityPivot(this PivotPoint pivotPoint) + { + if (pivotPoint == PivotPoint.WorldBoundingBoxCenter) + return PivotMode.Center; + + return PivotMode.Pivot; + } + + internal static PivotPoint ProBuilderPivot(this PivotMode pivotMode) + { + if (pivotMode == PivotMode.Center) + return PivotPoint.WorldBoundingBoxCenter; + + return PivotPoint.ModelBoundingBoxCenter; + } + + internal static void PushMatrix() + { + s_HandleMatrix.Push(Handles.matrix); + } + + internal static void PopMatrix() + { + Handles.matrix = s_HandleMatrix.Pop(); + } } } diff --git a/com.unity.probuilder/Editor/EditorCore/EditorMeshHandles.cs b/com.unity.probuilder/Editor/EditorCore/EditorMeshHandles.cs index f1bd2993f..fbe65ba4f 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorMeshHandles.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorMeshHandles.cs @@ -9,7 +9,7 @@ namespace UnityEditor.ProBuilder { - class EditorMeshHandles : IDisposable, IHasPreferences + partial class EditorMeshHandles : IDisposable, IHasPreferences { const HideFlags k_MeshHideFlags = (HideFlags) (1 | 2 | 4 | 8); @@ -52,6 +52,9 @@ class EditorMeshHandles : IDisposable, IHasPreferences [UserSetting] static Pref s_VertexPointSize = new Pref("graphics.vertexPointSize", 3f, SettingsScope.User); + [UserSetting("Graphics", "Handle Z Test", "The compare function to use when drawing mesh handles.")] + static Pref s_HandleCompareFunction = new Pref("graphics.handleZTest", CompareFunction.Always, SettingsScope.User); + [UserSettingBlock("Graphics")] static void HandleColorPreferences(string searchContext) { @@ -100,7 +103,6 @@ static void HandleColorPreferences(string searchContext) static Color s_VertexUnselectedColor; Material m_EdgeMaterial; - Material m_FaceMaterial; Material m_VertMaterial; Material m_WireMaterial; @@ -141,6 +143,8 @@ public static Color vertexUnselectedColor public EditorMeshHandles() { + Init(); + m_MeshPool = new ObjectPool( 0, 8, CreateMesh, DestroyMesh); m_WireHandles = new Dictionary(); m_VertexHandles = new Dictionary(); @@ -154,7 +158,6 @@ public EditorMeshHandles() m_EdgeMaterial = CreateMaterial(Shader.Find(lineShader), "ProBuilder::LineMaterial"); m_WireMaterial = CreateMaterial(Shader.Find(lineShader), "ProBuilder::WireMaterial"); m_VertMaterial = CreateMaterial(Shader.Find(vertShader), "ProBuilder::VertexMaterial"); - m_FaceMaterial = CreateMaterial(Shader.Find(BuiltinMaterials.faceShader), "ProBuilder::FaceMaterial"); ReloadPreferences(); } @@ -173,7 +176,6 @@ public void Dispose() UObject.DestroyImmediate(m_EdgeMaterial); UObject.DestroyImmediate(m_WireMaterial); UObject.DestroyImmediate(m_VertMaterial); - UObject.DestroyImmediate(m_FaceMaterial); } public void ReloadPreferences() @@ -207,10 +209,13 @@ public void ReloadPreferences() } m_WireMaterial.SetColor("_Color", s_WireframeColor); - m_FaceMaterial.SetFloat("_Dither", (s_UseUnityColors || s_DitherFaceHandle) ? 1f : 0f); + s_FaceMaterial.SetFloat("_Dither", (s_UseUnityColors || s_DitherFaceHandle) ? 1f : 0f); m_VertMaterial.SetFloat("_Scale", s_VertexPointSize * EditorGUIUtility.pixelsPerPoint); + m_WireMaterial.SetInt("_HandleZTest", (int) CompareFunction.LessEqual); + m_EdgeMaterial.SetInt("_HandleZTest", (int) CompareFunction.LessEqual); + if (BuiltinMaterials.geometryShadersSupported) { m_WireMaterial.SetFloat("_Scale", s_WireframeLineSize * EditorGUIUtility.pixelsPerPoint); @@ -249,46 +254,6 @@ void DestroyMesh(Mesh mesh) CompareFunction.Always }; - internal bool BeginDrawingLines(CompareFunction zTest) - { - if (Event.current.type != EventType.Repaint) - return false; - - if (!BuiltinMaterials.geometryShadersSupported || - !m_EdgeMaterial.SetPass(0)) - { - if (s_ApplyWireMaterial == null) - { - s_ApplyWireMaterial = typeof(HandleUtility).GetMethod( - "ApplyWireMaterial", - BindingFlags.Static | BindingFlags.NonPublic, - null, - new System.Type[] { typeof(CompareFunction) }, - null); - - if (s_ApplyWireMaterial == null) - { - Log.Info("Failed to find wire material, stopping draw lines."); - return false; - } - } - - s_ApplyWireMaterialArgs[0] = zTest; - s_ApplyWireMaterial.Invoke(null, s_ApplyWireMaterialArgs); - } - - GL.PushMatrix(); - GL.Begin(GL.LINES); - - return true; - } - - internal void EndDrawingLines() - { - GL.End(); - GL.PopMatrix(); - } - internal void DrawSceneSelection(SceneSelection selection) { var mesh = selection.mesh; @@ -301,38 +266,28 @@ internal void DrawSceneSelection(SceneSelection selection) // Draw nearest edge if (selection.face != null) { - m_FaceMaterial.SetColor("_Color", preselectionColor); - - if (!m_FaceMaterial.SetPass(0)) - return; - - GL.PushMatrix(); - GL.Begin(GL.TRIANGLES); - GL.MultMatrix(mesh.transform.localToWorldMatrix); + using (new TriangleDrawingScope(preselectionColor, s_HandleCompareFunction)) + { + GL.MultMatrix(mesh.transform.localToWorldMatrix); - var face = selection.face; - var ind = face.indexes; + var face = selection.face; + var ind = face.indexes; - for (int i = 0, c = ind.Count; i < c; i += 3) - { - GL.Vertex(positions[ind[i]]); - GL.Vertex(positions[ind[i+1]]); - GL.Vertex(positions[ind[i+2]]); + for (int i = 0, c = ind.Count; i < c; i += 3) + { + GL.Vertex(positions[ind[i]]); + GL.Vertex(positions[ind[i+1]]); + GL.Vertex(positions[ind[i+2]]); + } } - - GL.End(); - GL.PopMatrix(); } else if (selection.edge != Edge.Empty) { - m_EdgeMaterial.SetColor("_Color", preselectionColor); - - if (BeginDrawingLines(Handles.zTest)) + using (new LineDrawingScope(preselectionColor, -1f, s_HandleCompareFunction)) { GL.MultMatrix(mesh.transform.localToWorldMatrix); GL.Vertex(positions[selection.edge.a]); GL.Vertex(positions[selection.edge.b]); - EndDrawingLines(); } } else if (selection.vertex > -1) @@ -355,6 +310,7 @@ public void DrawSceneHandles(SelectMode mode) switch (mode) { case SelectMode.Edge: + case SelectMode.TextureEdge: { // render wireframe with edge material in edge mode so that the size change is reflected RenderWithColor(m_WireHandles, m_EdgeMaterial, s_EdgeUnselectedColor); @@ -362,12 +318,14 @@ public void DrawSceneHandles(SelectMode mode) break; } case SelectMode.Face: + case SelectMode.TextureFace: { RenderWithColor(m_WireHandles, m_WireMaterial, s_WireframeColor); - RenderWithColor(m_SelectedFaceHandles, m_FaceMaterial, s_FaceSelectedColor); + RenderWithColor(m_SelectedFaceHandles, s_FaceMaterial, s_FaceSelectedColor); break; } case SelectMode.Vertex: + case SelectMode.TextureVertex: { RenderWithColor(m_WireHandles, m_WireMaterial, s_WireframeColor); RenderWithColor(m_VertexHandles, m_VertMaterial, s_VertexUnselectedColor); @@ -412,6 +370,7 @@ public void RebuildSelectedHandles(IEnumerable meshes, SelectMod switch (selectionMode) { case SelectMode.Vertex: + case SelectMode.TextureVertex: { foreach (var handle in m_VertexHandles) handle.Value.mesh.vertices = handle.Key.positionsInternal; @@ -421,6 +380,7 @@ public void RebuildSelectedHandles(IEnumerable meshes, SelectMod } case SelectMode.Edge: + case SelectMode.TextureEdge: { foreach (var handle in m_SelectedEdgeHandles) handle.Value.mesh.vertices = handle.Key.positionsInternal; @@ -428,6 +388,7 @@ public void RebuildSelectedHandles(IEnumerable meshes, SelectMod } case SelectMode.Face: + case SelectMode.TextureFace: { foreach (var handle in m_SelectedFaceHandles) handle.Value.mesh.vertices = handle.Key.positionsInternal; @@ -448,6 +409,7 @@ public void RebuildSelectedHandles(IEnumerable meshes, SelectMod switch (selectionMode) { case SelectMode.Vertex: + case SelectMode.TextureVertex: { RebuildMeshHandle(mesh, m_VertexHandles, MeshHandles.CreateVertexMesh); @@ -459,6 +421,7 @@ public void RebuildSelectedHandles(IEnumerable meshes, SelectMod } case SelectMode.Edge: + case SelectMode.TextureEdge: { RebuildMeshHandle(mesh, m_SelectedEdgeHandles, (x, y) => { @@ -468,6 +431,7 @@ public void RebuildSelectedHandles(IEnumerable meshes, SelectMod } case SelectMode.Face: + case SelectMode.TextureFace: { RebuildMeshHandle(mesh, m_SelectedFaceHandles, MeshHandles.CreateFaceMesh); break; diff --git a/com.unity.probuilder/Editor/EditorCore/EditorSceneViewPicker.cs b/com.unity.probuilder/Editor/EditorCore/EditorSceneViewPicker.cs index ff64bb9ab..7100a42be 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorSceneViewPicker.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorSceneViewPicker.cs @@ -160,9 +160,9 @@ public static ProBuilderMesh DoMouseClick(Event evt, SelectMode selectionMode, S float pickedElementDistance = Mathf.Infinity; - if (selectionMode == SelectMode.Edge) + if (selectionMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge)) pickedElementDistance = EdgeRaycast(evt.mousePosition, pickerPreferences, true, s_Selection); - else if (selectionMode == SelectMode.Vertex) + else if (selectionMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex)) pickedElementDistance = VertexRaycast(evt.mousePosition, pickerPreferences, true, s_Selection); else pickedElementDistance = FaceRaycast(evt.mousePosition, pickerPreferences, true, s_Selection, evt.clickCount > 1 ? -1 : 0, false); @@ -246,8 +246,7 @@ public static void DoMouseDrag(Rect mouseDragRect, SelectMode selectionMode, Sce rectSelectMode = scenePickerPreferences.rectSelectMode }; - var selection = MeshSelection.topInternal; - UndoUtility.RecordSelection(selection, "Drag Select"); + UndoUtility.RecordSelection("Drag Select"); bool isAppendModifier = EditorHandleUtility.IsAppendModifier(Event.current.modifiers); if (!isAppendModifier) @@ -258,11 +257,12 @@ public static void DoMouseDrag(Rect mouseDragRect, SelectMode selectionMode, Sce switch (selectionMode) { case SelectMode.Vertex: + case SelectMode.TextureVertex: { Dictionary> selected = Picking.PickVerticesInRect( SceneView.lastActiveSceneView.camera, mouseDragRect, - selection, + MeshSelection.topInternal, pickingOptions, EditorGUIUtility.pixelsPerPoint); @@ -301,7 +301,7 @@ public static void DoMouseDrag(Rect mouseDragRect, SelectMode selectionMode, Sce Dictionary> selected = Picking.PickFacesInRect( SceneView.lastActiveSceneView.camera, mouseDragRect, - selection, + MeshSelection.topInternal, pickingOptions, EditorGUIUtility.pixelsPerPoint); @@ -333,11 +333,12 @@ public static void DoMouseDrag(Rect mouseDragRect, SelectMode selectionMode, Sce } case SelectMode.Edge: + case SelectMode.TextureEdge: { var selected = Picking.PickEdgesInRect( SceneView.lastActiveSceneView.camera, mouseDragRect, - selection, + MeshSelection.topInternal, pickingOptions, EditorGUIUtility.pixelsPerPoint); @@ -390,10 +391,10 @@ internal static float MouseRayHitTest( SceneSelection selection, bool allowUnselected = false) { - if (selectionMode == SelectMode.Edge) + if (selectionMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge)) return EdgeRaycast(mousePosition, pickerOptions, allowUnselected, selection); - if (selectionMode == SelectMode.Vertex) + if (selectionMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex)) return VertexRaycast(mousePosition, pickerOptions, allowUnselected, selection); return FaceRaycast(mousePosition, pickerOptions, allowUnselected, selection, 0, true); diff --git a/com.unity.probuilder/Editor/EditorCore/EditorToolbarLoader.cs b/com.unity.probuilder/Editor/EditorCore/EditorToolbarLoader.cs index abb10bf6c..60c78ca42 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorToolbarLoader.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorToolbarLoader.cs @@ -48,7 +48,7 @@ internal static List GetActions(bool forceReload = false) new Actions.OpenSmoothingEditor(), new Actions.ToggleSelectBackFaces(), - new Actions.ToggleHandleAlignment(), + new Actions.ToggleHandleOrientation(), new Actions.ToggleDragSelectionMode(), new Actions.ToggleDragRectMode(), diff --git a/com.unity.probuilder/Editor/EditorCore/EditorToolbarMenuItems.cs b/com.unity.probuilder/Editor/EditorCore/EditorToolbarMenuItems.cs index 3f11c3e20..a5f3ba2fb 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorToolbarMenuItems.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorToolbarMenuItems.cs @@ -606,7 +606,7 @@ static void MenuDoToggleDragSelectionMode() [MenuItem(k_MenuPrefix + "Interaction/Toggle Handle Alignment [p]", true)] static bool MenuVerifyToggleHandleAlignment() { - ToggleHandleAlignment instance = EditorToolbarLoader.GetInstance(); + ToggleHandleOrientation instance = EditorToolbarLoader.GetInstance(); return instance != null && instance.enabled; @@ -615,7 +615,7 @@ static bool MenuVerifyToggleHandleAlignment() [MenuItem(k_MenuPrefix + "Interaction/Toggle Handle Alignment [p]", false, PreferenceKeys.menuSelection + 1)] static void MenuDoToggleHandleAlignment() { - ToggleHandleAlignment instance = EditorToolbarLoader.GetInstance(); + ToggleHandleOrientation instance = EditorToolbarLoader.GetInstance(); if(instance != null) UnityEditor.ProBuilder.EditorUtility.ShowNotification(instance.DoAction().notification); } diff --git a/com.unity.probuilder/Editor/EditorCore/EditorUtility.cs b/com.unity.probuilder/Editor/EditorCore/EditorUtility.cs index 3604db851..40e76cf42 100644 --- a/com.unity.probuilder/Editor/EditorCore/EditorUtility.cs +++ b/com.unity.probuilder/Editor/EditorCore/EditorUtility.cs @@ -480,10 +480,71 @@ internal static void CreateCachedEditor(UnityEngine.Object[] targetObjects, r /// internal static bool IsMeshElementMode(this SelectMode mode) { - return mode.ContainsFlag(SelectMode.Vertex | SelectMode.Edge | SelectMode.Face | SelectMode.TextureFace); + return mode.ContainsFlag( + SelectMode.Vertex + | SelectMode.Edge + | SelectMode.Face + | SelectMode.TextureEdge + | SelectMode.TextureFace + | SelectMode.TextureVertex + ); } - // HasFlag doesn't exist in .NET 3.5 + internal static bool IsTextureMode(this SelectMode mode) + { + return mode.ContainsFlag( + SelectMode.TextureEdge + | SelectMode.TextureFace + | SelectMode.TextureVertex + ); + } + + internal static bool IsPositionMode(this SelectMode mode) + { + return mode.ContainsFlag( + SelectMode.TextureEdge + | SelectMode.TextureFace + | SelectMode.TextureVertex + ); + } + + internal static SelectMode GetPositionMode(this SelectMode mode) + { + if (mode.ContainsFlag(SelectMode.TextureFace)) + mode = (mode & ~SelectMode.TextureFace) | SelectMode.Face; + + if (mode.ContainsFlag(SelectMode.TextureEdge)) + mode = (mode & ~SelectMode.TextureEdge) | SelectMode.Edge; + + if (mode.ContainsFlag(SelectMode.TextureVertex)) + mode = (mode & ~SelectMode.TextureVertex) | SelectMode.Vertex; + + return mode; + } + + internal static SelectMode GetTextureMode(this SelectMode mode) + { + if (mode.ContainsFlag(SelectMode.Face)) + mode = (mode & ~SelectMode.Face) | SelectMode.TextureFace; + + if (mode.ContainsFlag(SelectMode.Edge)) + mode = (mode & ~SelectMode.Edge) | SelectMode.TextureEdge; + + if (mode.ContainsFlag(SelectMode.Vertex)) + mode = (mode & ~SelectMode.Vertex) | SelectMode.TextureVertex; + + return mode; + } + + /// + /// Test if SelectMode contains any of the value bits. + /// + /// + /// HasFlag doesn't exist in .NET 3.5 + /// + /// + /// + /// internal static bool ContainsFlag(this SelectMode target, SelectMode value) { return (target & value) != SelectMode.None; diff --git a/com.unity.probuilder/Editor/EditorCore/MenuCommands.cs b/com.unity.probuilder/Editor/EditorCore/MenuCommands.cs deleted file mode 100644 index a2529b436..000000000 --- a/com.unity.probuilder/Editor/EditorCore/MenuCommands.cs +++ /dev/null @@ -1,793 +0,0 @@ -using System; -using UnityEngine; -using UnityEditor; -using System.Collections.Generic; -using System.Linq; -using UnityEngine.ProBuilder; -using UnityEngine.ProBuilder.MeshOperations; -using MeshOps = UnityEngine.ProBuilder.MeshOperations; -using Object = UnityEngine.Object; -using UnityEngine.ProBuilder.Experimental.CSG; - -namespace UnityEditor.ProBuilder -{ - /// - /// - /// Contains Menu commands for most ProBuilder operations. Will also attempt to Update the pb_Editor. - /// - sealed class MenuCommands : UnityEditor.Editor - { - static ProBuilderEditor editor { get { return ProBuilderEditor.instance; } } - -#region Object Level - - public static ActionResult MenuMergeObjects(ProBuilderMesh[] selected) - { - if(selected.Length < 2) - return new ActionResult(ActionResult.Status.Canceled, "Must Select 2+ Objects"); - - List res = InternalMeshUtility.CombineObjects(selected); - - if (res != null) - { - foreach (var mesh in res) - { - mesh.Optimize(); - mesh.gameObject.name = "pb-MergedObject" + mesh.id; - UndoUtility.RegisterCreatedObjectUndo(mesh.gameObject, "Merge Objects"); - Selection.objects = res.Select(x => x.gameObject).ToArray(); - } - - // Delete donor objects - for(int i = 0; i < selected.Length; i++) - { - if(selected[i] != null) - UndoUtility.DestroyImmediate(selected[i].gameObject); - } - } - - ProBuilderEditor.Refresh(); - - return new ActionResult(ActionResult.Status.Success, "Merged Objects"); - } - - /** - * Set the pivot to the center of the current element selection. - * ProBuilder only. - */ - public static ActionResult MenuCenterPivot(ProBuilderMesh[] selection) - { - return SetPivotInternal(selection, null); - } - - public static ActionResult MenuSetPivot(ProBuilderMesh[] selection) - { - int[][] tri = new int[selection.Length][]; - - for(int i = 0; i < tri.Length; i++) - tri[i] = selection[i].selectedVertices.ToArray(); - - return SetPivotInternal(selection, tri); - } - - static ActionResult SetPivotInternal(ProBuilderMesh[] selection, int[][] triangles = null) - { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - Object[] objects = new Object[selection.Length * 2]; - System.Array.Copy(selection, 0, objects, 0, selection.Length); - for(int i = selection.Length; i < objects.Length; i++) - objects[i] = selection[i-selection.Length].transform; - - UndoUtility.RegisterCompleteObjectUndo(objects, "Set Pivot"); - - for(int i = 0; i < selection.Length; i++) - { - TransformUtility.UnparentChildren(selection[i].transform); - - if(triangles != null) - selection[i].CenterPivot(triangles[i]); - else - selection[i].CenterPivot(null); - - selection[i].Optimize(); - - TransformUtility.ReparentChildren(selection[i].transform); - } - - SceneView.RepaintAll(); - - if(editor != null) - ProBuilderEditor.Refresh(); - - return new ActionResult(ActionResult.Status.Success, "Set Pivot"); - } - - public static ActionResult MenuFreezeTransforms(ProBuilderMesh[] selection) - { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - List undoables = new List( selection.Select(x => (Object) x.transform) ); - undoables.AddRange(selection); - UndoUtility.RecordObjects(undoables.ToArray(), "Freeze Transforms"); - - Vector3[][] positions = new Vector3[selection.Length][]; - - for(int i = 0; i < selection.Length; i++) - positions[i] = selection[i].VerticesInWorldSpace(); - - for(int i = 0; i < selection.Length; i++) - { - ProBuilderMesh pb = selection[i]; - - pb.transform.position = Vector3.zero; - pb.transform.rotation = Quaternion.identity; - pb.transform.localScale = Vector3.one; - - foreach(Face face in pb.facesInternal) - face.manualUV = true; - - pb.positions = positions[i]; - - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - - ProBuilderEditor.Refresh(); - - SceneView.RepaintAll(); - - return new ActionResult(ActionResult.Status.Success, "Freeze Transforms"); - } - - /// - /// Set the pb_Entity entityType on selection. - /// - /// - /// - /// - [Obsolete("pb_Entity is obsolete.")] - public static ActionResult MenuSetEntityType(ProBuilderMesh[] selection, EntityType entityType) - { - if(selection.Length < 1) - return ActionResult.NoSelection; - - Object[] undoObjects = selection.SelectMany(x => x.GetComponents()).ToArray(); - - UndoUtility.RecordObjects(undoObjects, "Set Entity Type"); - - foreach(ProBuilderMesh pb in selection) - { - EntityUtility.SetEntityType(entityType, pb.gameObject); - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - - return new ActionResult(ActionResult.Status.Success, "Set " + entityType); - } - - public static void MenuOpenVertexColorsEditor() - { - VertexColorPalette.MenuOpenWindow(); - } - - public static ActionResult MenuTriangulateObject(ProBuilderMesh[] selection) - { - if(!editor || selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RegisterCompleteObjectUndo(selection, "Triangulate Objects"); - - for(int i = 0; i < selection.Length; i++) - { - selection[i].ToMesh(); - selection[i].ToTriangles(selection[i].facesInternal); - selection[i].Refresh(); - selection[i].Optimize(); - selection[i].ClearSelection(); - } - - ProBuilderEditor.Refresh(); - - return new ActionResult(ActionResult.Status.Success, "Triangulate " + selection.Length + (selection.Length > 1 ? " Objects" : " Object")); - } - - enum BooleanOperation - { - Union, - Subtract, - Intersect - } - - static ActionResult MenuBooleanOperation(BooleanOperation operation, ProBuilderMesh lhs, ProBuilderMesh rhs) - { - if(lhs == null || rhs == null) - return new ActionResult(ActionResult.Status.Failure, "Must Select 2 Objects"); - - string op_string = operation == BooleanOperation.Union ? "Union" : (operation == BooleanOperation.Subtract ? "Subtract" : "Intersect"); - - ProBuilderMesh[] sel = new ProBuilderMesh[] { lhs, rhs }; - - UndoUtility.RecordSelection(sel, op_string); - - Mesh c; - - switch(operation) - { - case BooleanOperation.Union: - c = CSG.Union(lhs.gameObject, rhs.gameObject); - break; - - case BooleanOperation.Subtract: - c = CSG.Subtract(lhs.gameObject, rhs.gameObject); - break; - - default: - c = CSG.Intersect(lhs.gameObject, rhs.gameObject); - break; - } - - GameObject go = new GameObject(); - - go.AddComponent().sharedMaterial = EditorUtility.GetUserMaterial(); - go.AddComponent().sharedMesh = c; - - ProBuilderMesh pb = InternalMeshUtility.CreateMeshWithTransform(go.transform, false); - DestroyImmediate(go); - - Selection.objects = new Object[] { pb.gameObject }; - - return new ActionResult(ActionResult.Status.Success, op_string); - } - - /** - * Union operation between two ProBuilder objects. - */ - public static ActionResult MenuUnion(ProBuilderMesh lhs, ProBuilderMesh rhs) - { - return MenuBooleanOperation(BooleanOperation.Union, lhs, rhs); - } - - /** - * Subtract boolean operation between two pb_Objects. - */ - public static ActionResult MenuSubtract(ProBuilderMesh lhs, ProBuilderMesh rhs) - { - return MenuBooleanOperation(BooleanOperation.Subtract, lhs, rhs); - } - - /** - * Intersect boolean operation between two pb_Objects. - */ - public static ActionResult MenuIntersect(ProBuilderMesh lhs, ProBuilderMesh rhs) - { - return MenuBooleanOperation(BooleanOperation.Intersect, lhs, rhs); - } -#endregion - -#region Normals - - /** - * Reverse the direction of all faces on each object. - */ - public static ActionResult MenuFlipObjectNormals(ProBuilderMesh[] selected) - { - if(selected == null || selected.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RecordSelection(InternalUtility.GetComponents(Selection.transforms), "Flip Object Normals"); - - foreach(ProBuilderMesh pb in selected) - { - foreach(var face in pb.facesInternal) - face.Reverse(); - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - - return new ActionResult(ActionResult.Status.Success, "Flip Object Normals"); - } - - /** - * Flips all face normals if editLevel == EditLevel.Top, else flips only pb_Object->SelectedFaces - */ - public static ActionResult MenuFlipNormals(ProBuilderMesh[] selected) - { - if(selected == null || selected.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RecordSelection(InternalUtility.GetComponents(Selection.transforms), "Flip Face Normals"); - - int c = 0; - int faceCount = MeshSelection.selectedFaceCount; - - foreach(ProBuilderMesh pb in InternalUtility.GetComponents(Selection.transforms)) - { - if( pb.selectedFaceCount < 1 && faceCount < 1 ) - { - foreach(var face in pb.facesInternal) - face.Reverse(); - - c += pb.facesInternal.Length; - } - else - { - foreach(var face in pb.GetSelectedFaces()) - face.Reverse(); - - c += pb.selectedFaceCount; - } - - - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - - if(c > 0) - return new ActionResult(ActionResult.Status.Success, "Flip " + c + (c > 1 ? " Face Normals" : " Face Normal")); - - return new ActionResult(ActionResult.Status.Canceled, "Flip Normals\nNo Faces Selected"); - } - - /** - * Attempt to make face normals uniform. - */ - public static ActionResult MenuConformObjectNormals(ProBuilderMesh[] selection) - { - return DoConformNormals(selection, false); - } - - public static ActionResult MenuConformNormals(ProBuilderMesh[] selection) - { - return DoConformNormals(selection, true); - } - - public static ActionResult DoConformNormals(ProBuilderMesh[] selection, bool perFace = true) - { - if(!editor || selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RecordSelection(selection, "Conform " + (MeshSelection.selectedFaceCount > 0 ? "Face" : "Object") + " Normals."); - - ActionResult res = ActionResult.NoSelection; - - foreach(ProBuilderMesh pb in selection) - { - Face[] faces = perFace ? pb.GetSelectedFaces() : pb.facesInternal; - - if(faces == null) - continue; - - res = UnityEngine.ProBuilder.MeshOperations.SurfaceTopology.ConformNormals(pb, faces); - - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - - ProBuilderEditor.Refresh(); - - return res; - } -#endregion - -#region Extrude / Bridge - - public static ActionResult MenuBridgeEdges(ProBuilderMesh[] selection) - { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RecordSelection(selection, "Bridge Edges"); - - bool success = false; - - foreach(ProBuilderMesh pb in selection) - { - if(pb.selectedEdgeCount == 2) - { - if(pb.Bridge(pb.selectedEdges[0], pb.selectedEdges[1], ProBuilderEditor.s_AllowNonManifoldActions) != null) - { - success = true; - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - } - } - - if(success) - { - ProBuilderEditor.Refresh(); - return new ActionResult(ActionResult.Status.Success, "Bridge Edges"); - } - else - { - Debug.LogWarning("Failed Bridge Edges. Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold)."); - return new ActionResult(ActionResult.Status.Failure, "Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold)."); - } - } - -#endregion - -#region Selection - - -#endregion - -#region Delete / Detach - - /** - * Delete selected faces. - * ProBuilder only. - */ - public static ActionResult MenuDeleteFace(ProBuilderMesh[] selection) - { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RegisterCompleteObjectUndo(selection, "Delete Face"); - - int count = 0; - - foreach(ProBuilderMesh pb in selection) - { - if(pb.selectedFaceCount == pb.facesInternal.Length) - { - Debug.LogWarning("Attempting to delete all faces on this mesh... I'm afraid I can't let you do that."); - continue; - } - - pb.DeleteFaces(pb.selectedFacesInternal); - count += pb.selectedFaceCount; - - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - - if(editor) - { - editor.ClearElementSelection(); - ProBuilderEditor.Refresh(); - } - - if(count > 0) - return new ActionResult(ActionResult.Status.Success, "Delete " + count + " Faces"); - else - return new ActionResult(ActionResult.Status.Failure, "No Faces Selected"); - } - - -#endregion - -#region Face / Triangles - - /** - * Treat selected faces as a single face. - */ - public static ActionResult MenuMergeFaces(ProBuilderMesh[] selection) - { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RecordObjects(selection, "Merge Faces"); - - int success = 0; - - foreach(ProBuilderMesh pb in selection) - { - if(pb.selectedFaceCount > 1) - { - success += pb.selectedFaceCount; - - Face face = MergeElements.Merge(pb, pb.selectedFacesInternal); - - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - - pb.SetSelectedFaces( new Face[] { face } ); - } - } - - if(editor) - ProBuilderEditor.Refresh(); - - if(success > 0) - return new ActionResult(ActionResult.Status.Success, "Merged " + success + " Faces"); - else - return new ActionResult(ActionResult.Status.Failure, "Merge Faces\nNo Faces Selected"); - } - - /** - * Turn / flip / swap a quad connecting edge. - */ - public static ActionResult MenuFlipEdges(ProBuilderMesh[] selection) - { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RecordSelection(selection, "Flip Face Edges"); - int success = 0; - int attempts = 0; - - foreach(ProBuilderMesh pb in selection) - { - foreach(Face face in pb.selectedFacesInternal) - { - if( pb.FlipEdge(face) ) - success++; - } - - attempts++; - - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - } - - if(editor) - ProBuilderEditor.Refresh(); - - if(success > 0) - return new ActionResult(ActionResult.Status.Success, "Flipped " + success + " Edges"); - else - return new ActionResult(ActionResult.Status.Failure, string.Format("Flip Edges\n{0}", attempts > 0 ? "Faces Must Be Quads" : "No Faces Selected")); - } -#endregion - -#region Vertex Operations - -#if !PROTOTYPE - - public static ActionResult MenuSplitVertices(ProBuilderMesh[] selection) - { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - int splitCount = 0; - UndoUtility.RecordSelection(selection, "Split Vertices"); - - foreach(ProBuilderMesh mesh in selection) - { - // loose verts to split - List tris = new List(mesh.selectedIndexesInternal); - - if (mesh.selectedFacesInternal.Length > 0) - { - int[] sharedVertexHandles = new int[mesh.selectedIndexesInternal.Length]; - - // Get shared index index for each vert in selection - for (int i = 0; i < mesh.selectedIndexesInternal.Length; i++) - sharedVertexHandles[i] = mesh.GetSharedVertexHandle(mesh.selectedIndexesInternal[i]); - - // cycle through selected faces and remove the tris that compose full faces. - foreach (Face face in mesh.selectedFacesInternal) - { - List faceSharedIndexes = new List(); - - for (int j = 0; j < face.distinctIndexesInternal.Length; j++) - faceSharedIndexes.Add(mesh.GetSharedVertexHandle(face.distinctIndexesInternal[j])); - - List usedTris = new List(); - for (int i = 0; i < sharedVertexHandles.Length; i++) - if (faceSharedIndexes.Contains(sharedVertexHandles[i])) - usedTris.Add(mesh.selectedIndexesInternal[i]); - - // This face *is* composed of selected tris. Remove these tris from the loose index list - foreach (int i in usedTris) - if (tris.Contains(i)) - tris.Remove(i); - } - } - - // Now split the faces, and any loose vertices - mesh.DetachFaces(mesh.selectedFacesInternal); - - splitCount += mesh.selectedIndexesInternal.Length; - mesh.SplitVertices(mesh.selectedIndexesInternal); - - // Reattach detached face vertices (if any are to be had) - if(mesh.selectedFacesInternal.Length > 0) - mesh.WeldVertices(mesh.selectedFacesInternal.SelectMany(x => x.indexes), Mathf.Epsilon); - - // And set the selected triangles to the newly split - List newTriSelection = new List(mesh.selectedFacesInternal.SelectMany(x => x.indexes)); - newTriSelection.AddRange(tris); - mesh.SetSelectedVertices(newTriSelection.ToArray()); - - mesh.ToMesh(); - mesh.Refresh(); - mesh.Optimize(); - } - - ProBuilderEditor.Refresh(); - - if(splitCount > 0) - return new ActionResult(ActionResult.Status.Success, "Split " + splitCount + (splitCount > 1 ? " Vertices" : " Vertex")); - else - return new ActionResult(ActionResult.Status.Failure, "Split Vertices\nInsuffient Vertices Selected"); - } - -#endif -#endregion - -#region Subdivide / Split - -#if !PROTOTYPE - - /** - * Attempts to subdivide the selected objects. If Edge or Face selection mode, splits at the - * center of the edge. Otherwise from Vertex. - * ProBuilder only. - */ - public static ActionResult MenuSubdivide(ProBuilderMesh[] selection) - { - if(!editor || selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RegisterCompleteObjectUndo(selection, "Subdivide Selection"); - - int success = 0; - - foreach(ProBuilderMesh pb in selection) - { - pb.ToMesh(); - - if( pb.Subdivide() ) - success++; - - pb.Refresh(); - pb.Optimize(); - - pb.SetSelectedVertices(new int[0]); - } - - ProBuilderEditor.Refresh(); - - return new ActionResult(ActionResult.Status.Success, "Subdivide " + selection.Length + " Objects"); - } - - /** - * Subdivides all currently selected faces. - * ProBuilder only. - */ - public static ActionResult MenuSubdivideFace(ProBuilderMesh[] selection) - { - if(!editor || selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - int success = 0; - - foreach(ProBuilderMesh pb in selection) - { - UndoUtility.RegisterCompleteObjectUndo(selection, "Subdivide Faces"); - - Face[] faces = pb.Subdivide(pb.selectedFacesInternal); - pb.ToMesh(); - - if(faces != null) - { - success += pb.selectedFacesInternal.Length; - pb.SetSelectedFaces(faces); - - pb.Refresh(); - pb.Optimize(); - } - } - - if(success > 0) - { - ProBuilderEditor.Refresh(); - - return new ActionResult(ActionResult.Status.Success, "Subdivide " + success + ((success > 1) ? " faces" : " face")); - } - else - { - Debug.LogWarning("Subdivide faces failed - did you not have any faces selected?"); - return new ActionResult(ActionResult.Status.Failure, "Subdivide Faces\nNo faces selected"); - } - } - - public static ActionResult MenuConnectEdges(ProBuilderMesh[] selection) - { - ActionResult res = ActionResult.NoSelection; - - UndoUtility.RegisterCompleteObjectUndo(selection, "Connect Edges"); - - foreach(ProBuilderMesh pb in selection) - { - Edge[] connections; - Face[] faces; - - res = ConnectElements.Connect(pb, pb.selectedEdges, out faces, out connections, true, true); - - if (connections != null) - { - pb.SetSelectedEdges(connections); - pb.Refresh(); - pb.Optimize(); - } - } - - ProBuilderEditor.Refresh(); - return res; - } - - /** - * Connects all currently selected vertices. - * ProBuilder only. - */ - public static ActionResult MenuConnectVertices(ProBuilderMesh[] selection) - { - ActionResult res = ActionResult.NoSelection; - - UndoUtility.RegisterCompleteObjectUndo(selection, "Connect Vertices"); - - foreach(ProBuilderMesh pb in selection) - { - pb.ToMesh(); - int[] splits = pb.Connect(pb.selectedIndexesInternal); - - if(splits != null) - { - pb.Refresh(); - pb.Optimize(); - pb.SetSelectedVertices(splits); - res = new ActionResult(ActionResult.Status.Success, "Connect Edges"); - } - else - { - res = new ActionResult(ActionResult.Status.Failure, "Failed Connecting Edges"); - } - } - ProBuilderEditor.Refresh(); - - return res; - } - - /** - * Inserts an edge loop along currently selected Edges. - * ProBuilder only. - */ - public static ActionResult MenuInsertEdgeLoop(ProBuilderMesh[] selection) - { - if(!editor || selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - int success = 0; - UndoUtility.RegisterCompleteObjectUndo(selection, "Insert Edge Loop"); - - foreach(ProBuilderMesh pb in selection) - { - Edge[] edges = pb.Connect(ElementSelection.GetEdgeRing(pb, pb.selectedEdges)).item2; - - if(edges != null) - { - pb.SetSelectedEdges(edges); - pb.ToMesh(); - pb.Refresh(); - pb.Optimize(); - success++; - } - } - - ProBuilderEditor.Refresh(); - - if(success > 0) - return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop"); - else - return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop"); - } - -#endif -#endregion - } -} diff --git a/com.unity.probuilder/Editor/EditorCore/MenuItems.cs b/com.unity.probuilder/Editor/EditorCore/MenuItems.cs index b0ac9404e..9e7827343 100644 --- a/com.unity.probuilder/Editor/EditorCore/MenuItems.cs +++ b/com.unity.probuilder/Editor/EditorCore/MenuItems.cs @@ -17,6 +17,7 @@ public static void MenuInitAbout() { AboutWindow.Init(); } + [MenuItem("Tools/" + PreferenceKeys.pluginTitle + "/" + PreferenceKeys.pluginTitle + " Window", false, PreferenceKeys.menuEditor)] public static void OpenEditorWindow() diff --git a/com.unity.probuilder/Editor/EditorCore/MeshSelection.cs b/com.unity.probuilder/Editor/EditorCore/MeshSelection.cs index 06ef0ea03..72fb190e3 100644 --- a/com.unity.probuilder/Editor/EditorCore/MeshSelection.cs +++ b/com.unity.probuilder/Editor/EditorCore/MeshSelection.cs @@ -14,7 +14,8 @@ namespace UnityEditor.ProBuilder [InitializeOnLoad] public static class MeshSelection { - static ProBuilderMesh[] s_TopSelection = new ProBuilderMesh[0]; + static List s_TopSelection = new List(); + static ProBuilderMesh s_ActiveMesh; static bool s_ElementCountCacheIsDirty = true; @@ -32,7 +33,11 @@ public static Bounds bounds static int s_TotalVertexCountCompiled; static int s_TotalTriangleCountCompiled; - internal static int selectedObjectCount { get; private set; } + /// + /// How many ProBuilderMesh components are currently selected. Corresponds to the length of Top. + /// + public static int selectedObjectCount { get; private set; } + internal static int selectedVertexCount { get; private set; } internal static int selectedSharedVertexCount { get; private set; } internal static int selectedFaceCount { get; private set; } @@ -69,6 +74,14 @@ static MeshSelection() OnObjectSelectionChanged(); } + /// + /// Returns the active selected mesh. + /// + public static ProBuilderMesh activeMesh + { + get { return s_ActiveMesh; } + } + /// /// Receive notifications when the object selection changes. /// @@ -78,9 +91,25 @@ internal static void OnObjectSelectionChanged() { // GameObjects returns both parent and child when both are selected, where transforms only returns the top-most // transform. - s_TopSelection = Selection.gameObjects.Select(x => x.GetComponent()).Where(x => x != null).ToArray(); + s_TopSelection.Clear(); + s_ActiveMesh = null; + + var gameObjects = Selection.gameObjects; + + for (int i = 0, c = gameObjects.Length; i < c; i++) + { + var mesh = gameObjects[i].GetComponent(); + + if (mesh != null) + { + if (gameObjects[i] == Selection.activeGameObject) + s_ActiveMesh = mesh; + s_TopSelection.Add(mesh); + } + } + + selectedObjectCount = s_TopSelection.Count; - selectedObjectCount = s_TopSelection.Length; s_ElementCountCacheIsDirty = true; if (objectSelectionChanged != null) @@ -169,7 +198,7 @@ public static IEnumerable top get { return new ReadOnlyCollection(s_TopSelection); } } - internal static ProBuilderMesh[] topInternal + internal static List topInternal { get { return s_TopSelection; } } @@ -188,14 +217,6 @@ internal static bool Contains(ProBuilderMesh mesh) return s_TopSelection.Contains(mesh); } - /// - /// How many ProBuilderMesh components are currently selected. Corresponds to the length of Top. - /// - public static int count - { - get { return topInternal.Length; } - } - /// /// Get the number of all selected vertices across the selected ProBuilder meshes. /// @@ -252,10 +273,17 @@ internal static void AddToSelection(GameObject t) { if(t == null || Selection.objects.Contains(t)) return; - Object[] temp = new Object[Selection.objects.Length + 1]; - temp[0] = t; - for(int i = 1; i < temp.Length; i++) - temp[i] = Selection.objects[i-1]; + + int len = Selection.objects.Length; + + Object[] temp = new Object[len + 1]; + + for(int i = 0; i < len; i++) + temp[i] = Selection.objects[i]; + + temp[len] = t; + + Selection.activeObject = t; Selection.objects = temp; } @@ -273,6 +301,9 @@ internal static void RemoveFromSelection(GameObject t) } Selection.objects = temp; + + if (Selection.activeGameObject == t) + Selection.activeObject = temp.FirstOrDefault(); } internal static void SetSelection(IList newSelection) @@ -284,8 +315,11 @@ internal static void SetSelection(IList newSelection) if(Tools.current == Tool.None) Tools.current = Tool.Move; - if(newSelection != null && newSelection.Count > 0) { - Selection.activeTransform = newSelection[0].transform; + var newCount = newSelection != null ? newSelection.Count : 0; + + if(newCount > 0) + { + Selection.activeTransform = newSelection[newCount - 1].transform; Selection.objects = newSelection.ToArray(); } else @@ -322,5 +356,77 @@ public static void ClearElementAndObjectSelection() ProBuilderEditor.instance.ClearElementSelection(); Selection.objects = new Object[0]; } + + internal static Vector3 GetHandlePosition() + { + switch (Tools.pivotMode) + { + case PivotMode.Pivot: + { + ProBuilderMesh mesh = activeMesh; + Face face; + Vector3 center = Vector3.zero; + + if (ProBuilderEditor.handleOrientation == HandleOrientation.Normal && GetActiveFace(out mesh, out face)) + center = Math.GetBounds(mesh.positionsInternal, face.distinctIndexesInternal).center; + else if(activeMesh != null) + center = Math.GetBounds(mesh.positionsInternal, mesh.selectedIndexesInternal).center; + + return mesh.transform.TransformPoint(center); + } + + default: + RecalculateSelectionBounds(); + return bounds.center; + } + } + + internal static Quaternion GetHandleRotation(HandleOrientation orientation) + { + switch (orientation) + { + case HandleOrientation.Normal: + + ProBuilderMesh mesh; + Face face; + + if(!GetActiveFace(out mesh, out face)) + goto case HandleOrientation.Local; + + // use average normal, tangent, and bi-tangent to calculate rotation relative to local space + var tup = Math.NormalTangentBitangent(mesh, face); + Vector3 nrm = tup.normal, bitan = tup.bitangent; + + if (nrm == Vector3.zero || bitan == Vector3.zero) + { + nrm = Vector3.up; + bitan = Vector3.right; + } + + return activeMesh.transform.rotation * Quaternion.LookRotation(nrm, bitan); + + case HandleOrientation.Local: + if (activeMesh == null) + goto default; + return activeMesh.transform.rotation; + + default: + return Quaternion.identity; + } + } + + static bool GetActiveFace(out ProBuilderMesh mesh, out Face face) + { + if (activeMesh != null && activeMesh.selectedFaceCount > 0) + { + mesh = activeMesh; + face = mesh.faces[activeMesh.selectedFaceIndicesInternal[0]]; + return true; + } + + mesh = activeMesh; + face = null; + return false; + } } } diff --git a/com.unity.probuilder/Editor/EditorCore/ProBuilderEditor.cs b/com.unity.probuilder/Editor/EditorCore/ProBuilderEditor.cs index a3fc7dd6a..743fedefd 100644 --- a/com.unity.probuilder/Editor/EditorCore/ProBuilderEditor.cs +++ b/com.unity.probuilder/Editor/EditorCore/ProBuilderEditor.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Collections.Generic; +using UnityEditor.ProBuilder.Actions; using UnityEngine.ProBuilder; using PMesh = UnityEngine.ProBuilder.ProBuilderMesh; using UnityEngine.ProBuilder.MeshOperations; @@ -26,16 +27,6 @@ public sealed class ProBuilderEditor : EditorWindow, IHasCustomMenu /// public static event Action selectionUpdated; - /// - /// Called when vertex modifications are complete. - /// - public static event Action afterMeshModification; - - /// - /// Called immediately prior to beginning vertex modifications. The ProBuilderMesh will be in un-altered state at this point (meaning ProBuilderMesh.ToMesh and ProBuilderMesh.Refresh have been called, but not Optimize). - /// - public static event Action beforeMeshModification; - /// /// Raised when the EditLevel is changed. /// @@ -67,12 +58,11 @@ public sealed class ProBuilderEditor : EditorWindow, IHasCustomMenu internal Pref m_BackfaceSelectEnabled = new Pref("editor.backFaceSelectEnabled", false); internal Pref m_DragSelectRectMode = new Pref("editor.dragSelectRectMode", RectSelectMode.Partial); - internal Pref m_ExtrudeMethod = new Pref("editor.extrudeMethod", ExtrudeMethod.FaceNormal); internal Pref m_SelectModifierBehavior = new Pref("editor.rectSelectModifier", SelectionModifierBehavior.Difference); - internal Pref m_ExtrudeEdgesAsGroup = new Pref("editor.extrudeEdgesAsGroup", true); - internal Pref m_HandleOrientation = new Pref("editor.handleAlignment", HandleOrientation.World); Pref m_SelectMode = new Pref("editor.selectMode", SelectMode.Object); + internal static Pref s_HandleOrientation = new Pref("editor.handleAlignment", HandleOrientation.World); + internal RectSelectMode rectSelectMode { get { return m_DragSelectRectMode.value; } @@ -98,22 +88,22 @@ internal SelectionModifierBehavior selectionModifierBehavior } } - internal HandleOrientation handleOrientation + internal static HandleOrientation handleOrientation { - get { return m_HandleOrientation.value; } + get { return s_HandleOrientation.value; } set { - if (value == m_HandleOrientation.value) + if (value == s_HandleOrientation.value) return; if (selectMode.ContainsFlag(SelectMode.TextureFace)) value = HandleOrientation.Normal; - m_HandleOrientation.SetValue(value, true); - m_HandleRotation = GetHandleRotation(); - } + s_HandleOrientation.SetValue(value, true); + SceneView.RepaintAll(); + } } internal bool backfaceSelectionEnabled @@ -130,13 +120,10 @@ internal bool backfaceSelectionEnabled } } - float m_SnapValue = .25f; - bool m_SnapAxisConstraint = true; - bool m_SnapEnabled; - MethodInfo m_FindNearestVertex; // used for 'g' key shortcut to swap between object/vef modes SelectMode m_LastComponentMode; HandleOrientation m_PreviousHandleOrientation; + PivotMode m_PreviousHandlePivot; [UserSetting] static internal Pref s_Shortcuts = new Pref("editor.sceneViewShortcuts", Shortcut.DefaultShortcuts().ToArray()); GUIStyle m_CommandStyle; @@ -158,40 +145,16 @@ internal bool backfaceSelectionEnabled // prevents leftClickUp from stealing focus after double click bool m_WasDoubleClick; // vertex handles - Vector3 m_ElementHandlePosition; - Vector3 m_ElementHandleCachedPosition; - bool m_IsMovingElements; bool m_IsRightMouseDown; + static Dictionary s_EditorTools = new Dictionary(); - bool m_DoSnapToVertex; - bool m_DoSnapToFace; - Vector3 m_HandleScalePrevious = Vector3.one; - Vector3 m_HandleScale = Vector3.one; Vector3[][] m_VertexPositions; Vector3[] m_VertexOffset; - Quaternion m_HandleRotationPrevious = Quaternion.identity; - Quaternion m_HandleRotation = Quaternion.identity; - Quaternion m_RotationInitial; - Quaternion m_RotationInitialInverse; GUIContent m_SceneInfo = new GUIContent(); - // Use for delta display - Vector3 m_TranslateOrigin = Vector3.zero; - Vector3 m_RotateOrigin = Vector3.zero; - Vector3 m_ScaleOrigin = Vector3.zero; - - Vector3 m_TextureHandlePosition = Vector3.zero; - Vector3 m_TextureHandlePositionPrevious = Vector3.zero; - bool m_IsMovingTextures; - Quaternion m_TextureRotation = Quaternion.identity; - Vector3 m_TextureScale = Vector3.one; Rect m_SceneInfoRect = new Rect(10, 10, 200, 40); - Vector3 m_HandlePosition = Vector3.zero; - - Matrix4x4 handleMatrix = Matrix4x4.identity; - #if !UNITY_2018_2_OR_NEWER static MethodInfo s_ResetOnSceneGUIState = null; #endif @@ -246,6 +209,9 @@ public static SelectMode selectMode if (previous == value) return; + var wasTextureMode = previous.ContainsFlag(SelectMode.TextureEdge | SelectMode.TextureVertex | SelectMode.TextureFace); + var isTextureMode = value.ContainsFlag(SelectMode.TextureEdge | SelectMode.TextureVertex | SelectMode.TextureFace); + s_Instance.m_SelectMode.SetValue(value, true); if (previous == SelectMode.Edge || previous == SelectMode.Vertex || previous == SelectMode.Face) @@ -254,16 +220,28 @@ public static SelectMode selectMode if (value == SelectMode.Object) Tools.current = s_Instance.m_CurrentTool; - if (value == SelectMode.TextureFace) - s_Instance.m_PreviousHandleOrientation = s_Instance.m_HandleOrientation; + if (!wasTextureMode && isTextureMode) + { + s_Instance.m_PreviousHandlePivot = Tools.pivotMode; + s_Instance.m_PreviousHandleOrientation = s_HandleOrientation; - if (previous == SelectMode.TextureFace) - s_Instance.handleOrientation = s_Instance.m_PreviousHandleOrientation; + Tools.pivotMode = PivotMode.Pivot; + handleOrientation = HandleOrientation.Normal; + } + else if(wasTextureMode && !isTextureMode) + { + if (Tools.pivotMode == PivotMode.Pivot + && handleOrientation == HandleOrientation.Normal) + { + Tools.pivotMode = s_Instance.m_PreviousHandlePivot; + handleOrientation = s_Instance.m_PreviousHandleOrientation; + } + } if (selectModeChanged != null) selectModeChanged(value); - s_Instance.UpdateMeshHandles(true); + UpdateMeshHandles(true); s_Instance.Repaint(); } } @@ -369,9 +347,6 @@ void OnEnable() UpdateSelection(); HideSelectedWireframe(); - m_FindNearestVertex = typeof(HandleUtility).GetMethod("FindNearestVertex", - BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance); - if (selectModeChanged != null) selectModeChanged(selectMode); } @@ -387,7 +362,8 @@ void OnDisable() UpdateSelection(); - m_EditorMeshHandles.Dispose(); + if(m_EditorMeshHandles != null) + m_EditorMeshHandles.Dispose(); if (selectionUpdated != null) selectionUpdated(null); @@ -432,10 +408,6 @@ void LoadSettings() // workaround for old single-key shortcuts if(s_Shortcuts.value == null || s_Shortcuts.value.Length < 1) s_Shortcuts.SetValue(Shortcut.DefaultShortcuts().ToArray(), true); - - m_SnapEnabled = ProGridsInterface.SnapEnabled(); - m_SnapValue = ProGridsInterface.SnapValue(); - m_SnapAxisConstraint = ProGridsInterface.UseAxisConstraints(); } void InitGUI() @@ -451,27 +423,6 @@ void InitGUI() VertexTranslationInfoStyle.normal.background = EditorGUIUtility.whiteTexture; VertexTranslationInfoStyle.normal.textColor = new Color(1f, 1f, 1f, .6f); VertexTranslationInfoStyle.padding = new RectOffset(3, 3, 3, 0); - - var object_Graphic_off = IconUtility.GetIcon("Modes/Mode_Object"); - var face_Graphic_off = IconUtility.GetIcon("Modes/Mode_Face"); - var vertex_Graphic_off = IconUtility.GetIcon("Modes/Mode_Vertex"); - var edge_Graphic_off = IconUtility.GetIcon("Modes/Mode_Edge"); - - m_EditModeIcons = new GUIContent[] - { - object_Graphic_off != null - ? new GUIContent(object_Graphic_off, "Object Selection") - : new GUIContent("OBJ", "Object Selection"), - vertex_Graphic_off != null - ? new GUIContent(vertex_Graphic_off, "Vertex Selection") - : new GUIContent("VRT", "Vertex Selection"), - edge_Graphic_off != null - ? new GUIContent(edge_Graphic_off, "Edge Selection") - : new GUIContent("EDG", "Edge Selection"), - face_Graphic_off != null - ? new GUIContent(face_Graphic_off, "Face Selection") - : new GUIContent("FCE", "Face Selection"), - }; } /// @@ -558,6 +509,38 @@ void SetIsUtilityWindow(bool isUtilityWindow) res.titleContent = windowTitle; } + VertexManipulationTool GetTool() where T : VertexManipulationTool, new() + { + VertexManipulationTool tool; + + if (s_EditorTools.TryGetValue(typeof(T), out tool)) + return tool; + tool = new T(); + s_EditorTools.Add(typeof(T), tool); + return tool; + } + + VertexManipulationTool GetToolForSelectMode(Tool tool, SelectMode mode) + { + switch (tool) + { + case Tool.Move: + return mode.IsTextureMode() + ? GetTool() + : GetTool(); + case Tool.Rotate: + return mode.IsTextureMode() + ? GetTool() + : GetTool(); + case Tool.Scale: + return mode.IsTextureMode() + ? GetTool() + : GetTool(); + default: + return null; + } + } + void OnSceneGUI(SceneView sceneView) { #if !UNITY_2018_2_OR_NEWER @@ -569,21 +552,6 @@ void OnSceneGUI(SceneView sceneView) m_CurrentEvent = Event.current; - if(selectMode.ContainsFlag(SelectMode.Face | SelectMode.Edge | SelectMode.Vertex)) - { - if (m_CurrentEvent.Equals(Event.KeyboardEvent("v"))) - m_DoSnapToVertex = true; - else if (m_CurrentEvent.Equals(Event.KeyboardEvent("c"))) - m_DoSnapToFace = true; - } - - // Snap stuff - if (m_CurrentEvent.type == EventType.KeyUp) - { - m_DoSnapToFace = false; - m_DoSnapToVertex = false; - } - if (m_CurrentEvent.type == EventType.MouseDown && m_CurrentEvent.button == 1) m_IsRightMouseDown = true; @@ -612,18 +580,10 @@ void OnSceneGUI(SceneView sceneView) if (selectMode == SelectMode.Object) return; - // Finished moving vertices, scaling, or adjusting uvs - if ((m_IsMovingElements || m_IsMovingTextures) && GUIUtility.hotControl < 1) - { - OnFinishVertexModification(); - m_HandleRotation = GetHandleRotation(); - UpdateTextureHandles(); - } - // Check mouse position in scene and determine if we should highlight something if (s_ShowHoverHighlight && m_CurrentEvent.type == EventType.MouseMove - && selectMode.ContainsFlag(SelectMode.Face | SelectMode.Edge | SelectMode.Vertex | SelectMode.TextureFace)) + && selectMode.IsMeshElementMode()) { m_Hovering.CopyTo(m_HoveringPrevious); @@ -645,42 +605,12 @@ void OnSceneGUI(SceneView sceneView) Tools.current = Tool.None; - if (selectMode.ContainsFlag(SelectMode.Vertex | SelectMode.Edge | SelectMode.Face | SelectMode.TextureFace)) + if (selectMode.IsMeshElementMode() && MeshSelection.selectedVertexCount > 0) { - if (MeshSelection.selectedVertexCount > 0) - { - if (selectMode == SelectMode.TextureFace) - { - switch (m_CurrentTool) - { - case Tool.Move: - TextureMoveTool(); - break; - case Tool.Rotate: - TextureRotateTool(); - break; - case Tool.Scale: - TextureScaleTool(); - break; - } - } - else - { - switch (m_CurrentTool) - { - case Tool.Move: - VertexMoveTool(); - break; - case Tool.Scale: - VertexScaleTool(); - break; - case Tool.Rotate: - VertexRotateTool(); - break; - } - } + var tool = GetToolForSelectMode(m_CurrentTool, m_SelectMode); - } + if(tool != null) + tool.OnSceneGUI(m_CurrentEvent); } if (EditorHandleUtility.SceneViewInUse(m_CurrentEvent) || m_CurrentEvent.isKey) @@ -767,14 +697,14 @@ void DoubleClick(Event e) if (mesh != null) { - if (selectMode == SelectMode.Edge) + if (selectMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge)) { if (e.shift) EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction()); else EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction()); } - else if (selectMode == SelectMode.Face) + else if (selectMode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace)) { if ((e.modifiers & (EventModifiers.Control | EventModifiers.Shift)) == (EventModifiers.Control | EventModifiers.Shift)) @@ -797,462 +727,6 @@ void DoubleClick(Event e) } } - void VertexMoveTool() - { - if (!m_IsMovingElements) - m_ElementHandlePosition = m_HandlePosition; - - m_ElementHandleCachedPosition = m_ElementHandlePosition; - - m_ElementHandlePosition = Handles.PositionHandle(m_ElementHandlePosition, m_HandleRotation); - - if (m_CurrentEvent.alt) - return; - - bool previouslyMoving = m_IsMovingElements; - - if (m_ElementHandlePosition != m_ElementHandleCachedPosition) - { - // profiler.BeginSample("VertexMoveTool()"); - Vector3 diff = m_ElementHandlePosition - m_ElementHandleCachedPosition; - - Vector3 mask = diff.ToMask(Math.handleEpsilon); - - if (m_DoSnapToVertex) - { - Vector3 v; - - if (FindNearestVertex(m_CurrentEvent.mousePosition, out v)) - diff = Vector3.Scale(v - m_ElementHandleCachedPosition, mask); - } - else if (m_DoSnapToFace) - { - ProBuilderMesh obj = null; - RaycastHit hit; - Dictionary> ignore = new Dictionary>(); - foreach (ProBuilderMesh pb in selection) - ignore.Add(pb, new HashSet(pb.selectedFacesInternal)); - - if (EditorHandleUtility.FaceRaycast(m_CurrentEvent.mousePosition, out obj, out hit, ignore)) - { - if (mask.IntSum() == 1) - { - Ray r = new Ray(m_ElementHandleCachedPosition, -mask); - Plane plane = new Plane(obj.transform.TransformDirection(hit.normal).normalized, - obj.transform.TransformPoint(hit.point)); - - float forward, backward; - plane.Raycast(r, out forward); - plane.Raycast(r, out backward); - float planeHit = Mathf.Abs(forward) < Mathf.Abs(backward) ? forward : backward; - r.direction = -r.direction; - plane.Raycast(r, out forward); - plane.Raycast(r, out backward); - float rev = Mathf.Abs(forward) < Mathf.Abs(backward) ? forward : backward; - if (Mathf.Abs(rev) > Mathf.Abs(planeHit)) - planeHit = rev; - - if (Mathf.Abs(planeHit) > Mathf.Epsilon) - diff = mask * -planeHit; - } - else - { - diff = Vector3.Scale(obj.transform.TransformPoint(hit.point) - m_ElementHandleCachedPosition, mask.Abs()); - } - } - } - // else if(snapToEdge && nearestEdge.IsValid()) - // { - // // FINDME - - // } - - m_IsMovingElements = true; - - if (previouslyMoving == false) - { - m_TranslateOrigin = m_ElementHandleCachedPosition; - m_RotateOrigin = m_HandleRotation.eulerAngles; - m_ScaleOrigin = m_HandleScale; - - OnBeginVertexMovement(); - - if (Event.current.modifiers == EventModifiers.Shift) - ShiftExtrude(); - - ProGridsInterface.OnHandleMove(mask); - } - - for (int i = 0; i < selection.Length; i++) - { - var mesh = selection[i]; - - mesh.TranslateVerticesInWorldSpace(mesh.selectedIndexesInternal, - diff, - m_SnapEnabled ? m_SnapValue : 0f, - m_SnapAxisConstraint); - - mesh.RefreshUV(MeshSelection.selectedFacesInEditZone[mesh]); - mesh.Refresh(RefreshMask.Normals); - mesh.mesh.RecalculateBounds(); - } - - UpdateMeshHandles(false); - } - } - - void VertexScaleTool() - { - m_ElementHandlePosition = m_HandlePosition; - - m_HandleScalePrevious = m_HandleScale; - - m_HandleScale = Handles.ScaleHandle(m_HandleScale, m_ElementHandlePosition, m_HandleRotation, - HandleUtility.GetHandleSize(m_ElementHandlePosition)); - - if (m_CurrentEvent.alt) return; - - bool previouslyMoving = m_IsMovingElements; - - if (m_HandleScalePrevious != m_HandleScale) - { - m_IsMovingElements = true; - if (previouslyMoving == false) - { - m_TranslateOrigin = m_ElementHandleCachedPosition; - m_RotateOrigin = m_HandleRotation.eulerAngles; - m_ScaleOrigin = m_HandleScale; - - OnBeginVertexMovement(); - - if (Event.current.modifiers == EventModifiers.Shift) - ShiftExtrude(); - - // cache vertex positions for scaling later - m_VertexPositions = new Vector3[selection.Length][]; - m_VertexOffset = new Vector3[selection.Length]; - - for (int i = 0; i < selection.Length; i++) - { - m_VertexPositions[i] = selection[i].positionsInternal.ValuesWithIndexes(selection[i].selectedIndexesInternal); - m_VertexOffset[i] = Math.Average(m_VertexPositions[i]); - } - } - - Vector3 ver; // resulting vertex from modification - Vector3 over; // vertex point to modify. different for world, local, and plane - - bool gotoWorld = Selection.transforms.Length > 1 && m_HandleOrientation == HandleOrientation.Normal; - bool gotoLocal = MeshSelection.selectedFaceCount < 1; - - // if(pref_snapEnabled) - // pbUndo.RecordSelection(selection as Object[], "Move vertices"); - - for (int i = 0; i < selection.Length; i++) - { - // get the plane rotation in local space - var mesh = selection[i]; - Vector3 nrm = Math.Normal(m_VertexPositions[i]); - Quaternion localRot = Quaternion.LookRotation(nrm == Vector3.zero ? Vector3.forward : nrm, Vector3.up); - - Vector3[] v = mesh.positionsInternal; - List coincident = new List(); - - for (int n = 0; n < mesh.selectedIndexesInternal.Length; n++) - { - switch (m_HandleOrientation.value) - { - case HandleOrientation.Normal: - { - if (gotoWorld) - goto case HandleOrientation.World; - - if (gotoLocal) - goto case HandleOrientation.Local; - - // move center of vertices to 0,0,0 and set rotation as close to identity as possible - over = Quaternion.Inverse(localRot) * (m_VertexPositions[i][n] - m_VertexOffset[i]); - - // apply scale - ver = Vector3.Scale(over, m_HandleScale); - - // re-apply original rotation - if (m_VertexPositions[i].Length > 2) - ver = localRot * ver; - - // re-apply world position offset - ver += m_VertexOffset[i]; - - coincident.Clear(); - mesh.GetCoincidentVertices(mesh.selectedIndexesInternal[n], coincident); - - for (int t = 0, c = coincident.Count; t < c; t++) - v[coincident[t]] = ver; - - break; - } - - case HandleOrientation.World: - case HandleOrientation.Local: - { - // move vertex to relative origin from center of selection - over = m_VertexPositions[i][n] - m_VertexOffset[i]; - // apply scale - ver = Vector3.Scale(over, m_HandleScale); - // move vertex back to locally offset position - ver += m_VertexOffset[i]; - - // set vertex in local space on pb-Object - coincident.Clear(); - mesh.GetCoincidentVertices(mesh.selectedIndexesInternal[n], coincident); - - for (int t = 0, c = coincident.Count; t < c; t++) - v[coincident[t]] = ver; - - break; - } - } - } - - mesh.mesh.vertices = v; - mesh.RefreshUV(MeshSelection.selectedFacesInEditZone[selection[i]]); - mesh.Refresh(RefreshMask.Normals); - mesh.mesh.RecalculateBounds(); - } - - UpdateMeshHandles(false); - } - } - - void VertexRotateTool() - { - if (!m_IsMovingElements) - m_ElementHandlePosition = m_HandlePosition; - - m_HandleRotationPrevious = m_HandleRotation; - - if (m_CurrentEvent.alt) - Handles.RotationHandle(m_HandleRotation, m_ElementHandlePosition); - else - m_HandleRotation = Handles.RotationHandle(m_HandleRotation, m_ElementHandlePosition); - - if (m_HandleRotation != m_HandleRotationPrevious) - { - // profiler.BeginSample("Rotate"); - if (!m_IsMovingElements) - { - m_IsMovingElements = true; - - m_TranslateOrigin = m_ElementHandleCachedPosition; - m_RotateOrigin = m_HandleRotation.eulerAngles; - m_ScaleOrigin = m_HandleScale; - - m_RotationInitial = m_HandleRotationPrevious; - m_RotationInitialInverse = Quaternion.Inverse(m_HandleRotationPrevious); - - OnBeginVertexMovement(); - - if (Event.current.modifiers == EventModifiers.Shift) - ShiftExtrude(); - - // cache vertex positions for modifying later - m_VertexPositions = new Vector3[selection.Length][]; - m_VertexOffset = new Vector3[selection.Length]; - - for (int i = 0; i < selection.Length; i++) - { - Vector3[] vertices = selection[i].positionsInternal; - int[] triangles = selection[i].selectedIndexesInternal; - m_VertexPositions[i] = new Vector3[triangles.Length]; - - for (int nn = 0; nn < triangles.Length; nn++) - m_VertexPositions[i][nn] = selection[i].transform.TransformPoint(vertices[triangles[nn]]); - - if (m_HandleOrientation == HandleOrientation.World) - m_VertexOffset[i] = m_ElementHandlePosition; - else - m_VertexOffset[i] = Math.GetBounds(m_VertexPositions[i]).center; - } - } - - // profiler.BeginSample("Calc Matrix"); - Quaternion transformedRotation = m_RotationInitialInverse * m_HandleRotation; - List coincident = new List(); - - // profiler.BeginSample("matrix mult"); - for (int i = 0; i < selection.Length; i++) - { - Vector3[] v = selection[i].positionsInternal; - SharedVertex[] sharedIndexes = selection[i].sharedVerticesInternal; - - Quaternion lr = m_RotationInitial; // selection[0].transform.localRotation; - Quaternion ilr = m_RotationInitialInverse; // Quaternion.Inverse(lr); - - for (int n = 0; n < selection[i].selectedIndexesInternal.Length; n++) - { - // move vertex to relative origin from center of selection - Vector3 ver = ilr * (m_VertexPositions[i][n] - m_VertexOffset[i]); - - // rotate - ver = transformedRotation * ver; - - // move vertex back to locally offset position - ver = (lr * ver) + m_VertexOffset[i]; - - coincident.Clear(); - selection[i].GetCoincidentVertices(selection[i].selectedIndexesInternal[n], coincident); - - for (int t = 0, c = coincident.Count; t < c; t++) - v[coincident[t]] = selection[i].transform.InverseTransformPoint(ver); - } - - selection[i].mesh.vertices = v; - selection[i].RefreshUV(MeshSelection.selectedFacesInEditZone[selection[i]]); - selection[i].Refresh(RefreshMask.Normals); - selection[i].mesh.RecalculateBounds(); - } - - UpdateMeshHandles(false); - } - } - - /// - /// Extrude the current selection with no translation. - /// - void ShiftExtrude() - { - int ef = 0; - foreach (ProBuilderMesh pb in selection) - { - Undo.RegisterCompleteObjectUndo(selection, "Extrude Vertices"); - - switch (selectMode) - { - case SelectMode.Edge: - if (pb.selectedFaceCount > 0) - goto default; - - Edge[] newEdges = pb.Extrude(pb.selectedEdges, - 0.0001f, - m_ExtrudeEdgesAsGroup, - s_AllowNonManifoldActions); - - if (newEdges != null) - { - ef += newEdges.Length; - pb.SetSelectedEdges(newEdges); - } - break; - - default: - int len = pb.selectedFacesInternal.Length; - - if (len > 0) - { - pb.Extrude(pb.selectedFacesInternal, m_ExtrudeMethod, - 0.0001f); - pb.SetSelectedFaces(pb.selectedFacesInternal); - ef += len; - } - - break; - } - - pb.ToMesh(); - pb.Refresh(); - } - - if (ef > 0) - { - EditorUtility.ShowNotification("Extrude"); - UpdateSelection(); - } - } - - void TextureMoveTool() - { - UVEditor uvEditor = UVEditor.instance; - if (!uvEditor) return; - - Vector3 cached = m_TextureHandlePosition; - - m_TextureHandlePosition = Handles.PositionHandle(m_TextureHandlePosition, m_HandleRotation); - - if (m_CurrentEvent.alt) return; - - if (m_TextureHandlePosition != cached) - { - cached = Quaternion.Inverse(m_HandleRotation) * m_TextureHandlePosition; - cached.y = -cached.y; - - Vector3 lossyScale = selection[0].transform.lossyScale; - Vector3 pos = cached.DivideBy(lossyScale); - - if (!m_IsMovingTextures) - { - m_TextureHandlePositionPrevious = pos; - m_IsMovingTextures = true; - } - - uvEditor.SceneMoveTool(pos - m_TextureHandlePositionPrevious); - m_TextureHandlePositionPrevious = pos; - uvEditor.Repaint(); - } - } - - void TextureRotateTool() - { - UVEditor uvEditor = UVEditor.instance; - if (!uvEditor) return; - - float size = HandleUtility.GetHandleSize(m_HandlePosition); - - if (m_CurrentEvent.alt) return; - - Matrix4x4 prev = Handles.matrix; - Handles.matrix = handleMatrix; - - Quaternion cached = m_TextureRotation; - - m_TextureRotation = Handles.Disc(m_TextureRotation, Vector3.zero, Vector3.forward, size, false, 0f); - - if (m_TextureRotation != cached) - { - if (!m_IsMovingTextures) - m_IsMovingTextures = true; - - uvEditor.SceneRotateTool(-m_TextureRotation.eulerAngles.z); - } - - Handles.matrix = prev; - } - - void TextureScaleTool() - { - UVEditor uvEditor = UVEditor.instance; - if (!uvEditor) return; - - float size = HandleUtility.GetHandleSize(m_HandlePosition); - - Matrix4x4 prev = Handles.matrix; - Handles.matrix = handleMatrix; - - Vector3 cached = m_TextureScale; - m_TextureScale = Handles.ScaleHandle(m_TextureScale, Vector3.zero, Quaternion.identity, size); - - if (m_CurrentEvent.alt) return; - - if (cached != m_TextureScale) - { - if (!m_IsMovingTextures) - m_IsMovingTextures = true; - - uvEditor.SceneScaleTool(m_TextureScale, cached); - } - - Handles.matrix = prev; - } - void DrawHandleGUI(SceneView sceneView) { if (sceneView != SceneView.lastActiveSceneView) @@ -1280,27 +754,6 @@ void DrawHandleGUI(SceneView sceneView) int screenWidth = (int) sceneView.position.width; int screenHeight = (int) sceneView.position.height; - int currentSelectionMode = -1; - - switch (m_SelectMode.value) - { - case SelectMode.Object: - currentSelectionMode = 0; - break; - case SelectMode.Vertex: - currentSelectionMode = 1; - break; - case SelectMode.Edge: - currentSelectionMode = 2; - break; - case SelectMode.Face: - currentSelectionMode = 3; - break; - default: - currentSelectionMode = -1; - break; - } - switch ((SceneToolbarLocation) s_SceneToolbarLocation) { case SceneToolbarLocation.BottomCenter: @@ -1335,43 +788,29 @@ void DrawHandleGUI(SceneView sceneView) break; } - EditorGUI.BeginChangeCheck(); - - currentSelectionMode = - GUI.Toolbar(m_ElementModeToolbarRect, (int) currentSelectionMode, m_EditModeIcons, m_CommandStyle); - - if (EditorGUI.EndChangeCheck()) - { - if (currentSelectionMode == 0) - selectMode = SelectMode.Object; - else if (currentSelectionMode == 1) - selectMode = SelectMode.Vertex; - else if (currentSelectionMode == 2) - selectMode = SelectMode.Edge; - else if (currentSelectionMode == 3) - selectMode = SelectMode.Face; - } - - if (m_IsMovingElements && s_ShowSceneInfo) - { - string handleTransformInfo = string.Format( - "translate: {0}\nrotate: {1}\nscale: {2}", - (m_ElementHandlePosition - m_TranslateOrigin).ToString(), - (m_HandleRotation.eulerAngles - m_RotateOrigin).ToString(), - (m_HandleScale - m_ScaleOrigin).ToString()); - - var gc = UI.EditorGUIUtility.TempContent(handleTransformInfo); - // sceneview screen.height includes the tab and toolbar - var toolbarHeight = EditorStyles.toolbar.CalcHeight(gc, Screen.width); - var size = UI.EditorStyles.sceneTextBox.CalcSize(gc); - - Rect handleTransformInfoRect = new Rect( - sceneView.position.width - (size.x + 8), sceneView.position.height - (size.y + 8 + toolbarHeight), - size.x, - size.y); - - GUI.Label(handleTransformInfoRect, gc, UI.EditorStyles.sceneTextBox); - } + selectMode = UI.EditorGUIUtility.DoElementModeToolbar(m_ElementModeToolbarRect, selectMode); + + // todo Move to VertexManipulationTool +// if (m_IsMovingElements && s_ShowSceneInfo) +// { +// string handleTransformInfo = string.Format( +// "translate: {0}\nrotate: {1}\nscale: {2}", +// (m_ElementHandlePosition - m_TranslateOrigin).ToString(), +// (m_HandleRotation.eulerAngles - m_RotateOrigin).ToString(), +// (m_HandleScale - m_ScaleOrigin).ToString()); +// +// var gc = UI.EditorGUIUtility.TempContent(handleTransformInfo); +// // sceneview screen.height includes the tab and toolbar +// var toolbarHeight = EditorStyles.toolbar.CalcHeight(gc, Screen.width); +// var size = UI.EditorStyles.sceneTextBox.CalcSize(gc); +// +// Rect handleTransformInfoRect = new Rect( +// sceneView.position.width - (size.x + 8), sceneView.position.height - (size.y + 8 + toolbarHeight), +// size.x, +// size.y); +// +// GUI.Label(handleTransformInfoRect, gc, UI.EditorStyles.sceneTextBox); +// } if (s_ShowSceneInfo) { @@ -1499,7 +938,7 @@ bool GeoLevelShortcuts(Shortcut shortcut) return true; case "Delete Face": - EditorUtility.ShowNotification(MenuCommands.MenuDeleteFace(selection).notification); + EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction().notification); return true; /* handle alignment */ @@ -1508,7 +947,7 @@ bool GeoLevelShortcuts(Shortcut shortcut) return false; ToggleHandleAlignment(); - EditorUtility.ShowNotification("Handle Alignment: " + m_HandleOrientation.value.ToString()); + EditorUtility.ShowNotification("Handle Alignment: " + s_HandleOrientation.value.ToString()); return true; case "Set Pivot": @@ -1562,7 +1001,7 @@ void SetTool_Internal(Tool newTool) internal void ToggleHandleAlignment() { - int newHa = (int) m_HandleOrientation.value + 1; + int newHa = (int) s_HandleOrientation.value + 1; if (newHa >= Enum.GetValues(typeof(HandleOrientation)).Length) newHa = 0; handleOrientation = ((HandleOrientation) newHa); @@ -1583,12 +1022,9 @@ internal void ToggleSelectionMode() void UpdateSelection(bool selectionChanged = true) { - selection = MeshSelection.topInternal; - - m_HandlePosition = GetHandlePosition(); - m_HandleRotation = GetHandleRotation(); + // todo remove selection property + selection = MeshSelection.topInternal.ToArray(); - UpdateTextureHandles(); UpdateMeshHandles(selectionChanged); if (selectionChanged) @@ -1601,19 +1037,22 @@ void UpdateSelection(bool selectionChanged = true) selectionUpdated(selection); } - void UpdateMeshHandles(bool selectionOrVertexCountChanged) + internal static void UpdateMeshHandles(bool selectionOrVertexCountChanged = true) { - if (m_EditorMeshHandles == null) + if (!s_Instance) + return; + + if (s_Instance.m_EditorMeshHandles == null) return; try { - m_EditorMeshHandles.RebuildSelectedHandles(MeshSelection.topInternal, selectMode, selectionOrVertexCountChanged); + s_Instance.m_EditorMeshHandles.RebuildSelectedHandles(MeshSelection.topInternal, selectMode, selectionOrVertexCountChanged); } catch { // happens on undo when c++ object is gone but c# isn't in the know - m_EditorMeshHandles.ClearHandles(); + s_Instance.m_EditorMeshHandles.ClearHandles(); } } @@ -1621,14 +1060,14 @@ void UpdateSceneInfo() { m_SceneInfo.text = string.Format( "Faces: {0}\nTriangles: {1}\nVertices: {2} ({3})\n\nSelected Faces: {4}\nSelected Edges: {5}\nSelected Vertices: {6} ({7})", - MeshSelection.totalFaceCount, - MeshSelection.totalTriangleCountCompiled, - MeshSelection.totalCommonVertexCount, - MeshSelection.totalVertexCountOptimized, - MeshSelection.selectedFaceCount, - MeshSelection.selectedEdgeCount, - MeshSelection.selectedSharedVertexCount, - MeshSelection.selectedVertexCount); + MeshSelection.totalFaceCount.ToString(), + MeshSelection.totalTriangleCountCompiled.ToString(), + MeshSelection.totalCommonVertexCount.ToString(), + MeshSelection.totalVertexCountOptimized.ToString(), + MeshSelection.selectedFaceCount.ToString(), + MeshSelection.selectedEdgeCount.ToString(), + MeshSelection.selectedSharedVertexCount.ToString(), + MeshSelection.selectedVertexCount.ToString()); } internal void ClearElementSelection() @@ -1639,110 +1078,6 @@ internal void ClearElementSelection() m_Hovering.Clear(); } - void UpdateTextureHandles() - { - if (!selectMode.ContainsFlag(SelectMode.TextureFace) || !selection.Any()) - return; - - // Reset temp vars - m_TextureHandlePosition = m_HandlePosition; - m_TextureScale = Vector3.one; - m_TextureRotation = Quaternion.identity; - - ProBuilderMesh pb; - Face face; - - handleMatrix = selection[0].transform.localToWorldMatrix; - - if (GetFirstSelectedFace(out pb, out face)) - { - var normals = Math.NormalTangentBitangent(pb, face); - var nrm = normals.normal; - var bitan = normals.bitangent; - - if (nrm == Vector3.zero || bitan == Vector3.zero) - { - nrm = Vector3.up; - bitan = Vector3.right; - } - - handleMatrix *= Matrix4x4.TRS(Math.GetBounds(pb.positionsInternal.ValuesWithIndexes(face.distinctIndexesInternal)).center, - Quaternion.LookRotation(nrm, bitan), Vector3.one); - } - } - - internal Vector3 GetHandlePosition() - { - MeshSelection.RecalculateSelectionBounds(); - return MeshSelection.bounds.center; - } - - internal Quaternion GetHandleRotation() - { - Quaternion localRotation = Selection.activeTransform == null ? Quaternion.identity : Selection.activeTransform.rotation; - - switch (m_HandleOrientation.value) - { - case HandleOrientation.Normal: - - if (Selection.transforms.Length > 1) - goto default; - - ProBuilderMesh pb; - Face face; - - if (!GetFirstSelectedFace(out pb, out face)) - goto case HandleOrientation.Local; - - // use average normal, tangent, and bi-tangent to calculate rotation relative to local space - var tup = Math.NormalTangentBitangent(pb, face); - Vector3 nrm = tup.normal, bitan = tup.bitangent; - - if (nrm == Vector3.zero || bitan == Vector3.zero) - { - nrm = Vector3.up; - bitan = Vector3.right; - } - - return localRotation * Quaternion.LookRotation(nrm, bitan); - - case HandleOrientation.Local: - return localRotation; - - default: - return Quaternion.identity; - } - } - - /// - /// Find the nearest vertex among all visible objects. - /// - /// - /// - /// - bool FindNearestVertex(Vector2 mousePosition, out Vector3 vertex) - { - List t = - new List( - (Transform[]) InternalUtility.GetComponents( - HandleUtility.PickRectObjects(new Rect(0, 0, Screen.width, Screen.height)))); - - GameObject nearest = HandleUtility.PickGameObject(mousePosition, false); - - if (nearest != null) - t.Add(nearest.transform); - - object[] parameters = new object[] { (Vector2) mousePosition, t.ToArray(), null }; - - if (m_FindNearestVertex == null) - m_FindNearestVertex = typeof(HandleUtility).GetMethod("findNearestVertex", - BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance); - - object result = m_FindNearestVertex.Invoke(this, parameters); - vertex = (bool) result ? (Vector3) parameters[2] : Vector3.zero; - return (bool) result; - } - /// /// If dragging a texture aroudn, this method ensures that if it's a member of a texture group it's cronies are also selected /// @@ -1845,81 +1180,6 @@ internal void OnBeginTextureModification() VerifyTextureGroupSelection(); } - /// - /// When beginning a vertex modification, nuke the UV2 and rebuild the mesh using PB data so that triangles - /// match vertices (and no inserted vertices from the Unwrapping.GenerateSecondaryUVSet() remain). - /// - void OnBeginVertexMovement() - { - switch (m_CurrentTool) - { - case Tool.Move: - UndoUtility.RegisterCompleteObjectUndo(selection, "Translate Vertices"); - break; - - case Tool.Rotate: - UndoUtility.RegisterCompleteObjectUndo(selection, "Rotate Vertices"); - break; - - case Tool.Scale: - UndoUtility.RegisterCompleteObjectUndo(selection, "Scale Vertices"); - break; - - default: - UndoUtility.RegisterCompleteObjectUndo(selection, "Modify Vertices"); - break; - } - - m_SnapEnabled = ProGridsInterface.SnapEnabled(); - m_SnapValue = ProGridsInterface.SnapValue(); - m_SnapAxisConstraint = ProGridsInterface.UseAxisConstraints(); - - // Disable iterative lightmapping - Lightmapping.PushGIWorkflowMode(); - - foreach (ProBuilderMesh pb in selection) - { - pb.ToMesh(); - pb.Refresh(); - } - - if (beforeMeshModification != null) - beforeMeshModification(selection); - } - - void OnFinishVertexModification() - { - Lightmapping.PopGIWorkflowMode(); - - m_HandleScale = Vector3.one; - m_HandleRotation = GetHandleRotation(); - - if (m_IsMovingTextures) - { - if (UVEditor.instance != null) - UVEditor.instance.OnFinishUVModification(); - - UpdateTextureHandles(); - m_IsMovingTextures = false; - } - else if (m_IsMovingElements) - { - foreach (ProBuilderMesh sel in selection) - { - sel.ToMesh(); - sel.Refresh(); - sel.Optimize(); - } - - m_IsMovingElements = false; - } - - UpdateSelection(); - - if (afterMeshModification != null) - afterMeshModification(selection); - } - /// /// Returns the first selected pb_Object and pb_Face, or false if not found. /// diff --git a/com.unity.probuilder/Editor/EditorCore/ProBuilderMeshEditor.cs b/com.unity.probuilder/Editor/EditorCore/ProBuilderMeshEditor.cs index 18f40edd6..c814452a3 100644 --- a/com.unity.probuilder/Editor/EditorCore/ProBuilderMeshEditor.cs +++ b/com.unity.probuilder/Editor/EditorCore/ProBuilderMeshEditor.cs @@ -6,7 +6,7 @@ namespace UnityEditor.ProBuilder { /// /// - /// Custom editor for pb_Object type. + /// Custom editor for ProBuilderMesh type. /// [CustomEditor(typeof(ProBuilderMesh))] [CanEditMultipleObjects] @@ -70,14 +70,14 @@ void OnEnable() foreach (var mesh in Selection.transforms.GetComponents()) EditorUtility.SynchronizeWithMeshFilter(mesh); - ProBuilderEditor.beforeMeshModification += OnBeginMeshModification; - ProBuilderEditor.afterMeshModification += OnFinishMeshModification; + VertexManipulationTool.beforeMeshModification += OnBeginMeshModification; + VertexManipulationTool.afterMeshModification += OnFinishMeshModification; } void OnDisable() { - ProBuilderEditor.beforeMeshModification -= OnBeginMeshModification; - ProBuilderEditor.afterMeshModification -= OnFinishMeshModification; + VertexManipulationTool.beforeMeshModification -= OnBeginMeshModification; + VertexManipulationTool.afterMeshModification -= OnFinishMeshModification; } void OnBeginMeshModification(ProBuilderMesh[] selection) diff --git a/com.unity.probuilder/Editor/EditorCore/SmoothGroupEditor.cs b/com.unity.probuilder/Editor/EditorCore/SmoothGroupEditor.cs index 2cd93d51f..f8a7ae756 100644 --- a/com.unity.probuilder/Editor/EditorCore/SmoothGroupEditor.cs +++ b/com.unity.probuilder/Editor/EditorCore/SmoothGroupEditor.cs @@ -334,8 +334,8 @@ void OnEnable() Selection.selectionChanged += OnSelectionChanged; Undo.undoRedoPerformed += OnSelectionChanged; ProBuilderMesh.elementSelectionChanged += OnElementSelectionChanged; - ProBuilderEditor.beforeMeshModification += OnBeginVertexMovement; - ProBuilderEditor.afterMeshModification += OnFinishVertexMovement; + VertexManipulationTool.beforeMeshModification += OnBeginVertexMovement; + VertexManipulationTool.afterMeshModification += OnFinishVertexMovement; autoRepaintOnSceneChange = true; m_HelpIcon = new GUIContent(IconUtility.GetIcon("Toolbar/Help"), "Open Documentation"); m_BreakSmoothingContent = new GUIContent(IconUtility.GetIcon("Toolbar/Face_BreakSmoothing"), diff --git a/com.unity.probuilder/Editor/EditorCore/StaticEditorMeshHandles.cs b/com.unity.probuilder/Editor/EditorCore/StaticEditorMeshHandles.cs new file mode 100644 index 000000000..3f583654d --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/StaticEditorMeshHandles.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UObject = UnityEngine.Object; +using UnityEngine.ProBuilder; +using System.Reflection; +using UnityEngine.Rendering; + +namespace UnityEditor.ProBuilder +{ + partial class EditorMeshHandles + { + static bool s_Initialized; + static Material s_LineMaterial; + static Material s_FaceMaterial; + + static Mesh s_FaceMesh; + + static void Init() + { + if (s_Initialized) + return; + s_Initialized = true; + + var shader = BuiltinMaterials.geometryShadersSupported ? BuiltinMaterials.lineShader : BuiltinMaterials.wireShader; + s_LineMaterial = CreateMaterial(Shader.Find(shader), "ProBuilder::GeneralUseLineMaterial"); + + s_FaceMesh = new Mesh(); + s_FaceMesh.hideFlags = HideFlags.HideAndDontSave; + + s_FaceMaterial = CreateMaterial(Shader.Find(BuiltinMaterials.faceShader), "ProBuilder::FaceMaterial"); + s_FaceMaterial.SetFloat("_Dither", (s_UseUnityColors || s_DitherFaceHandle) ? 1f : 0f); + } + + internal static void DrawGizmo(Vector3 position, Quaternion rotation, float size = -1f) + { + var p = position; + size = HandleUtility.GetHandleSize(p) * size < 0f ? .5f : size; + + using (var lineDrawer = new LineDrawingScope(Color.green, -1f, CompareFunction.Always)) + { + lineDrawer.DrawLine(p, p + rotation * Vector3.up * size); + lineDrawer.color = Color.red; + lineDrawer.DrawLine(p, p + rotation * Vector3.right * size); + lineDrawer.color = Color.blue; + lineDrawer.DrawLine(p, p + rotation * Vector3.forward * size); + } + } + + internal static void DrawGizmo(Vector3 position, Matrix4x4 matrix, float size = -1f) + { + var p = matrix.MultiplyPoint3x4(position); + size = HandleUtility.GetHandleSize(p) * size < 0f ? .2f : size; + + using (var lineDrawer = new LineDrawingScope(Color.green, -1f, CompareFunction.Always)) + { + lineDrawer.DrawLine(p, p + matrix.MultiplyVector(Vector3.up) * size); + lineDrawer.color = Color.red; + lineDrawer.DrawLine(p, p + matrix.MultiplyVector(Vector3.right) * size); + lineDrawer.color = Color.blue; + lineDrawer.DrawLine(p, p + matrix.MultiplyVector(Vector3.forward) * size); + } + } + + internal class LineDrawingScope : IDisposable + { + bool m_Wire; + Color m_Color; + float m_Thickness; + CompareFunction m_ZTest; + bool m_IsDisposed; + + public Color color + { + get { return m_Color; } + + set + { + if(!m_Wire) + End(); + + m_Color = value; + + if(!m_Wire) + Begin(); + } + } + + public float thickness + { + get { return m_Thickness; } + + set + { + End(); + if (value < Mathf.Epsilon) + m_Thickness = s_EdgeLineSize; + else + m_Thickness = value; + Begin(); + } + } + + public CompareFunction zTest + { + get { return m_ZTest; } + + set + { + End(); + m_ZTest = value; + Begin(); + } + } + + public LineDrawingScope(Color color, float thickness = -1f, CompareFunction zTest = CompareFunction.LessEqual) + { + Init(); + m_Color = color; + m_Thickness = thickness < 0f ? s_EdgeLineSize : thickness; + m_ZTest = zTest; + Begin(); + } + + void Begin() + { + m_Wire = thickness < .01f || !BuiltinMaterials.geometryShadersSupported; + + if (!m_Wire) + { + s_LineMaterial.SetColor("_Color", color); + s_LineMaterial.SetFloat("_Scale", thickness * EditorGUIUtility.pixelsPerPoint); + s_LineMaterial.SetInt("_HandleZTest", (int) zTest); + } + + if (m_Wire || !s_LineMaterial.SetPass(0)) + { + if (s_ApplyWireMaterial == null) + { + s_ApplyWireMaterial = typeof(HandleUtility).GetMethod( + "ApplyWireMaterial", + BindingFlags.Static | BindingFlags.NonPublic, + null, + new System.Type[] { typeof(CompareFunction) }, + null); + + if (s_ApplyWireMaterial == null) + throw new Exception("Failed to find wire material, stopping draw lines."); + } + + s_ApplyWireMaterialArgs[0] = zTest; + s_ApplyWireMaterial.Invoke(null, s_ApplyWireMaterialArgs); + } + + GL.PushMatrix(); + GL.Begin(GL.LINES); + } + + void End() + { + GL.End(); + GL.PopMatrix(); + } + + public void DrawLine(Vector3 a, Vector3 b) + { + if(m_Wire) + GL.Color(color); + + GL.Vertex(a); + GL.Vertex(b); + } + + public void Dispose() + { + if (m_IsDisposed) + return; + m_IsDisposed = true; + + End(); + } + } + + internal class TriangleDrawingScope : IDisposable + { + Color m_Color; + CompareFunction m_ZTest; + bool m_IsDisposed; + + public Color color + { + get { return m_Color; } + set + { + End(); + m_Color = value; + Begin(); + } + } + + public CompareFunction zTest + { + get { return m_ZTest; } + + set + { + End(); + m_ZTest = value; + Begin(); + } + } + + public TriangleDrawingScope(Color color, CompareFunction zTest = CompareFunction.LessEqual) + { + Init(); + m_Color = color; + m_ZTest = zTest; + Begin(); + } + + void Begin() + { + s_FaceMaterial.SetColor("_Color", color); + s_FaceMaterial.SetInt("_HandleZTest", (int) zTest); + + if (!s_FaceMaterial.SetPass(0)) + throw new Exception("Failed initializing face material."); + + GL.PushMatrix(); + GL.Begin(GL.TRIANGLES); + } + + void End() + { + GL.End(); + GL.PopMatrix(); + } + + public void Dispose() + { + if (m_IsDisposed) + return; + m_IsDisposed = true; + End(); + } + + public void Draw(Vector3 a, Vector3 b, Vector3 c) + { + GL.Vertex(a); + GL.Vertex(b); + GL.Vertex(c); + } + } + } +} diff --git a/com.unity.probuilder/Editor/EditorCore/MenuCommands.cs.meta b/com.unity.probuilder/Editor/EditorCore/StaticEditorMeshHandles.cs.meta similarity index 83% rename from com.unity.probuilder/Editor/EditorCore/MenuCommands.cs.meta rename to com.unity.probuilder/Editor/EditorCore/StaticEditorMeshHandles.cs.meta index 50995fd15..a37c44ec4 100644 --- a/com.unity.probuilder/Editor/EditorCore/MenuCommands.cs.meta +++ b/com.unity.probuilder/Editor/EditorCore/StaticEditorMeshHandles.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9d08e2d1f66b00d499490c18fffd5393 +guid: 36feefd7c908ad14fbe6f426fe0d702e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.probuilder/Editor/EditorCore/TextureMoveTool.cs b/com.unity.probuilder/Editor/EditorCore/TextureMoveTool.cs new file mode 100644 index 000000000..61c8329c6 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/TextureMoveTool.cs @@ -0,0 +1,84 @@ +using UnityEngine; +using UnityEngine.ProBuilder; + +namespace UnityEditor.ProBuilder +{ + class TextureMoveTool : TextureTool + { + Vector3 m_Position = Vector3.zero; + + protected override void DoTool(Vector3 handlePosition, Quaternion handleRotation) + { + if (!isEditing) + m_Position = Vector3.zero; + + EditorHandleUtility.PushMatrix(); + + Handles.matrix = Matrix4x4.TRS(handlePosition, handleRotation, Vector3.one); + + EditorGUI.BeginChangeCheck(); + + Handles.color = Color.blue; + + m_Position = Handles.Slider2D(m_Position, + Vector3.forward, + Vector3.right, + Vector3.up, + HandleUtility.GetHandleSize(m_Position) * .2f, + Handles.RectangleHandleCap, + 0f, + false); + + Handles.color = Color.green; + + m_Position = Handles.Slider(m_Position, Vector3.up); + + Handles.color = Color.red; + + m_Position = Handles.Slider(m_Position, Vector3.right); + + Handles.color = Color.white; + + if (EditorGUI.EndChangeCheck()) + { + if(!isEditing) + BeginEdit("Translate Textures"); + + if (relativeSnapEnabled) + { + m_Position.x = Snapping.SnapValue(m_Position.x, relativeSnapX); + m_Position.y = Snapping.SnapValue(m_Position.y, relativeSnapY); + } + else if (progridsSnapEnabled) + { + m_Position.x = Snapping.SnapValue(m_Position.x, progridsSnapValue); + m_Position.y = Snapping.SnapValue(m_Position.y, progridsSnapValue); + } + + // invert `y` because to users it's confusing that "up" in UV space visually moves the texture down + var delta = new Vector4(m_Position.x, -m_Position.y, 0f, 0f); + + foreach (var mesh in meshAndElementGroupPairs) + { + if (!(mesh is MeshAndTextures)) + continue; + + delta *= 1f / mesh.mesh.transform.lossyScale.magnitude; + + var origins = ((MeshAndTextures)mesh).origins; + var positions = ((MeshAndTextures)mesh).textures; + + foreach (var group in mesh.elementGroups) + { + foreach(var index in group.indices) + positions[index] = origins[index] + delta; + } + + mesh.mesh.mesh.SetUVs(k_TextureChannel, positions); + } + } + + EditorHandleUtility.PopMatrix(); + } + } +} diff --git a/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleAlignment.cs.meta b/com.unity.probuilder/Editor/EditorCore/TextureMoveTool.cs.meta similarity index 69% rename from com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleAlignment.cs.meta rename to com.unity.probuilder/Editor/EditorCore/TextureMoveTool.cs.meta index 0c7d3719f..0e63e5018 100644 --- a/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleAlignment.cs.meta +++ b/com.unity.probuilder/Editor/EditorCore/TextureMoveTool.cs.meta @@ -1,8 +1,7 @@ fileFormatVersion: 2 -guid: 1252cef8e85b4b55917bf9b538a8289c -timeCreated: 1457453191 -licenseType: Pro +guid: 7ebc93f048090074f80a100718d5a585 MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/com.unity.probuilder/Editor/EditorCore/TextureRotateTool.cs b/com.unity.probuilder/Editor/EditorCore/TextureRotateTool.cs new file mode 100644 index 000000000..fb0100aab --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/TextureRotateTool.cs @@ -0,0 +1,65 @@ +using UnityEngine; +using UnityEngine.ProBuilder; + +namespace UnityEditor.ProBuilder +{ + class TextureRotateTool : TextureTool + { + float m_Rotation; + Vector3 m_Euler; + Quaternion m_Quaternion; + + protected override void DoTool(Vector3 handlePosition, Quaternion handleRotation) + { + if (!isEditing) + m_Rotation = 0f; + + EditorGUI.BeginChangeCheck(); + + var size = HandleUtility.GetHandleSize(handlePosition); + + EditorHandleUtility.PushMatrix(); + + Handles.matrix = Matrix4x4.TRS(handlePosition, handleRotation, Vector3.one); + + Handles.color = Color.blue; + m_Euler.z = m_Rotation; + m_Quaternion = Quaternion.Euler(m_Euler); + m_Quaternion = Handles.Disc(m_Quaternion, Vector3.zero, Vector3.forward, size, relativeSnapEnabled, relativeSnapRotation); + m_Euler = m_Quaternion.eulerAngles; + m_Rotation = m_Euler.z; + + EditorHandleUtility.PopMatrix(); + + if (EditorGUI.EndChangeCheck()) + { + if(!isEditing) + BeginEdit("Rotate Textures"); + + if (relativeSnapEnabled) + m_Rotation = Snapping.SnapValue(m_Rotation, relativeSnapX); + else if (progridsSnapEnabled) + m_Rotation = Snapping.SnapValue(m_Rotation, progridsSnapValue); + + foreach (var mesh in meshAndElementGroupPairs) + { + if (!(mesh is MeshAndTextures)) + continue; + + var origins = ((MeshAndTextures)mesh).origins; + var positions = ((MeshAndTextures)mesh).textures; + + foreach (var group in mesh.elementGroups) + { + foreach(var index in group.indices) + positions[index] = group.inverseMatrix.MultiplyPoint( + Math.RotateAroundPoint( + group.matrix.MultiplyPoint3x4(origins[index]), Vector2.zero, -m_Rotation)); + } + + mesh.mesh.mesh.SetUVs(k_TextureChannel, positions); + } + } + } + } +} diff --git a/com.unity.probuilder/Editor/EditorCore/TextureRotateTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/TextureRotateTool.cs.meta new file mode 100644 index 000000000..a9d6d37fb --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/TextureRotateTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85900fb5729b6db42a6e417ad0d4c0f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/EditorCore/TextureScaleTool.cs b/com.unity.probuilder/Editor/EditorCore/TextureScaleTool.cs new file mode 100644 index 000000000..91fa348f9 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/TextureScaleTool.cs @@ -0,0 +1,75 @@ +using UnityEngine; +using UnityEngine.ProBuilder; + +namespace UnityEditor.ProBuilder +{ + class TextureScaleTool : TextureTool + { + Vector2 m_Scale; + float m_UniformScale; + + protected override void DoTool(Vector3 handlePosition, Quaternion handleRotation) + { + if (!isEditing) + { + m_Scale.x = 1f; + m_Scale.y = 1f; + m_UniformScale = 1f; + } + + EditorGUI.BeginChangeCheck(); + + var size = HandleUtility.GetHandleSize(handlePosition); + + EditorHandleUtility.PushMatrix(); + + Handles.matrix = Matrix4x4.TRS(handlePosition, handleRotation, Vector3.one); + + var snap = relativeSnapEnabled + ? relativeSnapScale + : progridsSnapEnabled + ? progridsSnapValue + : 0f; + + Handles.color = Color.red; + m_Scale.x = Handles.ScaleSlider(m_Scale.x, Vector3.zero, Vector3.right, Quaternion.identity, size, snap); + + Handles.color = Color.green; + m_Scale.y = Handles.ScaleSlider(m_Scale.y, Vector3.zero, Vector3.up, Quaternion.identity, size, snap); + + Handles.color = Color.blue; + m_UniformScale = Handles.ScaleValueHandle(m_UniformScale, Vector3.zero, Quaternion.identity, size, Handles.CubeHandleCap, snap); + + EditorHandleUtility.PopMatrix(); + + if (EditorGUI.EndChangeCheck()) + { + if(!isEditing) + BeginEdit("Scale Textures"); + + var delta = m_Scale * m_UniformScale; + + delta.x = 1f / delta.x; + delta.y = 1f / delta.y; + + foreach (var mesh in meshAndElementGroupPairs) + { + if (!(mesh is MeshAndTextures)) + continue; + + var origins = ((MeshAndTextures)mesh).origins; + var positions = ((MeshAndTextures)mesh).textures; + + foreach (var group in mesh.elementGroups) + { + foreach(var index in group.indices) + positions[index] = group.inverseMatrix.MultiplyPoint( + Vector2.Scale(group.matrix.MultiplyPoint3x4(origins[index]), delta)); + } + + mesh.mesh.mesh.SetUVs(k_TextureChannel, positions); + } + } + } + } +} diff --git a/com.unity.probuilder/Editor/EditorCore/TextureScaleTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/TextureScaleTool.cs.meta new file mode 100644 index 000000000..583b85f9f --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/TextureScaleTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ffea9638e10c9644aab23c2652f86279 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/EditorCore/TextureTool.cs b/com.unity.probuilder/Editor/EditorCore/TextureTool.cs new file mode 100644 index 000000000..e37de43b6 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/TextureTool.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.ProBuilder; + +namespace UnityEditor.ProBuilder +{ + abstract class TextureTool : VertexManipulationTool + { + const bool k_CollectCoincidentVertices = false; + protected const int k_TextureChannel = 0; + + const string UnityMoveSnapX = "MoveSnapX"; + const string UnityMoveSnapY = "MoveSnapY"; + const string UnityMoveSnapZ = "MoveSnapZ"; + const string UnityScaleSnap = "ScaleSnap"; + const string UnityRotateSnap = "RotationSnap"; + + protected static float relativeSnapX + { + get { return EditorPrefs.GetFloat(UnityMoveSnapX, 1f); } + } + + protected static float relativeSnapY + { + get { return EditorPrefs.GetFloat(UnityMoveSnapY, 1f); } + } + + protected static float relativeSnapZ + { + get { return EditorPrefs.GetFloat(UnityMoveSnapZ, 1f); } + } + + protected static float relativeSnapScale + { + get { return EditorPrefs.GetFloat(UnityScaleSnap, .1f); } + } + + protected static float relativeSnapRotation + { + get { return EditorPrefs.GetFloat(UnityRotateSnap, 15f); } + } + + protected class MeshAndTextures : MeshAndElementGroupPair + { + List m_Origins; + List m_Textures; + + public List textures + { + get { return m_Textures; } + } + + public List origins + { + get { return m_Origins; } + } + + public MeshAndTextures(ProBuilderMesh mesh, PivotPoint pivot) : base(mesh, pivot, k_CollectCoincidentVertices) + { + m_Textures = new List(); + mesh.GetUVs(k_TextureChannel, m_Textures); + m_Origins = new List(m_Textures); + + foreach (var group in elementGroups) + { + var bounds = Bounds2D.Center(m_Origins, group.indices); + group.matrix = Matrix4x4.Translate(-bounds); + } + } + } + + protected override void OnToolDisengaged() + { + var isFaceMode = ProBuilderEditor.selectMode.ContainsFlag(SelectMode.TextureFace | SelectMode.Face); + + foreach (var mesh in meshAndElementGroupPairs) + { + if (!(mesh is MeshAndTextures)) + continue; + + if (isFaceMode) + { + foreach (var face in mesh.mesh.selectedFacesInternal) + face.manualUV = true; + } + else + { + var indices = new HashSet(mesh.elementGroups.SelectMany(x => x.indices)); + + foreach (var face in mesh.mesh.facesInternal) + { + foreach (var index in face.distinctIndexesInternal) + { + if (indices.Contains(index)) + { + face.manualUV = true; + break; + } + } + } + } + + var textures = ((MeshAndTextures)mesh).textures; + mesh.mesh.SetUVs(k_TextureChannel, textures); + } + } + + protected override MeshAndElementGroupPair GetMeshAndElementGroupPair(ProBuilderMesh mesh, PivotPoint pivot) + { + return new MeshAndTextures(mesh, pivot); + } + } +} diff --git a/com.unity.probuilder/Editor/EditorCore/TextureTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/TextureTool.cs.meta new file mode 100644 index 000000000..600f166bc --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/TextureTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7fd9f5ac1b5cea54386d6497a518c29f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/EditorCore/UVEditor.cs b/com.unity.probuilder/Editor/EditorCore/UVEditor.cs index 27ddb5c82..9c86e897a 100644 --- a/com.unity.probuilder/Editor/EditorCore/UVEditor.cs +++ b/com.unity.probuilder/Editor/EditorCore/UVEditor.cs @@ -158,7 +158,6 @@ enum UVMode Tool tool = Tool.Move; GUIContent[] ToolIcons; - GUIContent[] SelectionIcons; struct ObjectElementIndex { @@ -289,10 +288,6 @@ void InitGUI() Texture2D scaleIcon = (Texture2D)loadIconMethod.Invoke(null, new object[] { "ScaleTool" }); Texture2D viewIcon = (Texture2D)loadIconMethod.Invoke(null, new object[] { "ViewToolMove" }); - Texture2D face_Graphic_off = IconUtility.GetIcon("Modes/Mode_Face"); - Texture2D vertex_Graphic_off = IconUtility.GetIcon("Modes/Mode_Vertex"); - Texture2D edge_Graphic_off = IconUtility.GetIcon("Modes/Mode_Edge"); - icon_textureMode_on = IconUtility.GetIcon("UVEditor/ProBuilderGUI_UV_ShowTexture_On", IconSkin.Pro); icon_textureMode_off = IconUtility.GetIcon("UVEditor/ProBuilderGUI_UV_ShowTexture_Off", IconSkin.Pro); @@ -308,13 +303,6 @@ void InitGUI() new GUIContent(rotateIcon, "Rotate Tool"), new GUIContent(scaleIcon, "Scale Tool") }; - - SelectionIcons = new GUIContent[3] - { - new GUIContent(vertex_Graphic_off, "Vertex Selection"), - new GUIContent(edge_Graphic_off, "Edge Selection"), - new GUIContent(face_Graphic_off, "Face Selection") - }; } #endregion #region GUI Loop @@ -1017,6 +1005,7 @@ bool UpdateNearestElement(Vector2 mousePosition) switch (ProBuilderEditor.selectMode) { case SelectMode.Edge: + case SelectMode.TextureEdge: float dist, best = 100f; try @@ -1052,6 +1041,7 @@ bool UpdateNearestElement(Vector2 mousePosition) break; case SelectMode.Face: + case SelectMode.TextureFace: try { @@ -1123,6 +1113,7 @@ void OnMouseClick(Vector2 mousePosition) switch (ProBuilderEditor.selectMode) { case SelectMode.Edge: + case SelectMode.TextureEdge: if (nearestElement.valid) { ProBuilderMesh mesh = selection[nearestElement.objectIndex]; @@ -1139,6 +1130,7 @@ void OnMouseClick(Vector2 mousePosition) break; case SelectMode.Face: + case SelectMode.TextureFace: Vector2 mpos = GUIToUVPoint(mousePosition); bool superBreak = false; @@ -1170,6 +1162,7 @@ void OnMouseClick(Vector2 mousePosition) break; case SelectMode.Vertex: + case SelectMode.TextureVertex: RefreshUVCoordinates(new Rect(mousePosition.x - 8, mousePosition.y - 8, 16, 16), true); break; } @@ -1236,7 +1229,7 @@ void MoveTool() if (ControlKey) { - handlePosition = Snapping.SnapValue(t_handlePosition, (handlePosition - t_handlePosition).ToMask(Math.handleEpsilon) * s_GridSnapIncrement); + handlePosition = Snapping.SnapValue(t_handlePosition, (Vector3) new Vector3Mask((handlePosition - t_handlePosition), Math.handleEpsilon) * s_GridSnapIncrement); } else { @@ -1298,7 +1291,7 @@ void MoveTool() Vector2 newUVPosition = t_handlePosition; if (ControlKey) - newUVPosition = Snapping.SnapValue(newUVPosition, (handlePosition - t_handlePosition).ToMask(Math.handleEpsilon) * s_GridSnapIncrement); + newUVPosition = Snapping.SnapValue(newUVPosition, new Vector3Mask((handlePosition - t_handlePosition), Math.handleEpsilon) * s_GridSnapIncrement); for (int n = 0; n < selection.Length; n++) { @@ -1388,7 +1381,7 @@ internal void SceneMoveTool(Vector2 delta) handlePosition.y += delta.y; if (ControlKey) - handlePosition = Snapping.SnapValue(handlePosition, (handlePosition - handlePosition).ToMask(Math.handleEpsilon) * s_GridSnapIncrement); + handlePosition = Snapping.SnapValue(handlePosition, new Vector3Mask((handlePosition - handlePosition), Math.handleEpsilon) * s_GridSnapIncrement); for (int n = 0; n < selection.Length; n++) { @@ -1824,7 +1817,7 @@ private void DrawUVGraph(Rect rect) r.height = DOT_SIZE; // Draw all vertices if in vertex mode - if (ProBuilderEditor.selectMode == SelectMode.Vertex && screenshotStatus == ScreenshotStatus.Done) + if (ProBuilderEditor.selectMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex) && screenshotStatus == ScreenshotStatus.Done) { for (int i = 0; i < selection.Length; i++) { @@ -1882,7 +1875,7 @@ private void DrawUVGraph(Rect rect) } Handles.EndGUI(); } - #endif +#endif GUI.color = Color.white; @@ -2000,7 +1993,7 @@ private void DrawUVGraph(Rect rect) if (pb.selectedEdgeCount > 0) { - foreach (Edge edge in pb.selectedEdges) + foreach (Edge edge in pb.selectedEdgesInternal) { x = UVToGUIPoint(uv[edge.a]); y = UVToGUIPoint(uv[edge.b]); @@ -2021,6 +2014,7 @@ private void DrawUVGraph(Rect rect) switch (ProBuilderEditor.selectMode) { case SelectMode.Edge: + case SelectMode.TextureEdge: GL.Begin(GL.LINES); GL.Color(Color.red); @@ -2036,6 +2030,7 @@ private void DrawUVGraph(Rect rect) break; case SelectMode.Face: + case SelectMode.TextureFace: { Vector3 v = Vector3.zero; @@ -2331,6 +2326,7 @@ void RefreshUVCoordinates(Rect? dragRect, bool isClick) switch (ProBuilderEditor.selectMode) { case SelectMode.Vertex: + case SelectMode.TextureVertex: List selectedTris = new List(pb.selectedIndexesInternal); for (int j = 0; j < len; j++) @@ -2354,6 +2350,7 @@ void RefreshUVCoordinates(Rect? dragRect, bool isClick) break; case SelectMode.Edge: + case SelectMode.TextureEdge: List selectedEdges = new List(pb.selectedEdges); for (int n = 0; n < pb.facesInternal.Length; n++) @@ -2379,6 +2376,7 @@ void RefreshUVCoordinates(Rect? dragRect, bool isClick) * Check if any of the faces intersect with the mousedrag rect. */ case SelectMode.Face: + case SelectMode.TextureFace: HashSet selectedFaces = new HashSet(selection[i].selectedFacesInternal); @@ -2399,16 +2397,6 @@ void RefreshUVCoordinates(Rect? dragRect, bool isClick) } } - // // if(dragBounds.Intersects(faceBounds)) - // for(int t = 0; t < uvs.Length; t++) - // { - // if(!dragBounds.ContainsPoint(uvs[t])) - // { - // allPointsContained = false; - // break; - // } - // } - if (allPointsContained) { if (selectedFaces.Contains(face)) @@ -2473,9 +2461,6 @@ void DrawUVTools(Rect rect) if (commandStyle == null) commandStyle = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle("Command"); - /** - * Handle toggles and SelectionMode toggles. - */ EditorGUI.BeginChangeCheck(); tool = (Tool)GUI.Toolbar(toolbarRect_tool, (int)tool < 0 ? 0 : (int)tool, ToolIcons, "Command"); @@ -2486,41 +2471,23 @@ void DrawUVTools(Rect rect) SceneView.RepaintAll(); } - var mode = ProBuilderEditor.selectMode; - - int currentSelectionMode = mode == SelectMode.Vertex ? 0 - : mode == SelectMode.Edge ? 1 - : mode == SelectMode.Face ? 2 : -1; - - GUI.enabled = channel == 0; - - EditorGUI.BeginChangeCheck(); - currentSelectionMode = GUI.Toolbar(toolbarRect_select, currentSelectionMode, SelectionIcons, "Command"); - if (EditorGUI.EndChangeCheck()) - { - if (currentSelectionMode == 0) - ProBuilderEditor.selectMode = SelectMode.Vertex; - else if (currentSelectionMode == 1) - ProBuilderEditor.selectMode = SelectMode.Edge; - else if (currentSelectionMode == 2) - ProBuilderEditor.selectMode = SelectMode.Face; - } - + ProBuilderEditor.selectMode = UI.EditorGUIUtility.DoElementModeToolbar(toolbarRect_select, ProBuilderEditor.selectMode); // begin Editor pref toggles (Show Texture, Lock UV sceneview handle, etc) - - Rect editor_toggles_rect = new Rect(toolbarRect_select.x + 130, PAD - 1, 36f, 22f); + Rect editor_toggles_rect = new Rect(toolbarRect_select.x + 140, PAD - 1, 36f, 22f); if (editor) { - gc_SceneViewUVHandles.image = ProBuilderEditor.selectMode == SelectMode.TextureFace ? icon_sceneUV_on : icon_sceneUV_off; + gc_SceneViewUVHandles.image = ProBuilderEditor.selectMode.IsTextureMode() ? icon_sceneUV_on : icon_sceneUV_off; if (GUI.Button(editor_toggles_rect, gc_SceneViewUVHandles)) { - if (ProBuilderEditor.selectMode == SelectMode.TextureFace) - ProBuilderEditor.ResetToLastSelectMode(); + if (ProBuilderEditor.selectMode.IsTextureMode()) + ProBuilderEditor.selectMode = ProBuilderEditor.selectMode.GetPositionMode(); else - ProBuilderEditor.selectMode = SelectMode.TextureFace; + ProBuilderEditor.selectMode = ProBuilderEditor.selectMode.GetTextureMode(); + + SceneView.RepaintAll(); } } diff --git a/com.unity.probuilder/Editor/EditorCore/UndoUtility.cs b/com.unity.probuilder/Editor/EditorCore/UndoUtility.cs index cb502059f..6228b7b2f 100644 --- a/com.unity.probuilder/Editor/EditorCore/UndoUtility.cs +++ b/com.unity.probuilder/Editor/EditorCore/UndoUtility.cs @@ -19,7 +19,7 @@ static void UndoRedoPerformed() if (SceneDragAndDropListener.isDragging) return; - foreach(var mesh in InternalUtility.GetComponents(Selection.transforms)) + foreach (var mesh in InternalUtility.GetComponents(Selection.transforms)) { mesh.InvalidateCaches(); mesh.ToMesh(); @@ -37,12 +37,32 @@ static void UndoRedoPerformed() */ public static void RecordSelection(ProBuilderMesh pb, string msg) { - if( pb.vertexCount > 256 ) + if (pb.vertexCount > 256) RegisterCompleteObjectUndo(pb, msg); else Undo.RecordObject(pb, msg); } + internal static void RecordSelection(string message) + { + RecordSelection(MeshSelection.topInternal.ToArray(), message); + } + + internal static void RecordMeshAndTransformSelection(string message) + { + var count = MeshSelection.selectedObjectCount; + var res = new Object [count]; + var selection = MeshSelection.topInternal; + + for (int i = 0, c = count; i < c; i++) + { + res[i] = selection[i]; + res[i+c] = selection[i].transform; + } + + Undo.RegisterCompleteObjectUndo(res, message); + } + /** * Tests if any pb_Object in the selection has more than 512 vertices, and if so records the entire object * instead of diffing the serialized object (which is very slow for large arrays). diff --git a/com.unity.probuilder/Editor/EditorCore/VertexManipulationTool.cs b/com.unity.probuilder/Editor/EditorCore/VertexManipulationTool.cs new file mode 100644 index 000000000..8199d33f7 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexManipulationTool.cs @@ -0,0 +1,442 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEngine.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; +using Math = UnityEngine.ProBuilder.Math; + +#if DEBUG_HANDLES +using UnityEngine.Rendering; +#endif + +namespace UnityEditor.ProBuilder +{ + abstract class MeshAndElementGroupPair + { + ProBuilderMesh m_Mesh; + List m_ElementGroups; + + public ProBuilderMesh mesh + { + get { return m_Mesh; } + } + + public List elementGroups + { + get { return m_ElementGroups; } + } + + public MeshAndElementGroupPair(ProBuilderMesh mesh, PivotPoint pivot, bool collectCoincidentIndices) + { + m_Mesh = mesh; + m_ElementGroups = ElementGroup.GetElementGroups(mesh, pivot, collectCoincidentIndices); + } + } + + class ElementGroup + { + List m_Indices; + Matrix4x4 m_PreApplyPositionsMatrix; + Matrix4x4 m_PostApplyPositionsMatrix; + + public List indices + { + get { return m_Indices; } + } + + public Matrix4x4 matrix + { + get { return m_PreApplyPositionsMatrix; } + set + { + m_PreApplyPositionsMatrix = value; + m_PostApplyPositionsMatrix = m_PreApplyPositionsMatrix.inverse; + } + } + + public Matrix4x4 inverseMatrix + { + get { return m_PostApplyPositionsMatrix; } + } + + public static List GetElementGroups(ProBuilderMesh mesh, PivotPoint pivot, bool collectCoincident) + { + var groups = new List(); + var trs = mesh.transform.localToWorldMatrix; + + switch (pivot) + { + case PivotPoint.ModelBoundingBoxCenter: + { + var bounds = Math.GetBounds(mesh.positionsInternal, mesh.selectedIndexesInternal); + var post = Matrix4x4.TRS(trs.MultiplyPoint3x4(bounds.center), mesh.transform.rotation, Vector3.one); + + groups.Add(new ElementGroup() + { + m_Indices = collectCoincident + ? mesh.GetCoincidentVertices(mesh.selectedIndexesInternal) + : new List(mesh.selectedIndexesInternal), + m_PostApplyPositionsMatrix = post, + m_PreApplyPositionsMatrix = post.inverse + }); + + break; + } + + case PivotPoint.IndividualOrigins: + { + if (ProBuilderEditor.selectMode != SelectMode.Face) + goto case PivotPoint.ModelBoundingBoxCenter; + + foreach (var list in GetFaceSelectionGroups(mesh)) + { + var bounds = Math.GetBounds(mesh.positionsInternal, list); + var ntb = Math.NormalTangentBitangent(mesh, list[0]); + var rot = mesh.transform.rotation * Quaternion.LookRotation(ntb.normal, ntb.bitangent); + var post = Matrix4x4.TRS(trs.MultiplyPoint3x4(bounds.center), rot, Vector3.one); + + List indices; + + if (collectCoincident) + { + indices = new List(); + mesh.GetCoincidentVertices(list, indices); + } + else + { + indices = list.SelectMany(x => x.distinctIndexesInternal).ToList(); + } + + groups.Add(new ElementGroup() + { + m_Indices = indices, + m_PostApplyPositionsMatrix = post, + m_PreApplyPositionsMatrix = post.inverse + }); + } + break; + } + + default: + { + var post = Matrix4x4.Translate(MeshSelection.GetHandlePosition()); + + groups.Add(new ElementGroup() + { + m_Indices = collectCoincident + ? mesh.GetCoincidentVertices(mesh.selectedIndexesInternal) + : new List(mesh.selectedIndexesInternal), + m_PostApplyPositionsMatrix = Matrix4x4.Translate(MeshSelection.GetHandlePosition()), + m_PreApplyPositionsMatrix = post.inverse + }); + + break; + } + } + + return groups; + } + + static List> 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 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); + } + } + } + + abstract class VertexManipulationTool + { + /// + /// Called when vertex modifications are complete. + /// + public static event Action afterMeshModification; + + /// + /// Called immediately prior to beginning vertex modifications. The ProBuilderMesh will be in un-altered state at this point (meaning ProBuilderMesh.ToMesh and ProBuilderMesh.Refresh have been called, but not Optimize). + /// + public static event Action beforeMeshModification; + + internal static Pref s_ExtrudeEdgesAsGroup = new Pref("editor.extrudeEdgesAsGroup", true); + internal static Pref s_ExtrudeMethod = new Pref("editor.extrudeMethod", ExtrudeMethod.FaceNormal); + + Vector3 m_HandlePosition; + Quaternion m_HandleRotation; + Vector3 m_HandlePositionOrigin; + Quaternion m_HandleRotationOrigin; + List m_MeshAndElementGroupPairs = new List(); + bool m_IsEditing; + + float m_ProgridsSnapValue = .25f; + bool m_SnapAxisConstraint = true; + bool m_ProgridsSnapEnabled; + static bool s_Initialized; + static FieldInfo s_VertexDragging; + static MethodInfo s_FindNearestVertex; + static object[] s_FindNearestVertexArguments = new object[] { null, null, null }; + + protected IEnumerable meshAndElementGroupPairs + { + get { return m_MeshAndElementGroupPairs; } + } + + protected static bool vertexDragging + { + get + { + Init(); + return s_VertexDragging != null && (bool) s_VertexDragging.GetValue(null); + } + } + + protected bool isEditing + { + get { return m_IsEditing; } + } + + protected Event currentEvent { get; private set; } + + protected PivotPoint pivotPoint { get; private set; } + + protected Vector3 handlePositionOrigin + { + get { return m_HandlePositionOrigin; } + } + + protected Quaternion handleRotationOriginInverse { get; private set; } + + protected Quaternion handleRotationOrigin + { + get { return m_HandleRotationOrigin; } + } + + protected float progridsSnapValue + { + get { return m_ProgridsSnapValue; } + } + + protected bool snapAxisConstraint + { + get { return m_SnapAxisConstraint; } + } + + protected bool progridsSnapEnabled + { + get { return m_ProgridsSnapEnabled; } + } + + protected bool relativeSnapEnabled + { + get { return currentEvent.control; } + } + + static void Init() + { + if (s_Initialized) + return; + s_Initialized = true; + s_VertexDragging = typeof(Tools).GetField("vertexDragging", BindingFlags.NonPublic | BindingFlags.Static); + s_FindNearestVertex = typeof(HandleUtility).GetMethod("FindNearestVertex", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance); + } + + protected abstract MeshAndElementGroupPair GetMeshAndElementGroupPair(ProBuilderMesh mesh, PivotPoint pivot); + + public void OnSceneGUI(Event evt) + { + currentEvent = evt; + + if (evt.type == EventType.MouseUp || evt.type == EventType.Ignore) + FinishEdit(); + + if (Tools.pivotMode == PivotMode.Center) + pivotPoint = PivotPoint.WorldBoundingBoxCenter; + else if(ProBuilderEditor.handleOrientation == HandleOrientation.Normal) + pivotPoint = PivotPoint.IndividualOrigins; + else + pivotPoint = PivotPoint.ModelBoundingBoxCenter; + + if (!m_IsEditing) + { + m_HandlePosition = MeshSelection.GetHandlePosition(); + m_HandleRotation = MeshSelection.GetHandleRotation(ProBuilderEditor.handleOrientation); + } + + DoTool(m_HandlePosition, m_HandleRotation); + } + + protected abstract void DoTool(Vector3 handlePosition, Quaternion handleRotation); + + protected virtual void OnToolEngaged() { } + + protected virtual void OnToolDisengaged() { } + + protected void BeginEdit(string undoMessage) + { + if (m_IsEditing) + return; + + // Disable iterative lightmapping + Lightmapping.PushGIWorkflowMode(); + + var selection = MeshSelection.topInternal.ToArray(); + + UndoUtility.RegisterCompleteObjectUndo(selection, string.IsNullOrEmpty(undoMessage) ? "Modify Vertices" : undoMessage); + + if (beforeMeshModification != null) + beforeMeshModification(selection); + + if (currentEvent.shift) + Extrude(); + + m_IsEditing = true; + + m_HandlePositionOrigin = m_HandlePosition; + m_HandleRotationOrigin = m_HandleRotation; + handleRotationOriginInverse = Quaternion.Inverse(m_HandleRotation); + + m_ProgridsSnapEnabled = ProGridsInterface.SnapEnabled(); + m_ProgridsSnapValue = ProGridsInterface.SnapValue(); + m_SnapAxisConstraint = ProGridsInterface.UseAxisConstraints(); + + foreach (var mesh in selection) + { + mesh.ToMesh(); + mesh.Refresh(); + } + + m_MeshAndElementGroupPairs.Clear(); + + foreach (var mesh in MeshSelection.topInternal) + m_MeshAndElementGroupPairs.Add(GetMeshAndElementGroupPair(mesh, pivotPoint)); + + OnToolEngaged(); + } + + protected void FinishEdit() + { + if (!m_IsEditing) + return; + + Lightmapping.PopGIWorkflowMode(); + + OnToolDisengaged(); + + var selection = MeshSelection.topInternal.ToArray(); + + foreach (var mesh in selection) + { + mesh.ToMesh(); + mesh.Refresh(); + mesh.Optimize(); + } + + ProBuilderEditor.Refresh(); + + if (afterMeshModification != null) + afterMeshModification(selection); + + m_IsEditing = false; + } + + static void Extrude() + { + int ef = 0; + + var selection = MeshSelection.topInternal; + var selectMode = ProBuilderEditor.selectMode; + + foreach (var mesh in selection) + { + switch (selectMode) + { + case SelectMode.Edge: + if (mesh.selectedFaceCount > 0) + goto default; + + Edge[] newEdges = mesh.Extrude(mesh.selectedEdges, + 0.0001f, + s_ExtrudeEdgesAsGroup, + ProBuilderEditor.s_AllowNonManifoldActions); + + if (newEdges != null) + { + ef += newEdges.Length; + mesh.SetSelectedEdges(newEdges); + } + break; + + default: + int len = mesh.selectedFacesInternal.Length; + + if (len > 0) + { + mesh.Extrude(mesh.selectedFacesInternal, s_ExtrudeMethod, 0.0001f); + mesh.SetSelectedFaces(mesh.selectedFacesInternal); + ef += len; + } + + break; + } + + mesh.ToMesh(); + mesh.Refresh(); + } + + if (ef > 0) + { + EditorUtility.ShowNotification("Extrude"); + ProBuilderEditor.Refresh(); + } + } + + /// + /// Find the nearest vertex among all visible objects. + /// + /// + /// + /// + protected static bool FindNearestVertex(Vector2 mousePosition, out Vector3 vertex) + { + s_FindNearestVertexArguments[0] = mousePosition; + + if (s_FindNearestVertex == null) + s_FindNearestVertex = typeof(HandleUtility).GetMethod("findNearestVertex", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance); + + object result = s_FindNearestVertex.Invoke(null, s_FindNearestVertexArguments); + vertex = (bool) result ? (Vector3) s_FindNearestVertexArguments[2] : Vector3.zero; + return (bool) result; + } + } +} diff --git a/com.unity.probuilder/Editor/EditorCore/VertexManipulationTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/VertexManipulationTool.cs.meta new file mode 100644 index 000000000..14316b76f --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexManipulationTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e4919131c75b7914b99be1a9252658bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/EditorCore/VertexMoveTool.cs b/com.unity.probuilder/Editor/EditorCore/VertexMoveTool.cs new file mode 100644 index 000000000..f699b9926 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexMoveTool.cs @@ -0,0 +1,103 @@ +using UnityEngine; +using UnityEngine.ProBuilder; + +namespace UnityEditor.ProBuilder +{ + class VertexMoveTool : VertexTool + { + const float k_CardinalAxisError = .001f; + Vector3 m_HandlePosition; + Matrix4x4 m_Translation = Matrix4x4.identity; + + bool m_SnapInWorldCoordinates; + Vector3 m_WorldSnapDirection; + Vector3 m_WorldSnapMask; + + protected override void OnToolEngaged() + { + m_SnapInWorldCoordinates = false; + m_WorldSnapMask = new Vector3Mask(0x0); + } + + protected override void DoTool(Vector3 handlePosition, Quaternion handleRotation) + { + base.DoTool(handlePosition, handleRotation); + + if (!isEditing) + m_HandlePosition = handlePosition; + + EditorGUI.BeginChangeCheck(); + + m_HandlePosition = Handles.PositionHandle(m_HandlePosition, handleRotation); + + if (EditorGUI.EndChangeCheck()) + { + if (!isEditing) + BeginEdit("Translate Selection"); + + var delta = m_HandlePosition - handlePositionOrigin; + + if (vertexDragging) + { + Vector3 nearest; + + if (FindNearestVertex(currentEvent.mousePosition, out nearest)) + { + var unrotated = handleRotationOriginInverse * delta; + var dir = new Vector3Mask(unrotated, k_CardinalAxisError); + + if (dir.active == 1) + { + var rot_dir = handleRotationOrigin * dir * 10000f; + + m_HandlePosition = HandleUtility.ProjectPointLine(nearest, + handlePositionOrigin + rot_dir, + handlePositionOrigin - rot_dir); + + delta = m_HandlePosition - handlePositionOrigin; + } + } + } + else if (progridsSnapEnabled) + { + var localDir = handleRotationOriginInverse * delta; + m_WorldSnapDirection = delta.normalized; + + if (!m_SnapInWorldCoordinates && (Math.IsCardinalAxis(delta) || !Math.IsCardinalAxis(localDir))) + m_SnapInWorldCoordinates = true; + + if (m_SnapInWorldCoordinates) + { + m_WorldSnapMask |= new Vector3Mask(m_WorldSnapDirection, k_CardinalAxisError); + m_HandlePosition = Snapping.SnapValue(m_HandlePosition, m_WorldSnapMask * progridsSnapValue); + delta = m_HandlePosition - handlePositionOrigin; + } + else + { + var travel = delta.magnitude; + delta = m_WorldSnapDirection * Snapping.SnapValue(travel, progridsSnapValue); + m_HandlePosition = handlePositionOrigin + delta; + } + } + + switch (pivotPoint) + { + case PivotPoint.WorldBoundingBoxCenter: + break; + + case PivotPoint.ModelBoundingBoxCenter: + delta = handleRotationOriginInverse * delta; + break; + + case PivotPoint.IndividualOrigins: + delta = handleRotationOriginInverse * delta; + break; + } + + m_Translation.SetTRS(delta, Quaternion.identity, Vector3.one); + + Apply(m_Translation); + } + } + } +} \ No newline at end of file diff --git a/com.unity.probuilder/Editor/EditorCore/VertexMoveTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/VertexMoveTool.cs.meta new file mode 100644 index 000000000..0afaadb7f --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexMoveTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ac1a3f68899bd64391da168c4badfab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/EditorCore/VertexRotateTool.cs b/com.unity.probuilder/Editor/EditorCore/VertexRotateTool.cs new file mode 100644 index 000000000..4ba3c908d --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexRotateTool.cs @@ -0,0 +1,53 @@ +using UnityEngine; +using UnityEngine.ProBuilder; + +namespace UnityEditor.ProBuilder +{ + class VertexRotateTool : VertexTool + { + Quaternion m_Rotation; + + protected override void DoTool(Vector3 handlePosition, Quaternion handleRotation) + { + base.DoTool(handlePosition, handleRotation); + + if (Tools.pivotMode == PivotMode.Pivot) + { + EditorGUI.BeginChangeCheck(); + + if (!isEditing) + m_Rotation = Quaternion.identity; + + var hm = Handles.matrix; + Handles.matrix = Matrix4x4.TRS(handlePosition, handleRotation, Vector3.one); + m_Rotation = Handles.RotationHandle(m_Rotation, Vector3.zero); + Handles.matrix = hm; + + if (EditorGUI.EndChangeCheck()) + { + if (!isEditing) + BeginEdit("Rotate Selection"); + + Apply(Matrix4x4.Rotate(m_Rotation)); + } + } + else + { + EditorGUI.BeginChangeCheck(); + + if (!isEditing) + m_Rotation = handleRotation; + + m_Rotation = Handles.RotationHandle(m_Rotation, handlePosition); + + if (EditorGUI.EndChangeCheck()) + { + if (!isEditing) + BeginEdit("Rotate Selection"); + + Apply(Matrix4x4.Rotate(m_Rotation * Quaternion.Inverse(handleRotationOrigin))); + } + } + } + } +} \ No newline at end of file diff --git a/com.unity.probuilder/Editor/EditorCore/VertexRotateTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/VertexRotateTool.cs.meta new file mode 100644 index 000000000..605e26831 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexRotateTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 889d171485914ca429a6b43878cb11ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/EditorCore/VertexScaleTool.cs b/com.unity.probuilder/Editor/EditorCore/VertexScaleTool.cs new file mode 100644 index 000000000..9fa191e5d --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexScaleTool.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace UnityEditor.ProBuilder +{ + class VertexScaleTool : VertexTool + { + Vector3 m_Scale; + + protected override void DoTool(Vector3 handlePosition, Quaternion handleRotation) + { + base.DoTool(handlePosition, handleRotation); + + if (!isEditing) + m_Scale = Vector3.one; + + EditorGUI.BeginChangeCheck(); + + m_Scale = Handles.ScaleHandle(m_Scale, handlePosition, handleRotation, HandleUtility.GetHandleSize(handlePosition)); + + if (EditorGUI.EndChangeCheck()) + { + if (!isEditing) + BeginEdit("Scale Selection"); + + Apply(Matrix4x4.Scale(m_Scale)); + } + } + } + +} diff --git a/com.unity.probuilder/Editor/EditorCore/VertexScaleTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/VertexScaleTool.cs.meta new file mode 100644 index 000000000..e3b8aa8b1 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexScaleTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 927abaf88d2d91448bf4d9cd53201db3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/EditorCore/VertexTool.cs b/com.unity.probuilder/Editor/EditorCore/VertexTool.cs new file mode 100644 index 000000000..86ca71112 --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexTool.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.ProBuilder; + +namespace UnityEditor.ProBuilder +{ + abstract class VertexTool : VertexManipulationTool + { + const bool k_CollectCoincidentVertices = true; +#if APPLY_POSITION_TO_SPACE_GIZMO + Matrix4x4 m_CurrentDelta = Matrix4x4.identity; +#endif + + class MeshAndPositions : MeshAndElementGroupPair + { + Vector3[] m_Positions; + + public Vector3[] positions + { + get { return m_Positions; } + } + + public MeshAndPositions(ProBuilderMesh mesh, PivotPoint pivot) : base(mesh, pivot, k_CollectCoincidentVertices) + { + m_Positions = mesh.positions.ToArray(); + + var l2w = mesh.transform.localToWorldMatrix; + + for (int i = 0, c = m_Positions.Length; i < c; i++) + m_Positions[i] = l2w.MultiplyPoint3x4(m_Positions[i]); + } + } + + protected override MeshAndElementGroupPair GetMeshAndElementGroupPair (ProBuilderMesh mesh, PivotPoint pivot) + { + return new MeshAndPositions(mesh, pivot); + } + + protected override void DoTool(Vector3 position, Quaternion rotation) + { + if ( isEditing && currentEvent.type == EventType.Repaint) + { + foreach (var key in meshAndElementGroupPairs) + { + foreach (var group in key.elementGroups) + { +#if DEBUG_HANDLES + using (var faceDrawer = new EditorMeshHandles.TriangleDrawingScope(Color.cyan, CompareFunction.Always)) + { + foreach (var face in key.mesh.GetSelectedFaces()) + { + var indices = face.indexesInternal; + + for (int i = 0, c = indices.Length; i < c; i += 3) + { + faceDrawer.Draw( + group.matrix.MultiplyPoint3x4(key.positions[indices[i]]), + group.matrix.MultiplyPoint3x4(key.positions[indices[i + 1]]), + group.matrix.MultiplyPoint3x4(key.positions[indices[i + 2]]) + ); + } + } + } +#endif + +#if APPLY_POSITION_TO_SPACE_GIZMO + EditorMeshHandles.DrawGizmo(Vector3.zero, group.matrix.inverse * m_CurrentDelta); +#else + EditorMeshHandles.DrawGizmo(Vector3.zero, group.matrix.inverse); +#endif + } + } + } + } + + protected void Apply(Matrix4x4 delta) + { +#if APPLY_POSITION_TO_SPACE_GIZMO + m_CurrentDelta.SetColumn(3, delta.GetColumn(3)); +#endif + + foreach (var key in meshAndElementGroupPairs) + { + if (!(key is MeshAndPositions)) + continue; + + var kvp = (MeshAndPositions)key; + var mesh = kvp.mesh; + var worldToLocal = mesh.transform.worldToLocalMatrix; + var origins = kvp.positions; + var positions = mesh.positionsInternal; + + foreach (var group in kvp.elementGroups) + { + foreach (var index in group.indices) + { + positions[index] = worldToLocal.MultiplyPoint3x4( + group.inverseMatrix.MultiplyPoint3x4( + delta.MultiplyPoint3x4(group.matrix.MultiplyPoint3x4(origins[index])))); + } + } + + mesh.mesh.vertices = positions; + mesh.RefreshUV(MeshSelection.selectedFacesInEditZone[mesh]); + mesh.Refresh(RefreshMask.Normals); + } + + ProBuilderEditor.UpdateMeshHandles(false); + } + } +} diff --git a/com.unity.probuilder/Editor/EditorCore/VertexTool.cs.meta b/com.unity.probuilder/Editor/EditorCore/VertexTool.cs.meta new file mode 100644 index 000000000..cac286e0e --- /dev/null +++ b/com.unity.probuilder/Editor/EditorCore/VertexTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 17514a8c8f75b3d4bb1d7a05d557b5c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/MenuActions/Editors/OpenVertexColorEditor.cs b/com.unity.probuilder/Editor/MenuActions/Editors/OpenVertexColorEditor.cs index 16f7fa331..b163953aa 100644 --- a/com.unity.probuilder/Editor/MenuActions/Editors/OpenVertexColorEditor.cs +++ b/com.unity.probuilder/Editor/MenuActions/Editors/OpenVertexColorEditor.cs @@ -1,10 +1,5 @@ using UnityEngine.ProBuilder; -using UnityEditor.ProBuilder; using UnityEngine; -using UnityEditor; -using UnityEditor.ProBuilder.UI; -using EditorGUILayout = UnityEditor.EditorGUILayout; -using EditorStyles = UnityEditor.EditorStyles; namespace UnityEditor.ProBuilder.Actions { @@ -28,7 +23,7 @@ public override bool enabled public override ActionResult DoAction() { - MenuCommands.MenuOpenVertexColorsEditor(); + VertexColorPalette.MenuOpenWindow(); return new ActionResult(ActionResult.Status.Success, "Open Vertex Color Window"); } } diff --git a/com.unity.probuilder/Editor/MenuActions/Export/ExportAsset.cs b/com.unity.probuilder/Editor/MenuActions/Export/ExportAsset.cs index 9d22e5c6a..8fa2e7373 100644 --- a/com.unity.probuilder/Editor/MenuActions/Export/ExportAsset.cs +++ b/com.unity.probuilder/Editor/MenuActions/Export/ExportAsset.cs @@ -25,7 +25,7 @@ sealed class ExportAsset : MenuAction public override bool enabled { - get { return MeshSelection.count > 0; } + get { return MeshSelection.selectedObjectCount > 0; } } public override bool hidden diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/BevelEdges.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/BevelEdges.cs index f3336b6a9..a675292bf 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/BevelEdges.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/BevelEdges.cs @@ -59,13 +59,11 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var selection = MeshSelection.topInternal; - ActionResult res = ActionResult.NoSelection; - UndoUtility.RecordSelection(selection, "Bevel Edges"); + UndoUtility.RecordSelection("Bevel Edges"); - foreach(ProBuilderMesh pb in selection) + foreach(ProBuilderMesh pb in MeshSelection.topInternal) { pb.ToMesh(); diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/BridgeEdges.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/BridgeEdges.cs index 565ac71a0..9b9dc1917 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/BridgeEdges.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/BridgeEdges.cs @@ -1,6 +1,7 @@ using UnityEngine; using System.Linq; using UnityEngine.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -40,7 +41,37 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuBridgeEdges(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Bridge Edges"); + + bool success = false; + + foreach(var mesh in MeshSelection.topInternal) + { + if(mesh.selectedEdgeCount == 2) + { + if(mesh.Bridge(mesh.selectedEdges[0], mesh.selectedEdges[1], ProBuilderEditor.s_AllowNonManifoldActions) != null) + { + success = true; + mesh.ToMesh(); + mesh.Refresh(); + mesh.Optimize(); + } + } + } + + if(success) + { + ProBuilderEditor.Refresh(); + return new ActionResult(ActionResult.Status.Success, "Bridge Edges"); + } + else + { + Debug.LogWarning("Failed Bridge Edges. Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold)."); + return new ActionResult(ActionResult.Status.Failure, "Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold)."); + } } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/CollapseVertices.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/CollapseVertices.cs index 0dbb7ff51..1adc05b3e 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/CollapseVertices.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/CollapseVertices.cs @@ -72,18 +72,16 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var selection = MeshSelection.topInternal; - - if (selection == null || selection.Length < 1) + if (MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; bool success = false; bool collapseToFirst = m_CollapseToFirst; - UndoUtility.RegisterCompleteObjectUndo(selection, "Collapse Vertices"); + UndoUtility.RecordSelection("Collapse Vertices"); - foreach (ProBuilderMesh mesh in selection) + foreach (var mesh in MeshSelection.topInternal) { if (mesh.selectedIndexesInternal.Length > 1) { diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/ConformFaceNormals.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/ConformFaceNormals.cs index 204aea4c8..e3207771b 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/ConformFaceNormals.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/ConformFaceNormals.cs @@ -11,10 +11,10 @@ sealed class ConformFaceNormals : MenuAction { public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } } public override Texture2D icon { get { return IconUtility.GetIcon("Toolbar/Face_ConformNormals", IconSkin.Pro); } } - public override TooltipContent tooltip { get { return _tooltip; } } + public override TooltipContent tooltip { get { return s_TooltipContent; } } public override string menuTitle { get { return "Conform Normals"; } } - static readonly TooltipContent _tooltip = new TooltipContent + static readonly TooltipContent s_TooltipContent = new TooltipContent ( "Conform Face Normals", @"Orients all selected faces to face the same direction." @@ -32,7 +32,29 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuConformNormals(MeshSelection.topInternal); + var selection = MeshSelection.topInternal; + + UndoUtility.RecordSelection("Conform " + (MeshSelection.selectedFaceCount > 0 ? "Face" : "Object") + " Normals."); + + ActionResult res = ActionResult.NoSelection; + + foreach(ProBuilderMesh pb in selection) + { + var faces = pb.GetSelectedFaces(); + + if(faces == null) + continue; + + res = UnityEngine.ProBuilder.MeshOperations.SurfaceTopology.ConformNormals(pb, faces); + + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + } + + ProBuilderEditor.Refresh(); + + return res; } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectEdges.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectEdges.cs index f61f688c5..48f4e53fe 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectEdges.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectEdges.cs @@ -1,9 +1,6 @@ using UnityEngine; -using UnityEditor; -using UnityEditor.ProBuilder.UI; -using System.Linq; using UnityEngine.ProBuilder; -using UnityEditor.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -33,7 +30,28 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuConnectEdges(MeshSelection.topInternal); + ActionResult res = ActionResult.NoSelection; + + UndoUtility.RecordSelection("Connect Edges"); + + foreach(var mesh in MeshSelection.topInternal) + { + Edge[] connections; + Face[] faces; + + res = ConnectElements.Connect(mesh, mesh.selectedEdges, out faces, out connections, true, true); + + if (connections != null) + { + mesh.SetSelectedEdges(connections); + mesh.Refresh(); + mesh.Optimize(); + } + } + + ProBuilderEditor.Refresh(); + return res; + } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectVertices.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectVertices.cs index 6c769cb36..889037e2f 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectVertices.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/ConnectVertices.cs @@ -1,9 +1,6 @@ using UnityEngine; -using UnityEditor; -using UnityEditor.ProBuilder.UI; -using System.Linq; using UnityEngine.ProBuilder; -using UnityEditor.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -33,7 +30,30 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuConnectVertices(MeshSelection.topInternal); + ActionResult res = ActionResult.NoSelection; + + UndoUtility.RecordSelection("Connect Vertices"); + + foreach(var mesh in MeshSelection.topInternal) + { + mesh.ToMesh(); + int[] splits = mesh.Connect(mesh.selectedIndexesInternal); + + if(splits != null) + { + mesh.Refresh(); + mesh.Optimize(); + mesh.SetSelectedVertices(splits); + res = new ActionResult(ActionResult.Status.Success, "Connect Edges"); + } + else + { + res = new ActionResult(ActionResult.Status.Failure, "Failed Connecting Edges"); + } + } + ProBuilderEditor.Refresh(); + + return res; } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/DeleteFaces.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/DeleteFaces.cs index 6cd41b4ca..f054dad68 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/DeleteFaces.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/DeleteFaces.cs @@ -1,5 +1,6 @@ using UnityEngine; using UnityEngine.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -39,7 +40,36 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuDeleteFace(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Delete Face"); + + int count = 0; + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + if(pb.selectedFaceCount == pb.facesInternal.Length) + { + Debug.LogWarning("Attempting to delete all faces on this mesh... I'm afraid I can't let you do that."); + continue; + } + + pb.DeleteFaces(pb.selectedFacesInternal); + count += pb.selectedFaceCount; + + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + } + + MeshSelection.ClearElementSelection(); + ProBuilderEditor.Refresh(); + + if(count > 0) + return new ActionResult(ActionResult.Status.Success, "Delete " + count + " Faces"); + + return new ActionResult(ActionResult.Status.Failure, "No Faces Selected"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/DetachFaces.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/DetachFaces.cs index f3ef9a39a..dbe5ad92c 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/DetachFaces.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/DetachFaces.cs @@ -62,22 +62,23 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Detach Face(s)"); + if (m_DetachSetting == DetachSetting.GameObject) - return DetachFacesToObject(MeshSelection.topInternal); + return DetachFacesToObject(); - return DetachFacesToSubmesh(MeshSelection.topInternal); + return DetachFacesToSubmesh(); } - static ActionResult DetachFacesToSubmesh(ProBuilderMesh[] selection) + static ActionResult DetachFacesToSubmesh() { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RegisterCompleteObjectUndo(selection, "Detach Face(s)"); int count = 0; - foreach(ProBuilderMesh pb in selection) + foreach(ProBuilderMesh pb in MeshSelection.topInternal) { pb.ToMesh(); List res = pb.DetachFaces(pb.selectedFacesInternal); @@ -97,17 +98,12 @@ static ActionResult DetachFacesToSubmesh(ProBuilderMesh[] selection) return new ActionResult(ActionResult.Status.Success, "Detach Faces"); } - static ActionResult DetachFacesToObject(ProBuilderMesh[] selection) + static ActionResult DetachFacesToObject() { - if(selection == null || selection.Length < 1) - return ActionResult.NoSelection; - - UndoUtility.RegisterCompleteObjectUndo(selection, "Detach Selection to GameObject"); - int detachedFaceCount = 0; List detached = new List(); - foreach(ProBuilderMesh mesh in selection) + foreach(ProBuilderMesh mesh in MeshSelection.topInternal) { if(mesh.selectedFaceCount < 1 || mesh.selectedFaceCount == mesh.facesInternal.Length) continue; diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeEdges.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeEdges.cs index 355f1535d..067ba3b7e 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeEdges.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeEdges.cs @@ -46,7 +46,7 @@ protected override void OnSettingsGUI() EditorGUI.BeginChangeCheck(); - ProBuilderEditor.instance.m_ExtrudeEdgesAsGroup.value = EditorGUILayout.Toggle("As Group", ProBuilderEditor.instance.m_ExtrudeEdgesAsGroup); + VertexManipulationTool.s_ExtrudeEdgesAsGroup.value = EditorGUILayout.Toggle("As Group", VertexManipulationTool.s_ExtrudeEdgesAsGroup); m_ExtrudeEdgeDistance.value = EditorGUILayout.FloatField("Distance", m_ExtrudeEdgeDistance); @@ -61,18 +61,15 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var editor = ProBuilderEditor.instance; - var selection = MeshSelection.topInternal; - - if(selection == null || selection.Length < 1) + if(MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; - UndoUtility.RegisterCompleteObjectUndo(selection, "Extrude"); + UndoUtility.RecordSelection("Extrude"); int extrudedFaceCount = 0; bool success = false; - foreach(ProBuilderMesh pb in selection) + foreach(ProBuilderMesh pb in MeshSelection.topInternal) { pb.ToMesh(); pb.Refresh(RefreshMask.Normals); @@ -84,7 +81,7 @@ public override ActionResult DoAction() Edge[] newEdges = pb.Extrude(pb.selectedEdges, m_ExtrudeEdgeDistance, - ProBuilderEditor.instance.m_ExtrudeEdgesAsGroup, + VertexManipulationTool.s_ExtrudeEdgesAsGroup, ProBuilderEditor.s_AllowNonManifoldActions); success |= newEdges != null; @@ -97,10 +94,7 @@ public override ActionResult DoAction() pb.Rebuild(); } - if(editor != null) - ProBuilderEditor.Refresh(); - - SceneView.RepaintAll(); + ProBuilderEditor.Refresh(); if( extrudedFaceCount > 0 ) return new ActionResult(ActionResult.Status.Success, "Extrude"); diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeFaces.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeFaces.cs index 258d1a831..065a7116d 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeFaces.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/ExtrudeFaces.cs @@ -11,8 +11,8 @@ sealed class ExtrudeFaces : MenuAction ExtrudeMethod extrudeMethod { - get { return ProBuilderEditor.instance.m_ExtrudeMethod; } - set { ProBuilderEditor.instance.m_ExtrudeMethod.value = value; } + get { return VertexManipulationTool.s_ExtrudeMethod; } + set { VertexManipulationTool.s_ExtrudeMethod.value = value; } } static string GetExtrudeIconString(ExtrudeMethod m) @@ -107,17 +107,14 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var editor = ProBuilderEditor.instance; - var selection = MeshSelection.topInternal; - - if (selection == null || selection.Length < 1) + if(MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; - UndoUtility.RegisterCompleteObjectUndo(selection, "Extrude"); + UndoUtility.RecordSelection("Extrude"); int extrudedFaceCount = 0; - foreach (ProBuilderMesh mesh in selection) + foreach (ProBuilderMesh mesh in MeshSelection.topInternal) { mesh.ToMesh(); mesh.Refresh(RefreshMask.Normals); @@ -129,7 +126,7 @@ public override ActionResult DoAction() var selectedFaces = mesh.GetSelectedFaces(); mesh.Extrude(selectedFaces, - ProBuilderEditor.instance.m_ExtrudeMethod, + VertexManipulationTool.s_ExtrudeMethod, m_ExtrudeDistance); mesh.SetSelectedFaces(selectedFaces); @@ -138,10 +135,7 @@ public override ActionResult DoAction() mesh.Optimize(); } - if (editor != null) - ProBuilderEditor.Refresh(); - - SceneView.RepaintAll(); + ProBuilderEditor.Refresh(); if (extrudedFaceCount > 0) return new ActionResult(ActionResult.Status.Success, "Extrude"); diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/FillHole.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/FillHole.cs index 5af5b8a60..3d292f102 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/FillHole.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/FillHole.cs @@ -73,19 +73,16 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var editor = ProBuilderEditor.instance; - var selection = MeshSelection.topInternal; - - if(editor == null) + if (MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; - UndoUtility.RecordSelection(selection, "Fill Hole"); + UndoUtility.RecordSelection("Fill Hole"); ActionResult res = new ActionResult(ActionResult.Status.NoChange, "No Holes Found"); int filled = 0; bool wholePath = m_SelectEntirePath; - foreach(ProBuilderMesh mesh in selection) + foreach(ProBuilderMesh mesh in MeshSelection.topInternal) { bool selectAll = mesh.selectedIndexesInternal == null || mesh.selectedIndexesInternal.Length < 1; IEnumerable indexes = selectAll ? mesh.facesInternal.SelectMany(x => x.indexes) : mesh.selectedIndexesInternal; diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceEdge.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceEdge.cs index 5d1e53572..c53be6331 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceEdge.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceEdge.cs @@ -1,5 +1,6 @@ using UnityEngine; using UnityEngine.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -38,7 +39,35 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuFlipEdges(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Flip Face Edges"); + int success = 0; + int attempts = 0; + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + foreach(Face face in pb.selectedFacesInternal) + { + if( pb.FlipEdge(face) ) + success++; + } + + attempts++; + + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + } + + ProBuilderEditor.Refresh(); + + if(success > 0) + return new ActionResult(ActionResult.Status.Success, "Flipped " + success + " Edges"); + + return new ActionResult(ActionResult.Status.Failure, string.Format("Flip Edges\n{0}", attempts > 0 ? "Faces Must Be Quads" : "No Faces Selected")); + } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceNormals.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceNormals.cs index 556a7bb60..ee0501a20 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceNormals.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/FlipFaceNormals.cs @@ -43,7 +43,41 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuFlipNormals(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Flip Face Normals"); + + int c = 0; + int faceCount = MeshSelection.selectedFaceCount; + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + if( pb.selectedFaceCount < 1 && faceCount < 1 ) + { + foreach(var face in pb.facesInternal) + face.Reverse(); + + c += pb.facesInternal.Length; + } + else + { + foreach(var face in pb.GetSelectedFaces()) + face.Reverse(); + + c += pb.selectedFaceCount; + } + + + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + } + + if(c > 0) + return new ActionResult(ActionResult.Status.Success, "Flip " + c + (c > 1 ? " Face Normals" : " Face Normal")); + + return new ActionResult(ActionResult.Status.Canceled, "Flip Normals\nNo Faces Selected"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/InsertEdgeLoop.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/InsertEdgeLoop.cs index a2a280704..038a2e96f 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/InsertEdgeLoop.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/InsertEdgeLoop.cs @@ -4,6 +4,7 @@ using System.Linq; using UnityEngine.ProBuilder; using UnityEditor.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -43,7 +44,32 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuInsertEdgeLoop(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + int success = 0; + UndoUtility.RecordSelection("Insert Edge Loop"); + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + Edge[] edges = pb.Connect(ElementSelection.GetEdgeRing(pb, pb.selectedEdges)).item2; + + if(edges != null) + { + pb.SetSelectedEdges(edges); + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + success++; + } + } + + ProBuilderEditor.Refresh(); + + if(success > 0) + return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop"); + + return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/MergeFaces.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/MergeFaces.cs index 0ef5ba042..1a607ff1f 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/MergeFaces.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/MergeFaces.cs @@ -1,6 +1,7 @@ using UnityEngine; using System.Linq; using UnityEngine.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -39,7 +40,35 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuMergeFaces(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Merge Faces"); + + int success = 0; + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + if(pb.selectedFaceCount > 1) + { + success += pb.selectedFaceCount; + + Face face = MergeElements.Merge(pb, pb.selectedFacesInternal); + + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + + pb.SetSelectedFaces( new Face[] { face } ); + } + } + + ProBuilderEditor.Refresh(); + + if(success > 0) + return new ActionResult(ActionResult.Status.Success, "Merged " + success + " Faces"); + + return new ActionResult(ActionResult.Status.Failure, "Merge Faces\nNo Faces Selected"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/SetPivotToSelection.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/SetPivotToSelection.cs index 21ca59775..b1eff99d4 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/SetPivotToSelection.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/SetPivotToSelection.cs @@ -4,6 +4,7 @@ using System.Linq; using UnityEngine.ProBuilder; using UnityEditor.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -38,7 +39,30 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuSetPivot(MeshSelection.topInternal); + if (MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + Object[] objects = new Object[MeshSelection.selectedObjectCount * 2]; + + for (int i = 0, c = MeshSelection.selectedObjectCount; i < c; i++) + { + objects[i] = MeshSelection.topInternal[i]; + objects[i + c] = MeshSelection.topInternal[i].transform; + } + + UndoUtility.RegisterCompleteObjectUndo(objects, "Set Pivot"); + + foreach(var mesh in MeshSelection.topInternal) + { + TransformUtility.UnparentChildren(mesh.transform); + mesh.CenterPivot(mesh.selectedIndexesInternal); + mesh.Optimize(); + TransformUtility.ReparentChildren(mesh.transform); + } + + ProBuilderEditor.Refresh(); + + return new ActionResult(ActionResult.Status.Success, "Set Pivot"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/SmartConnect.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/SmartConnect.cs index 5a62ba97e..5e939791a 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/SmartConnect.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/SmartConnect.cs @@ -45,10 +45,10 @@ public override ActionResult DoAction() switch (ProBuilderEditor.selectMode) { case SelectMode.Vertex: - return MenuCommands.MenuConnectVertices(MeshSelection.topInternal); + return EditorToolbarLoader.GetInstance().DoAction(); default: - return MenuCommands.MenuConnectEdges(MeshSelection.topInternal); + return EditorToolbarLoader.GetInstance().DoAction(); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/SplitVertices.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/SplitVertices.cs index 68e0ceb16..47e18e067 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/SplitVertices.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/SplitVertices.cs @@ -1,9 +1,8 @@ +using System.Collections.Generic; using UnityEngine; -using UnityEditor; -using UnityEditor.ProBuilder.UI; using System.Linq; using UnityEngine.ProBuilder; -using UnityEditor.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -43,7 +42,71 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuSplitVertices(MeshSelection.topInternal); + if (MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + int splitCount = 0; + UndoUtility.RecordSelection("Split Vertices"); + + foreach(ProBuilderMesh mesh in MeshSelection.topInternal) + { + // loose verts to split + List tris = new List(mesh.selectedIndexesInternal); + + if (mesh.selectedFacesInternal.Length > 0) + { + int[] sharedVertexHandles = new int[mesh.selectedIndexesInternal.Length]; + + // Get shared index index for each vert in selection + for (int i = 0; i < mesh.selectedIndexesInternal.Length; i++) + sharedVertexHandles[i] = mesh.GetSharedVertexHandle(mesh.selectedIndexesInternal[i]); + + // cycle through selected faces and remove the tris that compose full faces. + foreach (Face face in mesh.selectedFacesInternal) + { + List faceSharedIndexes = new List(); + + for (int j = 0; j < face.distinctIndexesInternal.Length; j++) + faceSharedIndexes.Add(mesh.GetSharedVertexHandle(face.distinctIndexesInternal[j])); + + List usedTris = new List(); + for (int i = 0; i < sharedVertexHandles.Length; i++) + if (faceSharedIndexes.Contains(sharedVertexHandles[i])) + usedTris.Add(mesh.selectedIndexesInternal[i]); + + // This face *is* composed of selected tris. Remove these tris from the loose index list + foreach (int i in usedTris) + if (tris.Contains(i)) + tris.Remove(i); + } + } + + // Now split the faces, and any loose vertices + mesh.DetachFaces(mesh.selectedFacesInternal); + + splitCount += mesh.selectedIndexesInternal.Length; + mesh.SplitVertices(mesh.selectedIndexesInternal); + + // Reattach detached face vertices (if any are to be had) + if(mesh.selectedFacesInternal.Length > 0) + mesh.WeldVertices(mesh.selectedFacesInternal.SelectMany(x => x.indexes), Mathf.Epsilon); + + // And set the selected triangles to the newly split + List newTriSelection = new List(mesh.selectedFacesInternal.SelectMany(x => x.indexes)); + newTriSelection.AddRange(tris); + mesh.SetSelectedVertices(newTriSelection.ToArray()); + + mesh.ToMesh(); + mesh.Refresh(); + mesh.Optimize(); + } + + ProBuilderEditor.Refresh(); + + if(splitCount > 0) + return new ActionResult(ActionResult.Status.Success, "Split " + splitCount + (splitCount > 1 ? " Vertices" : " Vertex")); + else + return new ActionResult(ActionResult.Status.Failure, "Split Vertices\nInsuffient Vertices Selected"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideEdges.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideEdges.cs index ab2f2f42b..eecd08e93 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideEdges.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideEdges.cs @@ -72,19 +72,16 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var editor = ProBuilderEditor.instance; - var selection = MeshSelection.topInternal; - - if (!editor || selection == null || selection.Length < 1) + if (MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; int subdivisions = m_SubdivisionCount; - UndoUtility.RegisterCompleteObjectUndo(selection, "Subdivide Edges"); + UndoUtility.RecordSelection("Subdivide Edges"); ActionResult result = ActionResult.NoSelection; - foreach (ProBuilderMesh pb in selection) + foreach (ProBuilderMesh pb in MeshSelection.topInternal) { List newEdgeSelection = AppendElements.AppendVerticesToEdge(pb, pb.selectedEdges, subdivisions); diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideFaces.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideFaces.cs index bbc45eea6..2e56ba0eb 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideFaces.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/SubdivideFaces.cs @@ -1,9 +1,6 @@ using UnityEngine; -using UnityEditor; -using UnityEditor.ProBuilder.UI; -using System.Linq; using UnityEngine.ProBuilder; -using UnityEditor.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -48,7 +45,39 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuSubdivideFace(MeshSelection.topInternal); + if (MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + int success = 0; + UndoUtility.RecordSelection("Subdivide Faces"); + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + Face[] faces = pb.Subdivide(pb.selectedFacesInternal); + + pb.ToMesh(); + + if(faces != null) + { + success += pb.selectedFacesInternal.Length; + pb.SetSelectedFaces(faces); + + pb.Refresh(); + pb.Optimize(); + } + } + + if(success > 0) + { + ProBuilderEditor.Refresh(); + + return new ActionResult(ActionResult.Status.Success, "Subdivide " + success + ((success > 1) ? " faces" : " face")); + } + else + { + Debug.LogWarning("Subdivide faces failed - did you not have any faces selected?"); + return new ActionResult(ActionResult.Status.Failure, "Subdivide Faces\nNo faces selected"); + } } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/TriangulateFaces.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/TriangulateFaces.cs index 2489ff3ce..025cd3cab 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/TriangulateFaces.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/TriangulateFaces.cs @@ -42,15 +42,15 @@ public override ActionResult DoAction() { ActionResult res = ActionResult.NoSelection; - UndoUtility.RecordSelection(MeshSelection.topInternal, "Triangulate Faces"); + UndoUtility.RecordSelection("Triangulate Faces"); - foreach (ProBuilderMesh pb in MeshSelection.topInternal) + foreach (ProBuilderMesh mesh in MeshSelection.topInternal) { - pb.ToMesh(); - Face[] triangulatedFaces = pb.ToTriangles(pb.selectedFacesInternal); - pb.Refresh(); - pb.Optimize(); - pb.SetSelectedFaces(triangulatedFaces); + mesh.ToMesh(); + Face[] triangulatedFaces = mesh.ToTriangles(mesh.selectedFacesInternal); + mesh.Refresh(); + mesh.Optimize(); + mesh.SetSelectedFaces(triangulatedFaces); res = new ActionResult(ActionResult.Status.Success, string.Format("Triangulated {0} {1}", triangulatedFaces.Length, triangulatedFaces.Length < 2 ? "Face" : "Faces")); } diff --git a/com.unity.probuilder/Editor/MenuActions/Geometry/WeldVertices.cs b/com.unity.probuilder/Editor/MenuActions/Geometry/WeldVertices.cs index bcb00f01b..06be8d418 100644 --- a/com.unity.probuilder/Editor/MenuActions/Geometry/WeldVertices.cs +++ b/com.unity.probuilder/Editor/MenuActions/Geometry/WeldVertices.cs @@ -71,18 +71,16 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var selection = MeshSelection.topInternal; - - if(selection == null || selection.Length < 1) + if (MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; ActionResult res = ActionResult.NoSelection; - UndoUtility.RegisterCompleteObjectUndo(selection, "Weld Vertices"); + UndoUtility.RecordSelection("Weld Vertices"); int weldCount = 0; - foreach(ProBuilderMesh mesh in selection) + foreach(ProBuilderMesh mesh in MeshSelection.topInternal) { weldCount += mesh.sharedVerticesInternal.Length; diff --git a/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleAlignment.cs b/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleOrientation.cs similarity index 90% rename from com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleAlignment.cs rename to com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleOrientation.cs index 83420202d..1290ca24b 100644 --- a/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleAlignment.cs +++ b/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleOrientation.cs @@ -7,14 +7,14 @@ namespace UnityEditor.ProBuilder.Actions { - sealed class ToggleHandleAlignment : MenuAction + sealed class ToggleHandleOrientation : MenuAction { Texture2D[] m_Icons; HandleOrientation handleOrientation { - get { return ProBuilderEditor.instance == null ? HandleOrientation.World : ProBuilderEditor.instance.handleOrientation; } - set { ProBuilderEditor.instance.handleOrientation = value; } + get { return ProBuilderEditor.instance == null ? HandleOrientation.World : ProBuilderEditor.handleOrientation; } + set { ProBuilderEditor.handleOrientation = value; } } public override ToolbarGroup group @@ -70,7 +70,12 @@ public override SelectMode validSelectModes get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face; } } - public ToggleHandleAlignment() + public override bool hidden + { + get { return false; } + } + + public ToggleHandleOrientation() { m_Icons = new Texture2D[] { diff --git a/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleOrientation.cs.meta b/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleOrientation.cs.meta new file mode 100644 index 000000000..1645609f9 --- /dev/null +++ b/com.unity.probuilder/Editor/MenuActions/Interaction/ToggleHandleOrientation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53d27af1247a7d547aa00100fae732a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Editor/MenuActions/Object/CenterPivot.cs b/com.unity.probuilder/Editor/MenuActions/Object/CenterPivot.cs index cd873a2e0..c5d93c070 100644 --- a/com.unity.probuilder/Editor/MenuActions/Object/CenterPivot.cs +++ b/com.unity.probuilder/Editor/MenuActions/Object/CenterPivot.cs @@ -1,5 +1,6 @@ using UnityEngine.ProBuilder; using UnityEngine; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -33,7 +34,30 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuCenterPivot(MeshSelection.topInternal); + if (MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + Object[] objects = new Object[MeshSelection.selectedObjectCount * 2]; + + for (int i = 0, c = MeshSelection.selectedObjectCount; i < c; i++) + { + objects[i] = MeshSelection.topInternal[i]; + objects[i + c] = MeshSelection.topInternal[i].transform; + } + + UndoUtility.RegisterCompleteObjectUndo(objects, "Center Pivot"); + + foreach(var mesh in MeshSelection.topInternal) + { + TransformUtility.UnparentChildren(mesh.transform); + mesh.CenterPivot(null); + mesh.Optimize(); + TransformUtility.ReparentChildren(mesh.transform); + } + + ProBuilderEditor.Refresh(); + + return new ActionResult(ActionResult.Status.Success, "Center Pivot"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Object/ConformObjectNormals.cs b/com.unity.probuilder/Editor/MenuActions/Object/ConformObjectNormals.cs index 67a521f04..3a3f47444 100644 --- a/com.unity.probuilder/Editor/MenuActions/Object/ConformObjectNormals.cs +++ b/com.unity.probuilder/Editor/MenuActions/Object/ConformObjectNormals.cs @@ -38,7 +38,22 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuConformObjectNormals(MeshSelection.topInternal); + UndoUtility.RecordSelection("Conform Object Normals"); + + ActionResult res = ActionResult.NoSelection; + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + res = UnityEngine.ProBuilder.MeshOperations.SurfaceTopology.ConformNormals(pb, pb.faces); + + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + } + + ProBuilderEditor.Refresh(); + + return res; } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Object/FlipObjectNormals.cs b/com.unity.probuilder/Editor/MenuActions/Object/FlipObjectNormals.cs index d910d8b7e..168bdfedc 100644 --- a/com.unity.probuilder/Editor/MenuActions/Object/FlipObjectNormals.cs +++ b/com.unity.probuilder/Editor/MenuActions/Object/FlipObjectNormals.cs @@ -41,7 +41,21 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuFlipObjectNormals(MeshSelection.topInternal); + if (MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Flip Object Normals"); + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + foreach(var face in pb.facesInternal) + face.Reverse(); + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + } + + return new ActionResult(ActionResult.Status.Success, "Flip Object Normals"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Object/FreezeTransform.cs b/com.unity.probuilder/Editor/MenuActions/Object/FreezeTransform.cs index 2902defb0..0d1e97230 100644 --- a/com.unity.probuilder/Editor/MenuActions/Object/FreezeTransform.cs +++ b/com.unity.probuilder/Editor/MenuActions/Object/FreezeTransform.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using UnityEngine.ProBuilder; using UnityEditor.ProBuilder; using UnityEngine; @@ -36,7 +37,40 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuFreezeTransforms(MeshSelection.topInternal); + if (MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordMeshAndTransformSelection("Freeze Transforms"); + + var selection = MeshSelection.topInternal; + Vector3[][] positions = new Vector3[selection.Count][]; + + for(int i = 0, c = selection.Count; i < c; i++) + positions[i] = selection[i].VerticesInWorldSpace(); + + for(int i = 0, c = selection.Count; i < c; i++) + { + ProBuilderMesh pb = selection[i]; + + pb.transform.position = Vector3.zero; + pb.transform.rotation = Quaternion.identity; + pb.transform.localScale = Vector3.one; + + foreach(Face face in pb.facesInternal) + face.manualUV = true; + + pb.positions = positions[i]; + + pb.ToMesh(); + pb.Refresh(); + pb.Optimize(); + } + + ProBuilderEditor.Refresh(); + + SceneView.RepaintAll(); + + return new ActionResult(ActionResult.Status.Success, "Freeze Transforms"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Object/MergeObjects.cs b/com.unity.probuilder/Editor/MenuActions/Object/MergeObjects.cs index 874bc8072..434da166d 100644 --- a/com.unity.probuilder/Editor/MenuActions/Object/MergeObjects.cs +++ b/com.unity.probuilder/Editor/MenuActions/Object/MergeObjects.cs @@ -1,8 +1,11 @@ +using System.Collections.Generic; +using System.Linq; using UnityEngine.ProBuilder; using UnityEditor.ProBuilder; using UnityEngine; using UnityEditor; using UnityEditor.ProBuilder.UI; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -36,7 +39,33 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuMergeObjects(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 2) + return new ActionResult(ActionResult.Status.Canceled, "Must Select 2+ Objects"); + + var selected = MeshSelection.top.ToArray(); + List res = InternalMeshUtility.CombineObjects(MeshSelection.topInternal); + + if (res != null) + { + foreach (var mesh in res) + { + mesh.Optimize(); + mesh.gameObject.name = "pb-MergedObject" + mesh.id; + UndoUtility.RegisterCreatedObjectUndo(mesh.gameObject, "Merge Objects"); + Selection.objects = res.Select(x => x.gameObject).ToArray(); + } + + // Delete donor objects + for(int i = 0; i < selected.Length; i++) + { + if(selected[i] != null) + UndoUtility.DestroyImmediate(selected[i].gameObject); + } + } + + ProBuilderEditor.Refresh(); + + return new ActionResult(ActionResult.Status.Success, "Merged Objects"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Object/SubdivideObject.cs b/com.unity.probuilder/Editor/MenuActions/Object/SubdivideObject.cs index 8f78d1b98..5e2adfb6c 100644 --- a/com.unity.probuilder/Editor/MenuActions/Object/SubdivideObject.cs +++ b/com.unity.probuilder/Editor/MenuActions/Object/SubdivideObject.cs @@ -1,8 +1,6 @@ using UnityEngine.ProBuilder; -using UnityEditor.ProBuilder; +using UnityEngine.ProBuilder.MeshOperations; using UnityEngine; -using UnityEditor; -using UnityEditor.ProBuilder.UI; namespace UnityEditor.ProBuilder.Actions { @@ -36,7 +34,29 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuSubdivide(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Subdivide Selection"); + + int success = 0; + + foreach(ProBuilderMesh pb in MeshSelection.topInternal) + { + pb.ToMesh(); + + if( pb.Subdivide() ) + success++; + + pb.Refresh(); + pb.Optimize(); + + pb.SetSelectedVertices(new int[0]); + } + + ProBuilderEditor.Refresh(); + + return new ActionResult(ActionResult.Status.Success, "Subdivide " + success + " Objects"); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Object/TriangulateObject.cs b/com.unity.probuilder/Editor/MenuActions/Object/TriangulateObject.cs index cadf8c1a7..0997644d6 100644 --- a/com.unity.probuilder/Editor/MenuActions/Object/TriangulateObject.cs +++ b/com.unity.probuilder/Editor/MenuActions/Object/TriangulateObject.cs @@ -1,8 +1,6 @@ using UnityEngine.ProBuilder; -using UnityEditor.ProBuilder; using UnityEngine; -using UnityEditor; -using UnityEditor.ProBuilder.UI; +using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder.Actions { @@ -26,7 +24,24 @@ public override bool enabled public override ActionResult DoAction() { - return MenuCommands.MenuTriangulateObject(MeshSelection.topInternal); + if(MeshSelection.selectedObjectCount < 1) + return ActionResult.NoSelection; + + UndoUtility.RecordSelection("Triangulate Objects"); + + foreach(var mesh in MeshSelection.topInternal) + { + mesh.ToMesh(); + mesh.ToTriangles(mesh.facesInternal); + mesh.Refresh(); + mesh.Optimize(); + mesh.ClearSelection(); + } + + ProBuilderEditor.Refresh(); + + var c = MeshSelection.selectedObjectCount; + return new ActionResult(ActionResult.Status.Success, "Triangulate " + c + (c > 1 ? " Objects" : " Object")); } } } diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/GrowSelection.cs b/com.unity.probuilder/Editor/MenuActions/Selection/GrowSelection.cs index 65e9f66a7..2b33c72d3 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/GrowSelection.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/GrowSelection.cs @@ -88,13 +88,10 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - var selection = MeshSelection.topInternal; - var editor = ProBuilderEditor.instance; - - if(!ProBuilderEditor.instance || selection == null || selection.Length < 1) + if (MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; - UndoUtility.RecordSelection(selection, "Grow Selection"); + UndoUtility.RecordSelection("Grow Selection"); int grown = 0; bool angleGrow = m_GrowSelectionWithAngle; diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/InvertSelection.cs b/com.unity.probuilder/Editor/MenuActions/Selection/InvertSelection.cs index d75a33033..b1486885b 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/InvertSelection.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/InvertSelection.cs @@ -44,18 +44,15 @@ public override bool enabled public override ActionResult DoAction() { - var selection = MeshSelection.topInternal; - var editor = ProBuilderEditor.instance; - - if(selection == null || selection.Length < 1) + if(MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; - UndoUtility.RecordSelection(selection, "Invert Selection"); + UndoUtility.RecordSelection("Invert Selection"); switch(ProBuilderEditor.selectMode) { case SelectMode.Vertex: - foreach(var mesh in selection) + foreach(var mesh in MeshSelection.topInternal) { SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal; List selectedSharedIndexes = new List(); @@ -77,7 +74,7 @@ public override ActionResult DoAction() case SelectMode.Face: case SelectMode.TextureFace: - foreach(var mesh in selection) + foreach(var mesh in MeshSelection.topInternal) { IEnumerable inverse = mesh.facesInternal.Where( x => !mesh.selectedFacesInternal.Contains(x) ); mesh.SetSelectedFaces(inverse.ToArray()); @@ -86,19 +83,17 @@ public override ActionResult DoAction() case SelectMode.Edge: - if(!editor) break; - - for(int i = 0; i < selection.Length; i++) + foreach(var mesh in MeshSelection.topInternal) { - var universalEdges = selection[i].GetSharedVertexHandleEdges(selection[i].facesInternal.SelectMany(x => x.edges)).ToArray(); - var universal_selected_edges = EdgeExtension.GetSharedVertexHandleEdges(selection[i], selection[i].selectedEdges).Distinct(); + var universalEdges = mesh.GetSharedVertexHandleEdges(mesh.facesInternal.SelectMany(x => x.edges)).ToArray(); + var universal_selected_edges = EdgeExtension.GetSharedVertexHandleEdges(mesh, mesh.selectedEdges).Distinct(); Edge[] inverse_universal = System.Array.FindAll(universalEdges, x => !universal_selected_edges.Contains(x)); Edge[] inverse = new Edge[inverse_universal.Length]; for(int n = 0; n < inverse_universal.Length; n++) - inverse[n] = new Edge( selection[i].sharedVerticesInternal[inverse_universal[n].a][0], selection[i].sharedVerticesInternal[inverse_universal[n].b][0] ); + inverse[n] = new Edge( mesh.sharedVerticesInternal[inverse_universal[n].a][0], mesh.sharedVerticesInternal[inverse_universal[n].b][0] ); - selection[i].SetSelectedEdges(inverse); + mesh.SetSelectedEdges(inverse); } break; } diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeLoop.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeLoop.cs index d6109face..9d89ea275 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeLoop.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeLoop.cs @@ -54,16 +54,14 @@ public override bool enabled public override ActionResult DoAction() { - var selection = MeshSelection.topInternal; - - if(selection == null || selection.Length < 1) + if(MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; - UndoUtility.RecordSelection(selection, "Select Edge Loop"); + UndoUtility.RecordSelection("Select Edge Loop"); bool foundLoop = false; - foreach(ProBuilderMesh pb in selection) + foreach(ProBuilderMesh pb in MeshSelection.topInternal) { Edge[] loop; bool success = ElementSelection.GetEdgeLoop(pb, pb.selectedEdges, out loop); diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeRing.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeRing.cs index 839a8727a..efcffc7ec 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeRing.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectEdgeRing.cs @@ -54,23 +54,21 @@ public override bool enabled public override ActionResult DoAction() { - var selection = MeshSelection.topInternal; - - if(selection == null || selection.Length < 1) + if(MeshSelection.selectedObjectCount < 1) return ActionResult.NoSelection; - UndoUtility.RecordSelection(selection, "Select Edge Ring"); + UndoUtility.RecordSelection("Select Edge Ring"); bool success = false; - foreach(ProBuilderMesh pb in InternalUtility.GetComponents(Selection.transforms)) + foreach(var mesh in MeshSelection.topInternal) { - Edge[] edges = ElementSelection.GetEdgeRing(pb, pb.selectedEdges).ToArray(); + Edge[] edges = ElementSelection.GetEdgeRing(mesh, mesh.selectedEdges).ToArray(); - if(edges.Length > pb.selectedEdgeCount) + if(edges.Length > mesh.selectedEdgeCount) success = true; - pb.SetSelectedEdges( edges ); + mesh.SetSelectedEdges( edges ); } ProBuilderEditor.Refresh(); diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceLoop.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceLoop.cs index 4cf18e19f..42001b351 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceLoop.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceLoop.cs @@ -54,7 +54,7 @@ public override ActionResult DoAction() { var selection = MeshSelection.topInternal; - UndoUtility.RecordSelection(selection, "Select Face Loop"); + UndoUtility.RecordSelection("Select Face Loop"); foreach (ProBuilderMesh pb in selection) { diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceRing.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceRing.cs index 538daea21..5464e2dec 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceRing.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectFaceRing.cs @@ -52,7 +52,7 @@ public override ActionResult DoAction() { var selection = MeshSelection.topInternal; - UndoUtility.RecordSelection(selection, "Select Face Ring"); + UndoUtility.RecordSelection("Select Face Ring"); foreach (ProBuilderMesh pb in selection) { diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectHole.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectHole.cs index befa68c32..afa1a99f4 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectHole.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectHole.cs @@ -44,7 +44,7 @@ public override bool enabled public override ActionResult DoAction() { - UndoUtility.RecordSelection(MeshSelection.topInternal, "Select Hole"); + UndoUtility.RecordSelection("Select Hole"); ActionResult res = ActionResult.NoSelection; diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectMaterial.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectMaterial.cs index 8bbb5d340..b47bdfa8f 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectMaterial.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectMaterial.cs @@ -69,9 +69,14 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - UndoUtility.RecordSelection(MeshSelection.topInternal, "Select Faces with Material"); + IEnumerable selection; - bool restrictToSelection = m_RestrictToSelectedObjects; + if (m_RestrictToSelectedObjects) + selection = MeshSelection.topInternal; + else + selection = Object.FindObjectsOfType(); + + UndoUtility.RecordSelection("Select Faces with Material"); HashSet sel = new HashSet( MeshSelection.topInternal @@ -79,7 +84,7 @@ public override ActionResult DoAction() List newSelection = new List(); - foreach (ProBuilderMesh pb in restrictToSelection ? MeshSelection.topInternal : Object.FindObjectsOfType()) + foreach (var pb in selection) { IEnumerable matches = pb.facesInternal.Where(x => sel.Contains(x.submeshIndex)); diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectSmoothingGroup.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectSmoothingGroup.cs index 9f43ac478..c5fab8431 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectSmoothingGroup.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectSmoothingGroup.cs @@ -59,7 +59,7 @@ protected override MenuActionState optionsMenuState public override ActionResult DoAction() { - UndoUtility.RecordSelection(MeshSelection.topInternal, "Select Faces with Smoothing Group"); + UndoUtility.RecordSelection("Select Faces with Smoothing Group"); HashSet selectedSmoothGroups = new HashSet(MeshSelection.topInternal.SelectMany(x => x.selectedFacesInternal.Select(y => y.smoothingGroup))); diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/SelectVertexColor.cs b/com.unity.probuilder/Editor/MenuActions/Selection/SelectVertexColor.cs index b5253f202..251993e1c 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/SelectVertexColor.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/SelectVertexColor.cs @@ -77,7 +77,7 @@ protected override void OnSettingsGUI() public override ActionResult DoAction() { - UndoUtility.RecordSelection(MeshSelection.topInternal, "Select Faces with Vertex Colors"); + UndoUtility.RecordSelection("Select Faces with Vertex Colors"); HashSet colors = new HashSet(); @@ -94,7 +94,13 @@ public override ActionResult DoAction() List newSelection = new List(); bool selectionOnly = m_SearchSelectedObjectsOnly; - ProBuilderMesh[] pool = selectionOnly ? MeshSelection.topInternal : Object.FindObjectsOfType(); + + IEnumerable pool; + + if (selectionOnly) + pool = MeshSelection.topInternal; + else + pool = Object.FindObjectsOfType(); foreach (ProBuilderMesh pb in pool) { diff --git a/com.unity.probuilder/Editor/MenuActions/Selection/ShrinkSelection.cs b/com.unity.probuilder/Editor/MenuActions/Selection/ShrinkSelection.cs index e06bbd37e..44eb4aa79 100644 --- a/com.unity.probuilder/Editor/MenuActions/Selection/ShrinkSelection.cs +++ b/com.unity.probuilder/Editor/MenuActions/Selection/ShrinkSelection.cs @@ -42,12 +42,13 @@ public override bool enabled public override ActionResult DoAction() { var selection = MeshSelection.topInternal; + var selectionCount = MeshSelection.selectedObjectCount; - UndoUtility.RecordSelection(selection, "Shrink Selection"); + UndoUtility.RecordSelection("Shrink Selection"); // find perimeter edges int rc = 0; - for(int i = 0; i < selection.Length; i++) + for(int i = 0; i < selectionCount; i++) { ProBuilderMesh mesh = selection[i]; diff --git a/com.unity.probuilder/Runtime/Core/Bounds2D.cs b/com.unity.probuilder/Runtime/Core/Bounds2D.cs index 1a7627e2e..82841a86d 100644 --- a/com.unity.probuilder/Runtime/Core/Bounds2D.cs +++ b/com.unity.probuilder/Runtime/Core/Bounds2D.cs @@ -366,14 +366,43 @@ public static Vector2 Center(Vector2[] points, int length = -1) /// /// /// - public static Vector2 Center(Vector2[] points, int[] indexes) + public static Vector2 Center(IList points, IList indexes) { float xMin = 0f, xMax = 0f, yMin = 0f, yMax = 0f; - int size = indexes.Length; + int size = indexes.Count; + + xMin = points[indexes[0]].x; + yMin = points[indexes[0]].y; + xMax = xMin; + yMax = yMin; + + for(int i = 1; i < size; i++) + { + float x = points[indexes[i]].x; + float y = points[indexes[i]].y; + + if(x < xMin) xMin = x; + if(x > xMax) xMax = x; + + if(y < yMin) yMin = y; + if(y > yMax) yMax = y; + } + + return new Vector2( (xMin + xMax) / 2f, (yMin + yMax) / 2f ); + } + + internal static Vector2 Center(IList points, IList indexes) + { + float xMin = 0f, + xMax = 0f, + yMin = 0f, + yMax = 0f; + + int size = indexes.Count; xMin = points[indexes[0]].x; yMin = points[indexes[0]].y; diff --git a/com.unity.probuilder/Runtime/Core/HandleOrientation.cs b/com.unity.probuilder/Runtime/Core/HandleOrientation.cs new file mode 100644 index 000000000..a3f2f6d40 --- /dev/null +++ b/com.unity.probuilder/Runtime/Core/HandleOrientation.cs @@ -0,0 +1,25 @@ +namespace UnityEngine.ProBuilder +{ + /// + /// How the handle gizmo is oriented with regards to the current element selection. + /// + /// + /// This overrides the Unity Pivot / Global setting when editing vertices, faces, or edges. + /// + /// Editor only. + public enum HandleOrientation + { + /// + /// The gizmo is aligned in world space. + /// + World = 0, + /// + /// The gizmo is aligned relative to the mesh transform. Also called coordinate or model space. + /// + Local = 1, + /// + /// The gizmo is aligned relative to the currently selected face. When editing vertices or edges, this falls back to alignment. + /// + Normal = 2 + } +} diff --git a/com.unity.probuilder/Runtime/Core/HandleOrientation.cs.meta b/com.unity.probuilder/Runtime/Core/HandleOrientation.cs.meta new file mode 100644 index 000000000..d1b2d7155 --- /dev/null +++ b/com.unity.probuilder/Runtime/Core/HandleOrientation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e52c205e35f30d4c81ff62d3f94e234 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Runtime/Core/InternalUtility.cs b/com.unity.probuilder/Runtime/Core/InternalUtility.cs index 5c275bceb..a63e0882c 100644 --- a/com.unity.probuilder/Runtime/Core/InternalUtility.cs +++ b/com.unity.probuilder/Runtime/Core/InternalUtility.cs @@ -12,7 +12,7 @@ namespace UnityEngine.ProBuilder /// static class InternalUtility { - // todo Change to shallow search + [Obsolete] public static T[] GetComponents(this IEnumerable gameObjects) where T : Component { List c = new List(); diff --git a/com.unity.probuilder/Runtime/Core/Math.cs b/com.unity.probuilder/Runtime/Core/Math.cs index 717a5a2d0..025a6d22b 100644 --- a/com.unity.probuilder/Runtime/Core/Math.cs +++ b/com.unity.probuilder/Runtime/Core/Math.cs @@ -937,24 +937,82 @@ internal static Vector2 LargestVector2(Vector2[] v, int[] indexes) /// /// /// - internal static Bounds GetBounds(Vector3[] positions) + internal static Bounds GetBounds(Vector3[] positions, int[] indices = null) { - if (positions.Length < 1) + bool hasIndices = indices != null; + + if ((hasIndices && indices.Length < 1) || positions.Length < 1) return default(Bounds); - Vector3 min = positions[0]; + Vector3 min = positions[hasIndices ? indices[0] : 0]; + Vector3 max = min; + + if (hasIndices) + { + for(int i = 1, c = indices.Length; i < c; i++) + { + min.x = Mathf.Min(positions[indices[i]].x, min.x); + max.x = Mathf.Max(positions[indices[i]].x, max.x); + + min.y = Mathf.Min(positions[indices[i]].y, min.y); + max.y = Mathf.Max(positions[indices[i]].y, max.y); + + min.z = Mathf.Min(positions[indices[i]].z, min.z); + max.z = Mathf.Max(positions[indices[i]].z, max.z); + } + } + else + { + for(int i = 1, c = positions.Length; i < c; i++) + { + min.x = Mathf.Min(positions[i].x, min.x); + max.x = Mathf.Max(positions[i].x, max.x); + + min.y = Mathf.Min(positions[i].y, min.y); + max.y = Mathf.Max(positions[i].y, max.y); + + min.z = Mathf.Min(positions[i].z, min.z); + max.z = Mathf.Max(positions[i].z, max.z); + } + } + + return new Bounds((min+max) * .5f, max - min); + } + + /// + /// Creates an AABB with a set of vertices. + /// + /// + /// + internal static Bounds GetBounds(Vector3[] positions, IEnumerable faces) + { + bool initialized = false; + + Vector3 min = Vector3.zero; Vector3 max = min; - for(int i = 1, c = positions.Length; i < c; i++) + foreach (var face in faces) { - min.x = Mathf.Min(positions[i].x, min.x); - max.x = Mathf.Max(positions[i].x, max.x); + var indices = face.distinctIndexesInternal; + + if (!initialized) + { + initialized = true; + min = positions[indices[0]]; + max = positions[indices[0]]; + } + + for(int i = 0, c = indices.Length; i < c; i++) + { + min.x = Mathf.Min(positions[indices[i]].x, min.x); + max.x = Mathf.Max(positions[indices[i]].x, max.x); - min.y = Mathf.Min(positions[i].y, min.y); - max.y = Mathf.Max(positions[i].y, max.y); + min.y = Mathf.Min(positions[indices[i]].y, min.y); + max.y = Mathf.Max(positions[indices[i]].y, max.y); - min.z = Mathf.Min(positions[i].z, min.z); - max.z = Mathf.Max(positions[i].z, max.z); + min.z = Mathf.Min(positions[indices[i]].z, min.z); + max.z = Mathf.Max(positions[indices[i]].z, max.z); + } } return new Bounds((min+max) * .5f, max - min); @@ -1180,23 +1238,6 @@ public static int Clamp(int value, int lowerBound, int upperBound) return value < lowerBound ? lowerBound : value > upperBound ? upperBound : value; } - internal static Vector2 ToMask(this Vector2 vec, float delta = floatEpsilon) - { - return new Vector2( - Mathf.Abs(vec.x) > delta ? 1f : 0f, - Mathf.Abs(vec.y) > delta ? 1f : 0f - ); - } - - internal static Vector3 ToMask(this Vector3 vec, float delta = floatEpsilon) - { - return new Vector3( - Mathf.Abs(vec.x) > delta ? 1f : 0f, - Mathf.Abs(vec.y) > delta ? 1f : 0f, - Mathf.Abs(vec.z) > delta ? 1f : 0f - ); - } - internal static Vector3 ToSignedMask(this Vector3 vec, float delta = floatEpsilon) { return new Vector3( diff --git a/com.unity.probuilder/Runtime/Core/MeshHandle.cs b/com.unity.probuilder/Runtime/Core/MeshHandle.cs index eb521c99d..503780765 100644 --- a/com.unity.probuilder/Runtime/Core/MeshHandle.cs +++ b/com.unity.probuilder/Runtime/Core/MeshHandle.cs @@ -21,6 +21,9 @@ public MeshHandle(Transform transform, Mesh mesh) public void DrawMeshNow(int submeshIndex) { + if (m_Transform == null || m_Mesh == null) + return; + Graphics.DrawMeshNow(m_Mesh, m_Transform.localToWorldMatrix, submeshIndex); } } diff --git a/com.unity.probuilder/Runtime/Core/PivotPoint.cs b/com.unity.probuilder/Runtime/Core/PivotPoint.cs new file mode 100644 index 000000000..bbecb0f1b --- /dev/null +++ b/com.unity.probuilder/Runtime/Core/PivotPoint.cs @@ -0,0 +1,27 @@ +namespace UnityEngine.ProBuilder +{ + public enum PivotPoint + { + /// + /// Transforms are applied from the center point of the selection bounding box. + /// Corresponds with . + /// + WorldBoundingBoxCenter = 1 << 0, + + /// + /// Transforms are applied in model space from the average point of selected elements. + /// Corresponds with . + /// + ModelBoundingBoxCenter = 1 << 1, + + /// + /// Transforms are applied from the origin of each selection group. + /// + IndividualOrigins = 1 << 2, + + /// + /// Transforms are applied from a user-defined pivot point. + /// + Custom = 1 << 3 + } +} diff --git a/com.unity.probuilder/Runtime/Core/PivotPoint.cs.meta b/com.unity.probuilder/Runtime/Core/PivotPoint.cs.meta new file mode 100644 index 000000000..1ba99919a --- /dev/null +++ b/com.unity.probuilder/Runtime/Core/PivotPoint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3abdb0569285bbf4389d38655d5a90a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Runtime/Core/ProBuilderEnum.cs b/com.unity.probuilder/Runtime/Core/ProBuilderEnum.cs index f583a855f..3337cf42a 100644 --- a/com.unity.probuilder/Runtime/Core/ProBuilderEnum.cs +++ b/com.unity.probuilder/Runtime/Core/ProBuilderEnum.cs @@ -39,11 +39,15 @@ public enum SelectMode /// TextureFace = 1 << 4, - // TextureEdge = 1 << 5, - Reserved0 = 1 << 5, + /// + /// Texture coordinates are selectable. + /// + TextureEdge = 1 << 5, - // TextureVertex = 1 << 6, - Reserved1 = 1 << 6, + /// + /// Texture coordinates are selectable. + /// + TextureVertex = 1 << 6, /// /// Other input tool (Poly Shape editor, Bezier editor, etc) diff --git a/com.unity.probuilder/Runtime/Core/ProBuilderMeshFunction.cs b/com.unity.probuilder/Runtime/Core/ProBuilderMeshFunction.cs index 2304a96f8..dd788ce62 100644 --- a/com.unity.probuilder/Runtime/Core/ProBuilderMeshFunction.cs +++ b/com.unity.probuilder/Runtime/Core/ProBuilderMeshFunction.cs @@ -459,6 +459,40 @@ public List GetCoincidentVertices(IEnumerable vertices) return shared; } + /// + /// Populate a list of vertices that are coincident to any of the vertices in the passed vertices parameter. + /// + /// A collection of faces to gather vertices from. + /// A list to be cleared and populated with any vertices that are coincident. + /// The vertices and coincident parameters may not be null. + public void GetCoincidentVertices(IEnumerable faces, List coincident) + { + if (faces == null) + throw new ArgumentNullException("faces"); + + if (coincident == null) + throw new ArgumentNullException("coincident"); + + s_CachedHashSet.Clear(); + var lookup = sharedVertexLookup; + + foreach(var face in faces) + { + foreach (var v in face.distinctIndexesInternal) + { + var common = lookup[v]; + + if (s_CachedHashSet.Add(common)) + { + var indices = m_SharedVertices[common]; + + for (int i = 0, c = indices.Count; i < c; i++) + coincident.Add(indices[i]); + } + } + } + } + /// /// Populate a list of vertices that are coincident to any of the vertices in the passed vertices parameter. /// diff --git a/com.unity.probuilder/Runtime/Core/ProBuilderMeshSelection.cs b/com.unity.probuilder/Runtime/Core/ProBuilderMeshSelection.cs index 21536ede8..5d75b20ad 100644 --- a/com.unity.probuilder/Runtime/Core/ProBuilderMeshSelection.cs +++ b/com.unity.probuilder/Runtime/Core/ProBuilderMeshSelection.cs @@ -113,6 +113,11 @@ internal Face[] selectedFacesInternal get { return GetSelectedFaces(); } } + internal int[] selectedFaceIndicesInternal + { + get { return m_SelectedFaces; } + } + /// /// A collection of the currently selected faces by their index in the @"UnityEngine.ProBuilder.ProBuilderMesh.faces" array. /// diff --git a/com.unity.probuilder/Runtime/Core/VectorMask.cs b/com.unity.probuilder/Runtime/Core/VectorMask.cs new file mode 100644 index 000000000..ce59e8564 --- /dev/null +++ b/com.unity.probuilder/Runtime/Core/VectorMask.cs @@ -0,0 +1,155 @@ +namespace UnityEngine.ProBuilder +{ + struct Vector2Mask + { + const byte X = 1 << 0; + const byte Y = 1 << 1; + + public static readonly Vector2Mask XY = new Vector2Mask(X | Y); + + byte m_Mask; + + public float x + { + get { return (m_Mask & X) == X ? 1f : 0f; } + } + + public float y + { + get { return (m_Mask & Y) == Y ? 1f : 0f; } + } + + public Vector2Mask(Vector3 v, float epsilon = float.Epsilon) + { + m_Mask = 0x0; + + if (Mathf.Abs(v.x) > epsilon) + m_Mask |= X; + if (Mathf.Abs(v.y) > epsilon) + m_Mask |= Y; + } + + public Vector2Mask(byte mask) + { + m_Mask = mask; + } + + public static implicit operator Vector2(Vector2Mask mask) + { + return new Vector2(mask.x, mask.y); + } + + public static implicit operator Vector2Mask(Vector2 v) + { + return new Vector2Mask(v); + } + + public static Vector2Mask operator |(Vector2Mask left, Vector2Mask right) + { + return new Vector2Mask((byte)(left.m_Mask | right.m_Mask)); + } + + public static Vector2Mask operator &(Vector2Mask left, Vector2Mask right) + { + return new Vector2Mask((byte)(left.m_Mask & right.m_Mask)); + } + + public static Vector2Mask operator ^(Vector2Mask left, Vector2Mask right) + { + return new Vector2Mask((byte)(left.m_Mask ^ right.m_Mask)); + } + + public static Vector2 operator *(Vector2Mask mask, float value) + { + return new Vector2(mask.x * value, mask.y * value); + } + } + + struct Vector3Mask + { + const byte X = 1 << 0; + const byte Y = 1 << 1; + const byte Z = 1 << 2; + + public static readonly Vector3Mask XYZ = new Vector3Mask(X | Y | Z); + + byte m_Mask; + + public float x + { + get { return (m_Mask & X) == X ? 1f : 0f; } + } + + public float y + { + get { return (m_Mask & Y) == Y ? 1f : 0f; } + } + + public float z + { + get { return (m_Mask & Z) == Z ? 1f : 0f; } + } + + public Vector3Mask(Vector3 v, float epsilon = float.Epsilon) + { + m_Mask = 0x0; + + if (Mathf.Abs(v.x) > epsilon) + m_Mask |= X; + if (Mathf.Abs(v.y) > epsilon) + m_Mask |= Y; + if (Mathf.Abs(v.z) > epsilon) + m_Mask |= Z; + } + + public Vector3Mask(byte mask) + { + m_Mask = mask; + } + + public int active + { + get + { + int count = 0; + if ((m_Mask & X) > 0) + count++; + if ((m_Mask & Y) > 0) + count++; + if ((m_Mask & Z) > 0) + count++; + return count; + } + } + + public static implicit operator Vector3(Vector3Mask mask) + { + return new Vector3(mask.x, mask.y, mask.z); + } + + public static implicit operator Vector3Mask(Vector3 v) + { + return new Vector3Mask(v); + } + + public static Vector3Mask operator |(Vector3Mask left, Vector3Mask right) + { + return new Vector3Mask((byte)(left.m_Mask | right.m_Mask)); + } + + public static Vector3Mask operator &(Vector3Mask left, Vector3Mask right) + { + return new Vector3Mask((byte)(left.m_Mask & right.m_Mask)); + } + + public static Vector3Mask operator ^(Vector3Mask left, Vector3Mask right) + { + return new Vector3Mask((byte)(left.m_Mask ^ right.m_Mask)); + } + + public static Vector3 operator *(Vector3Mask mask, float value) + { + return new Vector3(mask.x * value, mask.y * value, mask.z * value); + } + } +} diff --git a/com.unity.probuilder/Runtime/Core/VectorMask.cs.meta b/com.unity.probuilder/Runtime/Core/VectorMask.cs.meta new file mode 100644 index 000000000..c7860a22b --- /dev/null +++ b/com.unity.probuilder/Runtime/Core/VectorMask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: efd5cca1cb8dabd418d215e770d3fb01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.probuilder/Runtime/Core/Version.cs b/com.unity.probuilder/Runtime/Core/Version.cs index 3c6b03116..5eee823df 100644 --- a/com.unity.probuilder/Runtime/Core/Version.cs +++ b/com.unity.probuilder/Runtime/Core/Version.cs @@ -5,7 +5,7 @@ namespace UnityEngine.ProBuilder /// static class Version { - internal static readonly SemVer currentInfo = new SemVer("4.0.0-preview.27", "2018/10/09"); + internal static readonly SemVer currentInfo = new SemVer("4.0.0-preview.28", "2018/11/05"); /// /// Get the current version. diff --git a/com.unity.probuilder/Runtime/Core/VertexPositioning.cs b/com.unity.probuilder/Runtime/Core/VertexPositioning.cs index 76b378bc3..0aa958658 100644 --- a/com.unity.probuilder/Runtime/Core/VertexPositioning.cs +++ b/com.unity.probuilder/Runtime/Core/VertexPositioning.cs @@ -83,12 +83,12 @@ internal static void TranslateVerticesInWorldSpace(this ProBuilderMesh mesh, if(Mathf.Abs(snapValue) > Mathf.Epsilon) { Matrix4x4 l2w = mesh.transform.localToWorldMatrix; - Vector3 mask = snapAxisOnly ? offset.ToMask(Math.handleEpsilon) : Vector3.one; + var mask = snapAxisOnly ? new Vector3Mask(offset, Math.handleEpsilon) : Vector3Mask.XYZ; for(i = 0; i < s_CoincidentVertices.Count; i++) { var v = l2w.MultiplyPoint3x4(verts[s_CoincidentVertices[i]] + localOffset); - verts[s_CoincidentVertices[i]] = w2l.MultiplyPoint3x4( Snapping.SnapValue(v, snapValue * mask) ); + verts[s_CoincidentVertices[i]] = w2l.MultiplyPoint3x4( Snapping.SnapValue(v, ((Vector3)mask) * snapValue) ); } } else diff --git a/com.unity.probuilder/Runtime/Core/WingedEdge.cs b/com.unity.probuilder/Runtime/Core/WingedEdge.cs index eec7cc3e2..963323285 100644 --- a/com.unity.probuilder/Runtime/Core/WingedEdge.cs +++ b/com.unity.probuilder/Runtime/Core/WingedEdge.cs @@ -331,12 +331,11 @@ public static List GetWingedEdges(ProBuilderMesh mesh, IEnumerable distinct = faces.Distinct(); List winged = new List(); Dictionary opposites = new Dictionary(); - foreach (Face f in distinct) + foreach (Face f in faces) { List edges = SortEdgesByAdjacency(f); int edgeLength = edges.Count; diff --git a/com.unity.probuilder/Runtime/MeshOperations/MeshTransform.cs b/com.unity.probuilder/Runtime/MeshOperations/MeshTransform.cs index 0cbb9d5c9..ffc56aba0 100644 --- a/com.unity.probuilder/Runtime/MeshOperations/MeshTransform.cs +++ b/com.unity.probuilder/Runtime/MeshOperations/MeshTransform.cs @@ -47,8 +47,8 @@ public static void CenterPivot(this ProBuilderMesh mesh, int[] indexes) /// Set the pivot point of a mesh in world space. The Transform component position property is set to worldPosition, while the mesh geometry does not move. /// /// The target mesh. - /// The new transform position. - public static void CenterPivot(this ProBuilderMesh mesh, Vector3 worldPosition) + /// The new pivot position in world space. + public static void SetPivot(this ProBuilderMesh mesh, Vector3 worldPosition) { if (mesh == null) throw new System.ArgumentNullException("mesh"); diff --git a/com.unity.probuilder/Settings/Editor/UserSetting.cs b/com.unity.probuilder/Settings/Editor/UserSetting.cs index 90f8aef97..c3baf4abe 100644 --- a/com.unity.probuilder/Settings/Editor/UserSetting.cs +++ b/com.unity.probuilder/Settings/Editor/UserSetting.cs @@ -245,7 +245,7 @@ public void SetValue(T value, bool saveProjectSettingsImmediately = false) m_Value = value; settings.Set(key, m_Value, m_Scope); - if (m_Scope == SettingsScope.Project && saveProjectSettingsImmediately) + if (saveProjectSettingsImmediately) settings.Save(); } diff --git a/com.unity.probuilder/Settings/Editor/UserSettingsProvider.cs b/com.unity.probuilder/Settings/Editor/UserSettingsProvider.cs index c6f46a8f2..b2564b4f8 100644 --- a/com.unity.probuilder/Settings/Editor/UserSettingsProvider.cs +++ b/com.unity.probuilder/Settings/Editor/UserSettingsProvider.cs @@ -8,7 +8,13 @@ using System.Linq; using System.Reflection; using UnityEngine; +#if SETTINGS_PROVIDER_ENABLED +#if UNITY_2018_3 using UnityEngine.Experimental.UIElements; +#else +using UnityEngine.UIElements; +#endif +#endif namespace UnityEditor.SettingsManagement { diff --git a/com.unity.probuilder/package.json b/com.unity.probuilder/package.json index 2f7b49ca5..2af121271 100644 --- a/com.unity.probuilder/package.json +++ b/com.unity.probuilder/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.probuilder", "displayName":"ProBuilder", - "version": "4.0.0-preview.27", + "version": "4.0.0-preview.28", "unity": "2018.1", "description": "Build, edit, and texture custom geometry in Unity. Use ProBuilder for in-scene level design, prototyping, collision meshes, all with on-the-fly play-testing.\n\nAdvanced features include UV editing, vertex colors, parametric shapes, and texture blending. With ProBuilder's model export feature it's easy to tweak your levels in any external 3D modelling suite.\n\nDisclaimer: The ProBuilder API is currently in beta and will change before final release.", "keywords": ["3d", "model", "mesh", "modeling", "geometry", "shape", "cube", "blender", "max", "maya", "fbx", "obj", "level", "design", "block", "greybox", "graybox", "whitebox", "prototype", "probuilder"],