diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs index 9f1fb770a..7630d4c26 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 92846c907..886d56c4d 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 bool _isSelected; @@ -366,6 +367,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) { @@ -384,11 +386,29 @@ namespace FlaxEditor.Gizmo else snapValue.Z = (Real)b.Minimum.Z - b.Maximum.Z; } + + Vector3 absoluteDelta = Vector3.Zero; + if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World) + { + _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); + } + 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) @@ -418,12 +438,34 @@ namespace FlaxEditor.Gizmo if (RotationSnapEnabled || Owner.UseSnapping) { float snapValue = RotationSnapValue * Mathf.DegreesToRadians; + + float absoluteDelta = 0.0f; + if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World) + { + _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 d230f53fb..46e81b4c0 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -138,7 +138,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; @@ -170,6 +172,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)