Merge remote-tracking branch 'upstream/master'

This commit is contained in:
rkrahn
2024-03-29 13:38:38 -07:00
329 changed files with 7133 additions and 3928 deletions

View File

@@ -6,9 +6,7 @@ using Real = System.Double;
using Real = System.Single;
#endif
using System.Collections.Generic;
using FlaxEditor.Gizmo;
using FlaxEditor.SceneGraph;
using FlaxEngine;
namespace FlaxEditor.Viewport.Cameras
@@ -85,86 +83,8 @@ namespace FlaxEditor.Viewport.Cameras
_moveStartTime = Time.UnscaledGameTime;
}
/// <summary>
/// Moves the viewport to visualize the actor.
/// </summary>
/// <param name="actor">The actor to preview.</param>
public void ShowActor(Actor actor)
{
Editor.GetActorEditorSphere(actor, out BoundingSphere sphere);
ShowSphere(ref sphere);
}
/// <summary>
/// Moves the viewport to visualize selected actors.
/// </summary>
/// <param name="actor">The actors to show.</param>
/// <param name="orientation">The used orientation.</param>
public void ShowActor(Actor actor, ref Quaternion orientation)
{
Editor.GetActorEditorSphere(actor, out BoundingSphere sphere);
ShowSphere(ref sphere, ref orientation);
}
/// <summary>
/// Moves the viewport to visualize selected actors.
/// </summary>
/// <param name="selection">The actors to show.</param>
public void ShowActors(List<SceneGraphNode> selection)
{
if (selection.Count == 0)
return;
BoundingSphere mergesSphere = BoundingSphere.Empty;
for (int i = 0; i < selection.Count; i++)
{
selection[i].GetEditorSphere(out var sphere);
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
}
if (mergesSphere == BoundingSphere.Empty)
return;
ShowSphere(ref mergesSphere);
}
/// <summary>
/// Moves the viewport to visualize selected actors.
/// </summary>
/// <param name="selection">The actors to show.</param>
/// <param name="orientation">The used orientation.</param>
public void ShowActors(List<SceneGraphNode> selection, ref Quaternion orientation)
{
if (selection.Count == 0)
return;
BoundingSphere mergesSphere = BoundingSphere.Empty;
for (int i = 0; i < selection.Count; i++)
{
selection[i].GetEditorSphere(out var sphere);
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
}
if (mergesSphere == BoundingSphere.Empty)
return;
ShowSphere(ref mergesSphere, ref orientation);
}
/// <summary>
/// Moves the camera to visualize given world area defined by the sphere.
/// </summary>
/// <param name="sphere">The sphere.</param>
public void ShowSphere(ref BoundingSphere sphere)
{
var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f);
ShowSphere(ref sphere, ref q);
}
/// <summary>
/// Moves the camera to visualize given world area defined by the sphere.
/// </summary>
/// <param name="sphere">The sphere.</param>
/// <param name="orientation">The camera orientation.</param>
public void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation)
/// <inheritdoc />
public override void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation)
{
Vector3 position;
if (Viewport.UseOrthographicProjection)

View File

@@ -6,6 +6,9 @@ using Real = System.Double;
using Real = System.Single;
#endif
using System.Collections.Generic;
using FlaxEditor.Gizmo;
using FlaxEditor.SceneGraph;
using FlaxEngine;
namespace FlaxEditor.Viewport.Cameras
@@ -33,6 +36,90 @@ namespace FlaxEditor.Viewport.Cameras
/// </summary>
public virtual bool UseMovementSpeed => true;
/// <summary>
/// Focuses the viewport on the current selection of the gizmo.
/// </summary>
/// <param name="gizmos">The gizmo collection (from viewport).</param>
/// <param name="orientation">The target view orientation.</param>
public virtual void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation)
{
var transformGizmo = gizmos.Get<TransformGizmo>();
if (transformGizmo == null || transformGizmo.SelectedParents.Count == 0)
return;
if (gizmos.Active != null)
{
var gizmoBounds = gizmos.Active.FocusBounds;
if (gizmoBounds != BoundingSphere.Empty)
{
ShowSphere(ref gizmoBounds, ref orientation);
return;
}
}
ShowActors(transformGizmo.SelectedParents, ref orientation);
}
/// <summary>
/// Moves the viewport to visualize the actor.
/// </summary>
/// <param name="actor">The actor to preview.</param>
public virtual void ShowActor(Actor actor)
{
Editor.GetActorEditorSphere(actor, out BoundingSphere sphere);
ShowSphere(ref sphere);
}
/// <summary>
/// Moves the viewport to visualize selected actors.
/// </summary>
/// <param name="selection">The actors to show.</param>
public void ShowActors(List<SceneGraphNode> selection)
{
var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f);
ShowActors(selection, ref q);
}
/// <summary>
/// Moves the viewport to visualize selected actors.
/// </summary>
/// <param name="selection">The actors to show.</param>
/// <param name="orientation">The used orientation.</param>
public virtual void ShowActors(List<SceneGraphNode> selection, ref Quaternion orientation)
{
if (selection.Count == 0)
return;
BoundingSphere mergesSphere = BoundingSphere.Empty;
for (int i = 0; i < selection.Count; i++)
{
selection[i].GetEditorSphere(out var sphere);
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
}
if (mergesSphere == BoundingSphere.Empty)
return;
ShowSphere(ref mergesSphere, ref orientation);
}
/// <summary>
/// Moves the camera to visualize given world area defined by the sphere.
/// </summary>
/// <param name="sphere">The sphere.</param>
public void ShowSphere(ref BoundingSphere sphere)
{
var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f);
ShowSphere(ref sphere, ref q);
}
/// <summary>
/// Moves the camera to visualize given world area defined by the sphere.
/// </summary>
/// <param name="sphere">The sphere.</param>
/// <param name="orientation">The camera orientation.</param>
public virtual void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation)
{
SetArcBallView(orientation, sphere.Center, sphere.Radius);
}
/// <summary>
/// Sets view orientation and position to match the arc ball camera style view for the given target object bounds.
/// </summary>

View File

