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