Merge branch 'master' into Visject-DescriptionPanel

This commit is contained in:
Nils Hausfeld
2024-06-11 20:57:54 +02:00
26 changed files with 1580 additions and 77 deletions

View File

@@ -111,6 +111,11 @@ namespace FlaxEditor.CustomEditors
/// </summary>
public PropertyNameLabel LinkedLabel;
/// <summary>
/// Gets the layout for this editor. Used to calculate bounds.
/// </summary>
public LayoutElementsContainer Layout => _layout;
internal virtual void Initialize(CustomEditorPresenter presenter, LayoutElementsContainer layout, ValueContainer values)
{
_layout = layout;

View File

@@ -38,6 +38,9 @@ namespace FlaxEditor.CustomEditors.Editors
/// </summary>
public readonly int Index;
private Rectangle _arrangeButtonRect;
private bool _arrangeButtonInUse;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionItemLabel"/> class.
/// </summary>
@@ -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;
}
/// <inheritdoc />
public override void OnEndMouseCapture()
{
base.OnEndMouseCapture();
_arrangeButtonInUse = false;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
}
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <summary>
/// Shifts the specified item at the given index and moves it through the list to the other item. It supports undo.
/// </summary>
/// <param name="srcIndex">Index of the source item.</param>
/// <param name="dstIndex">Index of the destination to move to.</param>
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);
}
/// <summary>
/// Removes the item at the specified index. It supports undo.
/// </summary>

View File

@@ -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;

View File

@@ -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<Shader>("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();
}

View File

@@ -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;

View File

@@ -228,7 +228,7 @@ namespace FlaxEditor.Modules
new SearchResult { Name = item.ShortName, Type = assetItem.TypeName, Item = item }
};
}
var actor = FlaxEngine.Object.Find<Actor>(ref id);
var actor = FlaxEngine.Object.Find<Actor>(ref id, true);
if (actor != null)
{
return new List<SearchResult>
@@ -236,6 +236,16 @@ namespace FlaxEditor.Modules
new SearchResult { Name = actor.Name, Type = actor.TypeName, Item = actor }
};
}
var script = FlaxEngine.Object.Find<Script>(ref id, true);
if (script != null && script.Actor != null)
{
string actorPathStart = $"{script.Actor.Name}/";
return new List<SearchResult>
{
new SearchResult { Name = $"{actorPathStart}{script.TypeName}", Type = script.TypeName, Item = script }
};
}
}
Profiler.BeginEvent("ContentFinding.Search");
@@ -388,6 +398,13 @@ namespace FlaxEditor.Modules
Editor.Instance.SceneEditing.Select(actor);
Editor.Instance.Windows.EditWin.Viewport.FocusSelection();
break;
case Script script:
if (script.Actor != null)
{
Editor.Instance.SceneEditing.Select(script.Actor);
Editor.Instance.Windows.EditWin.Viewport.FocusSelection();
}
break;
}
}

View File

@@ -192,6 +192,7 @@ namespace FlaxEditor.Options
internalOptions.AutoReloadScriptsOnMainWindowFocus = (byte)(Options.General.AutoReloadScriptsOnMainWindowFocus ? 1 : 0);
internalOptions.ForceScriptCompilationOnStartup = (byte)(Options.General.ForceScriptCompilationOnStartup ? 1 : 0);
internalOptions.UseAssetImportPathRelative = (byte)(Options.General.UseAssetImportPathRelative ? 1 : 0);
internalOptions.EnableParticlesPreview = (byte)(Options.Visual.EnableParticlesPreview ? 1 : 0);
internalOptions.AutoRebuildCSG = (byte)(Options.General.AutoRebuildCSG ? 1 : 0);
internalOptions.AutoRebuildCSGTimeoutMs = Options.General.AutoRebuildCSGTimeoutMs;
internalOptions.AutoRebuildNavMesh = (byte)(Options.General.AutoRebuildNavMesh ? 1 : 0);

View File

