diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs
index 9f1fb770a..4d9326bb5 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 (snaps objects to world-space grid, not the one relative to gizmo location)
+ ///
+ public bool AbsoluteSnapEnabled = false;
+
///
/// Translation snap value
///
diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs
index 92846c907..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;
@@ -40,6 +42,7 @@ namespace FlaxEditor.Gizmo
private Vector3 _intersectPosition;
private bool _isActive;
private bool _isDuplicating;
+ private bool _hasAbsoluteSnapped;
private bool _isTransforming;
private bool _isSelected;
@@ -366,6 +369,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 +388,29 @@ namespace FlaxEditor.Gizmo
else
snapValue.Z = (Real)b.Minimum.Z - b.Maximum.Z;
}
+
+ 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(
+ 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(
(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 +440,33 @@ namespace FlaxEditor.Gizmo
if (RotationSnapEnabled || Owner.UseSnapping)
{
float snapValue = RotationSnapValue * Mathf.DegreesToRadians;
+
+ 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)
+ {
+ 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..39c3d4bf3 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;
@@ -162,13 +164,31 @@ namespace FlaxEditor.Viewport
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
Parent = transformSpaceWidget
};
+ transformSpaceWidget.Parent = viewport;
+
+ // Absolute snapping widget
+ var absoluteSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
+ var enableAbsoluteSnapping = new ViewportWidgetButton("A", SpriteHandle.Invalid, null, true)
+ {
+ Checked = transformGizmo.AbsoluteSnapEnabled,
+ 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);
+ };
+ absoluteSnappingWidget.Parent = viewport;
+
transformSpaceToggle.Toggled += _ =>
{
transformGizmo.ToggleTransformSpace();
if (useProjectCache)
editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString());
+ absoluteSnappingWidget.Visible = transformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World;
};
- transformSpaceWidget.Parent = viewport;
// Scale snapping widget
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
@@ -383,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)