diff --git a/Content/Engine/DefaultRadialMenu.flax b/Content/Engine/DefaultRadialMenu.flax
new file mode 100644
index 000000000..1159e4719
--- /dev/null
+++ b/Content/Engine/DefaultRadialMenu.flax
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7c517175501d1b41143a9d2d81950bf0d91d0556cfebca5784e7ef2c4ba94cf
+size 20340
diff --git a/Content/Shaders/Editor/Grid.flax b/Content/Shaders/Editor/Grid.flax
new file mode 100644
index 000000000..1c87a1332
--- /dev/null
+++ b/Content/Shaders/Editor/Grid.flax
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ce516159bb427e1c2cbd48f86be17b2b3c065d2ecd6943aace05451853d6b3e1
+size 4626
diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs
index 7f540b767..54cea319f 100644
--- a/Source/Editor/CustomEditors/CustomEditor.cs
+++ b/Source/Editor/CustomEditors/CustomEditor.cs
@@ -111,6 +111,11 @@ namespace FlaxEditor.CustomEditors
///
public PropertyNameLabel LinkedLabel;
+ ///
+ /// Gets the layout for this editor. Used to calculate bounds.
+ ///
+ public LayoutElementsContainer Layout => _layout;
+
internal virtual void Initialize(CustomEditorPresenter presenter, LayoutElementsContainer layout, ValueContainer values)
{
_layout = layout;
diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
index b06e2d55d..7c0670010 100644
--- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
@@ -38,6 +38,9 @@ namespace FlaxEditor.CustomEditors.Editors
///
public readonly int Index;
+ private Rectangle _arrangeButtonRect;
+ private bool _arrangeButtonInUse;
+
///
/// Initializes a new instance of the class.
///
@@ -50,6 +53,12 @@ namespace FlaxEditor.CustomEditors.Editors
Index = index;
SetupContextMenu += OnSetupContextMenu;
+ _arrangeButtonRect = new Rectangle(2, 3, 12, 12);
+
+ // Extend margin of the label to support a dragging handle.
+ Margin m = Margin;
+ m.Left += 16;
+ Margin = m;
}
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
@@ -71,6 +80,107 @@ namespace FlaxEditor.CustomEditors.Editors
b.Enabled = !Editor._readOnly;
}
+ ///
+ public override void OnEndMouseCapture()
+ {
+ base.OnEndMouseCapture();
+
+ _arrangeButtonInUse = false;
+ }
+
+
+ ///
+ public override void Draw()
+ {
+ base.Draw();
+ var style = FlaxEngine.GUI.Style.Current;
+
+ var mousePosition = PointFromScreen(Input.MouseScreenPosition);
+ var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey;
+ Render2D.DrawSprite(FlaxEditor.Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
+ if (_arrangeButtonInUse && ArrangeAreaCheck(out _, out var arrangeTargetRect))
+ {
+ Render2D.FillRectangle(arrangeTargetRect, style.Selection);
+ }
+ }
+
+ private bool ArrangeAreaCheck(out int index, out Rectangle rect)
+ {
+ var child = Editor.ChildrenEditors[0];
+ var container = child.Layout.ContainerControl;
+ var mousePosition = container.PointFromScreen(Input.MouseScreenPosition);
+ var barSidesExtend = 20.0f;
+ var barHeight = 5.0f;
+ var barCheckAreaHeight = 40.0f;
+ var pos = mousePosition.Y + barCheckAreaHeight * 0.5f;
+
+ for (int i = 0; i < container.Children.Count / 2; i++)
+ {
+ var containerChild = container.Children[i * 2]; // times 2 to skip the value editor
+ if (Mathf.IsInRange(pos, containerChild.Top, containerChild.Top + barCheckAreaHeight) || (i == 0 && pos < containerChild.Top))
+ {
+ index = i;
+ var p1 = containerChild.UpperLeft;
+ rect = new Rectangle(PointFromParent(p1) - new Float2(barSidesExtend * 0.5f, barHeight * 0.5f), Width + barSidesExtend, barHeight);
+ return true;
+ }
+ }
+
+ var p2 = container.Children[((container.Children.Count / 2) - 1) * 2].BottomLeft;
+ if (pos > p2.Y)
+ {
+ index = (container.Children.Count / 2) - 1;
+ rect = new Rectangle(PointFromParent(p2) - new Float2(barSidesExtend * 0.5f, barHeight * 0.5f), Width + barSidesExtend, barHeight);
+ return true;
+ }
+
+ index = -1;
+ rect = Rectangle.Empty;
+ return false;
+ }
+
+ ///
+ public override bool OnMouseDown(Float2 location, MouseButton button)
+ {
+ if (button == MouseButton.Left && _arrangeButtonRect.Contains(ref location))
+ {
+ _arrangeButtonInUse = true;
+ Focus();
+ StartMouseCapture();
+ return true;
+ }
+
+ return base.OnMouseDown(location, button);
+ }
+
+ ///
+ public override bool OnMouseUp(Float2 location, MouseButton button)
+ {
+ if (button == MouseButton.Left && _arrangeButtonInUse)
+ {
+ _arrangeButtonInUse = false;
+ EndMouseCapture();
+ if (ArrangeAreaCheck(out var index, out _))
+ {
+ Editor.Shift(Index, index);
+ }
+ }
+
+ return base.OnMouseUp(location, button);
+ }
+
+ ///
+ public override void OnLostFocus()
+ {
+ if (_arrangeButtonInUse)
+ {
+ _arrangeButtonInUse = false;
+ EndMouseCapture();
+ }
+
+ base.OnLostFocus();
+ }
+
private void OnMoveUpClicked()
{
Editor.Move(Index, Index - 1);
@@ -106,6 +216,9 @@ namespace FlaxEditor.CustomEditors.Editors
private bool _canReorder = true;
+ private Rectangle _arrangeButtonRect;
+ private bool _arrangeButtonInUse;
+
public void Setup(CollectionEditor editor, int index, bool canReorder = true)
{
HeaderHeight = 18;
@@ -123,10 +236,92 @@ namespace FlaxEditor.CustomEditors.Editors
MouseButtonRightClicked += OnMouseButtonRightClicked;
if (_canReorder)
{
- // TODO: Drag drop
+ HeaderTextMargin = new Margin(18, 0, 0, 0);
+ _arrangeButtonRect = new Rectangle(16, 3, 12, 12);
}
}
+ private bool ArrangeAreaCheck(out int index, out Rectangle rect)
+ {
+ var container = Parent;
+ var mousePosition = container.PointFromScreen(Input.MouseScreenPosition);
+ var barSidesExtend = 20.0f;
+ var barHeight = 5.0f;
+ var barCheckAreaHeight = 40.0f;
+ var pos = mousePosition.Y + barCheckAreaHeight * 0.5f;
+
+ for (int i = 0; i < (container.Children.Count + 1) / 2; i++) // Add 1 to pretend there is a spacer at the end.
+ {
+ var containerChild = container.Children[i * 2]; // times 2 to skip the value editor
+ if (Mathf.IsInRange(pos, containerChild.Top, containerChild.Top + barCheckAreaHeight) || (i == 0 && pos < containerChild.Top))
+ {
+ index = i;
+ var p1 = containerChild.UpperLeft;
+ rect = new Rectangle(PointFromParent(p1) - new Float2(barSidesExtend * 0.5f, barHeight * 0.5f), Width + barSidesExtend, barHeight);
+ return true;
+ }
+ }
+
+ var p2 = container.Children[container.Children.Count - 1].BottomLeft;
+ if (pos > p2.Y)
+ {
+ index = ((container.Children.Count + 1) / 2) - 1;
+ rect = new Rectangle(PointFromParent(p2) - new Float2(barSidesExtend * 0.5f, barHeight * 0.5f), Width + barSidesExtend, barHeight);
+ return true;
+ }
+
+ index = -1;
+ rect = Rectangle.Empty;
+ return false;
+ }
+
+ public override void Draw()
+ {
+ base.Draw();
+ if (_canReorder)
+ {
+ var style = FlaxEngine.GUI.Style.Current;
+
+ var mousePosition = PointFromScreen(Input.MouseScreenPosition);
+ var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey;
+ Render2D.DrawSprite(FlaxEditor.Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
+ if (_arrangeButtonInUse && ArrangeAreaCheck(out _, out var arrangeTargetRect))
+ {
+ Render2D.FillRectangle(arrangeTargetRect, style.Selection);
+ }
+ }
+ }
+
+ ///
+ public override bool OnMouseDown(Float2 location, MouseButton button)
+ {
+ if (button == MouseButton.Left && _arrangeButtonRect.Contains(ref location))
+ {
+ _arrangeButtonInUse = true;
+ Focus();
+ StartMouseCapture();
+ return true;
+ }
+
+ return base.OnMouseDown(location, button);
+ }
+
+ ///
+ public override bool OnMouseUp(Float2 location, MouseButton button)
+ {
+ if (button == MouseButton.Left && _arrangeButtonInUse)
+ {
+ _arrangeButtonInUse = false;
+ EndMouseCapture();
+ if (ArrangeAreaCheck(out var index, out _))
+ {
+ Editor.Shift(Index, index);
+ }
+ }
+
+ return base.OnMouseUp(location, button);
+ }
+
private void OnMouseButtonRightClicked(DropPanel panel, Float2 location)
{
if (LinkedEditor == null)
@@ -324,7 +519,6 @@ namespace FlaxEditor.CustomEditors.Editors
(elementType.GetProperties().Length == 1 && elementType.GetFields().Length == 0) ||
elementType.Equals(new ScriptType(typeof(JsonAsset))) ||
elementType.Equals(new ScriptType(typeof(SettingsBase)));
-
for (int i = 0; i < size; i++)
{
// Apply spacing
@@ -440,6 +634,39 @@ namespace FlaxEditor.CustomEditors.Editors
SetValue(cloned);
}
+ ///
+ /// Shifts the specified item at the given index and moves it through the list to the other item. It supports undo.
+ ///
+ /// Index of the source item.
+ /// Index of the destination to move to.
+ private void Shift(int srcIndex, int dstIndex)
+ {
+ if (IsSetBlocked)
+ return;
+
+ var cloned = CloneValues();
+ if (dstIndex > srcIndex)
+ {
+ for (int i = srcIndex; i < dstIndex; i++)
+ {
+ var tmp = cloned[i + 1];
+ cloned[i + 1] = cloned[i];
+ cloned[i] = tmp;
+ }
+ }
+ else
+ {
+ for (int i = srcIndex; i > dstIndex; i--)
+ {
+ var tmp = cloned[i - 1];
+ cloned[i - 1] = cloned[i];
+ cloned[i] = tmp;
+ }
+ }
+
+ SetValue(cloned);
+ }
+
///
/// Removes the item at the specified index. It supports undo.
///
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 12d222c4e..8af1bc9f4 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -1364,6 +1364,7 @@ namespace FlaxEditor
public byte AutoReloadScriptsOnMainWindowFocus;
public byte ForceScriptCompilationOnStartup;
public byte UseAssetImportPathRelative;
+ public byte EnableParticlesPreview;
public byte AutoRebuildCSG;
public float AutoRebuildCSGTimeoutMs;
public byte AutoRebuildNavMesh;
diff --git a/Source/Editor/Gizmo/GridGizmo.cs b/Source/Editor/Gizmo/GridGizmo.cs
index 74a16449e..b5fcf9a31 100644
--- a/Source/Editor/Gizmo/GridGizmo.cs
+++ b/Source/Editor/Gizmo/GridGizmo.cs
@@ -2,6 +2,7 @@
using System;
using FlaxEngine;
+using System.Runtime.InteropServices;
namespace FlaxEditor.Gizmo
{
@@ -15,91 +16,120 @@ namespace FlaxEditor.Gizmo
[HideInEditor]
private sealed class Renderer : PostProcessEffect
{
- private IntPtr _debugDrawContext;
+ [StructLayout(LayoutKind.Sequential)]
+ private struct Data
+ {
+ public Matrix WorldMatrix;
+ public Matrix ViewProjectionMatrix;
+ public Float4 GridColor;
+ public Float3 ViewPos;
+ public float Far;
+ public Float3 Padding;
+ public float GridSize;
+ }
+
+ private static readonly uint[] _triangles =
+ {
+ 0, 2, 1, // Face front
+ 1, 3, 0,
+ };
+
+ private GPUBuffer[] _vbs = new GPUBuffer[1];
+ private GPUBuffer _vertexBuffer;
+ private GPUBuffer _indexBuffer;
+ private GPUPipelineState _psGrid;
+ private Shader _shader;
public Renderer()
{
- Order = -100;
UseSingleTarget = true;
- Location = PostProcessEffectLocation.BeforeForwardPass;
+ Location = PostProcessEffectLocation.Default;
+ _shader = FlaxEngine.Content.LoadAsyncInternal("Shaders/Editor/Grid");
}
~Renderer()
{
- if (_debugDrawContext != IntPtr.Zero)
- {
- DebugDraw.FreeContext(_debugDrawContext);
- _debugDrawContext = IntPtr.Zero;
- }
+ Destroy(ref _psGrid);
+ Destroy(ref _vertexBuffer);
+ Destroy(ref _indexBuffer);
+ _shader = null;
}
- public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
+ public override unsafe void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
{
+ if (_shader == null)
+ return;
Profiler.BeginEventGPU("Editor Grid");
- if (_debugDrawContext == IntPtr.Zero)
- _debugDrawContext = DebugDraw.AllocateContext();
- DebugDraw.SetContext(_debugDrawContext);
- DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1));
-
- var viewPos = (Vector3)renderContext.View.Position;
- var plane = new Plane(Vector3.Zero, Vector3.UnitY);
- var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos);
-
var options = Editor.Instance.Options.Options;
- float space = options.Viewport.ViewportGridScale, size;
- if (dst <= 500.0f)
+ Float3 camPos = renderContext.View.WorldPosition;
+ float gridSize = renderContext.View.Far + 20000;
+
+ // Lazy-init resources
+ if (_vertexBuffer == null)
{
- size = 8000;
+ _vertexBuffer = new GPUBuffer();
+ var desc = GPUBufferDescription.Vertex(sizeof(Float3), 4);
+ _vertexBuffer.Init(ref desc);
}
- else if (dst <= 2000.0f)
+ if (_indexBuffer == null)
{
- space *= 2;
- size = 8000;
+ _indexBuffer = new GPUBuffer();
+ fixed (uint* ptr = _triangles)
+ {
+ var desc = GPUBufferDescription.Index(sizeof(uint), _triangles.Length, new IntPtr(ptr));
+ _indexBuffer.Init(ref desc);
+ }
}
- else
+ if (_psGrid == null)
{
- space *= 20;
- size = 100000;
+ _psGrid = new GPUPipelineState();
+ var desc = GPUPipelineState.Description.Default;
+ desc.BlendMode = BlendingMode.AlphaBlend;
+ desc.CullMode = CullMode.TwoSided;
+ desc.VS = _shader.GPU.GetVS("VS_Grid");
+ desc.PS = _shader.GPU.GetPS("PS_Grid");
+ _psGrid.Init(ref desc);
}
- float bigLineIntensity = 0.8f;
- Color bigColor = Color.Gray * bigLineIntensity;
- Color color = bigColor * 0.8f;
- int count = (int)(size / space);
- int midLine = count / 2;
- int bigLinesMod = count / 8;
-
- Vector3 start = new Vector3(0, 0, size * -0.5f);
- Vector3 end = new Vector3(0, 0, size * 0.5f);
-
- for (int i = 0; i <= count; i++)
+ // Update vertices of the plane
+ // TODO: perf this operation in a Vertex Shader
+ float y = 1.5f; // Add small bias to reduce Z-fighting with geometry at scene origin
+ var vertices = new Float3[]
{
- start.X = end.X = i * space + start.Z;
- Color lineColor = color;
- if (i == midLine)
- lineColor = Color.Blue * bigLineIntensity;
- else if (i % bigLinesMod == 0)
- lineColor = bigColor;
- DebugDraw.DrawLine(start, end, lineColor);
+ new Float3(-gridSize + camPos.X, y, -gridSize + camPos.Z),
+ new Float3(gridSize + camPos.X, y, gridSize + camPos.Z),
+ new Float3(-gridSize + camPos.X, y, gridSize + camPos.Z),
+ new Float3(gridSize + camPos.X, y, -gridSize + camPos.Z),
+ };
+ fixed (Float3* ptr = vertices)
+ {
+ context.UpdateBuffer(_vertexBuffer, new IntPtr(ptr), (uint)(sizeof(Float3) * vertices.Length));
}
- start = new Vector3(size * -0.5f, 0, 0);
- end = new Vector3(size * 0.5f, 0, 0);
-
- for (int i = 0; i <= count; i++)
+ // Update constant buffer data
+ var cb = _shader.GPU.GetCB(0);
+ if (cb != IntPtr.Zero)
{
- start.Z = end.Z = i * space + start.X;
- Color lineColor = color;
- if (i == midLine)
- lineColor = Color.Red * bigLineIntensity;
- else if (i % bigLinesMod == 0)
- lineColor = bigColor;
- DebugDraw.DrawLine(start, end, lineColor);
+ var data = new Data();
+ Matrix.Multiply(ref renderContext.View.View, ref renderContext.View.Projection, out var viewProjection);
+ data.WorldMatrix = Matrix.Identity;
+ Matrix.Transpose(ref viewProjection, out data.ViewProjectionMatrix);
+ data.ViewPos = renderContext.View.WorldPosition;
+ data.GridColor = options.Viewport.ViewportGridColor;
+ data.Far = renderContext.View.Far;
+ data.GridSize = options.Viewport.ViewportGridViewDistance;
+ context.UpdateCB(cb, new IntPtr(&data));
}
- DebugDraw.Draw(ref renderContext, input.View(), null, true);
- DebugDraw.SetContext(IntPtr.Zero);
+ // Draw geometry using custom Pixel Shader and Vertex Shader
+ context.BindCB(0, cb);
+ context.BindIB(_indexBuffer);
+ _vbs[0] = _vertexBuffer;
+ context.BindVB(_vbs);
+ context.SetState(_psGrid);
+ context.SetRenderTarget(renderContext.Buffers.DepthBuffer.View(), input.View());
+ context.DrawIndexed((uint)_triangles.Length);
Profiler.EndEventGPU();
}
diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h
index e51749e5f..5c7907d17 100644
--- a/Source/Editor/Managed/ManagedEditor.h
+++ b/Source/Editor/Managed/ManagedEditor.h
@@ -27,6 +27,7 @@ API_CLASS(Namespace="FlaxEditor", Name="Editor", NoSpawn, NoConstructor) class M
byte AutoReloadScriptsOnMainWindowFocus = 1;
byte ForceScriptCompilationOnStartup = 1;
byte UseAssetImportPathRelative = 1;
+ byte EnableParticlesPreview = 1;
byte AutoRebuildCSG = 1;
float AutoRebuildCSGTimeoutMs = 50;
byte AutoRebuildNavMesh = 1;
diff --git a/Source/Editor/Modules/ContentFindingModule.cs b/Source/Editor/Modules/ContentFindingModule.cs
index 480844ac4..73e22d769 100644
--- a/Source/Editor/Modules/ContentFindingModule.cs
+++ b/Source/Editor/Modules/ContentFindingModule.cs
@@ -228,7 +228,7 @@ namespace FlaxEditor.Modules
new SearchResult { Name = item.ShortName, Type = assetItem.TypeName, Item = item }
};
}
- var actor = FlaxEngine.Object.Find(ref id);
+ var actor = FlaxEngine.Object.Find(ref id, true);
if (actor != null)
{
return new List
@@ -236,6 +236,16 @@ namespace FlaxEditor.Modules
new SearchResult { Name = actor.Name, Type = actor.TypeName, Item = actor }
};
}
+ var script = FlaxEngine.Object.Find