@@ -129,5 +129,19 @@ namespace FlaxEditor.Options
[DefaultValue(50.0f), Limit(25.0f, 500.0f, 5.0f)]
[EditorDisplay("Defaults"), EditorOrder(220), Tooltip("The default editor viewport grid scale.")]
public float ViewportGridScale { get; set; } = 50.0f;
/// <summary>
/// Gets or sets the view distance you can see the grid.
/// </summary>
[DefaultValue(2500.0f)]
[EditorDisplay("Grid"), EditorOrder(300), Tooltip("The maximum distance you will be able to see the grid.")]
public float ViewportGridViewDistance { get; set; } = 2500.0f;
/// <summary>
/// Gets or sets the grid color.
/// </summary>
[DefaultValue(typeof(Color), "0.5,0.5,0.5,1.0")]
[EditorDisplay("Grid"), EditorOrder(310), Tooltip("The color for the viewport grid.")]
public Color ViewportGridColor { get; set; } = new Color(0.5f, 0.5f, 0.5f, 1.0f);
}
}

View File

@@ -59,5 +59,12 @@ namespace FlaxEditor.Options
[DefaultValue(true)]
[EditorDisplay("Quality", "Enable MSAA For Debug Draw"), EditorOrder(500), Tooltip("Determines whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance.")]
public bool EnableMSAAForDebugDraw { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether show looping particle effects in Editor viewport to simulate in-game look.
/// </summary>
[DefaultValue(true)]
[EditorDisplay("Preview"), EditorOrder(1000)]
public bool EnableParticlesPreview { get; set; } = true;
}
}

View File

@@ -936,6 +936,142 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2),
}
},
new NodeArchetype
{
TypeID = 43,
Title = "Rotate UV",
Description = "Rotates 2D vector by given angle around (0,0) origin",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(250, 40),
ConnectionsHints = ConnectionsHint.Vector,
DefaultValues =
[
0.0f,
],
Elements =
[
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
NodeElementArchetype.Factory.Input(1, "Angle", true, typeof(float), 1, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float2), 2),
]
},
new NodeArchetype
{
TypeID = 44,
Title = "Cone Gradient",
Description = "Creates cone gradient around normalized UVs (range [-1; 1]), angle is in radians (range [0; TwoPi])",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(175, 40),
ConnectionsHints = ConnectionsHint.Vector,
DefaultValues =
[
0.0f,
],
Elements =
[
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
NodeElementArchetype.Factory.Input(1, "Angle", true, typeof(float), 1, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2),
]
},
new NodeArchetype
{
TypeID = 45,
Title = "Cycle Gradient",
Description = "Creates 2D sphere mask gradient around normalized UVs (range [-1; 1])",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(175, 20),
ConnectionsHints = ConnectionsHint.Vector,
Elements =
[
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 1),
]
},
new NodeArchetype
{
TypeID = 46,
Title = "Falloff and Offset",
Description = "",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(175, 60),
ConnectionsHints = ConnectionsHint.Numeric,
DefaultValues =
[
0.0f,
0.9f
],
Elements =
[
NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0),
NodeElementArchetype.Factory.Input(1, "Offset", true, typeof(float), 1, 0),
NodeElementArchetype.Factory.Input(2, "Falloff", true, typeof(float), 2, 1),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 3),
]
},
new NodeArchetype
{
TypeID = 47,
Title = "Linear Gradient",
Description = "x = Gradient along X axis, y = Gradient along Y axis",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(175, 60),
ConnectionsHints = ConnectionsHint.Vector,
DefaultValues =
[
0.0f,
false
],
Elements =
[
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
NodeElementArchetype.Factory.Input(1, "Angle", true, typeof(float), 1, 0),
NodeElementArchetype.Factory.Input(2, "Mirror", true, typeof(bool), 2, 1),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float2), 3),
]
},
new NodeArchetype
{
TypeID = 48,
Title = "Radial Gradient",
Description = "",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(175, 40),
ConnectionsHints = ConnectionsHint.Vector,
DefaultValues =
[
0.0f,
],
Elements =
[
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
NodeElementArchetype.Factory.Input(1, "Angle", true, typeof(float), 1, 0),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2),
]
},
new NodeArchetype
{
TypeID = 49,
Title = "Ring Gradient",
Description = "x = InnerMask,y = OuterMask,z = Mask",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(175, 80),
ConnectionsHints = ConnectionsHint.Vector,
DefaultValues =
[
1.0f,
0.8f,
0.05f,
],
Elements =
[
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
NodeElementArchetype.Factory.Input(1, "OuterBounds", true, typeof(float), 1, 0),
NodeElementArchetype.Factory.Input(2, "InnerBounds", true, typeof(float), 2, 1),
NodeElementArchetype.Factory.Input(3, "Falloff", true, typeof(float), 3, 2),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 4),
]
},
};
}
}

