From b57e847916d3f3ef03de1b51b9073dec152f7be7 Mon Sep 17 00:00:00 2001 From: Zode Date: Mon, 17 Jun 2024 21:19:31 +0300 Subject: [PATCH 1/5] Add absolute snapping --- .../Gizmo/TransformGizmoBase.Settings.cs | 5 +++ Source/Editor/Gizmo/TransformGizmoBase.cs | 42 +++++++++++++++++++ Source/Editor/Viewport/EditorGizmoViewport.cs | 20 ++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs index bbd13f688..c7af85c14 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs @@ -75,6 +75,11 @@ namespace FlaxEditor.Gizmo /// public bool ScaleSnapEnabled = false; + /// + /// True if enable absolute grid snapping + /// + public bool AbsoluteSnapEnabled = false; + /// /// Translation snap value /// diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 81f76d392..2c7580c79 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -40,6 +40,7 @@ namespace FlaxEditor.Gizmo private Vector3 _intersectPosition; private bool _isActive; private bool _isDuplicating; + private bool _hasAbsoluteSnapped; private bool _isTransforming; private Vector3 _lastIntersectionPosition; @@ -316,6 +317,7 @@ namespace FlaxEditor.Gizmo if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping) { var snapValue = new Vector3(isScaling ? ScaleSnapValue : TranslationSnapValue); + _translationScaleSnapDelta += delta; if (!isScaling && snapValue.X < 0.0f) { @@ -334,11 +336,29 @@ namespace FlaxEditor.Gizmo else snapValue.Z = (Real)b.Minimum.Z - b.Maximum.Z; } + + Vector3 absoluteDelta = Vector3.Zero; + if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled) + { + _hasAbsoluteSnapped = true; + + Vector3 currentTranslationScale = isScaling ? GetSelectedTransform(0).Scale : GetSelectedTransform(0).Translation; + absoluteDelta = currentTranslationScale - new Vector3( + Mathf.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, + Mathf.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, + Mathf.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); + } + delta = new Vector3( (int)(_translationScaleSnapDelta.X / snapValue.X) * snapValue.X, (int)(_translationScaleSnapDelta.Y / snapValue.Y) * snapValue.Y, (int)(_translationScaleSnapDelta.Z / snapValue.Z) * snapValue.Z); _translationScaleSnapDelta -= delta; + delta -= absoluteDelta; + } + else + { + _hasAbsoluteSnapped = false; } if (_activeMode == Mode.Translate) @@ -368,12 +388,34 @@ namespace FlaxEditor.Gizmo if (RotationSnapEnabled || Owner.UseSnapping) { float snapValue = RotationSnapValue * Mathf.DegreesToRadians; + + float absoluteDelta = 0.0f; + if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled) + { + _hasAbsoluteSnapped = true; + + float currentAngle = 0.0f; + switch (_activeAxis) + { + case Axis.X: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.X; break; + case Axis.Y: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.Y; break; + case Axis.Z: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.Z; break; + } + + absoluteDelta = currentAngle - (Mathf.Round(currentAngle / RotationSnapValue) * RotationSnapValue); + } + _rotationSnapDelta += delta; float snapped = Mathf.Round(_rotationSnapDelta / snapValue) * snapValue; _rotationSnapDelta -= snapped; delta = snapped; + delta -= absoluteDelta * Mathf.DegreesToRadians; + } + else + { + _hasAbsoluteSnapped = false; } switch (_activeAxis) diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index f0681e48d..e79d3f19e 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -137,7 +137,9 @@ namespace FlaxEditor.Viewport if (useProjectCache) { // Initialize snapping enabled from cached values - if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out bool cachedBool)) + if (editor.ProjectCache.TryGetCustomData("AbsoluteSnapState", out bool cachedBool)) + transformGizmo.AbsoluteSnapEnabled = cachedBool; + if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out cachedBool)) transformGizmo.TranslationSnapEnable = cachedBool; if (editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedBool)) transformGizmo.RotationSnapEnabled = cachedBool; @@ -169,6 +171,22 @@ namespace FlaxEditor.Viewport }; transformSpaceWidget.Parent = viewport; + // Absolute snapping widget + var absoluteSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var enableAbsoluteSnapping = new ViewportWidgetButton("A", SpriteHandle.Default, null, true) + { + Checked = transformGizmo.AbsoluteSnapEnabled, + TooltipText = "Enable absolute snapping", + Parent = absoluteSnappingWidget + }; + enableAbsoluteSnapping.Toggled += _ => + { + transformGizmo.AbsoluteSnapEnabled = !transformGizmo.AbsoluteSnapEnabled; + if (useProjectCache) + editor.ProjectCache.SetCustomData("AbsoluteSnapState", transformGizmo.AbsoluteSnapEnabled); + }; + absoluteSnappingWidget.Parent = viewport; + // Scale snapping widget var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true) From 840abf8cdb4750bfaab47f02e3a03fd75ed69b46 Mon Sep 17 00:00:00 2001 From: Zode Date: Mon, 17 Jun 2024 21:27:13 +0300 Subject: [PATCH 2/5] absolute snap only in world space --- Source/Editor/Gizmo/TransformGizmoBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 2c7580c79..5e2a20d17 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -338,7 +338,7 @@ namespace FlaxEditor.Gizmo } Vector3 absoluteDelta = Vector3.Zero; - if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled) + if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World) { _hasAbsoluteSnapped = true; @@ -390,7 +390,7 @@ namespace FlaxEditor.Gizmo float snapValue = RotationSnapValue * Mathf.DegreesToRadians; float absoluteDelta = 0.0f; - if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled) + if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World) { _hasAbsoluteSnapped = true; From 5780fb7392608e2403ee306ee9428a349bc4cf58 Mon Sep 17 00:00:00 2001 From: Zode Date: Mon, 17 Jun 2024 22:16:10 +0300 Subject: [PATCH 3/5] absolute snapping: large worlds fix --- Source/Editor/Gizmo/TransformGizmoBase.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 5e2a20d17..1715a5b02 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -343,10 +343,20 @@ namespace FlaxEditor.Gizmo _hasAbsoluteSnapped = true; Vector3 currentTranslationScale = isScaling ? GetSelectedTransform(0).Scale : GetSelectedTransform(0).Translation; - absoluteDelta = currentTranslationScale - new Vector3( - Mathf.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, - Mathf.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, - Mathf.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); + if(LargeWorlds.Enable) + { + absoluteDelta = currentTranslationScale - new Vector3( + Double.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, + Double.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, + Double.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); + } + else + { + absoluteDelta = currentTranslationScale - new Vector3( + Mathf.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, + Mathf.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, + Mathf.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); + } } delta = new Vector3( From 7ede2504032933ce8ff089e89632cacdb1f65d84 Mon Sep 17 00:00:00 2001 From: Zode Date: Mon, 17 Jun 2024 22:59:19 +0300 Subject: [PATCH 4/5] absolute snapping: actually fix large world this time --- Source/Editor/Gizmo/TransformGizmoBase.cs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 1715a5b02..2ce601d1e 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -343,20 +343,10 @@ namespace FlaxEditor.Gizmo _hasAbsoluteSnapped = true; Vector3 currentTranslationScale = isScaling ? GetSelectedTransform(0).Scale : GetSelectedTransform(0).Translation; - if(LargeWorlds.Enable) - { - absoluteDelta = currentTranslationScale - new Vector3( - Double.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, - Double.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, - Double.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); - } - else - { - absoluteDelta = currentTranslationScale - new Vector3( - Mathf.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, - Mathf.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, - Mathf.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); - } + absoluteDelta = currentTranslationScale - new Vector3( + (Real)Math.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, + (Real)Math.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, + (Real)Math.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); } delta = new Vector3( From 9c15094855bc0271a052b265159983eb437ddcf4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 11 Mar 2025 14:05:18 +0100 Subject: [PATCH 5/5] Minor tweaks #2722 --- .../Gizmo/TransformGizmoBase.Settings.cs | 2 +- Source/Editor/Gizmo/TransformGizmoBase.cs | 13 +++++---- Source/Editor/Viewport/EditorGizmoViewport.cs | 28 ++++++++++--------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs index 7630d4c26..4d9326bb5 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.Gizmo public bool ScaleSnapEnabled = false; /// - /// True if enable absolute grid snapping + /// True if enable absolute grid snapping (snaps objects to world-space grid, not the one relative to gizmo location) /// public bool AbsoluteSnapEnabled = false; diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 886d56c4d..da1ed575e 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -2,8 +2,10 @@ #if USE_LARGE_WORLDS using Real = System.Double; +using Mathr = FlaxEngine.Mathd; #else using Real = System.Single; +using Mathr = FlaxEngine.Mathf; #endif using System; @@ -390,13 +392,13 @@ namespace FlaxEditor.Gizmo Vector3 absoluteDelta = Vector3.Zero; if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World) { + // Remove delta to offset local-space grid into the world-space grid _hasAbsoluteSnapped = true; - Vector3 currentTranslationScale = isScaling ? GetSelectedTransform(0).Scale : GetSelectedTransform(0).Translation; absoluteDelta = currentTranslationScale - new Vector3( - (Real)Math.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, - (Real)Math.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, - (Real)Math.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); + Mathr.Round(currentTranslationScale.X / snapValue.X) * snapValue.X, + Mathr.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y, + Mathr.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z); } delta = new Vector3( @@ -442,8 +444,8 @@ namespace FlaxEditor.Gizmo float absoluteDelta = 0.0f; if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World) { + // Remove delta to offset world-space grid into the local-space grid _hasAbsoluteSnapped = true; - float currentAngle = 0.0f; switch (_activeAxis) { @@ -451,7 +453,6 @@ namespace FlaxEditor.Gizmo case Axis.Y: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.Y; break; case Axis.Z: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.Z; break; } - absoluteDelta = currentAngle - (Mathf.Round(currentAngle / RotationSnapValue) * RotationSnapValue); } diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index 46e81b4c0..39c3d4bf3 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -164,30 +164,32 @@ namespace FlaxEditor.Viewport TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})", Parent = transformSpaceWidget }; - transformSpaceToggle.Toggled += _ => - { - transformGizmo.ToggleTransformSpace(); - if (useProjectCache) - editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString()); - }; transformSpaceWidget.Parent = viewport; // Absolute snapping widget var absoluteSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableAbsoluteSnapping = new ViewportWidgetButton("A", SpriteHandle.Default, null, true) + var enableAbsoluteSnapping = new ViewportWidgetButton("A", SpriteHandle.Invalid, null, true) { Checked = transformGizmo.AbsoluteSnapEnabled, - TooltipText = "Enable absolute snapping", + TooltipText = "Enable absolute grid snapping (world-space absolute grid, rather than object-relative grid)", Parent = absoluteSnappingWidget }; enableAbsoluteSnapping.Toggled += _ => { transformGizmo.AbsoluteSnapEnabled = !transformGizmo.AbsoluteSnapEnabled; if (useProjectCache) - editor.ProjectCache.SetCustomData("AbsoluteSnapState", transformGizmo.AbsoluteSnapEnabled); + editor.ProjectCache.SetCustomData("AbsoluteSnapState", transformGizmo.AbsoluteSnapEnabled); }; absoluteSnappingWidget.Parent = viewport; + transformSpaceToggle.Toggled += _ => + { + transformGizmo.ToggleTransformSpace(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString()); + absoluteSnappingWidget.Visible = transformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World; + }; + // Scale snapping widget var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true) @@ -401,17 +403,17 @@ namespace FlaxEditor.Viewport gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate; gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; }; - + // Setup input actions - viewport.InputActions.Add(options => options.TranslateMode, () => + viewport.InputActions.Add(options => options.TranslateMode, () => { viewport.GetInput(out var input); if (input.IsMouseRightDown) return; - + transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate; }); - viewport.InputActions.Add(options => options.RotateMode, () => + viewport.InputActions.Add(options => options.RotateMode, () => { viewport.GetInput(out var input); if (input.IsMouseRightDown)