@@ -1,9 +1,12 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using FlaxEditor.Gizmo;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.SceneGraph;
using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Widgets;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -41,6 +44,7 @@ namespace FlaxEditor.Viewport
Gizmos[i].Update(deltaTime);
}
}
/// <inheritdoc />
public EditorViewport Viewport => this;
@@ -66,19 +70,19 @@ namespace FlaxEditor.Viewport
public bool IsControlDown => _input.IsControlDown;
/// <inheritdoc />
public bool SnapToGround => Editor.Instance.Options.Options.Input.SnapToGround.Process(Root);
public bool SnapToGround => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToGround.Process(Root);
/// <inheritdoc />
public bool SnapToVertex => Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc />
public Float2 MouseDelta => _mouseDelta * 1000;
/// <inheritdoc />
public bool UseSnapping => Root.GetKey(KeyboardKeys.Control);
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
/// <inheritdoc />
public bool UseDuplicate => Root.GetKey(KeyboardKeys.Shift);
public bool UseDuplicate => Root?.GetKey(KeyboardKeys.Shift) ?? false;
/// <inheritdoc />
public Undo Undo { get; }
@@ -92,6 +96,9 @@ namespace FlaxEditor.Viewport
/// <inheritdoc />
public abstract void Spawn(Actor actor);
/// <inheritdoc />
public abstract void OpenContextMenu();
/// <inheritdoc />
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
@@ -121,5 +128,284 @@ namespace FlaxEditor.Viewport
base.OnDestroy();
}
internal static void AddGizmoViewportWidgets(EditorViewport viewport, TransformGizmo transformGizmo, bool useProjectCache = false)
{
var editor = Editor.Instance;
var inputOptions = editor.Options.Options.Input;
if (useProjectCache)
{
// Initialize snapping enabled from cached values
if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState))
transformGizmo.TranslationSnapEnable = bool.Parse(cachedState);
if (editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState))
transformGizmo.RotationSnapEnabled = bool.Parse(cachedState);
if (editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState))
transformGizmo.ScaleSnapEnabled = bool.Parse(cachedState);
if (editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState))
transformGizmo.TranslationSnapValue = float.Parse(cachedState);
if (editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState))
transformGizmo.RotationSnapValue = float.Parse(cachedState);
if (editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState))
transformGizmo.ScaleSnapValue = float.Parse(cachedState);
if (editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space))
transformGizmo.ActiveTransformSpace = space;
}
// Transform space widget
var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true)
{
Checked = transformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
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;
// Scale snapping widget
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true)
{
Checked = transformGizmo.ScaleSnapEnabled,
TooltipText = "Enable scale snapping",
Parent = scaleSnappingWidget
};
enableScaleSnapping.Toggled += _ =>
{
transformGizmo.ScaleSnapEnabled = !transformGizmo.ScaleSnapEnabled;
if (useProjectCache)
editor.ProjectCache.SetCustomData("ScaleSnapState", transformGizmo.ScaleSnapEnabled.ToString());
};
var scaleSnappingCM = new ContextMenu();
var scaleSnapping = new ViewportWidgetButton(transformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM)
{
TooltipText = "Scale snapping values"
};
for (int i = 0; i < ScaleSnapValues.Length; i++)
{
var v = ScaleSnapValues[i];
var button = scaleSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
scaleSnappingCM.ButtonClicked += button =>
{
var v = (float)button.Tag;
transformGizmo.ScaleSnapValue = v;
scaleSnapping.Text = v.ToString();
if (useProjectCache)
editor.ProjectCache.SetCustomData("ScaleSnapValue", transformGizmo.ScaleSnapValue.ToString("N"));
};
scaleSnappingCM.VisibleChanged += control =>
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(transformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
}
}
};
scaleSnapping.Parent = scaleSnappingWidget;
scaleSnappingWidget.Parent = viewport;
// Rotation snapping widget
var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true)
{
Checked = transformGizmo.RotationSnapEnabled,
TooltipText = "Enable rotation snapping",
Parent = rotateSnappingWidget
};
enableRotateSnapping.Toggled += _ =>
{
transformGizmo.RotationSnapEnabled = !transformGizmo.RotationSnapEnabled;
if (useProjectCache)
editor.ProjectCache.SetCustomData("RotationSnapState", transformGizmo.RotationSnapEnabled.ToString());
};
var rotateSnappingCM = new ContextMenu();
var rotateSnapping = new ViewportWidgetButton(transformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM)
{
TooltipText = "Rotation snapping values"
};
for (int i = 0; i < RotateSnapValues.Length; i++)
{
var v = RotateSnapValues[i];
var button = rotateSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
rotateSnappingCM.ButtonClicked += button =>
{
var v = (float)button.Tag;
transformGizmo.RotationSnapValue = v;
rotateSnapping.Text = v.ToString();
if (useProjectCache)
editor.ProjectCache.SetCustomData("RotationSnapValue", transformGizmo.RotationSnapValue.ToString("N"));
};
rotateSnappingCM.VisibleChanged += control =>
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(transformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
}
}
};
rotateSnapping.Parent = rotateSnappingWidget;
rotateSnappingWidget.Parent = viewport;
// Translation snapping widget
var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true)
{
Checked = transformGizmo.TranslationSnapEnable,
TooltipText = "Enable position snapping",
Parent = translateSnappingWidget
};
enableTranslateSnapping.Toggled += _ =>
{
transformGizmo.TranslationSnapEnable = !transformGizmo.TranslationSnapEnable;
if (useProjectCache)
editor.ProjectCache.SetCustomData("TranslateSnapState", transformGizmo.TranslationSnapEnable.ToString());
};
var translateSnappingCM = new ContextMenu();
var translateSnapping = new ViewportWidgetButton(transformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM)
{
TooltipText = "Position snapping values"
};
if (transformGizmo.TranslationSnapValue < 0.0f)
translateSnapping.Text = "Bounding Box";
for (int i = 0; i < TranslateSnapValues.Length; i++)
{
var v = TranslateSnapValues[i];
var button = translateSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume");
buttonBB.Tag = -1.0f;
translateSnappingCM.ButtonClicked += button =>
{
var v = (float)button.Tag;
transformGizmo.TranslationSnapValue = v;
if (v < 0.0f)
translateSnapping.Text = "Bounding Box";
else
translateSnapping.Text = v.ToString();
if (useProjectCache)
editor.ProjectCache.SetCustomData("TranslateSnapValue", transformGizmo.TranslationSnapValue.ToString("N"));
};
translateSnappingCM.VisibleChanged += control =>
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(transformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
}
}
};
translateSnapping.Parent = translateSnappingWidget;
translateSnappingWidget.Parent = viewport;
// Gizmo mode widget
var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true)
{
Tag = TransformGizmoBase.Mode.Translate,
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
Checked = true,
Parent = gizmoMode
};
gizmoModeTranslate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate;
var gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true)
{
Tag = TransformGizmoBase.Mode.Rotate,
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
Parent = gizmoMode
};
gizmoModeRotate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate;
var gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true)
{
Tag = TransformGizmoBase.Mode.Scale,
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
Parent = gizmoMode
};
gizmoModeScale.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale;
gizmoMode.Parent = viewport;
transformGizmo.ModeChanged += () =>
{
var mode = transformGizmo.ActiveMode;
gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate;
gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
};
// Setup input actions
viewport.InputActions.Add(options => options.TranslateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
viewport.InputActions.Add(options => options.RotateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
viewport.InputActions.Add(options => options.ScaleMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
viewport.InputActions.Add(options => options.ToggleTransformSpace, () =>
{
transformGizmo.ToggleTransformSpace();
if (useProjectCache)
editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString());
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
});
}
internal static readonly float[] TranslateSnapValues =
{
0.1f,
0.5f,
1.0f,
5.0f,
10.0f,
100.0f,
1000.0f,
};
internal static readonly float[] RotateSnapValues =
{
1.0f,
5.0f,
10.0f,
15.0f,
30.0f,
45.0f,
60.0f,
90.0f,
};
internal static readonly float[] ScaleSnapValues =
{
0.05f,
0.1f,
0.25f,
0.5f,
1.0f,
2.0f,
4.0f,
6.0f,
8.0f,
};
}
}