View File

@@ -35,6 +35,12 @@ namespace FlaxEditor.Surface
return RootContext.GetParameter(name);
}
/// <inheritdoc />
public void OnParamReordered()
{
MarkAsEdited();
}
/// <inheritdoc />
public void OnParamCreated(SurfaceParameter param)
{

View File

@@ -350,6 +350,213 @@ namespace FlaxEditor.Surface
}
}
sealed class ReorderParamAction : IUndoAction
{
/// <summary>
/// The window reference.
/// </summary>
public IVisjectSurfaceWindow Window;
/// <summary>
/// The parameters editor for this action.
/// </summary>
public ParametersEditor Editor;
/// <summary>
/// The old index the parameter was at.
/// </summary>
public int OldIndex;
/// <summary>
/// The new index the parameter will be at.
/// </summary>
public int NewIndex;
/// <inheritdoc />
public string ActionString => "Reorder Parameter";
/// <inheritdoc />
public void Dispose()
{
Window = null;
Editor = null;
}
public void Swap(int oldIdx, int newIdx)
{
if (oldIdx == newIdx)
return; // ?
var parameters = Window.VisjectSurface.Parameters;
if (newIdx > oldIdx)
{
for (int i = oldIdx; i < newIdx; i++)
{
SurfaceParameter old = parameters[i + 1];
parameters[i + 1] = parameters[i];
parameters[i] = old;
}
}
else
{
for (int i = oldIdx; i > newIdx; i--)
{
SurfaceParameter old = parameters[i - 1];
parameters[i - 1] = parameters[i];
parameters[i] = old;
}
}
}
/// <inheritdoc />
public void Do()
{
Swap(OldIndex, NewIndex);
Window.VisjectSurface.OnParamReordered();
}
/// <inheritdoc />
public void Undo()
{
Swap(NewIndex, OldIndex);
Window.VisjectSurface.OnParamReordered();
}
}
/// <summary>
/// Custom draggable property name label that handles reordering visject parameters.
/// </summary>
/// <seealso cref="FlaxEditor.CustomEditors.GUI.DraggablePropertyNameLabel" />
[HideInEditor]
public class ParameterPropertyNameLabel : DraggablePropertyNameLabel
{
private ParametersEditor _editor;
private IVisjectSurfaceWindow _window;
private Rectangle _arrangeButtonRect;
private bool _arrangeButtonInUse;
/// <inheritdoc />
public ParameterPropertyNameLabel(string name, ParametersEditor editor)
: base(name)
{
_editor = editor;
_window = _editor.Values[0] as IVisjectSurfaceWindow;
_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 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;
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
var style = Style.Current;
var mousePosition = PointFromScreen(Input.MouseScreenPosition);
var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey;
Render2D.DrawSprite(Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
if (_arrangeButtonInUse && ArrangeAreaCheck(out _, out var arrangeTargetRect))
{
Render2D.FillRectangle(arrangeTargetRect, style.Selection);
}
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _arrangeButtonInUse)
{
_arrangeButtonInUse = false;
EndMouseCapture();
ArrangeAreaCheck(out var index, out _);
var action = new ReorderParamAction
{
OldIndex = (int)Tag,
NewIndex = index,
Window = _window,
Editor = _editor
};
action.Do();
_window.Undo.AddAction(action);
}
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
public override void OnLostFocus()
{
if (_arrangeButtonInUse)
{
_arrangeButtonInUse = false;
EndMouseCapture();
}
base.OnLostFocus();
}
/// <inheritdoc />
protected override void OnSizeChanged()
{
base.OnSizeChanged();
// Center the drag button vertically
_arrangeButtonRect = new Rectangle(2, Mathf.Ceil((Height - 12) * 0.5f) + 1, 12, 12);
}
}
/// <summary>
/// Custom editor for editing Visject Surface parameters collection.
/// </summary>
@@ -431,10 +638,10 @@ namespace FlaxEditor.Surface
attributes
);
var propertyLabel = new DraggablePropertyNameLabel(name)
var propertyLabel = new ParameterPropertyNameLabel(name, this)
{
Tag = pIndex,
Drag = OnDragParameter
Drag = OnDragParameter,
};
if (!p.IsPublic)
propertyLabel.TextColor = propertyLabel.TextColor.RGBMultiplied(0.7f);