Merge remote-tracking branch 'origin/master' into 1.9
This commit is contained in:
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
Normal file
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Content/Shaders/Editor/Grid.flax
(Stored with Git LFS)
Normal file
BIN
Content/Shaders/Editor/Grid.flax
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -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;
|
||||
|
||||
@@ -41,6 +41,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
if (XElement.ValueBox.Parent is UniformGridPanel ug)
|
||||
{
|
||||
ug.Height += 2;
|
||||
ug.SlotSpacing = new Float2(4);
|
||||
ug.SlotPadding = new Margin(0, 0, 1, 1);
|
||||
}
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
@@ -66,6 +73,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
if (XElement.ValueBox.Parent is UniformGridPanel ug)
|
||||
{
|
||||
ug.Height += 2;
|
||||
ug.SlotSpacing = new Float2(4);
|
||||
ug.SlotPadding = new Margin(0, 0, 1, 1);
|
||||
}
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
@@ -122,6 +136,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling");
|
||||
};
|
||||
}
|
||||
|
||||
if (XElement.ValueBox.Parent is UniformGridPanel ug)
|
||||
{
|
||||
ug.Height += 2;
|
||||
ug.SlotSpacing = new Float2(4);
|
||||
ug.SlotPadding = new Margin(0, 0, 1, 1);
|
||||
}
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1378,6 +1378,7 @@ namespace FlaxEditor
|
||||
public byte AutoReloadScriptsOnMainWindowFocus;
|
||||
public byte ForceScriptCompilationOnStartup;
|
||||
public byte UseAssetImportPathRelative;
|
||||
public byte EnableParticlesPreview;
|
||||
public byte AutoRebuildCSG;
|
||||
public float AutoRebuildCSGTimeoutMs;
|
||||
public byte AutoRebuildNavMesh;
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace FlaxEditor.GUI.Input
|
||||
base.Draw();
|
||||
|
||||
var style = Style.Current;
|
||||
var r = new Rectangle(2, 2, Width - 4, Height - 4);
|
||||
var r = new Rectangle(0, 0, Width, Height);
|
||||
|
||||
Render2D.FillRectangle(r, _value);
|
||||
Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace FlaxEditor.GUI
|
||||
[HideInEditor]
|
||||
public class Item : Control
|
||||
{
|
||||
private bool _isStartsWithMatch;
|
||||
private bool _isFullMatch;
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse down flag.
|
||||
/// </summary>
|
||||
@@ -43,6 +46,11 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
public string Category;
|
||||
|
||||
/// <summary>
|
||||
/// A computed score for the context menu order
|
||||
/// </summary>
|
||||
public float SortScore;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when items gets clicked by the user.
|
||||
/// </summary>
|
||||
@@ -61,44 +69,69 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="SortScore"/>
|
||||
/// </summary>
|
||||
public void UpdateScore()
|
||||
{
|
||||
SortScore = 0;
|
||||
|
||||
if (!Visible)
|
||||
return;
|
||||
|
||||
if (_highlights is { Count: > 0 })
|
||||
SortScore += 1;
|
||||
if (_isStartsWithMatch)
|
||||
SortScore += 2;
|
||||
if (_isFullMatch)
|
||||
SortScore += 5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the filter.
|
||||
/// </summary>
|
||||
/// <param name="filterText">The filter text.</param>
|
||||
public void UpdateFilter(string filterText)
|
||||
{
|
||||
_isStartsWithMatch = _isFullMatch = false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(filterText))
|
||||
{
|
||||
// Clear filter
|
||||
_highlights?.Clear();
|
||||
Visible = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (QueryFilterHelper.Match(filterText, Name, out var ranges))
|
||||
{
|
||||
if (QueryFilterHelper.Match(filterText, Name, out var ranges))
|
||||
{
|
||||
// Update highlights
|
||||
if (_highlights == null)
|
||||
_highlights = new List<Rectangle>(ranges.Length);
|
||||
else
|
||||
_highlights.Clear();
|
||||
var style = Style.Current;
|
||||
var font = style.FontSmall;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
var start = font.GetCharPosition(Name, ranges[i].StartIndex);
|
||||
var end = font.GetCharPosition(Name, ranges[i].EndIndex);
|
||||
_highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height));
|
||||
}
|
||||
Visible = true;
|
||||
}
|
||||
// Update highlights
|
||||
if (_highlights == null)
|
||||
_highlights = new List<Rectangle>(ranges.Length);
|
||||
else
|
||||
_highlights.Clear();
|
||||
var style = Style.Current;
|
||||
var font = style.FontSmall;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
// Hide
|
||||
_highlights?.Clear();
|
||||
Visible = false;
|
||||
var start = font.GetCharPosition(Name, ranges[i].StartIndex);
|
||||
var end = font.GetCharPosition(Name, ranges[i].EndIndex);
|
||||
_highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height));
|
||||
|
||||
if (ranges[i].StartIndex <= 0)
|
||||
{
|
||||
_isStartsWithMatch = true;
|
||||
if (ranges[i].Length == Name.Length)
|
||||
_isFullMatch = true;
|
||||
}
|
||||
}
|
||||
Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide
|
||||
_highlights?.Clear();
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -178,7 +211,14 @@ namespace FlaxEditor.GUI
|
||||
public override int Compare(Control other)
|
||||
{
|
||||
if (other is Item otherItem)
|
||||
return string.Compare(Name, otherItem.Name, StringComparison.Ordinal);
|
||||
{
|
||||
int order = -1 * SortScore.CompareTo(otherItem.SortScore);
|
||||
if (order == 0)
|
||||
{
|
||||
order = string.Compare(Name, otherItem.Name, StringComparison.Ordinal);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
return base.Compare(other);
|
||||
}
|
||||
}
|
||||
@@ -249,7 +289,10 @@ namespace FlaxEditor.GUI
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i] is Item item)
|
||||
{
|
||||
item.UpdateFilter(_searchBox.Text);
|
||||
item.UpdateScore();
|
||||
}
|
||||
}
|
||||
if (_categoryPanels != null)
|
||||
{
|
||||
@@ -262,6 +305,7 @@ namespace FlaxEditor.GUI
|
||||
if (category.Children[j] is Item item2)
|
||||
{
|
||||
item2.UpdateFilter(_searchBox.Text);
|
||||
item2.UpdateScore();
|
||||
anyVisible |= item2.Visible;
|
||||
}
|
||||
}
|
||||
@@ -273,6 +317,8 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
|
||||
SortItems();
|
||||
|
||||
UnlockChildrenRecursive();
|
||||
PerformLayout(true);
|
||||
_searchBox.Focus();
|
||||
|
||||
@@ -363,6 +363,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (_previewValue != null)
|
||||
Timeline.ShowPreviewValuesChanged -= OnTimelineShowPreviewValuesChanged;
|
||||
_previewValue = null;
|
||||
_rightKey = null;
|
||||
_addKey = null;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -223,6 +223,19 @@ void ManagedEditor::Init()
|
||||
{
|
||||
LOG(Info, "Loading managed assemblies (due to disabled compilation on startup)");
|
||||
Scripting::Load();
|
||||
|
||||
const auto endInitMethod = mclass->GetMethod("EndInit");
|
||||
if (endInitMethod == nullptr)
|
||||
{
|
||||
LOG(Fatal, "Invalid Editor assembly! Missing EndInit method.");
|
||||
}
|
||||
endInitMethod->Invoke(instance, nullptr, &exception);
|
||||
if (exception)
|
||||
{
|
||||
MException ex(exception);
|
||||
ex.Log(LogType::Warning, TEXT("ManagedEditor::EndInit"));
|
||||
LOG_STR(Fatal, TEXT("Failed to initialize editor during EndInit! ") + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// Call building if need to (based on CL)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,13 +175,6 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface", "New Window Location"), EditorOrder(150), Tooltip("Define the opening method for new windows, open in a new tab by default.")]
|
||||
public DockStateProxy NewWindowLocation { get; set; } = DockStateProxy.Float;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamps prefix mode for debug log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(TimestampsFormats.None)]
|
||||
[EditorDisplay("Interface"), EditorOrder(210), Tooltip("The timestamps prefix mode for debug log messages.")]
|
||||
public TimestampsFormats DebugLogTimestampsFormat { get; set; } = TimestampsFormats.None;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the editor icons scale. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -220,21 +213,70 @@ namespace FlaxEditor.Options
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamps prefix mode for output log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(TimestampsFormats.TimeSinceStartup)]
|
||||
[EditorDisplay("Output Log", "Timestamps Format"), EditorOrder(300), Tooltip("The timestamps prefix mode for output log messages.")]
|
||||
public TimestampsFormats OutputLogTimestampsFormat { get; set; } = TimestampsFormats.TimeSinceStartup;
|
||||
[DefaultValue(TimestampsFormats.None)]
|
||||
[EditorDisplay("Debug Log"), EditorOrder(350), Tooltip("The timestamps prefix mode for debug log messages.")]
|
||||
public TimestampsFormats DebugLogTimestampsFormat { get; set; } = TimestampsFormats.None;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the clear on play for debug log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Debug Log", "Clear on Play"), EditorOrder(360), Tooltip("Clears all log entries on enter playmode.")]
|
||||
public bool DebugLogClearOnPlay { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collapse mode for debug log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Debug Log"), EditorOrder(361), Tooltip("Collapses similar or repeating log entries.")]
|
||||
public bool DebugLogCollapse { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the automatic pause on error for debug log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorDisplay("Debug Log", "Pause on Error"), EditorOrder(362), Tooltip("Performs auto pause on error.")]
|
||||
public bool DebugLogPauseOnError { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the automatic pause on error for debug log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Debug Log", "Show error messages"), EditorOrder(370), Tooltip("Shows/hides error messages.")]
|
||||
public bool DebugLogShowErrorMessages { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the automatic pause on error for debug log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Debug Log", "Show warning messages"), EditorOrder(371), Tooltip("Shows/hides warning messages.")]
|
||||
public bool DebugLogShowWarningMessages { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the automatic pause on error for debug log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Debug Log", "Show info messages"), EditorOrder(372), Tooltip("Shows/hides info messages.")]
|
||||
public bool DebugLogShowInfoMessages { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamps prefix mode for output log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(TimestampsFormats.TimeSinceStartup)]
|
||||
[EditorDisplay("Output Log", "Timestamps Format"), EditorOrder(400), Tooltip("The timestamps prefix mode for output log messages.")]
|
||||
public TimestampsFormats OutputLogTimestampsFormat { get; set; } = TimestampsFormats.TimeSinceStartup;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log type prefix mode for output log messages.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Output Log", "Show Log Type"), EditorOrder(310), Tooltip("Determines whether show log type prefix in output log messages.")]
|
||||
[EditorDisplay("Output Log", "Show Log Type"), EditorOrder(410), Tooltip("Determines whether show log type prefix in output log messages.")]
|
||||
public bool OutputLogShowLogType { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output log text font.
|
||||
/// </summary>
|
||||
[EditorDisplay("Output Log", "Text Font"), EditorOrder(320), Tooltip("The output log text font.")]
|
||||
[EditorDisplay("Output Log", "Text Font"), EditorOrder(420), Tooltip("The output log text font.")]
|
||||
public FontReference OutputLogTextFont
|
||||
{
|
||||
get => _outputLogFont;
|
||||
@@ -253,63 +295,70 @@ namespace FlaxEditor.Options
|
||||
/// Gets or sets the output log text color.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Color), "1,1,1,1")]
|
||||
[EditorDisplay("Output Log", "Text Color"), EditorOrder(330), Tooltip("The output log text color.")]
|
||||
[EditorDisplay("Output Log", "Text Color"), EditorOrder(430), Tooltip("The output log text color.")]
|
||||
public Color OutputLogTextColor { get; set; } = Color.White;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output log text shadow color.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Color), "0,0,0,0.5")]
|
||||
[EditorDisplay("Output Log", "Text Shadow Color"), EditorOrder(340), Tooltip("The output log text shadow color.")]
|
||||
[EditorDisplay("Output Log", "Text Shadow Color"), EditorOrder(440), Tooltip("The output log text shadow color.")]
|
||||
public Color OutputLogTextShadowColor { get; set; } = new Color(0, 0, 0, 0.5f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output log text shadow offset. Set to 0 to disable this feature.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Float2), "1,1")]
|
||||
[EditorDisplay("Output Log", "Text Shadow Offset"), EditorOrder(340), Tooltip("The output log text shadow offset. Set to 0 to disable this feature.")]
|
||||
[EditorDisplay("Output Log", "Text Shadow Offset"), EditorOrder(445), Tooltip("The output log text shadow offset. Set to 0 to disable this feature.")]
|
||||
public Float2 OutputLogTextShadowOffset { get; set; } = new Float2(1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether auto-focus output log window on code compilation error.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Output Log", "Focus Output Log On Compilation Error"), EditorOrder(350), Tooltip("Determines whether auto-focus output log window on code compilation error.")]
|
||||
[EditorDisplay("Output Log", "Focus Output Log On Compilation Error"), EditorOrder(450), Tooltip("Determines whether auto-focus output log window on code compilation error.")]
|
||||
public bool FocusOutputLogOnCompilationError { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether auto-focus output log window on game build error.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Output Log", "Focus Output Log On Game Build Error"), EditorOrder(360), Tooltip("Determines whether auto-focus output log window on game build error.")]
|
||||
[EditorDisplay("Output Log", "Focus Output Log On Game Build Error"), EditorOrder(460), Tooltip("Determines whether auto-focus output log window on game build error.")]
|
||||
public bool FocusOutputLogOnGameBuildError { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value for automatic scroll to bottom in output log.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Output Log", "Scroll to bottom"), EditorOrder(470), Tooltip("Scroll the output log view to bottom automatically after new lines are added.")]
|
||||
public bool OutputLogScrollToBottom { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether auto-focus game window on play mode start.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Play In-Editor", "Focus Game Window On Play"), EditorOrder(400), Tooltip("Determines whether auto-focus game window on play mode start.")]
|
||||
[EditorDisplay("Play In-Editor", "Focus Game Window On Play"), EditorOrder(500), Tooltip("Determines whether auto-focus game window on play mode start.")]
|
||||
public bool FocusGameWinOnPlay { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating what action should be taken upon pressing the play button.
|
||||
/// </summary>
|
||||
[DefaultValue(PlayAction.PlayScenes)]
|
||||
[EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410)]
|
||||
[EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(510)]
|
||||
public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how the game window should be displayed when the game is launched.
|
||||
/// </summary>
|
||||
[DefaultValue(GameWindowMode.Docked)]
|
||||
[EditorDisplay("Play In-Editor", "Game Window Mode"), EditorOrder(420), Tooltip("Determines how the game window is displayed when the game is launched.")]
|
||||
[EditorDisplay("Play In-Editor", "Game Window Mode"), EditorOrder(520), Tooltip("Determines how the game window is displayed when the game is launched.")]
|
||||
public GameWindowMode DefaultGameWindowMode { get; set; } = GameWindowMode.Docked;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game.
|
||||
/// </summary>
|
||||
[DefaultValue(1), Range(1, 4)]
|
||||
[EditorDisplay("Cook & Run"), EditorOrder(500)]
|
||||
[EditorDisplay("Cook & Run"), EditorOrder(600)]
|
||||
public int NumberOfGameClientsToLaunch = 1;
|
||||
|
||||
/// <summary>
|
||||
@@ -331,13 +380,13 @@ namespace FlaxEditor.Options
|
||||
/// <summary>
|
||||
/// The list of fallback fonts to use when main text font is missing certain characters. Empty to use fonts from GraphicsSettings.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(650)]
|
||||
[EditorDisplay("Fonts"), EditorOrder(750)]
|
||||
public FontAsset[] FallbackFonts = new FontAsset[1] { FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.FallbackFont) };
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")]
|
||||
[EditorDisplay("Fonts"), EditorOrder(700), Tooltip("The title font for editor UI.")]
|
||||
public FontReference TitleFont
|
||||
{
|
||||
get => _titleFont;
|
||||
@@ -355,7 +404,7 @@ namespace FlaxEditor.Options
|
||||
/// <summary>
|
||||
/// Gets or sets the large font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")]
|
||||
[EditorDisplay("Fonts"), EditorOrder(710), Tooltip("The large font for editor UI.")]
|
||||
public FontReference LargeFont
|
||||
{
|
||||
get => _largeFont;
|
||||
@@ -373,7 +422,7 @@ namespace FlaxEditor.Options
|
||||
/// <summary>
|
||||
/// Gets or sets the medium font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")]
|
||||
[EditorDisplay("Fonts"), EditorOrder(720), Tooltip("The medium font for editor UI.")]
|
||||
public FontReference MediumFont
|
||||
{
|
||||
get => _mediumFont;
|
||||
@@ -391,7 +440,7 @@ namespace FlaxEditor.Options
|
||||
/// <summary>
|
||||
/// Gets or sets the small font for editor UI.
|
||||
/// </summary>
|
||||
[EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")]
|
||||
[EditorDisplay("Fonts"), EditorOrder(730), Tooltip("The small font for editor UI.")]
|
||||
public FontReference SmallFont
|
||||
{
|
||||
get => _smallFont;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ namespace FlaxEditor.States
|
||||
{
|
||||
// Skip compilation on startup
|
||||
OnCompilationEnd(true);
|
||||
Editor.EndInit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2198,21 +2198,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_combobox.ClearItems();
|
||||
_tooltips.Clear();
|
||||
_functionNodesIds.Clear();
|
||||
var nodes = Surface.Nodes;
|
||||
var count = _signature != null ? nodes.Count : 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
if (Surface != null && _signature != null)
|
||||
{
|
||||
if (nodes[i] is VisualScriptFunctionNode functionNode)
|
||||
var nodes = Surface.Nodes;
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
// Get if function signature matches the event signature
|
||||
functionNode.GetSignature(out var functionSig);
|
||||
if (IsValidFunctionSignature(ref functionSig))
|
||||
if (nodes[i] is VisualScriptFunctionNode functionNode)
|
||||
{
|
||||
if (functionNode.ID == handlerFunctionNodeId)
|
||||
toSelect = _functionNodesIds.Count;
|
||||
_functionNodesIds.Add(functionNode.ID);
|
||||
_tooltips.Add(functionNode.TooltipText);
|
||||
_combobox.AddItem(functionSig.ToString());
|
||||
// Get if function signature matches the event signature
|
||||
functionNode.GetSignature(out var functionSig);
|
||||
if (IsValidFunctionSignature(ref functionSig))
|
||||
{
|
||||
if (functionNode.ID == handlerFunctionNodeId)
|
||||
toSelect = _functionNodesIds.Count;
|
||||
_functionNodesIds.Add(functionNode.ID);
|
||||
_tooltips.Add(functionNode.TooltipText);
|
||||
_combobox.AddItem(functionSig.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1266,7 +1266,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <inheritdoc />
|
||||
public void DrawConnectingLine(ref Float2 startPos, ref Float2 endPos, ref Color color)
|
||||
{
|
||||
OutputBox.DrawConnection(Surface.Style, ref startPos, ref endPos, ref color, 2);
|
||||
OutputBox.DrawConnection(Surface.Style, ref startPos, ref endPos, ref color, OutputBox.ConnectingConnectionThickness);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -738,7 +738,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
/// <inheritdoc />
|
||||
public void DrawConnectingLine(ref Float2 startPos, ref Float2 endPos, ref Color color)
|
||||
{
|
||||
OutputBox.DrawConnection(Surface.Style, ref startPos, ref endPos, ref color, 2);
|
||||
OutputBox.DrawConnection(Surface.Style, ref startPos, ref endPos, ref color, OutputBox.ConnectingConnectionThickness);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
ParentNode.ValuesChanged += OnNodeValuesChanged;
|
||||
|
||||
// Disable slider if surface doesn't allow it
|
||||
if (!ParentNode.Surface.CanLivePreviewValueChanges)
|
||||
if (ParentNode.Surface != null && !ParentNode.Surface.CanLivePreviewValueChanges)
|
||||
_slideSpeed = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,26 @@ namespace FlaxEditor.Surface.Elements
|
||||
[HideInEditor]
|
||||
public class OutputBox : Box
|
||||
{
|
||||
/// <summary>
|
||||
/// The default thickness of the connection line
|
||||
/// </summary>
|
||||
public const float DefaultConnectionThickness = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The thickness of a highlighted connection line
|
||||
/// </summary>
|
||||
public const float ConnectingConnectionThickness = 2.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The thickness of the selected connection line
|
||||
/// </summary>
|
||||
public const float SelectedConnectionThickness = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// The offset between the connection line and the box
|
||||
/// </summary>
|
||||
public const float DefaultConnectionOffset = 24f;
|
||||
|
||||
/// <summary>
|
||||
/// Distance for the mouse to be considered above the connection
|
||||
/// </summary>
|
||||
@@ -33,7 +53,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
/// <param name="end">The end location.</param>
|
||||
/// <param name="color">The connection color.</param>
|
||||
/// <param name="thickness">The connection thickness.</param>
|
||||
public static void DrawConnection(SurfaceStyle style, ref Float2 start, ref Float2 end, ref Color color, float thickness = 1)
|
||||
public static void DrawConnection(SurfaceStyle style, ref Float2 start, ref Float2 end, ref Color color, float thickness = DefaultConnectionThickness)
|
||||
{
|
||||
if (style.DrawConnection != null)
|
||||
{
|
||||
@@ -41,11 +61,22 @@ namespace FlaxEditor.Surface.Elements
|
||||
return;
|
||||
}
|
||||
|
||||
float connectionOffset = Mathf.Max(0f, DefaultConnectionOffset * (1 - Editor.Instance.Options.Options.Interface.ConnectionCurvature));
|
||||
Float2 offsetStart = new Float2(start.X + connectionOffset, start.Y);
|
||||
Float2 offsetEnd = new Float2(end.X - connectionOffset, end.Y);
|
||||
|
||||
// Calculate control points
|
||||
CalculateBezierControlPoints(start, end, out var control1, out var control2);
|
||||
CalculateBezierControlPoints(offsetStart, offsetEnd, out var control1, out var control2);
|
||||
|
||||
// Draw offset lines only if necessary
|
||||
if (connectionOffset >= float.Epsilon)
|
||||
{
|
||||
Render2D.DrawLine(start, offsetStart, color, thickness);
|
||||
Render2D.DrawLine(offsetEnd, end, color, thickness);
|
||||
}
|
||||
|
||||
// Draw line
|
||||
Render2D.DrawBezier(start, control1, control2, end, color, thickness);
|
||||
Render2D.DrawBezier(offsetStart, control1, control2, offsetEnd, color, thickness);
|
||||
|
||||
/*
|
||||
// Debug drawing control points
|
||||
@@ -80,9 +111,10 @@ namespace FlaxEditor.Surface.Elements
|
||||
/// <param name="mousePosition">The mouse position</param>
|
||||
public bool IntersectsConnection(Box targetBox, ref Float2 mousePosition)
|
||||
{
|
||||
var startPos = ConnectionOrigin;
|
||||
var endPos = targetBox.ConnectionOrigin;
|
||||
return IntersectsConnection(ref startPos, ref endPos, ref mousePosition, MouseOverConnectionDistance);
|
||||
float connectionOffset = Mathf.Max(0f, DefaultConnectionOffset * (1 - Editor.Instance.Options.Options.Interface.ConnectionCurvature));
|
||||
Float2 start = new Float2(ConnectionOrigin.X + connectionOffset, ConnectionOrigin.Y);
|
||||
Float2 end = new Float2(targetBox.ConnectionOrigin.X - connectionOffset, targetBox.ConnectionOrigin.Y);
|
||||
return IntersectsConnection(ref start, ref end, ref mousePosition, MouseOverConnectionDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -102,22 +134,26 @@ namespace FlaxEditor.Surface.Elements
|
||||
if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0)
|
||||
return false;
|
||||
|
||||
float squaredDistance = distance;
|
||||
CalculateBezierControlPoints(start, end, out var control1, out var control2);
|
||||
float connectionOffset = Mathf.Max(0f, DefaultConnectionOffset * (1 - Editor.Instance.Options.Options.Interface.ConnectionCurvature));
|
||||
Float2 offsetStart = new Float2(start.X + connectionOffset, start.Y);
|
||||
Float2 offsetEnd = new Float2(end.X - connectionOffset, end.Y);
|
||||
|
||||
var d1 = control1 - start;
|
||||
float squaredDistance = distance;
|
||||
CalculateBezierControlPoints(offsetStart, offsetEnd, out var control1, out var control2);
|
||||
|
||||
var d1 = control1 - offsetStart;
|
||||
var d2 = control2 - control1;
|
||||
var d3 = end - control2;
|
||||
var d3 = offsetEnd - control2;
|
||||
float len = d1.Length + d2.Length + d3.Length;
|
||||
int segmentCount = Math.Min(Math.Max(Mathf.CeilToInt(len * 0.05f), 1), 100);
|
||||
float segmentCountInv = 1.0f / segmentCount;
|
||||
|
||||
Bezier(ref start, ref control1, ref control2, ref end, 0, out var p);
|
||||
Bezier(ref offsetStart, ref control1, ref control2, ref offsetEnd, 0, out var p);
|
||||
for (int i = 1; i <= segmentCount; i++)
|
||||
{
|
||||
var oldp = p;
|
||||
float t = i * segmentCountInv;
|
||||
Bezier(ref start, ref control1, ref control2, ref end, t, out p);
|
||||
Bezier(ref offsetStart, ref control1, ref control2, ref offsetEnd, t, out p);
|
||||
|
||||
// Maybe it would be reasonable to return the point?
|
||||
CollisionsHelper.ClosestPointPointLine(ref point, ref oldp, ref p, out var result);
|
||||
@@ -153,16 +189,21 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
Box targetBox = Connections[i];
|
||||
var endPos = targetBox.ConnectionOrigin;
|
||||
var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity);
|
||||
var highlight = DefaultConnectionThickness + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity);
|
||||
var alpha = targetBox.Enabled && targetBox.IsActive ? 1.0f : 0.6f;
|
||||
var color = _currentTypeColor * highlight * alpha;
|
||||
|
||||
// We have to calculate an offset here to preserve the original color for when the default connection thickness is larger than 1
|
||||
var highlightOffset = (highlight - (DefaultConnectionThickness - 1));
|
||||
var color = _currentTypeColor * highlightOffset * alpha;
|
||||
|
||||
// TODO: Figure out how to only draw the topmost connection
|
||||
if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, mouseOverDistance))
|
||||
{
|
||||
highlight += 0.5f;
|
||||
}
|
||||
highlight += DefaultConnectionThickness * 0.5f;
|
||||
|
||||
// Increase thickness on impulse/ execution lines
|
||||
if (targetBox.CurrentType.IsVoid)
|
||||
highlight *= 2;
|
||||
|
||||
DrawConnection(style, ref startPos, ref endPos, ref color, highlight);
|
||||
}
|
||||
}
|
||||
@@ -177,7 +218,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
var endPos = targetBox.ConnectionOrigin;
|
||||
var alpha = targetBox.Enabled && targetBox.IsActive ? 1.0f : 0.6f;
|
||||
var color = _currentTypeColor * alpha;
|
||||
DrawConnection(Surface.Style, ref startPos, ref endPos, ref color, 2.5f);
|
||||
DrawConnection(Surface.Style, ref startPos, ref endPos, ref color, SelectedConnectionThickness);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -129,7 +129,12 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="args">The drag drop arguments data.</param>
|
||||
protected virtual void HandleDragDropParameters(List<string> objects, DragDropEventArgs args)
|
||||
{
|
||||
var arch = GetParameterGetterNodeArchetype(out var groupId);
|
||||
// Try to get the setter node when holding the ALT key, otherwise get the getter node
|
||||
if (!Input.GetKey(KeyboardKeys.Alt) || !TryGetParameterSetterNodeArchetype(out var groupId, out var arch))
|
||||
{
|
||||
arch = GetParameterGetterNodeArchetype(out groupId);
|
||||
}
|
||||
|
||||
for (int i = 0; i < objects.Count; i++)
|
||||
{
|
||||
var parameter = GetParameter(objects[i]);
|
||||
@@ -156,5 +161,18 @@ namespace FlaxEditor.Surface
|
||||
groupId = 6;
|
||||
return Archetypes.Parameters.Nodes[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the parameter setter node archetype to use.
|
||||
/// </summary>
|
||||
/// <param name="groupId">The group ID.</param>
|
||||
/// <param name="archetype">The node archetype.</param>
|
||||
/// <returns>True if a setter node exists.</returns>
|
||||
protected virtual bool TryGetParameterSetterNodeArchetype(out ushort groupId, out NodeArchetype archetype)
|
||||
{
|
||||
groupId = 0;
|
||||
archetype = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ namespace FlaxEditor.Surface
|
||||
return RootContext.GetParameter(name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnParamReordered()
|
||||
{
|
||||
MarkAsEdited();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnParamCreated(SurfaceParameter param)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -187,6 +187,14 @@ namespace FlaxEditor.Surface
|
||||
return Archetypes.Parameters.Nodes[2];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool TryGetParameterSetterNodeArchetype(out ushort groupId, out NodeArchetype archetype)
|
||||
{
|
||||
groupId = 6;
|
||||
archetype = Archetypes.Parameters.Nodes[3];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox)
|
||||
{
|
||||
|
||||
@@ -318,7 +318,6 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
Title = "Debug Log";
|
||||
Icon = IconInfo;
|
||||
OnEditorOptionsChanged(Editor.Options.Options);
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
|
||||
// Toolstrip
|
||||
@@ -327,17 +326,42 @@ namespace FlaxEditor.Windows
|
||||
Parent = this,
|
||||
};
|
||||
toolstrip.AddButton("Clear", Clear).LinkTooltip("Clears all log entries");
|
||||
_clearOnPlayButton = (ToolStripButton)toolstrip.AddButton("Clear on Play").SetAutoCheck(true).SetChecked(true).LinkTooltip("Clears all log entries on enter playmode");
|
||||
bool collapse = true;
|
||||
if (Editor.ProjectCache.TryGetCustomData("DebugLogCollapse", out bool setCollapse))
|
||||
collapse = setCollapse;
|
||||
_collapseLogsButton = (ToolStripButton)toolstrip.AddButton("Collapse", () => Editor.ProjectCache.SetCustomData("DebugLogCollapse", _collapseLogsButton.Checked.ToString())).SetAutoCheck(true).SetChecked(collapse).LinkTooltip("Collapses similar logs.");
|
||||
_pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error").SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
|
||||
_clearOnPlayButton = (ToolStripButton)toolstrip.AddButton("Clear on Play", () =>
|
||||
{
|
||||
editor.Options.Options.Interface.DebugLogClearOnPlay = _clearOnPlayButton.Checked;
|
||||
editor.Options.Apply(editor.Options.Options);
|
||||
}).SetAutoCheck(true).LinkTooltip("Clears all log entries on enter playmode");
|
||||
_collapseLogsButton = (ToolStripButton)toolstrip.AddButton("Collapse", () =>
|
||||
{
|
||||
editor.Options.Options.Interface.DebugLogCollapse = _collapseLogsButton.Checked;
|
||||
editor.Options.Apply(editor.Options.Options);
|
||||
}).SetAutoCheck(true).LinkTooltip("Collapses similar logs.");
|
||||
_pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error", () =>
|
||||
{
|
||||
editor.Options.Options.Interface.DebugLogPauseOnError = _pauseOnErrorButton.Checked;
|
||||
editor.Options.Apply(editor.Options.Options);
|
||||
}).SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
|
||||
toolstrip.AddSeparator();
|
||||
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides error messages");
|
||||
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () => UpdateLogTypeVisibility(LogGroup.Warning, _groupButtons[1].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides warning messages");
|
||||
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () => UpdateLogTypeVisibility(LogGroup.Info, _groupButtons[2].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides info messages");
|
||||
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () =>
|
||||
{
|
||||
UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked);
|
||||
editor.Options.Options.Interface.DebugLogShowErrorMessages = _groupButtons[0].Checked;
|
||||
editor.Options.Apply(editor.Options.Options);
|
||||
}).SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
|
||||
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () =>
|
||||
{
|
||||
UpdateLogTypeVisibility(LogGroup.Warning, _groupButtons[1].Checked);
|
||||
editor.Options.Options.Interface.DebugLogShowWarningMessages = _groupButtons[1].Checked;
|
||||
editor.Options.Apply(editor.Options.Options);
|
||||
}).SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
|
||||
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () =>
|
||||
{
|
||||
UpdateLogTypeVisibility(LogGroup.Info, _groupButtons[2].Checked);
|
||||
editor.Options.Options.Interface.DebugLogShowInfoMessages = _groupButtons[2].Checked;
|
||||
editor.Options.Apply(editor.Options.Options);
|
||||
}).SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
|
||||
UpdateCount();
|
||||
OnEditorOptionsChanged(Editor.Options.Options);
|
||||
|
||||
// Split panel
|
||||
_split = new SplitPanel(Orientation.Vertical, ScrollBars.Vertical, ScrollBars.Both)
|
||||
@@ -383,6 +407,12 @@ namespace FlaxEditor.Windows
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
_timestampsFormats = options.Interface.DebugLogTimestampsFormat;
|
||||
_clearOnPlayButton.Checked = options.Interface.DebugLogClearOnPlay;
|
||||
_collapseLogsButton.Checked = options.Interface.DebugLogCollapse;
|
||||
_pauseOnErrorButton.Checked = options.Interface.DebugLogPauseOnError;
|
||||
_groupButtons[0].Checked = options.Interface.DebugLogShowErrorMessages;
|
||||
_groupButtons[1].Checked = options.Interface.DebugLogShowWarningMessages;
|
||||
_groupButtons[2].Checked = options.Interface.DebugLogShowInfoMessages;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
Editor.Options.Apply(_options);
|
||||
|
||||
ClearDirtyFlag();
|
||||
GatherData();
|
||||
}
|
||||
|
||||
private void SetupCustomTabs()
|
||||
@@ -234,6 +234,18 @@ namespace FlaxEditor.Windows
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShow()
|
||||
{
|
||||
if (!_isDataDirty)
|
||||
{
|
||||
// Refresh the data, skip when data is modified during window docking
|
||||
GatherData();
|
||||
}
|
||||
|
||||
base.OnShow();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool OnClosing(ClosingReason reason)
|
||||
{
|
||||
|
||||
@@ -145,6 +145,8 @@ namespace FlaxEditor.Windows.Profiler
|
||||
#endif
|
||||
Type = gpuResource.ResourceType,
|
||||
};
|
||||
if (resource.Name == null)
|
||||
resource.Name = string.Empty;
|
||||
|
||||
// Create tooltip
|
||||
sb.Clear();
|
||||
|
||||
@@ -212,7 +212,7 @@ bool BehaviorKnowledge::HasGoal(ScriptingTypeHandle type) const
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant BehaviorKnowledge::GetGoal(ScriptingTypeHandle type)
|
||||
const Variant& BehaviorKnowledge::GetGoal(ScriptingTypeHandle type) const
|
||||
{
|
||||
for (const Variant& goal : Goals)
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">The goal type.</param>
|
||||
/// <returns>The goal value or null if not found.</returns>
|
||||
API_FUNCTION() Variant GetGoal(ScriptingTypeHandle type);
|
||||
API_FUNCTION() const Variant& GetGoal(ScriptingTypeHandle type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Adds the goal to the knowledge. If goal of that type already exists then it's value is updated.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using FlaxEngine.Interop;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
OnSet(other._asset);
|
||||
}
|
||||
|
||||
AssetReference(AssetReference&& other)
|
||||
AssetReference(AssetReference&& other) noexcept
|
||||
{
|
||||
OnSet(other._asset);
|
||||
other.OnSet(nullptr);
|
||||
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
/// <summary>
|
||||
/// Binds a static function.
|
||||
/// </summary>
|
||||
template<ReturnType (*Method)(Params...)>
|
||||
template<ReturnType(*Method)(Params...)>
|
||||
void Bind()
|
||||
{
|
||||
if (_lambda)
|
||||
@@ -288,6 +288,7 @@ protected:
|
||||
// Single allocation for list of binded functions. Thread-safe access via atomic operations. Removing binded function simply clears the entry to handle function unregister during invocation.
|
||||
intptr volatile _ptr = 0;
|
||||
intptr volatile _size = 0;
|
||||
typedef void (*StubSignature)(void*, Params...);
|
||||
#else
|
||||
struct Data
|
||||
{
|
||||
@@ -297,7 +298,6 @@ protected:
|
||||
// Holds pointer to Data with Functions and Locker. Thread-safe access via atomic operations.
|
||||
intptr volatile _data = 0;
|
||||
#endif
|
||||
typedef void (*StubSignature)(void*, Params...);
|
||||
|
||||
public:
|
||||
Delegate()
|
||||
@@ -811,7 +811,7 @@ public:
|
||||
auto function = (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings->_function);
|
||||
auto callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings->_callee);
|
||||
if (function != nullptr && function == (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings->_function))
|
||||
function(callee, Forward<Params>(params)...);
|
||||
function(callee, params...);
|
||||
++bindings;
|
||||
}
|
||||
#else
|
||||
@@ -823,7 +823,7 @@ public:
|
||||
{
|
||||
const FunctionType& item = i->Item;
|
||||
ASSERT_LOW_LAYER(item._function);
|
||||
item._function(item._callee, Forward<Params>(params)...);
|
||||
item._function(item._callee, params...);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
67
Source/Engine/Core/LogContext.cpp
Normal file
67
Source/Engine/Core/LogContext.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "LogContext.h"
|
||||
#include "Engine/Core/Types/Guid.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Threading/ThreadLocal.h"
|
||||
|
||||
struct LogContextThreadData
|
||||
{
|
||||
LogContextData* Ptr;
|
||||
uint32 Count;
|
||||
uint32 Capacity;
|
||||
|
||||
void Push(LogContextData&& item)
|
||||
{
|
||||
if (Count == Capacity)
|
||||
{
|
||||
Capacity = Capacity == 0 ? 32 : Capacity * 2;
|
||||
auto ptr = (LogContextData*)Allocator::Allocate(Capacity * sizeof(LogContextData));
|
||||
Platform::MemoryCopy(ptr, Ptr, Count * sizeof(LogContextData));
|
||||
Allocator::Free(Ptr);
|
||||
Ptr = ptr;
|
||||
}
|
||||
Ptr[Count++] = MoveTemp(item);
|
||||
}
|
||||
|
||||
void Pop()
|
||||
{
|
||||
Count--;
|
||||
}
|
||||
|
||||
LogContextData Peek()
|
||||
{
|
||||
return Count > 0 ? Ptr[Count - 1] : LogContextData();
|
||||
}
|
||||
};
|
||||
|
||||
ThreadLocal<LogContextThreadData> GlobalLogContexts;
|
||||
|
||||
String LogContext::GetInfo()
|
||||
{
|
||||
LogContextData context = LogContext::Get();
|
||||
if (context.ObjectID != Guid::Empty)
|
||||
return String::Format(TEXT("(Loading source was {0})"), context.ObjectID);
|
||||
return String::Empty;
|
||||
}
|
||||
|
||||
void LogContext::Push(const Guid& id)
|
||||
{
|
||||
LogContextData context;
|
||||
context.ObjectID = id;
|
||||
auto& stack = GlobalLogContexts.Get();
|
||||
stack.Push(MoveTemp(context));
|
||||
}
|
||||
|
||||
void LogContext::Pop()
|
||||
{
|
||||
auto& stack = GlobalLogContexts.Get();
|
||||
stack.Pop();
|
||||
}
|
||||
|
||||
LogContextData LogContext::Get()
|
||||
{
|
||||
auto& stack = GlobalLogContexts.Get();
|
||||
return stack.Peek();
|
||||
}
|
||||
77
Source/Engine/Core/LogContext.h
Normal file
77
Source/Engine/Core/LogContext.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Config.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
|
||||
class String;
|
||||
struct Guid;
|
||||
|
||||
/// <summary>
|
||||
/// Log context data structure. Contains different kinds of context data for different situtations.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API LogContextData
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_STRUCTURE(LogContextData)
|
||||
|
||||
/// <summary>
|
||||
/// A GUID for an object which this context applies to.
|
||||
/// </summary>
|
||||
API_FIELD() Guid ObjectID;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TIsPODType<LogContextData>
|
||||
{
|
||||
enum { Value = true };
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Log context interaction class. Methods are thread local, and as such, the context is as well.
|
||||
/// This system is used to pass down important information to be logged through large callstacks
|
||||
/// which don't have any reason to be passing down the information otherwise.
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API LogContext
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(LogContext)
|
||||
|
||||
/// <summary>
|
||||
/// Adds a log context element to the stack to be displayed in warning and error logs.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the object this context applies to.</param>
|
||||
API_FUNCTION() static void Push(const Guid& id);
|
||||
|
||||
/// <summary>
|
||||
/// Pops a log context element off of the stack and discards it.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void Pop();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log context element off the top of stack.
|
||||
/// </summary>
|
||||
/// <returns>The log context element at the top of the stack.</returns>
|
||||
API_FUNCTION() static LogContextData Get();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string which represents the current log context on the stack.
|
||||
/// </summary>
|
||||
/// <returns>The formatted string representing the current log context.</returns>
|
||||
API_FUNCTION() static String GetInfo();
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Helper structure used to call LogContext Push/Pop within single code block.
|
||||
/// </summary>
|
||||
struct LogContextScope
|
||||
{
|
||||
FORCE_INLINE LogContextScope(const Guid& id)
|
||||
{
|
||||
LogContext::Push(id);
|
||||
}
|
||||
|
||||
FORCE_INLINE ~LogContextScope()
|
||||
{
|
||||
LogContext::Pop();
|
||||
}
|
||||
};
|
||||
@@ -79,6 +79,26 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public Float2 UpperRight => new Float2(Location.X + Size.X, Location.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Gets position of the bottom right corner of the rectangle
|
||||
/// </summary>
|
||||
public Float2 LowerRight => Location + Size;
|
||||
|
||||
/// <summary>
|
||||
/// Gets position of the bottom left corner of the rectangle
|
||||
/// </summary>
|
||||
public Float2 LowerLeft => new Float2(Location.X, Location.Y + Size.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Gets position of the upper left corner of the rectangle
|
||||
/// </summary>
|
||||
public Float2 TopLeft => Location;
|
||||
|
||||
/// <summary>
|
||||
/// Gets position of the upper right corner of the rectangle
|
||||
/// </summary>
|
||||
public Float2 TopRight => new Float2(Location.X + Size.X, Location.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Gets position of the bottom right corner of the rectangle
|
||||
/// </summary>
|
||||
|
||||
@@ -117,6 +117,30 @@ public:
|
||||
return Location + Float2(Size.X, 0);
|
||||
}
|
||||
|
||||
// Gets position of the bottom right corner of the rectangle
|
||||
Float2 GetLowerRight() const
|
||||
{
|
||||
return Location + Size;
|
||||
}
|
||||
|
||||
// Gets position of the bottom left corner of the rectangle
|
||||
Float2 GetLowerLeft() const
|
||||
{
|
||||
return Location + Float2(0, Size.Y);
|
||||
}
|
||||
|
||||
// Gets position of the upper left corner of the rectangle
|
||||
Float2 GetTopLeft() const
|
||||
{
|
||||
return Location;
|
||||
}
|
||||
|
||||
// Gets position of the upper right corner of the rectangle
|
||||
Float2 GetTopRight() const
|
||||
{
|
||||
return Location + Float2(Size.X, 0);
|
||||
}
|
||||
|
||||
// Gets position of the bottom right corner of the rectangle
|
||||
Float2 GetBottomRight() const
|
||||
{
|
||||
|
||||
@@ -319,7 +319,6 @@ inline T&& Forward(typename TRemoveReference<T>::Type& t) noexcept
|
||||
template<typename T>
|
||||
inline T&& Forward(typename TRemoveReference<T>::Type&& t) noexcept
|
||||
{
|
||||
static_assert(!TIsLValueReference<T>::Value, "Can not forward an rvalue as an lvalue.");
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ void GameplayGlobals::SetDefaultValues(const Dictionary<String, Variant>& values
|
||||
}
|
||||
}
|
||||
|
||||
Variant GameplayGlobals::GetValue(const StringView& name) const
|
||||
const Variant& GameplayGlobals::GetValue(const StringView& name) const
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
auto e = Variables.TryGet(name);
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="name">The variable name.</param>
|
||||
/// <returns>The value.</returns>
|
||||
API_FUNCTION() Variant GetValue(const StringView& name) const;
|
||||
API_FUNCTION() const Variant& GetValue(const StringView& name) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the global variable (it must be added first).
|
||||
|
||||
@@ -1019,9 +1019,6 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
// Clear all caches which might hold references to assemblies in collectible ALC
|
||||
typeCache.Clear();
|
||||
|
||||
// Release all references in collectible ALC
|
||||
cachedDelegatesCollectible.Clear();
|
||||
foreach (var pair in managedTypesCollectible)
|
||||
pair.Value.handle.Free();
|
||||
|
||||
@@ -31,8 +31,6 @@ namespace FlaxEngine.Interop
|
||||
|
||||
private static bool firstAssemblyLoaded = false;
|
||||
|
||||
private static Dictionary<string, Type> typeCache = new();
|
||||
|
||||
private static IntPtr boolTruePtr = ManagedHandle.ToIntPtr(ManagedHandle.Alloc((int)1, GCHandleType.Pinned));
|
||||
private static IntPtr boolFalsePtr = ManagedHandle.ToIntPtr(ManagedHandle.Alloc((int)0, GCHandleType.Pinned));
|
||||
|
||||
@@ -279,44 +277,6 @@ namespace FlaxEngine.Interop
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static Type FindType(string typeName)
|
||||
{
|
||||
if (typeCache.TryGetValue(typeName, out Type type))
|
||||
return type;
|
||||
|
||||
type = Type.GetType(typeName, ResolveAssembly, null);
|
||||
if (type == null)
|
||||
type = ResolveSlow(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
string fullTypeName = typeName;
|
||||
typeName = typeName.Substring(0, typeName.IndexOf(','));
|
||||
type = Type.GetType(typeName, ResolveAssembly, null);
|
||||
if (type == null)
|
||||
type = ResolveSlow(typeName);
|
||||
|
||||
typeName = fullTypeName;
|
||||
}
|
||||
|
||||
typeCache.Add(typeName, type);
|
||||
|
||||
return type;
|
||||
|
||||
static Type ResolveSlow(string typeName)
|
||||
{
|
||||
foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
{
|
||||
var type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Assembly ResolveAssembly(AssemblyName name) => ResolveScriptingAssemblyByName(name, allowPartial: false);
|
||||
}
|
||||
|
||||
/// <summary>Find <paramref name="assemblyName"/> among the scripting assemblies.</summary>
|
||||
/// <param name="assemblyName">The name to find</param>
|
||||
/// <param name="allowPartial">If true, partial names should be allowed to be resolved.</param>
|
||||
@@ -378,18 +338,15 @@ namespace FlaxEngine.Interop
|
||||
/// </summary>
|
||||
internal static Type GetInternalType(Type type)
|
||||
{
|
||||
string[] splits = type.AssemblyQualifiedName.Split(',');
|
||||
string @namespace = string.Join('.', splits[0].Split('.').SkipLast(1));
|
||||
string className = @namespace.Length > 0 ? splits[0].Substring(@namespace.Length + 1) : splits[0];
|
||||
string parentClassName = "";
|
||||
if (className.Contains('+'))
|
||||
{
|
||||
parentClassName = className.Substring(0, className.LastIndexOf('+') + 1);
|
||||
className = className.Substring(parentClassName.Length);
|
||||
}
|
||||
string marshallerName = className + "Marshaller";
|
||||
string internalAssemblyQualifiedName = $"{@namespace}.{parentClassName}{marshallerName}+{className}Internal,{String.Join(',', splits.Skip(1))}";
|
||||
return FindType(internalAssemblyQualifiedName);
|
||||
Type marshallerType = type.GetCustomAttribute<System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute>()?.NativeType;
|
||||
if (marshallerType == null)
|
||||
return null;
|
||||
|
||||
Type internalType = marshallerType.GetNestedType($"{type.Name}Internal");
|
||||
if (internalType == null)
|
||||
return null;
|
||||
|
||||
return internalType;
|
||||
}
|
||||
|
||||
internal class ReferenceTypePlaceholder { }
|
||||
@@ -1338,7 +1295,7 @@ namespace FlaxEngine.Interop
|
||||
if (invokeDelegate == null && !method.DeclaringType.IsValueType)
|
||||
{
|
||||
// Thread-safe creation
|
||||
lock (typeCache)
|
||||
lock (method)
|
||||
{
|
||||
if (invokeDelegate == null)
|
||||
{
|
||||
|
||||
@@ -273,7 +273,7 @@ AnimGraphParameter* AnimatedModel::GetParameter(const StringView& name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Variant AnimatedModel::GetParameterValue(const StringView& name)
|
||||
const Variant& AnimatedModel::GetParameterValue(const StringView& name) const
|
||||
{
|
||||
CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(Variant::Null);
|
||||
for (auto& param : GraphInstance.Parameters)
|
||||
@@ -299,7 +299,7 @@ void AnimatedModel::SetParameterValue(const StringView& name, const Variant& val
|
||||
LOG(Warning, "Failed to set animated model '{0}' missing parameter '{1}'", ToString(), name);
|
||||
}
|
||||
|
||||
Variant AnimatedModel::GetParameterValue(const Guid& id)
|
||||
const Variant& AnimatedModel::GetParameterValue(const Guid& id) const
|
||||
{
|
||||
CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(Variant::Null);
|
||||
for (auto& param : GraphInstance.Parameters)
|
||||
|
||||
@@ -308,7 +308,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="name">The parameter name.</param>
|
||||
/// <returns>The value.</returns>
|
||||
API_FUNCTION() Variant GetParameterValue(const StringView& name);
|
||||
API_FUNCTION() const Variant& GetParameterValue(const StringView& name) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the anim graph instance parameter value.
|
||||
@@ -322,7 +322,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="id">The parameter id.</param>
|
||||
/// <returns>The value.</returns>
|
||||
API_FUNCTION() Variant GetParameterValue(const Guid& id);
|
||||
API_FUNCTION() const Variant& GetParameterValue(const Guid& id) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the anim graph instance parameter value.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Core/Cache.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Serialization/ISerializeModifier.h"
|
||||
@@ -247,6 +248,7 @@ void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISeria
|
||||
CHECK(obj);
|
||||
#endif
|
||||
ISerializeModifier* modifier = context.GetModifier();
|
||||
LogContextScope logContext(obj->GetID());
|
||||
|
||||
// Check for prefab instance
|
||||
Guid prefabObjectId;
|
||||
|
||||
@@ -91,14 +91,14 @@ Variant ParticleEffectParameter::GetDefaultValue() const
|
||||
return paramValue;
|
||||
}
|
||||
|
||||
Variant ParticleEffectParameter::GetDefaultEmitterValue() const
|
||||
const Variant& ParticleEffectParameter::GetDefaultEmitterValue() const
|
||||
{
|
||||
CHECK_RETURN(IsValid(), Variant::False);
|
||||
const ParticleSystemParameter& param = _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex];
|
||||
return param.Value;
|
||||
}
|
||||
|
||||
Variant ParticleEffectParameter::GetValue() const
|
||||
const Variant& ParticleEffectParameter::GetValue() const
|
||||
{
|
||||
CHECK_RETURN(IsValid(), Variant::False);
|
||||
const Variant& paramValue = _effect->Instance.Emitters[_emitterIndex].Parameters[_paramIndex];
|
||||
@@ -205,7 +205,7 @@ ParticleEffectParameter* ParticleEffect::GetParameter(const StringView& emitterT
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Variant ParticleEffect::GetParameterValue(const StringView& emitterTrackName, const StringView& paramName)
|
||||
const Variant& ParticleEffect::GetParameterValue(const StringView& emitterTrackName, const StringView& paramName)
|
||||
{
|
||||
const auto param = GetParameter(emitterTrackName, paramName);
|
||||
CHECK_RETURN(param, Variant::Null);
|
||||
@@ -474,15 +474,21 @@ void ParticleEffect::Update()
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
|
||||
void ParticleEffect::UpdateExecuteInEditor()
|
||||
{
|
||||
// Auto-play in Editor
|
||||
if (!Editor::IsPlayMode && !_isStopped)
|
||||
if (!Editor::IsPlayMode && !_isStopped && IsLooping && PlayOnStart && Editor::Managed->ManagedEditorOptions.EnableParticlesPreview)
|
||||
{
|
||||
_isPlaying = true;
|
||||
Update();
|
||||
}
|
||||
else if (!Editor::IsPlayMode && _isPlaying)
|
||||
{
|
||||
_isPlaying = false;
|
||||
ResetSimulation();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -109,13 +109,13 @@ public:
|
||||
/// Gets the default value of the parameter (set in particle emitter asset).
|
||||
/// </summary>
|
||||
/// <returns>The default value.</returns>
|
||||
API_PROPERTY() Variant GetDefaultEmitterValue() const;
|
||||
API_PROPERTY() const Variant& GetDefaultEmitterValue() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the parameter.
|
||||
/// </summary>
|
||||
/// <returns>The value.</returns>
|
||||
API_PROPERTY() Variant GetValue() const;
|
||||
API_PROPERTY() const Variant& GetValue() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the parameter.
|
||||
@@ -293,7 +293,7 @@ public:
|
||||
/// <param name="emitterTrackName">The emitter track name (in particle system asset).</param>
|
||||
/// <param name="paramName">The emitter parameter name (in particle emitter asset).</param>
|
||||
/// <returns>The value.</returns>
|
||||
API_FUNCTION() Variant GetParameterValue(const StringView& emitterTrackName, const StringView& paramName);
|
||||
API_FUNCTION() const Variant& GetParameterValue(const StringView& emitterTrackName, const StringView& paramName);
|
||||
|
||||
/// <summary>
|
||||
/// Set the particle parameter value.
|
||||
|
||||
@@ -21,6 +21,8 @@ Collider::Collider(const SpawnParams& params)
|
||||
, _cachedScale(1.0f)
|
||||
, _contactOffset(2.0f)
|
||||
{
|
||||
Material.Loaded.Bind<Collider, &Collider::OnMaterialChanged>(this);
|
||||
Material.Unload.Bind<Collider, &Collider::OnMaterialChanged>(this);
|
||||
Material.Changed.Bind<Collider, &Collider::OnMaterialChanged>(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -4474,6 +4474,16 @@ void PhysicsBackend::DestroyController(void* controller)
|
||||
controllerPhysX->release();
|
||||
}
|
||||
|
||||
void PhysicsBackend::DestroyMaterial(void* material)
|
||||
{
|
||||
ASSERT_LOW_LAYER(material);
|
||||
auto materialPhysX = (PxMaterial*)material;
|
||||
materialPhysX->userData = nullptr;
|
||||
FlushLocker.Lock();
|
||||
DeleteObjects.Add(materialPhysX);
|
||||
FlushLocker.Unlock();
|
||||
}
|
||||
|
||||
void PhysicsBackend::DestroyObject(void* object)
|
||||
{
|
||||
ASSERT_LOW_LAYER(object);
|
||||
|
||||
@@ -81,7 +81,7 @@ void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier*
|
||||
PhysicalMaterial::~PhysicalMaterial()
|
||||
{
|
||||
if (_material)
|
||||
PhysicsBackend::DestroyObject(_material);
|
||||
PhysicsBackend::DestroyMaterial(_material);
|
||||
}
|
||||
|
||||
bool PhysicsService::Init()
|
||||
|
||||
@@ -314,6 +314,7 @@ public:
|
||||
static void DestroyShape(void* shape);
|
||||
static void DestroyJoint(void* joint);
|
||||
static void DestroyController(void* controller);
|
||||
static void DestroyMaterial(void* material);
|
||||
static void DestroyObject(void* object);
|
||||
static void RemoveCollider(PhysicsColliderActor* collider);
|
||||
static void RemoveJoint(Joint* joint);
|
||||
|
||||
@@ -865,6 +865,10 @@ void PhysicsBackend::DestroyController(void* controller)
|
||||
{
|
||||
}
|
||||
|
||||
void PhysicsBackend::DestroyMaterial(void* material)
|
||||
{
|
||||
}
|
||||
|
||||
void PhysicsBackend::DestroyObject(void* object)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -778,7 +778,16 @@ void WindowsWindow::UpdateCursor()
|
||||
if (!_lastCursorHidden)
|
||||
{
|
||||
_lastCursorHidden = true;
|
||||
::ShowCursor(FALSE);
|
||||
while(::ShowCursor(FALSE) >= 0)
|
||||
{
|
||||
if (_cursorHiddenSafetyCount >= 100)
|
||||
{
|
||||
LOG(Warning, "Cursor has failed to hide.");
|
||||
break;
|
||||
}
|
||||
_cursorHiddenSafetyCount += 1;
|
||||
}
|
||||
_cursorHiddenSafetyCount = 0;
|
||||
}
|
||||
::SetCursor(nullptr);
|
||||
return;
|
||||
@@ -786,7 +795,16 @@ void WindowsWindow::UpdateCursor()
|
||||
else if (_lastCursorHidden)
|
||||
{
|
||||
_lastCursorHidden = false;
|
||||
::ShowCursor(TRUE);
|
||||
while(::ShowCursor(TRUE) < 0)
|
||||
{
|
||||
if (_cursorHiddenSafetyCount >= 100)
|
||||
{
|
||||
LOG(Warning, "Cursor has failed to show.");
|
||||
break;
|
||||
}
|
||||
_cursorHiddenSafetyCount += 1;
|
||||
}
|
||||
_cursorHiddenSafetyCount = 0;
|
||||
}
|
||||
|
||||
int32 index = 0;
|
||||
|
||||
@@ -29,6 +29,7 @@ private:
|
||||
bool _trackingMouse = false;
|
||||
bool _clipCursorSet = false;
|
||||
bool _lastCursorHidden = false;
|
||||
int _cursorHiddenSafetyCount = 0;
|
||||
bool _isDuringMaximize = false;
|
||||
Windows::HANDLE _monitor = nullptr;
|
||||
Windows::LONG _clipCursorRect[4];
|
||||
|
||||
@@ -126,10 +126,11 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="id">Unique ID of the object.</param>
|
||||
/// <typeparam name="T">Type of the object.</typeparam>
|
||||
/// <param name="skipLog">Whether or not to log warnings when objects aren't found.</param>
|
||||
/// <returns>Found object or null if missing.</returns>
|
||||
public static T Find<T>(ref Guid id) where T : Object
|
||||
public static T Find<T>(ref Guid id, bool skipLog = false) where T : Object
|
||||
{
|
||||
return Internal_FindObject(ref id, typeof(T)) as T;
|
||||
return Internal_FindObject(ref id, typeof(T), skipLog) as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -137,10 +138,11 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="id">Unique ID of the object.</param>
|
||||
/// <param name="type">Type of the object.</param>
|
||||
/// <param name="skipLog">Whether or not to log warnings when objects aren't found.</param>
|
||||
/// <returns>Found object or null if missing.</returns>
|
||||
public static Object Find(ref Guid id, Type type)
|
||||
public static Object Find(ref Guid id, Type type, bool skipLog = false)
|
||||
{
|
||||
return Internal_FindObject(ref id, type);
|
||||
return Internal_FindObject(ref id, type, skipLog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -335,7 +337,7 @@ namespace FlaxEngine
|
||||
internal static partial string Internal_GetTypeName(IntPtr obj);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type);
|
||||
internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type, [MarshalAs(UnmanagedType.U1)] bool skipLog = false);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial Object Internal_TryFindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "ManagedCLR/MCore.h"
|
||||
#include "ManagedCLR/MException.h"
|
||||
#include "Internal/StdTypesContainer.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Core/ObjectsRemovalService.h"
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Core/Types/Stopwatch.h"
|
||||
@@ -880,7 +881,7 @@ ScriptingObject* Scripting::FindObject(Guid id, const MClass* type)
|
||||
// Check type
|
||||
if (!type || result->Is(type))
|
||||
return result;
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}.", id, String(result->GetType().Fullname), String(type->GetFullName()));
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", id, String(result->GetType().Fullname), String(type->GetFullName()), LogContext::GetInfo());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -899,7 +900,7 @@ ScriptingObject* Scripting::FindObject(Guid id, const MClass* type)
|
||||
return asset;
|
||||
}
|
||||
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}.", id, String(type->GetFullName()));
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", id, String(type->GetFullName()), LogContext::GetInfo());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "BinaryModule.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Core/Types/Pair.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
@@ -718,7 +719,7 @@ DEFINE_INTERNAL_CALL(MString*) ObjectInternal_GetTypeName(ScriptingObject* obj)
|
||||
return MUtils::ToString(obj->GetType().Fullname);
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject* type)
|
||||
DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject* type, bool skipLog = false)
|
||||
{
|
||||
if (!id->IsValid())
|
||||
return nullptr;
|
||||
@@ -735,15 +736,20 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject*
|
||||
{
|
||||
if (klass && !obj->Is(klass))
|
||||
{
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}.", *id, String(obj->GetType().Fullname), String(klass->GetFullName()));
|
||||
if (!skipLog)
|
||||
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", *id, String(obj->GetType().Fullname), String(klass->GetFullName()), LogContext::GetInfo());
|
||||
return nullptr;
|
||||
}
|
||||
return obj->GetOrCreateManagedInstance();
|
||||
}
|
||||
if (klass)
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}.", *id, String(klass->GetFullName()));
|
||||
else
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}", *id);
|
||||
|
||||
if (!skipLog)
|
||||
{
|
||||
if (klass)
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", *id, String(klass->GetFullName()), LogContext::GetInfo());
|
||||
else
|
||||
LOG(Warning, "Unable to find scripting object with ID={0}", *id);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -569,6 +569,106 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
// Do the inverse interpolation and saturate it
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("saturate((({0} - {1}) / ({2} - {1})))"), gradient.Value, lowerEdge.Value, upperEdge.Value), node);
|
||||
}
|
||||
// Rotate UV [Rotator Simple]
|
||||
case 43:
|
||||
{
|
||||
//cosine = cos(rotation);
|
||||
//sine = sin(rotation);
|
||||
//float2 out = float2(cosine * uv.x + sine * uv.y,cosine * uv.y - sine * uv.x);
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2();
|
||||
const auto rotationAngle = tryGetValue(node->GetBox(1), node->Values[0].AsFloat).AsFloat();
|
||||
auto c = writeLocal(ValueType::Float, String::Format(TEXT("cos({0})"), rotationAngle.Value), node);
|
||||
auto s = writeLocal(ValueType::Float, String::Format(TEXT("sin({0})"), rotationAngle.Value), node);
|
||||
value = writeLocal(ValueType::Float2, String::Format(TEXT("float2({1} * {0}.x + {2} * {0}.y, {1} * {0}.y - {2} * {0}.x)"), uv.Value, c.Value, s.Value), node);
|
||||
break;
|
||||
}
|
||||
// Cone Gradient
|
||||
case 44:
|
||||
{
|
||||
//float gradient = angle - abs(atan2(uv.x,uv.y));
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2();
|
||||
const auto rotationAngle = tryGetValue(node->GetBox(1), node->Values[0].AsFloat).AsFloat();
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("{1} - abs(atan2({0}.x, {0}.y))"), uv.Value, rotationAngle.Value), node);
|
||||
break;
|
||||
}
|
||||
// Cycle Gradient
|
||||
case 45:
|
||||
{
|
||||
//float gradient = 1 - length(uv * 2);
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2();
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("1 - length({0} * 2.0)"), uv.Value), node);
|
||||
break;
|
||||
}
|
||||
// Falloff and Offset
|
||||
case 46:
|
||||
{
|
||||
//float out = clamp((((Value - (1 - Offset)) + Falloff) / Falloff),0,1)
|
||||
const auto in = tryGetValue(node->GetBox(0), ShaderGraphValue::Zero);
|
||||
const auto graphValue = tryGetValue(node->GetBox(1), node->Values[0].AsFloat);
|
||||
const auto falloff = tryGetValue(node->GetBox(2), node->Values[1].AsFloat);
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("saturate(((({0} - (1.0 - {1})) + {2}) / {2}))"), in.Value, graphValue.Value, falloff.Value), node);
|
||||
break;
|
||||
}
|
||||
// Linear Gradient
|
||||
case 47:
|
||||
{
|
||||
// float2 uv = Input0.xy;
|
||||
// float r = Input0.z;
|
||||
// float2 A = 1.0 - float2(cos(r) * uv.x + sin(r) * uv.y, cos(r) * uv.y - sin(r) * uv.x);
|
||||
// float2 out = float2(Mirror ? abs(A.x < 1.0 ? (A.x - 0.5) * 2 : (2 - ((A.x - 0.5) * 2)) * -1) : A.x < 1.0 ? (A.x - 0.5) * 2 : 1,Mirror ? abs(A.y < 1.0 ? (A.y - 0.5) * 2 : (2 - ((A.y - 0.5) * 2)) * -1) : A.y < 1.0 ? (A.y - 0.5) * 2 : 1);
|
||||
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2();
|
||||
const auto rotationAngle = tryGetValue(node->GetBox(1), node->Values[0].AsFloat).AsFloat();
|
||||
const auto mirror = tryGetValue(node->GetBox(2), node->Values[1].AsBool).AsBool();
|
||||
|
||||
auto c = writeLocal(ValueType::Float, String::Format(TEXT("cos({0})"), rotationAngle.Value), node);
|
||||
auto s = writeLocal(ValueType::Float, String::Format(TEXT("sin({0})"), rotationAngle.Value), node);
|
||||
auto a = writeLocal(ValueType::Float2, String::Format(TEXT("1.0 - float2({1} * {0}.x + {2} * {0}.y, {1} * {0}.y - {2} * {0}.x)"), uv.Value, c.Value, s.Value), node);
|
||||
value = writeLocal(
|
||||
ValueType::Float2, String::Format(TEXT
|
||||
(
|
||||
"float2({0} ? abs({1}.x < 1.0 ? ({1}.x - 0.5) * 2 : (2 - (({1}.x - 0.5) * 2)) * -1) : {1}.x < 1.0 ? ({1}.x - 0.5) * 2 : 1,{0} ? abs({1}.y < 1.0 ? ({1}.y - 0.5) * 2 : (2 - (({1}.y - 0.5) * 2)) * -1) : {1}.y < 1.0 ? ({1}.y - 0.5) * 2 : 1)"
|
||||
), mirror.Value, a.Value),
|
||||
node);
|
||||
break;
|
||||
}
|
||||
// Radial Gradient
|
||||
case 48:
|
||||
{
|
||||
//float gradient = clamp(atan2(uv.x,uv.y) - angle,0.0,1.0);
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2();
|
||||
const auto rotationAngle = tryGetValue(node->GetBox(1), node->Values[0].AsFloat).AsFloat();
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("saturate(atan2({0}.x, {0}.y) - {1})"), uv.Value, rotationAngle.Value), node);
|
||||
break;
|
||||
}
|
||||
// Ring Gradient
|
||||
case 49:
|
||||
{
|
||||
// Nodes:
|
||||
// float c = CycleGradient(uv)
|
||||
// float InnerMask = FalloffAndOffset(c,(OuterBounds - Falloff),Falloff)
|
||||
// float OuterMask = FalloffAndOffset(1-c,1-InnerBounds,Falloff)
|
||||
// float Mask = OuterMask * InnerMask;
|
||||
|
||||
// TODO: check if there is some useless operators
|
||||
|
||||
//expanded
|
||||
//float cycleGradient = 1 - length(uv * 2);
|
||||
//float InnerMask = clamp((((c - (1 - (OuterBounds - Falloff))) + Falloff) / Falloff),0,1)
|
||||
//float OuterMask = clamp(((((1-c) - (1 - (1-InnerBounds))) + Falloff) / Falloff),0,1)
|
||||
//float Mask = OuterMask * InnerMask;
|
||||
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2();
|
||||
const auto outerBounds = tryGetValue(node->GetBox(1), node->Values[0].AsFloat).AsFloat();
|
||||
const auto innerBounds = tryGetValue(node->GetBox(2), node->Values[1].AsFloat).AsFloat();
|
||||
const auto falloff = tryGetValue(node->GetBox(3), node->Values[2].AsFloat).AsFloat();
|
||||
auto c = writeLocal(ValueType::Float, String::Format(TEXT("1 - length({0} * 2.0)"), uv.Value), node);
|
||||
auto innerMask = writeLocal(ValueType::Float, String::Format(TEXT("saturate(((({0} - (1.0 - ({1} - {2}))) + {2}) / {2}))"), c.Value, outerBounds.Value, falloff.Value), node);
|
||||
auto outerMask = writeLocal(ValueType::Float, String::Format(TEXT("saturate(((((1.0 - {0}) - (1.0 - (1.0 - {1}))) + {2}) / {2}))"), c.Value, innerBounds.Value, falloff.Value), node);
|
||||
auto mask = writeLocal(ValueType::Float, String::Format(TEXT("{0} * {1}"), innerMask.Value, outerMask.Value), node);
|
||||
value = writeLocal(ValueType::Float3, String::Format(TEXT("float3({0}, {1}, {2})"), innerMask.Value, outerMask.Value, mask.Value), node);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ HRESULT LoadFromEXRFile(const StringView& path, DirectX::ScratchImage& image)
|
||||
LOG_STR(Warning, String(err));
|
||||
FreeEXRErrorMessage(err);
|
||||
}
|
||||
return S_FALSE;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Setup image
|
||||
@@ -326,7 +326,7 @@ HRESULT LoadFromEXRFile(const StringView& path, DirectX::ScratchImage& image)
|
||||
return result;
|
||||
#else
|
||||
LOG(Warning, "EXR format is not supported.");
|
||||
return S_FALSE;
|
||||
return E_FAIL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
base.DrawSelf();
|
||||
|
||||
float progressNormalized = (_current - _minimum) / _maximum;
|
||||
float progressNormalized = Mathf.InverseLerp(_minimum, _maximum, _current);
|
||||
if (progressNormalized > 0.001f)
|
||||
{
|
||||
Rectangle barRect = new Rectangle(0, 0, Width * progressNormalized, Height);
|
||||
|
||||
@@ -100,6 +100,7 @@ namespace FlaxEngine.GUI
|
||||
if (value > _maximum)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
_minimum = value;
|
||||
UpdateThumb();
|
||||
if (Value < _minimum)
|
||||
Value = _minimum;
|
||||
}
|
||||
@@ -116,6 +117,7 @@ namespace FlaxEngine.GUI
|
||||
if (value < _minimum)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
_maximum = value;
|
||||
UpdateThumb();
|
||||
if (Value > _maximum)
|
||||
Value = _maximum;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
private Margin _slotPadding;
|
||||
private int _slotsV, _slotsH;
|
||||
private Float2 _slotSpacing;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the padding given to each slot.
|
||||
@@ -62,11 +63,25 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets grid slot spacing.
|
||||
/// </summary>
|
||||
[EditorOrder(30), Limit(0), Tooltip("The Grid slot spacing.")]
|
||||
public Float2 SlotSpacing
|
||||
{
|
||||
get => _slotSpacing;
|
||||
set
|
||||
{
|
||||
_slotSpacing = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UniformGridPanel"/> class.
|
||||
/// </summary>
|
||||
public UniformGridPanel()
|
||||
: this(2)
|
||||
: this(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -74,10 +89,11 @@ namespace FlaxEngine.GUI
|
||||
/// Initializes a new instance of the <see cref="UniformGridPanel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="slotPadding">The slot padding.</param>
|
||||
public UniformGridPanel(float slotPadding = 2)
|
||||
public UniformGridPanel(float slotPadding = 0)
|
||||
{
|
||||
AutoFocus = false;
|
||||
SlotPadding = new Margin(slotPadding);
|
||||
SlotSpacing = new Float2(2);
|
||||
_slotsH = _slotsV = 5;
|
||||
}
|
||||
|
||||
@@ -122,6 +138,42 @@ namespace FlaxEngine.GUI
|
||||
var slotBounds = new Rectangle(slotSize.X * x, slotSize.Y * y, slotSize.X, slotSize.Y);
|
||||
_slotPadding.ShrinkRectangle(ref slotBounds);
|
||||
|
||||
if (slotsV > 1)
|
||||
{
|
||||
if (y == 0)
|
||||
{
|
||||
slotBounds.Height -= _slotSpacing.Y * 0.5f;
|
||||
}
|
||||
else if (y == slotsV - 1)
|
||||
{
|
||||
slotBounds.Height -= _slotSpacing.Y * 0.5f;
|
||||
slotBounds.Y += _slotSpacing.Y * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
slotBounds.Height -= _slotSpacing.Y;
|
||||
slotBounds.Y += _slotSpacing.Y * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
if (slotsH > 1)
|
||||
{
|
||||
if (x == 0)
|
||||
{
|
||||
slotBounds.Width -= _slotSpacing.X * 0.5f;
|
||||
}
|
||||
else if (x == slotsH - 1)
|
||||
{
|
||||
slotBounds.Width -= _slotSpacing.X * 0.5f;
|
||||
slotBounds.X += _slotSpacing.X * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
slotBounds.Width -= _slotSpacing.X;
|
||||
slotBounds.X += _slotSpacing.X * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
var c = _children[i++];
|
||||
c.Bounds = slotBounds;
|
||||
}
|
||||
|
||||
389
Source/Engine/UI/GUI/Special/RadialMenu.cs
Normal file
389
Source/Engine/UI/GUI/Special/RadialMenu.cs
Normal file
@@ -0,0 +1,389 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Radial menu control that arranges child controls (of type Image) in a circle.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class RadialMenu : ContainerControl
|
||||
{
|
||||
private bool _materialIsDirty = true;
|
||||
private float _angle;
|
||||
private float _selectedSegment;
|
||||
private int _highlightSegment = -1;
|
||||
private MaterialBase _material;
|
||||
private MaterialInstance _materialInstance;
|
||||
private int _segmentCount;
|
||||
private Color _highlightColor;
|
||||
private Color _foregroundColor;
|
||||
private Color _selectionColor;
|
||||
private float _edgeOffset;
|
||||
private float _thickness = 0.2f;
|
||||
|
||||
private float USize => Size.X < Size.Y ? Size.X : Size.Y;
|
||||
private bool ShowMatProp => _material != null;
|
||||
|
||||
/// <summary>
|
||||
/// The material to use for menu background drawing.
|
||||
/// </summary>
|
||||
[EditorOrder(1)]
|
||||
public MaterialBase Material
|
||||
{
|
||||
get => _material;
|
||||
set
|
||||
{
|
||||
if (_material == value)
|
||||
return;
|
||||
_material = value;
|
||||
Object.Destroy(ref _materialInstance);
|
||||
_materialIsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the edge offset.
|
||||
/// </summary>
|
||||
[EditorOrder(2), Range(0, 1)]
|
||||
public float EdgeOffset
|
||||
{
|
||||
get => _edgeOffset;
|
||||
set
|
||||
{
|
||||
_edgeOffset = Math.Clamp(value, 0, 1);
|
||||
_materialIsDirty = true;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thickness.
|
||||
/// </summary>
|
||||
[EditorOrder(3), Range(0, 1), VisibleIf(nameof(ShowMatProp))]
|
||||
public float Thickness
|
||||
{
|
||||
get => _thickness;
|
||||
set
|
||||
{
|
||||
_thickness = Math.Clamp(value, 0, 1);
|
||||
_materialIsDirty = true;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets control background color (transparent color (alpha=0) means no background rendering).
|
||||
/// </summary>
|
||||
[VisibleIf(nameof(ShowMatProp))]
|
||||
public new Color BackgroundColor
|
||||
{
|
||||
get => base.BackgroundColor;
|
||||
set
|
||||
{
|
||||
_materialIsDirty = true;
|
||||
base.BackgroundColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the highlight.
|
||||
/// </summary>
|
||||
[VisibleIf(nameof(ShowMatProp))]
|
||||
public Color HighlightColor
|
||||
{
|
||||
get => _highlightColor;
|
||||
set
|
||||
{
|
||||
_materialIsDirty = true;
|
||||
_highlightColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the foreground.
|
||||
/// </summary>
|
||||
[VisibleIf(nameof(ShowMatProp))]
|
||||
public Color ForegroundColor
|
||||
{
|
||||
get => _foregroundColor;
|
||||
set
|
||||
{
|
||||
_materialIsDirty = true;
|
||||
_foregroundColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the selection.
|
||||
/// </summary>
|
||||
[VisibleIf(nameof(ShowMatProp))]
|
||||
public Color SelectionColor
|
||||
{
|
||||
get => _selectionColor;
|
||||
set
|
||||
{
|
||||
_materialIsDirty = true;
|
||||
_selectionColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The selected callback
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public Action<int> Selected;
|
||||
|
||||
/// <summary>
|
||||
/// The allow change selection when inside
|
||||
/// </summary>
|
||||
[VisibleIf(nameof(ShowMatProp))]
|
||||
public bool AllowChangeSelectionWhenInside;
|
||||
|
||||
/// <summary>
|
||||
/// The center as button
|
||||
/// </summary>
|
||||
[VisibleIf(nameof(ShowMatProp))]
|
||||
public bool CenterAsButton;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadialMenu"/> class.
|
||||
/// </summary>
|
||||
public RadialMenu()
|
||||
: this(0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadialMenu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="x">Position X coordinate</param>
|
||||
/// <param name="y">Position Y coordinate</param>
|
||||
/// <param name="width">Width</param>
|
||||
/// <param name="height">Height</param>
|
||||
public RadialMenu(float x, float y, float width = 100, float height = 100)
|
||||
: base(x, y, width, height)
|
||||
{
|
||||
var style = Style.Current;
|
||||
if (style != null)
|
||||
{
|
||||
BackgroundColor = style.BackgroundNormal;
|
||||
HighlightColor = style.BackgroundSelected;
|
||||
ForegroundColor = style.BackgroundHighlighted;
|
||||
SelectionColor = style.BackgroundSelected;
|
||||
}
|
||||
_material = Content.LoadAsyncInternal<MaterialBase>("Engine/DefaultRadialMenu");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadialMenu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="location">Position</param>
|
||||
/// <param name="size">Size</param>
|
||||
public RadialMenu(Float2 location, Float2 size)
|
||||
: this(location.X, location.Y, size.X, size.Y)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DrawSelf()
|
||||
{
|
||||
if (_materialInstance == null && Material != null)
|
||||
{
|
||||
_materialInstance = Material.CreateVirtualInstance();
|
||||
_materialIsDirty = true;
|
||||
}
|
||||
if (_materialInstance != null)
|
||||
{
|
||||
if (_materialIsDirty)
|
||||
{
|
||||
_materialInstance.SetParameterValue("RadialMenu_EdgeOffset", Math.Clamp(1 - _edgeOffset, 0, 1));
|
||||
_materialInstance.SetParameterValue("RadialMenu_Thickness", Math.Clamp(_thickness, 0, 1));
|
||||
_materialInstance.SetParameterValue("RadialMenu_Angle", (1.0f / _segmentCount) * Mathf.Pi);
|
||||
_materialInstance.SetParameterValue("RadialMenu_SegmentCount", _segmentCount);
|
||||
_materialInstance.SetParameterValue("RadialMenu_HighlightColor", _highlightColor);
|
||||
_materialInstance.SetParameterValue("RadialMenu_ForegroundColor", _foregroundColor);
|
||||
_materialInstance.SetParameterValue("RadialMenu_BackgroundColor", BackgroundColor);
|
||||
_materialInstance.SetParameterValue("RadialMenu_Rotation", -_angle + Mathf.Pi);
|
||||
UpdateSelectionColor();
|
||||
_materialIsDirty = false;
|
||||
}
|
||||
Render2D.DrawMaterial(_materialInstance, new Rectangle(Float2.Zero, new Float2(Size.X < Size.Y ? Size.X : Size.Y)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
if (_materialInstance != null)
|
||||
{
|
||||
if (_highlightSegment == -1)
|
||||
{
|
||||
var min = ((1 - _edgeOffset) - _thickness) * USize * 0.5f;
|
||||
var max = (1 - _edgeOffset) * USize * 0.5f;
|
||||
var val = ((USize * 0.5f) - location).Length;
|
||||
if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside)
|
||||
{
|
||||
UpdateAngle(ref location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (_materialInstance == null)
|
||||
return base.OnMouseDown(location, button);
|
||||
|
||||
var min = ((1 - _edgeOffset) - _thickness) * USize * 0.5f;
|
||||
var max = (1 - _edgeOffset) * USize * 0.5f;
|
||||
var val = ((USize * 0.5f) - location).Length;
|
||||
var c = val < min && CenterAsButton;
|
||||
var selected = (int)_selectedSegment;
|
||||
selected++;
|
||||
if (Mathf.IsInRange(val, min, max) || c)
|
||||
{
|
||||
_highlightSegment = c ? 0 : selected;
|
||||
UpdateSelectionColor();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_highlightSegment = -1;
|
||||
UpdateSelectionColor();
|
||||
}
|
||||
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (_materialInstance == null)
|
||||
return base.OnMouseDown(location, button);
|
||||
|
||||
if (_highlightSegment >= 0)
|
||||
{
|
||||
Selected?.Invoke(_highlightSegment);
|
||||
_highlightSegment = -1;
|
||||
UpdateSelectionColor();
|
||||
var min = ((1 - _edgeOffset) - _thickness) * USize * 0.5f;
|
||||
var max = (1 - _edgeOffset) * USize * 0.5f;
|
||||
var val = ((USize * 0.5f) - location).Length;
|
||||
if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside)
|
||||
{
|
||||
UpdateAngle(ref location);
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
if (_materialInstance == null)
|
||||
return;
|
||||
|
||||
_selectedSegment = 0;
|
||||
_highlightSegment = -1;
|
||||
Selected?.Invoke(_highlightSegment);
|
||||
UpdateSelectionColor();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnChildrenChanged()
|
||||
{
|
||||
_segmentCount = 0;
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i] is Image)
|
||||
{
|
||||
_segmentCount++;
|
||||
}
|
||||
}
|
||||
_materialIsDirty = true;
|
||||
|
||||
base.OnChildrenChanged();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void PerformLayout(bool force = false)
|
||||
{
|
||||
var sa = -1.0f / _segmentCount * Mathf.TwoPi;
|
||||
var midp = USize * 0.5f;
|
||||
var mp = ((1 - _edgeOffset) - (_thickness * 0.5f)) * midp;
|
||||
float f = 0;
|
||||
if (_segmentCount % 2 != 0)
|
||||
{
|
||||
f += sa * 0.5f;
|
||||
}
|
||||
if (_materialInstance == null)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
Children[i].Center = Rotate2D(new Float2(0, mp), f) + midp;
|
||||
f += sa;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i] is Image)
|
||||
{
|
||||
Children[i].Center = Rotate2D(new Float2(0, mp), f) + midp;
|
||||
f += sa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.PerformLayout(force);
|
||||
}
|
||||
|
||||
private void UpdateSelectionColor()
|
||||
{
|
||||
Color color;
|
||||
if (_highlightSegment == -1)
|
||||
{
|
||||
color = _foregroundColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CenterAsButton)
|
||||
{
|
||||
color = _highlightSegment > 0 ? SelectionColor : _foregroundColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = SelectionColor;
|
||||
}
|
||||
}
|
||||
_materialInstance.SetParameterValue("RadialMenu_SelectionColor", color);
|
||||
}
|
||||
|
||||
private void UpdateAngle(ref Float2 location)
|
||||
{
|
||||
var size = new Float2(USize);
|
||||
var p = (size * 0.5f) - location;
|
||||
var sa = (1.0f / _segmentCount) * Mathf.TwoPi;
|
||||
_angle = Mathf.Atan2(p.X, p.Y);
|
||||
_angle = Mathf.Ceil((_angle - (sa * 0.5f)) / sa) * sa;
|
||||
_selectedSegment = _angle;
|
||||
_selectedSegment = Mathf.RoundToInt((_selectedSegment < 0 ? Mathf.TwoPi + _selectedSegment : _selectedSegment) / sa);
|
||||
if (float.IsNaN(_angle) || float.IsInfinity(_angle))
|
||||
_angle = 0;
|
||||
_materialInstance.SetParameterValue("RadialMenu_Rotation", -_angle + Mathf.Pi);
|
||||
}
|
||||
|
||||
private static Float2 Rotate2D(Float2 point, float angle)
|
||||
{
|
||||
return new Float2(Mathf.Cos(angle) * point.X + Mathf.Sin(angle) * point.Y,
|
||||
Mathf.Cos(angle) * point.Y - Mathf.Sin(angle) * point.X);
|
||||
}
|
||||
}
|
||||
}
|
||||
151
Source/Shaders/Editor/Grid.shader
Normal file
151
Source/Shaders/Editor/Grid.shader
Normal file
@@ -0,0 +1,151 @@
|
||||
// Implementation based on:
|
||||
// "The Best Darn Grid Shader (Yet)", Medium, Oct 2023
|
||||
// Ben Golus
|
||||
// https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8#3e73
|
||||
|
||||
#define USE_FORWARD true;
|
||||
|
||||
#include "./Flax/Common.hlsl"
|
||||
|
||||
META_CB_BEGIN(0, Data)
|
||||
float4x4 WorldMatrix;
|
||||
float4x4 ViewProjectionMatrix;
|
||||
float4 GridColor;
|
||||
float3 ViewPos;
|
||||
float Far;
|
||||
float3 Padding;
|
||||
float GridSize;
|
||||
META_CB_END
|
||||
|
||||
// Geometry data passed to the vertex shader
|
||||
struct ModelInput
|
||||
{
|
||||
float3 Position : POSITION;
|
||||
};
|
||||
|
||||
// Interpolants passed from the vertex shader
|
||||
struct VertexOutput
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 TexCoord : TEXCOORD0;
|
||||
float3 WorldPosition : TEXCOORD1;
|
||||
};
|
||||
|
||||
// Interpolants passed to the pixel shader
|
||||
struct PixelInput
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
noperspective float2 TexCoord : TEXCOORD0;
|
||||
float3 WorldPosition : TEXCOORD1;
|
||||
};
|
||||
|
||||
// Vertex shader function for grid rendering
|
||||
META_VS(true, FEATURE_LEVEL_ES2)
|
||||
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
|
||||
META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true)
|
||||
VertexOutput VS_Grid(ModelInput input)
|
||||
{
|
||||
VertexOutput output;
|
||||
output.WorldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz;
|
||||
output.Position = mul(float4(input.Position.xyz, 1), ViewProjectionMatrix);
|
||||
return output;
|
||||
}
|
||||
|
||||
float invLerp(float from, float to, float value)
|
||||
{
|
||||
return (value - from) / (to - from);
|
||||
}
|
||||
|
||||
float remap(float origFrom, float origTo, float targetFrom, float targetTo, float value)
|
||||
{
|
||||
float rel = invLerp(origFrom, origTo, value);
|
||||
return lerp(targetFrom, targetTo, rel);
|
||||
}
|
||||
|
||||
float ddLength(float a)
|
||||
{
|
||||
return length(float2(ddx(a), ddy(a)));
|
||||
}
|
||||
|
||||
float GetLine(float pos, float scale, float thickness)
|
||||
{
|
||||
float lineWidth = thickness;
|
||||
float coord = (pos * 0.01) * scale;
|
||||
|
||||
float2 uvDDXY = float2(ddx(coord), ddy(coord));
|
||||
|
||||
float deriv = float(length(uvDDXY.xy));
|
||||
float drawWidth = clamp(lineWidth, deriv, 0.5);
|
||||
float lineAA = deriv * 1.5;
|
||||
float gridUV = abs(coord);
|
||||
float grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
|
||||
grid2 *= saturate(lineWidth / drawWidth);
|
||||
grid2 = lerp(grid2, lineWidth, saturate(deriv * 2.0 - 1.0));
|
||||
|
||||
float grid = lerp(grid2, 1.0, grid2);
|
||||
return grid;
|
||||
}
|
||||
|
||||
float GetGrid(float3 pos, float scale, float thickness)
|
||||
{
|
||||
float lineWidth = thickness;
|
||||
float2 coord = (pos.xz * 0.01) * scale;
|
||||
|
||||
float4 uvDDXY = float4(ddx(coord), ddy(coord));
|
||||
|
||||
float2 deriv = float2(length(uvDDXY.xz), length(uvDDXY.yw));
|
||||
float2 drawWidth = clamp(lineWidth, deriv, 0.5);
|
||||
float2 lineAA = deriv * 1.5;
|
||||
float2 gridUV = 1.0 - abs(frac(coord) * 2.0 - 1.0);
|
||||
float2 grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
|
||||
grid2 *= saturate(lineWidth / drawWidth);
|
||||
grid2 = lerp(grid2, lineWidth, saturate(deriv * 2.0 - 1.0));
|
||||
|
||||
float grid = lerp(grid2.x, 1.0, grid2.y);
|
||||
return grid;
|
||||
}
|
||||
|
||||
float4 GetColor(float3 pos, float scale)
|
||||
{
|
||||
float dist = 1 - saturate(distance(float3(ViewPos.x, 0, ViewPos.z), pos) / GridSize);
|
||||
|
||||
// Line width
|
||||
float g1LW = 0.01;
|
||||
// Major line Z
|
||||
float l1 = GetLine(pos.x, 1, g1LW * 2);
|
||||
// Major line X
|
||||
float l2 = GetLine(pos.z, 1, g1LW);
|
||||
|
||||
// Main grid
|
||||
float g1 = GetGrid(pos, 1, g1LW * 0.8);
|
||||
float g2 = GetGrid(pos, 2, g1LW * 0.4);
|
||||
float g3 = GetGrid(pos, 0.1, g1LW * 2);
|
||||
|
||||
float camFadeLarge = clamp(invLerp(2500, 4000, abs(ViewPos.y)),0, g3);
|
||||
g3 *= camFadeLarge;
|
||||
|
||||
float g4 = GetGrid(pos, 10, g1LW);
|
||||
float camFadeTiny = clamp(invLerp(150, 100, abs(ViewPos.y)), 0, g4);
|
||||
g4 *= camFadeTiny;
|
||||
|
||||
float grid = 0;
|
||||
grid = max(l1, l2);
|
||||
grid = max(grid, g1);
|
||||
grid = max(grid, g2);
|
||||
grid = max(grid, g3);
|
||||
grid = max(grid, g4);
|
||||
|
||||
float4 color = grid * GridColor;
|
||||
color = lerp(color, float4(1,0,0,1), l2);
|
||||
color = lerp(color, float4(0,0,1,1), l1);
|
||||
color *= dist;
|
||||
return color;
|
||||
}
|
||||
|
||||
// Pixel shader function for grid rendering
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Grid(PixelInput input) : SV_Target
|
||||
{
|
||||
float4 color = GetColor(input.WorldPosition, 1);
|
||||
return color;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
//#define AUTO_DOC_TOOLTIPS
|
||||
//#define MARSHALLER_FULL_NAME
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -582,8 +583,13 @@ namespace Flax.Build.Bindings
|
||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ManagedHandleMarshaller))";
|
||||
else if (FindApiTypeInfo(buildData, functionInfo.ReturnType, caller)?.IsInterface ?? false)
|
||||
{
|
||||
var apiType = FindApiTypeInfo(buildData, functionInfo.ReturnType, caller);
|
||||
var fullReturnValueType = returnValueType;
|
||||
if (!string.IsNullOrEmpty(apiType?.Namespace))
|
||||
fullReturnValueType = $"{apiType.Namespace}.Interop.{returnValueType}";
|
||||
|
||||
// Interfaces are not supported by NativeMarshallingAttribute, marshal the parameter
|
||||
returnMarshalType = $"MarshalUsing(typeof({returnValueType}Marshaller))";
|
||||
returnMarshalType = $"MarshalUsing(typeof({fullReturnValueType}Marshaller))";
|
||||
}
|
||||
else if (functionInfo.ReturnType.Type == "MonoArray" || functionInfo.ReturnType.Type == "MArray")
|
||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))";
|
||||
@@ -941,10 +947,11 @@ namespace Flax.Build.Bindings
|
||||
contents.AppendLine();
|
||||
|
||||
// Namespace begin
|
||||
string interopNamespace = "";
|
||||
if (!string.IsNullOrEmpty(classInfo.Namespace))
|
||||
{
|
||||
contents.AppendFormat("namespace ");
|
||||
contents.AppendLine(classInfo.Namespace);
|
||||
interopNamespace = $"{classInfo.Namespace}.Interop";
|
||||
contents.AppendLine($"namespace {classInfo.Namespace}");
|
||||
contents.AppendLine("{");
|
||||
indent += " ";
|
||||
}
|
||||
@@ -956,10 +963,18 @@ namespace Flax.Build.Bindings
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, useUnmanaged);
|
||||
#if USE_NETCORE
|
||||
string marshallerName = "";
|
||||
string marshallerFullName = "";
|
||||
if (!classInfo.IsStatic)
|
||||
{
|
||||
marshallerName = classInfo.Name + "Marshaller";
|
||||
contents.Append(indent).AppendLine($"[NativeMarshalling(typeof({marshallerName}))]");
|
||||
#if MARSHALLER_FULL_NAME
|
||||
marshallerFullName = !string.IsNullOrEmpty(interopNamespace) ? $"{interopNamespace}.{marshallerName}" : marshallerName;
|
||||
#else
|
||||
if (!string.IsNullOrEmpty(interopNamespace))
|
||||
CSharpUsedNamespaces.Add(interopNamespace);
|
||||
marshallerFullName = marshallerName;
|
||||
#endif
|
||||
contents.Append(indent).AppendLine($"[NativeMarshalling(typeof({marshallerFullName}))]");
|
||||
}
|
||||
#endif
|
||||
contents.Append(indent).Append(GenerateCSharpAccessLevel(classInfo.Access));
|
||||
@@ -1384,6 +1399,13 @@ namespace Flax.Build.Bindings
|
||||
#if USE_NETCORE
|
||||
if (!string.IsNullOrEmpty(marshallerName))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(interopNamespace))
|
||||
{
|
||||
contents.AppendLine("}");
|
||||
contents.AppendLine($"namespace {interopNamespace}");
|
||||
contents.AppendLine("{");
|
||||
}
|
||||
|
||||
contents.AppendLine();
|
||||
contents.AppendLine(string.Join("\n" + indent, (indent + $$"""
|
||||
/// <summary>
|
||||
@@ -1392,15 +1414,15 @@ namespace Flax.Build.Bindings
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerName}}))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
|
||||
{{GenerateCSharpAccessLevel(classInfo.Access)}}static class {{marshallerName}}
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
@@ -1454,64 +1476,57 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
contents.AppendLine();
|
||||
|
||||
// Namespace begin
|
||||
if (!string.IsNullOrEmpty(structureInfo.Namespace))
|
||||
{
|
||||
contents.AppendFormat("namespace ");
|
||||
contents.AppendLine(structureInfo.Namespace);
|
||||
contents.AppendLine("{");
|
||||
indent += " ";
|
||||
}
|
||||
#if USE_NETCORE
|
||||
// Generate blittable structure
|
||||
string structNativeMarshaling = "";
|
||||
if (UseCustomMarshalling(buildData, structureInfo, structureInfo))
|
||||
{
|
||||
// Namespace begin
|
||||
string interopNamespace = "";
|
||||
if (!string.IsNullOrEmpty(structureInfo.Namespace))
|
||||
{
|
||||
interopNamespace = $"{structureInfo.Namespace}.Interop";
|
||||
contents.AppendLine($"namespace {interopNamespace}");
|
||||
contents.AppendLine("{");
|
||||
indent += " ";
|
||||
}
|
||||
|
||||
// NOTE: Permanent FlaxEngine.Object GCHandles must not be released when marshalling from native to managed.
|
||||
|
||||
string marshallerName = structureInfo.Name + "Marshaller";
|
||||
structNativeMarshaling = $"[NativeMarshalling(typeof({marshallerName}))]";
|
||||
|
||||
contents.Append(indent).AppendLine($"/// <summary>");
|
||||
contents.Append(indent).AppendLine($"/// Marshaller for type <see cref=\"{structureInfo.Name}\"/>.");
|
||||
contents.Append(indent).AppendLine($"/// </summary>");
|
||||
if (buildData.Target != null & buildData.Target.IsEditor)
|
||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedIn, typeof({marshallerName}.ManagedToNative))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedOut, typeof({marshallerName}.ManagedToNative))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementIn, typeof({marshallerName}.ManagedToNative))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedOut, typeof({marshallerName}.NativeToManaged))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedIn, typeof({marshallerName}.NativeToManaged))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementOut, typeof({marshallerName}.NativeToManaged))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedRef, typeof({marshallerName}.Bidirectional))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedRef, typeof({marshallerName}.Bidirectional))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementRef, typeof({marshallerName}))]");
|
||||
contents.Append(indent).AppendLine($"{GenerateCSharpAccessLevel(structureInfo.Access)}static unsafe class {marshallerName}");
|
||||
contents.Append(indent).AppendLine("{");
|
||||
contents.AppendLine("#pragma warning disable 1591");
|
||||
|
||||
indent += " ";
|
||||
#if MARSHALLER_FULL_NAME
|
||||
string marshallerFullName = !string.IsNullOrEmpty(interopNamespace) ? $"{interopNamespace}.{marshallerName}" : marshallerName;
|
||||
#else
|
||||
if (!string.IsNullOrEmpty(interopNamespace))
|
||||
CSharpUsedNamespaces.Add(interopNamespace);
|
||||
string marshallerFullName = marshallerName;
|
||||
#endif
|
||||
structNativeMarshaling = $"[NativeMarshalling(typeof({marshallerFullName}))]";
|
||||
|
||||
var toManagedContent = GetStringBuilder();
|
||||
var toNativeContent = GetStringBuilder();
|
||||
var freeContents = GetStringBuilder();
|
||||
var freeContents2 = GetStringBuilder();
|
||||
var structContents = GetStringBuilder();
|
||||
|
||||
{
|
||||
// Native struct begin
|
||||
// TODO: skip using this utility structure if the auto-generated C# struct is already the same as XXXInternal here below
|
||||
var structIndent = "";
|
||||
if (buildData.Target != null & buildData.Target.IsEditor)
|
||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||
contents.Append(indent).AppendLine("[StructLayout(LayoutKind.Sequential)]");
|
||||
contents.Append(indent).Append("public struct ").Append(structureInfo.Name).Append("Internal");
|
||||
structContents.Append(structIndent).AppendLine("[HideInEditor]");
|
||||
structContents.Append(structIndent).AppendLine("[StructLayout(LayoutKind.Sequential)]");
|
||||
structContents.Append(structIndent).Append("public struct ").Append(structureInfo.Name).Append("Internal");
|
||||
if (structureInfo.BaseType != null && structureInfo.IsPod)
|
||||
contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = structureInfo.BaseType.Name }, structureInfo));
|
||||
contents.AppendLine();
|
||||
contents.Append(indent + "{");
|
||||
indent += " ";
|
||||
structContents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = structureInfo.BaseType.Name }, structureInfo));
|
||||
structContents.AppendLine();
|
||||
structContents.Append(structIndent + "{");
|
||||
structIndent += " ";
|
||||
|
||||
toNativeContent.Append($"var unmanaged = new {structureInfo.Name}Internal();").AppendLine();
|
||||
toManagedContent.Append($"var managed = new {structureInfo.Name}();").AppendLine();
|
||||
|
||||
contents.AppendLine();
|
||||
structContents.AppendLine();
|
||||
foreach (var fieldInfo in structureInfo.Fields)
|
||||
{
|
||||
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
|
||||
@@ -1528,7 +1543,7 @@ namespace Flax.Build.Bindings
|
||||
else
|
||||
originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
|
||||
|
||||
contents.Append(indent).Append("public ");
|
||||
structContents.Append(structIndent).Append("public ");
|
||||
|
||||
var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo);
|
||||
bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
|
||||
@@ -1540,7 +1555,7 @@ namespace Flax.Build.Bindings
|
||||
if (GenerateCSharpUseFixedBuffer(originalType))
|
||||
{
|
||||
// Use fixed statement with primitive types of buffers
|
||||
contents.Append($"fixed {originalType} {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}];").AppendLine();
|
||||
structContents.Append($"fixed {originalType} {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}];").AppendLine();
|
||||
|
||||
// Copy fixed-size array
|
||||
toManagedContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(managed.{fieldInfo.Name}0), new IntPtr(unmanaged.{fieldInfo.Name}0), sizeof({originalType}) * {fieldInfo.Type.ArraySize}ul);");
|
||||
@@ -1550,12 +1565,12 @@ namespace Flax.Build.Bindings
|
||||
#endif
|
||||
{
|
||||
// Padding in structs for fixed-size array
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name).Append("0;").AppendLine();
|
||||
structContents.Append(type).Append(' ').Append(fieldInfo.Name).Append("0;").AppendLine();
|
||||
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
|
||||
{
|
||||
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
|
||||
contents.Append(indent).Append("public ");
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
|
||||
GenerateCSharpAttributes(buildData, structContents, structIndent, structureInfo, fieldInfo, fieldInfo.IsStatic);
|
||||
structContents.Append(structIndent).Append("public ");
|
||||
structContents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
|
||||
}
|
||||
|
||||
// Copy fixed-size array item one-by-one
|
||||
@@ -1601,8 +1616,7 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
//else if (type == "Guid")
|
||||
// type = "GuidNative";
|
||||
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name).Append(';').AppendLine();
|
||||
structContents.Append($"{type} {fieldInfo.Name};").Append(type == "IntPtr" ? $" // {originalType}" : "").AppendLine();
|
||||
}
|
||||
|
||||
// Generate struct constructor/getter and deconstructor/setter function
|
||||
@@ -1649,13 +1663,13 @@ namespace Flax.Build.Bindings
|
||||
if (internalType)
|
||||
{
|
||||
// Marshal blittable array elements back to original non-blittable elements
|
||||
string originalElementTypeMarshaller = $"{originalElementType}Marshaller";
|
||||
string originalElementTypeMarshaller = (!string.IsNullOrEmpty(apiType?.Namespace) ? $"{apiType?.Namespace}.Interop." : "") + $"{originalElementType}Marshaller";
|
||||
string originalElementTypeName = originalElementType.Substring(originalElementType.LastIndexOf('.') + 1); // Strip namespace
|
||||
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementTypeName}Internal";
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.ConvertArray((Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ConvertArray(managed.{fieldInfo.Name}, {originalElementTypeMarshaller}.ToNative)), GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.NativeToManaged.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
}
|
||||
else if (fieldInfo.Type.GenericArgs[0].IsObjectRef)
|
||||
{
|
||||
@@ -1706,7 +1720,7 @@ namespace Flax.Build.Bindings
|
||||
toManagedContent.AppendLine($"{internalTypeMarshaller}.ToManaged(unmanaged.{fieldInfo.Name});");
|
||||
toNativeContent.AppendLine($"{internalTypeMarshaller}.ToNative(managed.{fieldInfo.Name});");
|
||||
freeContents.AppendLine($"{internalTypeMarshaller}.Free(unmanaged.{fieldInfo.Name});");
|
||||
freeContents2.AppendLine($"{internalTypeMarshaller}.Free(unmanaged.{fieldInfo.Name});");
|
||||
freeContents2.AppendLine($"{internalTypeMarshaller}.NativeToManaged.Free(unmanaged.{fieldInfo.Name});");
|
||||
}
|
||||
/*else if (originalType == "Guid")
|
||||
{
|
||||
@@ -1721,73 +1735,109 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
|
||||
// Native struct end
|
||||
indent = indent.Substring(0, indent.Length - 4);
|
||||
contents.AppendLine(indent + "}").AppendLine();
|
||||
structIndent = structIndent.Substring(0, structIndent.Length - 4);
|
||||
structContents.AppendLine(structIndent + "}").AppendLine();
|
||||
|
||||
toNativeContent.Append("return unmanaged;");
|
||||
toManagedContent.Append("return managed;");
|
||||
}
|
||||
|
||||
var indent2 = indent + " ";
|
||||
var indent3 = indent2 + " ";
|
||||
contents.AppendLine(string.Join("\n" + indent, (indent + $$"""
|
||||
/// <summary>
|
||||
/// Marshaller for type <see cref="{{structureInfo.Name}}"/>.
|
||||
/// </summary>
|
||||
{{InsertHideInEditorSection()}}
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
|
||||
{{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}}
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
{{structContents.Replace("\n", "\n" + " ").ToString().TrimEnd()}}
|
||||
|
||||
// NativeToManaged stateless shape
|
||||
// NOTE: GCHandles of FlaxEngine.Object must not be released in this case
|
||||
if (buildData.Target != null && buildData.Target.IsEditor)
|
||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||
contents.Append(indent).AppendLine("public static class NativeToManaged").Append(indent).AppendLine("{");
|
||||
contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);");
|
||||
contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);");
|
||||
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged)");
|
||||
contents.Append(indent2).AppendLine("{").Append(indent3).AppendLine(freeContents2.Replace("\n", "\n" + indent3).ToString().TrimEnd()).Append(indent2).AppendLine("}");
|
||||
contents.Append(indent).AppendLine("}");
|
||||
{{InsertHideInEditorSection()}}
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{structureInfo.Name}}Internal unmanaged)
|
||||
{
|
||||
{{freeContents2.Replace("\n", "\n" + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
}
|
||||
{{InsertHideInEditorSection()}}
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.Free(unmanaged);
|
||||
}
|
||||
{{InsertHideInEditorSection()}}
|
||||
public struct Bidirectional
|
||||
{
|
||||
{{structureInfo.Name}} managed;
|
||||
{{structureInfo.Name}}Internal unmanaged;
|
||||
public void FromManaged({{structureInfo.Name}} managed) => this.managed = managed;
|
||||
public {{structureInfo.Name}}Internal ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
|
||||
//public void FromUnmanaged({{structureInfo.Name}}Internal unmanaged) { {{marshallerFullName}}.Free(this.unmanaged.Value); this.unmanaged = unmanaged; }
|
||||
public void FromUnmanaged({{structureInfo.Name}}Internal unmanaged) => this.unmanaged = unmanaged;
|
||||
public {{structureInfo.Name}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
|
||||
public void Free() => NativeToManaged.Free(unmanaged);
|
||||
}
|
||||
internal static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => ToManaged(unmanaged);
|
||||
internal static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => ToNative(managed);
|
||||
internal static void Free({{structureInfo.Name}}Internal unmanaged)
|
||||
{
|
||||
{{freeContents.Replace("\n", "\n" + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
|
||||
// ManagedToNative stateless shape
|
||||
if (buildData.Target != null && buildData.Target.IsEditor)
|
||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||
contents.Append(indent).AppendLine($"public static class ManagedToNative").Append(indent).AppendLine("{");
|
||||
contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);");
|
||||
contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);");
|
||||
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged) => {marshallerName}.Free(unmanaged);");
|
||||
contents.Append(indent).AppendLine("}");
|
||||
internal static {{structureInfo.Name}} ToManaged({{structureInfo.Name}}Internal unmanaged)
|
||||
{
|
||||
{{toManagedContent.Replace("\n", "\n" + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
internal static {{structureInfo.Name}}Internal ToNative({{structureInfo.Name}} managed)
|
||||
{
|
||||
{{toNativeContent.Replace("\n", "\n" + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
""").Split(new char[] { '\n' })));
|
||||
|
||||
// Bidirectional stateful shape
|
||||
// NOTE: GCHandles of FlaxEngine.Object must not be released unless they were allocated by this marshaller
|
||||
if (buildData.Target != null && buildData.Target.IsEditor)
|
||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||
contents.Append(indent).AppendLine($"public struct Bidirectional").Append(indent).AppendLine("{");
|
||||
contents.Append(indent2).AppendLine($"{structureInfo.Name} managed;");
|
||||
contents.Append(indent2).AppendLine($"{structureInfo.Name}Internal unmanaged;");
|
||||
contents.Append(indent2).AppendLine($"public void FromManaged({structureInfo.Name} managed) => this.managed = managed;");
|
||||
contents.Append(indent2).AppendLine($"public {structureInfo.Name}Internal ToUnmanaged() {{ unmanaged = {marshallerName}.ToNative(managed); return unmanaged; }}");
|
||||
//contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) {{ {marshallerName}.Free(this.unmanaged.Value); this.unmanaged = unmanaged; }}");
|
||||
contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) => this.unmanaged = unmanaged;");
|
||||
contents.Append(indent2).AppendLine($"public {structureInfo.Name} ToManaged() {{ managed = {marshallerName}.ToManaged(unmanaged); return managed; }}");
|
||||
contents.Append(indent2).AppendLine($"public void Free() => NativeToManaged.Free(unmanaged);");
|
||||
contents.Append(indent).AppendLine("}");
|
||||
|
||||
// Bidirectional stateless shape
|
||||
contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => ToManaged(unmanaged);");
|
||||
contents.Append(indent).AppendLine($"internal static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => ToNative(managed);");
|
||||
contents.Append(indent).AppendLine($"internal static void Free({structureInfo.Name}Internal unmanaged)");
|
||||
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(freeContents.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
|
||||
|
||||
// Managed/native converters
|
||||
contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ToManaged({structureInfo.Name}Internal unmanaged)");
|
||||
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toManagedContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
|
||||
contents.Append(indent).AppendLine($"internal static {structureInfo.Name}Internal ToNative({structureInfo.Name} managed)");
|
||||
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toNativeContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
|
||||
|
||||
contents.AppendLine("#pragma warning restore 1591");
|
||||
indent = indent.Substring(0, indent.Length - 4);
|
||||
contents.Append(indent).AppendLine("}").AppendLine();
|
||||
string InsertHideInEditorSection()
|
||||
{
|
||||
return (buildData.Target != null & buildData.Target.IsEditor) ? $$"""
|
||||
[HideInEditor]
|
||||
""" : "";
|
||||
}
|
||||
|
||||
PutStringBuilder(toManagedContent);
|
||||
PutStringBuilder(toNativeContent);
|
||||
PutStringBuilder(freeContents);
|
||||
PutStringBuilder(freeContents2);
|
||||
PutStringBuilder(structContents);
|
||||
|
||||
// Namespace end
|
||||
if (!string.IsNullOrEmpty(structureInfo.Namespace))
|
||||
{
|
||||
contents.AppendLine("}");
|
||||
indent = indent.Substring(0, indent.Length - 4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Namespace begin
|
||||
if (!string.IsNullOrEmpty(structureInfo.Namespace))
|
||||
{
|
||||
contents.AppendLine($"namespace {structureInfo.Namespace}");
|
||||
contents.AppendLine("{");
|
||||
indent += " ";
|
||||
}
|
||||
|
||||
// Struct docs
|
||||
GenerateCSharpComment(contents, indent, structureInfo.Comment);
|
||||
|
||||
@@ -1979,8 +2029,7 @@ namespace Flax.Build.Bindings
|
||||
// Namespace begin
|
||||
if (!string.IsNullOrEmpty(enumInfo.Namespace))
|
||||
{
|
||||
contents.AppendFormat("namespace ");
|
||||
contents.AppendLine(enumInfo.Namespace);
|
||||
contents.AppendLine($"namespace {enumInfo.Namespace}");
|
||||
contents.AppendLine("{");
|
||||
indent += " ";
|
||||
}
|
||||
@@ -2026,10 +2075,11 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
// Begin
|
||||
contents.AppendLine();
|
||||
string interopNamespace = "";
|
||||
if (!string.IsNullOrEmpty(interfaceInfo.Namespace))
|
||||
{
|
||||
contents.AppendFormat("namespace ");
|
||||
contents.AppendLine(interfaceInfo.Namespace);
|
||||
interopNamespace = $"{interfaceInfo.Namespace}.Interop";
|
||||
contents.AppendLine($"namespace {interfaceInfo.Namespace}");
|
||||
contents.AppendLine("{");
|
||||
indent += " ";
|
||||
}
|
||||
@@ -2096,14 +2146,22 @@ namespace Flax.Build.Bindings
|
||||
|
||||
#if USE_NETCORE
|
||||
{
|
||||
if (!string.IsNullOrEmpty(interopNamespace))
|
||||
{
|
||||
contents.AppendLine("}");
|
||||
contents.AppendLine($"namespace {interopNamespace}");
|
||||
contents.AppendLine("{");
|
||||
}
|
||||
|
||||
string marshallerName = interfaceInfo.Name + "Marshaller";
|
||||
string marshallerFullName = !string.IsNullOrEmpty(interopNamespace) ? $"{interopNamespace}.{marshallerName}" : marshallerName;
|
||||
contents.AppendLine();
|
||||
contents.Append(indent).AppendLine("/// <summary>");
|
||||
contents.Append(indent).AppendLine($"/// Marshaller for type <see cref=\"{interfaceInfo.Name}\"/>.");
|
||||
contents.Append(indent).AppendLine("/// </summary>");
|
||||
if (buildData.Target != null & buildData.Target.IsEditor)
|
||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({interfaceInfo.Name}), MarshalMode.Default, typeof({marshallerName}))]");
|
||||
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({interfaceInfo.Name}), MarshalMode.Default, typeof({marshallerFullName}))]");
|
||||
contents.Append(indent).AppendLine($"public static class {marshallerName}");
|
||||
contents.Append(indent).AppendLine("{");
|
||||
contents.AppendLine("#pragma warning disable 1591");
|
||||
|
||||
Reference in New Issue
Block a user