View File

@@ -10,7 +10,6 @@ using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Widgets;
using FlaxEngine;
using FlaxEngine.GUI;
using Newtonsoft.Json;
using JsonSerializer = FlaxEngine.Json.JsonSerializer;
namespace FlaxEditor.Viewport
@@ -154,6 +153,7 @@ namespace FlaxEditor.Viewport
// Input
internal bool _disableInputUpdate;
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
private int _deltaFilteringStep;
private Float2 _startPos;
@@ -704,9 +704,9 @@ namespace FlaxEditor.Viewport
// Camera Viewpoints
{
var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu;
for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++)
for (int i = 0; i < CameraViewpointValues.Length; i++)
{
var co = EditorViewportCameraViewpointValues[i];
var co = CameraViewpointValues[i];
var button = cameraView.AddButton(co.Name);
button.Tag = co.Orientation;
}
@@ -899,9 +899,9 @@ namespace FlaxEditor.Viewport
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
viewFlags.AddSeparator();
for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++)
for (int i = 0; i < ViewFlagsValues.Length; i++)
{
var v = EditorViewportViewFlagsValues[i];
var v = ViewFlagsValues[i];
var button = viewFlags.AddButton(v.Name);
button.CloseMenuOnClick = false;
button.Tag = v.Mode;
@@ -933,9 +933,9 @@ namespace FlaxEditor.Viewport
}
});
debugView.AddSeparator();
for (int i = 0; i < EditorViewportViewModeValues.Length; i++)
for (int i = 0; i < ViewModeValues.Length; i++)
{
ref var v = ref EditorViewportViewModeValues[i];
ref var v = ref ViewModeValues[i];
if (v.Options != null)
{
var childMenu = debugView.AddChildMenu(v.Name).ContextMenu;
@@ -989,12 +989,12 @@ namespace FlaxEditor.Viewport
#endregion View mode widget
}
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation)));
InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Front").Orientation)));
InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Back").Orientation)));
InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Right").Orientation)));
InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Left").Orientation)));
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation)));
InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Front").Orientation)));
InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Back").Orientation)));
InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Right").Orientation)));
InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Left").Orientation)));
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
@@ -1497,6 +1497,9 @@ namespace FlaxEditor.Viewport
{
base.Update(deltaTime);
if (_disableInputUpdate)
return;
// Update camera
bool useMovementSpeed = false;
if (_camera != null)
@@ -1535,7 +1538,7 @@ namespace FlaxEditor.Viewport
}
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
_prevInput = _input;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl));
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
if (canUseInput && ContainsFocus && hit == null)
_input.Gather(win.Window, useMouse);
else
@@ -1868,7 +1871,7 @@ namespace FlaxEditor.Viewport
}
}
private readonly CameraViewpoint[] EditorViewportCameraViewpointValues =
private readonly CameraViewpoint[] CameraViewpointValues =
{
new CameraViewpoint("Front", new Float3(0, 180, 0)),
new CameraViewpoint("Back", new Float3(0, 0, 0)),
@@ -1899,7 +1902,7 @@ namespace FlaxEditor.Viewport
}
}
private static readonly ViewModeOptions[] EditorViewportViewModeValues =
private static readonly ViewModeOptions[] ViewModeValues =
{
new ViewModeOptions(ViewMode.Default, "Default"),
new ViewModeOptions(ViewMode.Unlit, "Unlit"),
@@ -1971,7 +1974,7 @@ namespace FlaxEditor.Viewport
}
}
private static readonly ViewFlagOptions[] EditorViewportViewFlagsValues =
private static readonly ViewFlagOptions[] ViewFlagsValues =
{
new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"),
new ViewFlagOptions(ViewFlags.Shadows, "Shadows"),
@@ -2006,16 +2009,13 @@ namespace FlaxEditor.Viewport
{
if (cm.Visible == false)
return;
var ccm = (ContextMenu)cm;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b && b.Tag != null)
{
var v = (ViewFlags)b.Tag;
b.Icon = (Task.View.Flags & v) != 0
? Style.Current.CheckBoxTick
: SpriteHandle.Invalid;
b.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
}
}
}
@@ -2024,7 +2024,6 @@ namespace FlaxEditor.Viewport
{
if (cm.Visible == false)
return;
var ccm = (ContextMenu)cm;
var layersMask = Task.ViewLayersMask;
foreach (var e in ccm.Items)

View File

@@ -2,15 +2,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Content;
using FlaxEditor.Gizmo;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.SceneGraph;
using FlaxEditor.Scripting;
using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Modes;
using FlaxEditor.Viewport.Widgets;
using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -28,13 +25,6 @@ namespace FlaxEditor.Viewport
private readonly ContextMenuButton _showGridButton;
private readonly ContextMenuButton _showNavigationButton;
private readonly ViewportWidgetButton _gizmoModeTranslate;
private readonly ViewportWidgetButton _gizmoModeRotate;
private readonly ViewportWidgetButton _gizmoModeScale;
private readonly ViewportWidgetButton _translateSnapping;
private readonly ViewportWidgetButton _rotateSnapping;
private readonly ViewportWidgetButton _scaleSnapping;
private SelectionOutline _customSelectionOutline;
@@ -196,7 +186,6 @@ namespace FlaxEditor.Viewport
{
_editor = editor;
DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType, ValidateDragScriptItem);
var inputOptions = editor.Options.Options.Input;
// Prepare rendering task
Task.ActorsSource = ActorsSources.Scenes;
@@ -222,8 +211,7 @@ namespace FlaxEditor.Viewport
// Add transformation gizmo
TransformGizmo = new TransformGizmo(this);
TransformGizmo.ApplyTransformation += ApplyTransform;
TransformGizmo.ModeChanged += OnGizmoModeChanged;
TransformGizmo.Duplicate += Editor.Instance.SceneEditing.Duplicate;
TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate;
Gizmos.Active = TransformGizmo;
// Add grid
@@ -232,144 +220,8 @@ namespace FlaxEditor.Viewport
editor.SceneEditing.SelectionChanged += OnSelectionChanged;
// Initialize snapping enabled from cached values
if (_editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState))
TransformGizmo.TranslationSnapEnable = bool.Parse(cachedState);
if (_editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState))
TransformGizmo.RotationSnapEnabled = bool.Parse(cachedState);
if (_editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState))
TransformGizmo.ScaleSnapEnabled = bool.Parse(cachedState);
if (_editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState))
TransformGizmo.TranslationSnapValue = float.Parse(cachedState);
if (_editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState))
TransformGizmo.RotationSnapValue = float.Parse(cachedState);
if (_editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState))
TransformGizmo.ScaleSnapValue = float.Parse(cachedState);
if (_editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space))
TransformGizmo.ActiveTransformSpace = space;
// Transform space widget
var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true)
{
Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
Parent = transformSpaceWidget
};
transformSpaceToggle.Toggled += OnTransformSpaceToggle;
transformSpaceWidget.Parent = this;
// Scale snapping widget
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true)
{
Checked = TransformGizmo.ScaleSnapEnabled,
TooltipText = "Enable scale snapping",
Parent = scaleSnappingWidget
};
enableScaleSnapping.Toggled += OnScaleSnappingToggle;
var scaleSnappingCM = new ContextMenu();
_scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM)
{
TooltipText = "Scale snapping values"
};
for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++)
{
var v = EditorViewportScaleSnapValues[i];
var button = scaleSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick;
scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide;
_scaleSnapping.Parent = scaleSnappingWidget;
scaleSnappingWidget.Parent = this;
// Rotation snapping widget
var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true)
{
Checked = TransformGizmo.RotationSnapEnabled,
TooltipText = "Enable rotation snapping",
Parent = rotateSnappingWidget
};
enableRotateSnapping.Toggled += OnRotateSnappingToggle;
var rotateSnappingCM = new ContextMenu();
_rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM)
{
TooltipText = "Rotation snapping values"
};
for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++)
{
var v = EditorViewportRotateSnapValues[i];
var button = rotateSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick;
rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide;
_rotateSnapping.Parent = rotateSnappingWidget;
rotateSnappingWidget.Parent = this;
// Translation snapping widget
var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true)
{
Checked = TransformGizmo.TranslationSnapEnable,
TooltipText = "Enable position snapping",
Parent = translateSnappingWidget
};
enableTranslateSnapping.Toggled += OnTranslateSnappingToggle;
var translateSnappingCM = new ContextMenu();
_translateSnapping = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM)
{
TooltipText = "Position snapping values"
};
if (TransformGizmo.TranslationSnapValue < 0.0f)
_translateSnapping.Text = "Bounding Box";
for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++)
{
var v = EditorViewportTranslateSnapValues[i];
var button = translateSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume");
buttonBB.Tag = -1.0f;
translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick;
translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide;
_translateSnapping.Parent = translateSnappingWidget;
translateSnappingWidget.Parent = this;
// Gizmo mode widget
var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
_gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true)
{
Tag = TransformGizmoBase.Mode.Translate,
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
Checked = true,
Parent = gizmoMode
};
_gizmoModeTranslate.Toggled += OnGizmoModeToggle;
_gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true)
{
Tag = TransformGizmoBase.Mode.Rotate,
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
Parent = gizmoMode
};
_gizmoModeRotate.Toggled += OnGizmoModeToggle;
_gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true)
{
Tag = TransformGizmoBase.Mode.Scale,
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
Parent = gizmoMode
};
_gizmoModeScale.Toggled += OnGizmoModeToggle;
gizmoMode.Parent = this;
// Gizmo widgets
AddGizmoViewportWidgets(this, TransformGizmo);
// Show grid widget
_showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled);
@@ -400,14 +252,6 @@ namespace FlaxEditor.Viewport
}
// Setup input actions
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
InputActions.Add(options => options.ToggleTransformSpace, () =>
{
OnTransformSpaceToggle(transformSpaceToggle);
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
});
InputActions.Add(options => options.LockFocusSelection, LockFocusSelection);
InputActions.Add(options => options.FocusSelection, FocusSelection);
InputActions.Add(options => options.RotateSelection, RotateSelection);
@@ -486,7 +330,7 @@ namespace FlaxEditor.Viewport
};
// Spawn
Editor.Instance.SceneEditing.Spawn(actor, parent);
_editor.SceneEditing.Spawn(actor, parent);
}
private void OnBegin(RenderTask task, GPUContext context)
@@ -552,7 +396,7 @@ namespace FlaxEditor.Viewport
var task = renderContext.Task;
// Render editor primitives, gizmo and debug shapes in debug view modes
// Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers
// Note: can use Output buffer as both input and output because EditorPrimitives is using an intermediate buffer
if (EditorPrimitives && EditorPrimitives.CanRender())
{
EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output);
@@ -581,161 +425,6 @@ namespace FlaxEditor.Viewport
}
}
private void OnGizmoModeToggle(ViewportWidgetButton button)
{
TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag;
}
private void OnTranslateSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable;
_editor.ProjectCache.SetCustomData("TranslateSnapState", TransformGizmo.TranslationSnapEnable.ToString());
}
private void OnRotateSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled;
_editor.ProjectCache.SetCustomData("RotationSnapState", TransformGizmo.RotationSnapEnabled.ToString());
}
private void OnScaleSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled;
_editor.ProjectCache.SetCustomData("ScaleSnapState", TransformGizmo.ScaleSnapEnabled.ToString());
}
private void OnTransformSpaceToggle(ViewportWidgetButton button)
{
TransformGizmo.ToggleTransformSpace();
_editor.ProjectCache.SetCustomData("TransformSpaceState", TransformGizmo.ActiveTransformSpace.ToString());
}
private void OnGizmoModeChanged()
{
// Update all viewport widgets status
var mode = TransformGizmo.ActiveMode;
_gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate;
_gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
_gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
}
private static readonly float[] EditorViewportScaleSnapValues =
{
0.05f,
0.1f,
0.25f,
0.5f,
1.0f,
2.0f,
4.0f,
6.0f,
8.0f,
};
private void OnWidgetScaleSnapClick(ContextMenuButton button)
{
var v = (float)button.Tag;
TransformGizmo.ScaleSnapValue = v;
_scaleSnapping.Text = v.ToString();
_editor.ProjectCache.SetCustomData("ScaleSnapValue", TransformGizmo.ScaleSnapValue.ToString("N"));
}
private void OnWidgetScaleSnapShowHide(Control control)
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f
? Style.Current.CheckBoxTick
: SpriteHandle.Invalid;
}
}
}
private static readonly float[] EditorViewportRotateSnapValues =
{
1.0f,
5.0f,
10.0f,
15.0f,
30.0f,
45.0f,
60.0f,
90.0f,
};
private void OnWidgetRotateSnapClick(ContextMenuButton button)
{
var v = (float)button.Tag;
TransformGizmo.RotationSnapValue = v;
_rotateSnapping.Text = v.ToString();
_editor.ProjectCache.SetCustomData("RotationSnapValue", TransformGizmo.RotationSnapValue.ToString("N"));
}
private void OnWidgetRotateSnapShowHide(Control control)
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f
? Style.Current.CheckBoxTick
: SpriteHandle.Invalid;
}
}
}
private static readonly float[] EditorViewportTranslateSnapValues =
{
0.1f,
0.5f,
1.0f,
5.0f,
10.0f,
100.0f,
1000.0f,
};
private void OnWidgetTranslateSnapClick(ContextMenuButton button)
{
var v = (float)button.Tag;
TransformGizmo.TranslationSnapValue = v;
if (v < 0.0f)
_translateSnapping.Text = "Bounding Box";
else
_translateSnapping.Text = v.ToString();
_editor.ProjectCache.SetCustomData("TranslateSnapValue", TransformGizmo.TranslationSnapValue.ToString("N"));
}
private void OnWidgetTranslateSnapShowHide(Control control)
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f
? Style.Current.CheckBoxTick
: SpriteHandle.Invalid;
}
}
}
private void OnSelectionChanged()
{
var selection = _editor.SceneEditing.Selection;
@@ -761,7 +450,7 @@ namespace FlaxEditor.Viewport
Vector3 gizmoPosition = TransformGizmo.Position;
// Rotate selected objects
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
bool isPlayMode = _editor.StateMachine.IsPlayMode;
TransformGizmo.StartTransforming();
for (int i = 0; i < selection.Count; i++)
{
@@ -819,14 +508,7 @@ namespace FlaxEditor.Viewport
/// <param name="orientation">The target view orientation.</param>
public void FocusSelection(ref Quaternion orientation)
{
if (TransformGizmo.SelectedParents.Count == 0)
return;
var gizmoBounds = Gizmos.Active.FocusBounds;
if (gizmoBounds != BoundingSphere.Empty)
((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation);
else
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation);
ViewportCamera.FocusSelection(Gizmos, ref orientation);
}
/// <summary>
@@ -843,7 +525,7 @@ namespace FlaxEditor.Viewport
Vector3 gizmoPosition = TransformGizmo.Position;
// Transform selected objects
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
bool isPlayMode = _editor.StateMachine.IsPlayMode;
for (int i = 0; i < selection.Count; i++)
{
var obj = selection[i];
@@ -985,7 +667,14 @@ namespace FlaxEditor.Viewport
{
var parent = actor.Parent ?? Level.GetScene(0);
actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null);
Editor.Instance.SceneEditing.Spawn(actor);
_editor.SceneEditing.Spawn(actor);
}
/// <inheritdoc />
public override void OpenContextMenu()
{
var mouse = PointFromWindow(Root.MousePosition);
_editor.Windows.SceneWin.ShowContextMenu(this, mouse);
}
/// <inheritdoc />

View File

@@ -5,12 +5,10 @@ using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Content;
using FlaxEditor.Gizmo;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.SceneGraph;
using FlaxEditor.Scripting;
using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Previews;
using FlaxEditor.Viewport.Widgets;
using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -30,7 +28,6 @@ namespace FlaxEditor.Viewport
{
public PrefabWindowViewport Viewport;
/// <inheritdoc />
public override bool CanRender()
{
return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled;
@@ -42,19 +39,34 @@ namespace FlaxEditor.Viewport
}
}
[HideInEditor]
private sealed class PrefabUIEditorRoot : UIEditorRoot
{
private readonly PrefabWindowViewport _viewport;
private bool UI => _viewport._hasUILinkedCached;
public PrefabUIEditorRoot(PrefabWindowViewport viewport)
: base(true)
{
_viewport = viewport;
Parent = viewport;
}
public override bool EnableInputs => !UI;
public override bool EnableSelecting => UI;
public override bool EnableBackground => UI;
public override TransformGizmo TransformGizmo => _viewport.TransformGizmo;
}
private readonly PrefabWindow _window;
private UpdateDelegate _update;
private readonly ViewportWidgetButton _gizmoModeTranslate;
private readonly ViewportWidgetButton _gizmoModeRotate;
private readonly ViewportWidgetButton _gizmoModeScale;
private ViewportWidgetButton _translateSnappng;
private ViewportWidgetButton _rotateSnapping;
private ViewportWidgetButton _scaleSnapping;
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
private PrefabSpritesRenderer _spritesRenderer;
private IntPtr _tempDebugDrawContext;
private bool _hasUILinkedCached;
private PrefabUIEditorRoot _uiRoot;
/// <summary>
/// Drag and drop handlers
@@ -107,144 +119,74 @@ namespace FlaxEditor.Viewport
// Add transformation gizmo
TransformGizmo = new TransformGizmo(this);
TransformGizmo.ApplyTransformation += ApplyTransform;
TransformGizmo.ModeChanged += OnGizmoModeChanged;
TransformGizmo.Duplicate += _window.Duplicate;
Gizmos.Active = TransformGizmo;
// Transform space widget
var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true)
{
Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
Parent = transformSpaceWidget
};
transformSpaceToggle.Toggled += OnTransformSpaceToggle;
transformSpaceWidget.Parent = this;
// Use custom root for UI controls
_uiRoot = new PrefabUIEditorRoot(this);
_uiRoot.IndexInParent = 0; // Move viewport down below other widgets in the viewport
_uiParentLink = _uiRoot.UIRoot;
// Scale snapping widget
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableScaleSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.ScaleSnap32, null, true)
{
Checked = TransformGizmo.ScaleSnapEnabled,
TooltipText = "Enable scale snapping",
Parent = scaleSnappingWidget
};
enableScaleSnapping.Toggled += OnScaleSnappingToggle;
var scaleSnappingCM = new ContextMenu();
_scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM)
{
TooltipText = "Scale snapping values"
};
for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++)
{
var v = EditorViewportScaleSnapValues[i];
var button = scaleSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick;
scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide;
_scaleSnapping.Parent = scaleSnappingWidget;
scaleSnappingWidget.Parent = this;
// Rotation snapping widget
var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableRotateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.RotateSnap32, null, true)
{
Checked = TransformGizmo.RotationSnapEnabled,
TooltipText = "Enable rotation snapping",
Parent = rotateSnappingWidget
};
enableRotateSnapping.Toggled += OnRotateSnappingToggle;
var rotateSnappingCM = new ContextMenu();
_rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM)
{
TooltipText = "Rotation snapping values"
};
for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++)
{
var v = EditorViewportRotateSnapValues[i];
var button = rotateSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick;
rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide;
_rotateSnapping.Parent = rotateSnappingWidget;
rotateSnappingWidget.Parent = this;
// Translation snapping widget
var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Grid32, null, true)
{
Checked = TransformGizmo.TranslationSnapEnable,
TooltipText = "Enable position snapping",
Parent = translateSnappingWidget
};
enableTranslateSnapping.Toggled += OnTranslateSnappingToggle;
var translateSnappingCM = new ContextMenu();
_translateSnappng = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM)
{
TooltipText = "Position snapping values"
};
for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++)
{
var v = EditorViewportTranslateSnapValues[i];
var button = translateSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick;
translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide;
_translateSnappng.Parent = translateSnappingWidget;
translateSnappingWidget.Parent = this;
// Gizmo mode widget
var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
_gizmoModeTranslate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Translate32, null, true)
{
Tag = TransformGizmoBase.Mode.Translate,
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
Checked = true,
Parent = gizmoMode
};
_gizmoModeTranslate.Toggled += OnGizmoModeToggle;
_gizmoModeRotate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Rotate32, null, true)
{
Tag = TransformGizmoBase.Mode.Rotate,
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
Parent = gizmoMode
};
_gizmoModeRotate.Toggled += OnGizmoModeToggle;
_gizmoModeScale = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Scale32, null, true)
{
Tag = TransformGizmoBase.Mode.Scale,
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
Parent = gizmoMode
};
_gizmoModeScale.Toggled += OnGizmoModeToggle;
gizmoMode.Parent = this;
EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo);
// Setup input actions
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
InputActions.Add(options => options.ToggleTransformSpace, () =>
{
OnTransformSpaceToggle(transformSpaceToggle);
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
});
InputActions.Add(options => options.FocusSelection, ShowSelectedActors);
SetUpdate(ref _update, OnUpdate);
}
/// <summary>
/// Updates the viewport's gizmos, especially to toggle between 3D and UI editing modes.
/// </summary>
internal void UpdateGizmoMode()
{
// Skip if gizmo mode was unmodified
if (_hasUILinked == _hasUILinkedCached)
return;
_hasUILinkedCached = _hasUILinked;
if (_hasUILinked)
{
// UI widget
Gizmos.Active = null;
ViewportCamera = new UIEditorCamera { UIEditor = _uiRoot };
// Hide 3D visuals
ShowEditorPrimitives = false;
ShowDefaultSceneActors = false;
ShowDebugDraw = false;
// Show whole UI on startup
var canvas = (CanvasRootControl)_uiParentLink.Children.FirstOrDefault(x => x is CanvasRootControl);
if (canvas != null)
ViewportCamera.ShowActor(canvas.Canvas);
else if (Instance is UIControl)
ViewportCamera.ShowActor(Instance);
}
else
{
// Generic prefab
Gizmos.Active = TransformGizmo;
ViewportCamera = new FPSCamera();
}
// Update default components usage
bool defaultFeatures = !_hasUILinked;
_disableInputUpdate = _hasUILinked;
_spritesRenderer.Enabled = defaultFeatures;
SelectionOutline.Enabled = defaultFeatures;
_showDefaultSceneButton.Visible = defaultFeatures;
_cameraWidget.Visible = defaultFeatures;
_cameraButton.Visible = defaultFeatures;
_orthographicModeButton.Visible = defaultFeatures;
Task.Enabled = defaultFeatures;
UseAutomaticTaskManagement = defaultFeatures;
TintColor = defaultFeatures ? Color.White : Color.Transparent;
}
private void OnUpdate(float deltaTime)
{
UpdateGizmoMode();
for (int i = 0; i < Gizmos.Count; i++)
{
Gizmos[i].Update(deltaTime);
@@ -259,11 +201,19 @@ namespace FlaxEditor.Viewport
var selectedParents = TransformGizmo.SelectedParents;
if (selectedParents.Count > 0)
{
// Use temporary Debug Draw context to pull any debug shapes drawing in Scene Graph Nodes - those are used in OnDebugDraw down below
if (_tempDebugDrawContext == IntPtr.Zero)
_tempDebugDrawContext = DebugDraw.AllocateContext();
DebugDraw.SetContext(_tempDebugDrawContext);
DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f);
for (int i = 0; i < selectedParents.Count; i++)
{
if (selectedParents[i].IsActiveInHierarchy)
selectedParents[i].OnDebugDraw(_debugDrawData);
}
DebugDraw.SetContext(IntPtr.Zero);
}
}
@@ -307,7 +257,7 @@ namespace FlaxEditor.Viewport
public void ShowSelectedActors()
{
var orient = ViewOrientation;
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient);
ViewportCamera.ShowActors(TransformGizmo.SelectedParents, ref orient);
}
/// <inheritdoc />
@@ -338,7 +288,7 @@ namespace FlaxEditor.Viewport
public bool SnapToGround => false;
/// <inheritdoc />
public bool SnapToVertex => Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc />
public Float2 MouseDelta => _mouseDelta * 1000;
@@ -367,6 +317,13 @@ namespace FlaxEditor.Viewport
_window.Spawn(actor);
}
/// <inheritdoc />
public void OpenContextMenu()
{
var mouse = PointFromWindow(Root.MousePosition);
_window.ShowContextMenu(this, ref mouse);
}
/// <inheritdoc />
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
@@ -386,151 +343,6 @@ namespace FlaxEditor.Viewport
root.UpdateCallbacksToRemove.Add(_update);
}
private void OnGizmoModeToggle(ViewportWidgetButton button)
{
TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag;
}
private void OnTranslateSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable;
}
private void OnRotateSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled;
}
private void OnScaleSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled;
}
private void OnTransformSpaceToggle(ViewportWidgetButton button)
{
TransformGizmo.ToggleTransformSpace();
}
private void OnGizmoModeChanged()
{
// Update all viewport widgets status
var mode = TransformGizmo.ActiveMode;
_gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate;
_gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
_gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
}
private static readonly float[] EditorViewportScaleSnapValues =
{
0.05f,
0.1f,
0.25f,
0.5f,
1.0f,
2.0f,
4.0f,
6.0f,
8.0f,
};
private void OnWidgetScaleSnapClick(ContextMenuButton button)
{
var v = (float)button.Tag;
TransformGizmo.ScaleSnapValue = v;
_scaleSnapping.Text = v.ToString();
}
private void OnWidgetScaleSnapShowHide(Control control)
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f
? Style.Current.CheckBoxTick
: SpriteHandle.Invalid;
}
}
}
private static readonly float[] EditorViewportRotateSnapValues =
{
1.0f,
5.0f,
10.0f,
15.0f,
30.0f,
45.0f,
60.0f,
90.0f,
};
private void OnWidgetRotateSnapClick(ContextMenuButton button)
{
var v = (float)button.Tag;
TransformGizmo.RotationSnapValue = v;
_rotateSnapping.Text = v.ToString();
}
private void OnWidgetRotateSnapShowHide(Control control)
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f
? Style.Current.CheckBoxTick
: SpriteHandle.Invalid;
}
}
}
private static readonly float[] EditorViewportTranslateSnapValues =
{
0.1f,
0.5f,
1.0f,
5.0f,
10.0f,
100.0f,
1000.0f,
};
private void OnWidgetTranslateSnapClick(ContextMenuButton button)
{
var v = (float)button.Tag;
TransformGizmo.TranslationSnapValue = v;
_translateSnappng.Text = v.ToString();
}
private void OnWidgetTranslateSnapShowHide(Control control)
{
if (control.Visible == false)
return;
var ccm = (ContextMenu)control;
foreach (var e in ccm.Items)
{
if (e is ContextMenuButton b)
{
var v = (float)b.Tag;
b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f
? Style.Current.CheckBoxTick
: SpriteHandle.Invalid;
}
}
}
private void OnSelectionChanged()
{
Gizmos.ForEach(x => x.OnSelectionChanged(_window.Selection));
@@ -585,23 +397,6 @@ namespace FlaxEditor.Viewport
}
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
// Selected UI controls outline
for (var i = 0; i < _window.Selection.Count; i++)
{
if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
{
var control = controlActor.Control;
var bounds = Rectangle.FromPoints(control.PointToParent(this, Float2.Zero), control.PointToParent(this, control.Size));
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
}
}
}
/// <inheritdoc />
protected override void OnLeftMouseButtonUp()
{
@@ -744,14 +539,7 @@ namespace FlaxEditor.Viewport
/// <param name="orientation">The target view orientation.</param>
public void FocusSelection(ref Quaternion orientation)
{
if (TransformGizmo.SelectedParents.Count == 0)
return;
var gizmoBounds = Gizmos.Active.FocusBounds;
if (gizmoBounds != BoundingSphere.Empty)
((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation);
else
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation);
ViewportCamera.FocusSelection(Gizmos, ref orientation);
}
/// <inheritdoc />
@@ -776,6 +564,13 @@ namespace FlaxEditor.Viewport
/// <inheritdoc />
public override void OnDestroy()
{
if (IsDisposing)
return;
if (_tempDebugDrawContext != IntPtr.Zero)
{
DebugDraw.FreeContext(_tempDebugDrawContext);
_tempDebugDrawContext = IntPtr.Zero;
}
FlaxEngine.Object.Destroy(ref SelectionOutline);
FlaxEngine.Object.Destroy(ref _spritesRenderer);
@@ -799,6 +594,15 @@ namespace FlaxEditor.Viewport
{
base.OnDebugDraw(context, ref renderContext);
// Collect selected objects debug shapes again when DebugDraw is active with a custom context
_debugDrawData.Clear();
var selectedParents = TransformGizmo.SelectedParents;
for (int i = 0; i < selectedParents.Count; i++)
{
if (selectedParents[i].IsActiveInHierarchy)
selectedParents[i].OnDebugDraw(_debugDrawData);
}
unsafe
{
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)

View File

@@ -21,7 +21,7 @@ namespace FlaxEditor.Viewport.Previews
/// <seealso cref="FlaxEditor.Viewport.EditorViewport" />
public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner
{
private ContextMenuButton _showDefaultSceneButton;
internal ContextMenuButton _showDefaultSceneButton;
private IntPtr _debugDrawContext;
private bool _debugDrawEnable;
private bool _editorPrimitivesEnable;
@@ -239,6 +239,8 @@ namespace FlaxEditor.Viewport.Previews
/// <inheritdoc />
public override void OnDestroy()
{
if (IsDisposing)
return;
Object.Destroy(ref PreviewLight);
Object.Destroy(ref EnvProbe);
Object.Destroy(ref Sky);

View File

@@ -1,8 +1,8 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using FlaxEngine;
using FlaxEngine.GUI;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Viewport.Previews
@@ -13,19 +13,11 @@ namespace FlaxEditor.Viewport.Previews
/// <seealso cref="AssetPreview" />
public class PrefabPreview : AssetPreview
{
/// <summary>
/// The currently spawned prefab instance owner. Used to link some actors such as UIControl to preview scene and view.
/// </summary>
internal static PrefabPreview LoadingPreview;
/// <summary>
/// The list of active prefab previews. Used to link some actors such as UIControl to preview scene and view.
/// </summary>
internal static List<PrefabPreview> ActivePreviews;
private Prefab _prefab;
private Actor _instance;
internal UIControl customControlLinked;
private UIControl _uiControlLinked;
internal bool _hasUILinked;
internal ContainerControl _uiParentLink;
/// <summary>
/// Gets or sets the prefab asset to preview.
@@ -54,13 +46,10 @@ namespace FlaxEditor.Viewport.Previews
_prefab.WaitForLoaded();
// Spawn prefab
LoadingPreview = this;
var instance = PrefabManager.SpawnPrefab(_prefab, null);
LoadingPreview = null;
if (instance == null)
{
_prefab = null;
ActivePreviews.Remove(this);
throw new Exception("Failed to spawn a prefab for the preview.");
}
@@ -84,11 +73,11 @@ namespace FlaxEditor.Viewport.Previews
if (_instance)
{
// Unlink UI control
if (customControlLinked)
if (_uiControlLinked)
{
if (customControlLinked.Control?.Parent == this)
customControlLinked.Control.Parent = null;
customControlLinked = null;
if (_uiControlLinked.Control?.Parent == _uiParentLink)
_uiControlLinked.Control.Parent = null;
_uiControlLinked = null;
}
// Remove for the preview
@@ -96,27 +85,51 @@ namespace FlaxEditor.Viewport.Previews
}
_instance = value;
_hasUILinked = false;
if (_instance)
{
// Add to the preview
Task.AddCustomActor(_instance);
// Link UI canvases to the preview
LinkCanvas(_instance);
UpdateLinkage();
}
}
}
private void UpdateLinkage()
{
// Clear flag
_hasUILinked = false;
// Link UI canvases to the preview (eg. after canvas added to the prefab)
LinkCanvas(_instance);
// Link UI control to the preview
if (_uiControlLinked == null &&
_instance is UIControl uiControl &&
uiControl.Control != null &&
uiControl.Control.Parent == null)
{
uiControl.Control.Parent = _uiParentLink;
_uiControlLinked = uiControl;
_hasUILinked = true;
}
else if (_uiControlLinked != null)
_hasUILinked = true;
}
private void LinkCanvas(Actor actor)
{
if (actor is UICanvas uiCanvas)
uiCanvas.EditorOverride(Task, this);
{
uiCanvas.EditorOverride(Task, _uiParentLink);
if (uiCanvas.GUI.Parent == _uiParentLink)
_hasUILinked = true;
}
var children = actor.ChildrenCount;
for (int i = 0; i < children; i++)
{
LinkCanvas(actor.GetChild(i));
}
}
/// <summary>
@@ -126,9 +139,8 @@ namespace FlaxEditor.Viewport.Previews
public PrefabPreview(bool useWidgets)
: base(useWidgets)
{
if (ActivePreviews == null)
ActivePreviews = new List<PrefabPreview>();
ActivePreviews.Add(this);
// Link to itself by default
_uiParentLink = this;
}
/// <inheritdoc />
@@ -138,15 +150,13 @@ namespace FlaxEditor.Viewport.Previews
if (_instance != null)
{
// Link UI canvases to the preview (eg. after canvas added to the prefab)
LinkCanvas(_instance);
UpdateLinkage();
}
}
/// <inheritdoc />
public override void OnDestroy()
{
ActivePreviews.Remove(this);
Prefab = null;
base.OnDestroy();

View File

@@ -146,7 +146,7 @@ namespace FlaxEditor.Viewport
var gridPlane = new Plane(Vector3.Zero, Vector3.Up);
var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives;
hit = _owner.SceneGraphRoot.RayCast(ref ray, ref view, out var closest, out var normal, flags);
var girdGizmo = (GridGizmo)_owner.Gizmos.FirstOrDefault(x => x is GridGizmo);
var girdGizmo = _owner.Gizmos.Get<GridGizmo>();
if (hit != null)
{
// Use hit location
@@ -180,7 +180,7 @@ namespace FlaxEditor.Viewport
var location = hitLocation + new Vector3(0, bottomToCenter, 0);
// Apply grid snapping if enabled
var transformGizmo = (TransformGizmo)_owner.Gizmos.FirstOrDefault(x => x is TransformGizmo);
var transformGizmo = _owner.Gizmos.Get<TransformGizmo>();
if (transformGizmo != null && (_owner.UseSnapping || transformGizmo.TranslationSnapEnable))
{
float snapValue = transformGizmo.TranslationSnapValue;