Merge remote-tracking branch 'origin/master' into 1.11
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
@@ -16,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
private int _layersCount;
|
||||
private List<CheckBox> _checkBoxes;
|
||||
private VerticalPanel _upperRightCell;
|
||||
private VerticalPanel _bottomLeftCell;
|
||||
private UniformGridPanel _grid;
|
||||
private Border _horizontalHighlight;
|
||||
private Border _verticalHighlight;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
|
||||
@@ -37,12 +41,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
_horizontalHighlight = new Border()
|
||||
{
|
||||
Parent = panel,
|
||||
BorderColor = style.Foreground,
|
||||
BorderWidth = 1.0f,
|
||||
Visible = false,
|
||||
};
|
||||
|
||||
_verticalHighlight = new Border()
|
||||
{
|
||||
Parent = panel,
|
||||
BorderColor = style.Foreground,
|
||||
BorderWidth = 1.0f,
|
||||
Visible = false,
|
||||
};
|
||||
|
||||
var upperLeftCell = new Label
|
||||
{
|
||||
Parent = gridPanel,
|
||||
};
|
||||
|
||||
var upperRightCell = new VerticalPanel
|
||||
_upperRightCell = new VerticalPanel
|
||||
{
|
||||
ClipChildren = false,
|
||||
Pivot = new Float2(0.00001f, 0.0f),
|
||||
@@ -54,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
Parent = gridPanel,
|
||||
};
|
||||
|
||||
var bottomLeftCell = new VerticalPanel
|
||||
_bottomLeftCell = new VerticalPanel
|
||||
{
|
||||
Pivot = Float2.Zero,
|
||||
Spacing = 0,
|
||||
@@ -63,7 +84,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
Parent = gridPanel,
|
||||
};
|
||||
|
||||
var grid = new UniformGridPanel(0)
|
||||
_grid = new UniformGridPanel(0)
|
||||
{
|
||||
SlotsHorizontally = layersCount,
|
||||
SlotsVertically = layersCount,
|
||||
@@ -74,13 +95,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
int layerIndex = 0;
|
||||
for (; layerIndex < layerNames.Length; layerIndex++)
|
||||
{
|
||||
upperRightCell.AddChild(new Label
|
||||
_upperRightCell.AddChild(new Label
|
||||
{
|
||||
Height = labelsHeight,
|
||||
Text = layerNames[layerNames.Length - layerIndex - 1],
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
});
|
||||
bottomLeftCell.AddChild(new Label
|
||||
_bottomLeftCell.AddChild(new Label
|
||||
{
|
||||
Height = labelsHeight,
|
||||
Text = layerNames[layerIndex],
|
||||
@@ -90,13 +111,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
for (; layerIndex < layersCount; layerIndex++)
|
||||
{
|
||||
string name = "Layer " + layerIndex;
|
||||
upperRightCell.AddChild(new Label
|
||||
_upperRightCell.AddChild(new Label
|
||||
{
|
||||
Height = labelsHeight,
|
||||
Text = name,
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
});
|
||||
bottomLeftCell.AddChild(new Label
|
||||
_bottomLeftCell.AddChild(new Label
|
||||
{
|
||||
Height = labelsHeight,
|
||||
Text = name,
|
||||
@@ -118,7 +139,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var box = new CheckBox(0, 0, true)
|
||||
{
|
||||
Tag = new Float2(_layersCount - column - 1, row),
|
||||
Parent = grid,
|
||||
Parent = _grid,
|
||||
Checked = GetBit(column, row),
|
||||
};
|
||||
box.StateChanged += OnCheckBoxChanged;
|
||||
@@ -126,7 +147,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
for (; column < layersCount; column++)
|
||||
{
|
||||
grid.AddChild(new Label());
|
||||
_grid.AddChild(new Label());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +162,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
int selectedColumn = -1;
|
||||
int selectedRow = -1;
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
bool mouseOverGrid = _grid.IsMouseOver;
|
||||
|
||||
// Only hide highlights if mouse is not over the grid to reduce flickering
|
||||
if (!mouseOverGrid)
|
||||
{
|
||||
_horizontalHighlight.Visible = false;
|
||||
_verticalHighlight.Visible = false;
|
||||
}
|
||||
|
||||
// Sync check boxes
|
||||
for (int i = 0; i < _checkBoxes.Count; i++)
|
||||
{
|
||||
@@ -148,6 +181,39 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
int column = (int)((Float2)box.Tag).X;
|
||||
int row = (int)((Float2)box.Tag).Y;
|
||||
box.Checked = GetBit(column, row);
|
||||
|
||||
if (box.IsMouseOver)
|
||||
{
|
||||
selectedColumn = column;
|
||||
selectedRow = row;
|
||||
|
||||
_horizontalHighlight.X = _grid.X - _bottomLeftCell.Width;
|
||||
_horizontalHighlight.Y = _grid.Y + box.Y;
|
||||
_horizontalHighlight.Width = _bottomLeftCell.Width + box.Width + box.X;
|
||||
_horizontalHighlight.Height = box.Height;
|
||||
_horizontalHighlight.Visible = true;
|
||||
|
||||
_verticalHighlight.X = _grid.X + box.X;
|
||||
_verticalHighlight.Y = _grid.Y - _upperRightCell.Height;
|
||||
_verticalHighlight.Width = box.Width;
|
||||
_verticalHighlight.Height = _upperRightCell.Height + box.Height + box.Y;
|
||||
_verticalHighlight.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _checkBoxes.Count; i++)
|
||||
{
|
||||
var box = _checkBoxes[i];
|
||||
int column = (int)((Float2)box.Tag).X;
|
||||
int row = (int)((Float2)box.Tag).Y;
|
||||
|
||||
if (!mouseOverGrid)
|
||||
box.ImageColor = style.BorderSelected;
|
||||
else if (selectedColumn > -1 && selectedRow > -1)
|
||||
{
|
||||
bool isRowOrColumn = column == selectedColumn || row == selectedRow;
|
||||
box.ImageColor = style.BorderSelected * (isRowOrColumn ? 1.2f : 0.75f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -670,6 +670,8 @@ namespace FlaxEditor
|
||||
{
|
||||
FlaxEngine.Networking.NetworkManager.Stop(); // Shutdown any multiplayer from playmode
|
||||
PlayModeEnding?.Invoke();
|
||||
for (int i = 0; i < _modules.Count; i++)
|
||||
_modules[i].OnPlayEnding();
|
||||
}
|
||||
|
||||
internal void OnPlayEnd()
|
||||
|
||||
@@ -299,6 +299,7 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||
}
|
||||
}
|
||||
else if (Button1Rect.Contains(location))
|
||||
@@ -312,6 +313,7 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||
}
|
||||
else if (Button3Rect.Contains(location))
|
||||
{
|
||||
|
||||
@@ -563,8 +563,17 @@ namespace FlaxEditor.GUI
|
||||
case KeyboardKeys.Escape:
|
||||
Hide();
|
||||
return true;
|
||||
case KeyboardKeys.Backspace:
|
||||
// Alow the user to quickly focus the searchbar
|
||||
if (_searchBox != null && !_searchBox.IsFocused)
|
||||
{
|
||||
_searchBox.Focus();
|
||||
_searchBox.SelectAll();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case KeyboardKeys.ArrowDown:
|
||||
{
|
||||
case KeyboardKeys.ArrowUp:
|
||||
if (RootWindow.FocusedControl == null)
|
||||
{
|
||||
// Focus search box if nothing is focused
|
||||
@@ -572,39 +581,17 @@ namespace FlaxEditor.GUI
|
||||
return true;
|
||||
}
|
||||
|
||||
// Focus the first visible item or then next one
|
||||
// Get the next item
|
||||
var items = GetVisibleItems();
|
||||
var focusedIndex = items.IndexOf(focusedItem);
|
||||
if (focusedIndex == -1)
|
||||
focusedIndex = -1;
|
||||
if (focusedIndex + 1 < items.Count)
|
||||
{
|
||||
var item = items[focusedIndex + 1];
|
||||
item.Focus();
|
||||
_scrollPanel.ScrollViewTo(item);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyboardKeys.ArrowUp:
|
||||
if (focusedItem != null)
|
||||
{
|
||||
// Focus the previous visible item or the search box
|
||||
var items = GetVisibleItems();
|
||||
var focusedIndex = items.IndexOf(focusedItem);
|
||||
if (focusedIndex == 0)
|
||||
{
|
||||
_searchBox?.Focus();
|
||||
}
|
||||
else if (focusedIndex > 0)
|
||||
{
|
||||
var item = items[focusedIndex - 1];
|
||||
item.Focus();
|
||||
_scrollPanel.ScrollViewTo(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
|
||||
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, items.Count - 1);
|
||||
var nextItem = items[nextIndex];
|
||||
|
||||
// Focus the next item
|
||||
nextItem.Focus();
|
||||
_scrollPanel.ScrollViewTo(nextItem);
|
||||
return true;
|
||||
case KeyboardKeys.Return:
|
||||
if (focusedItem != null)
|
||||
{
|
||||
|
||||
@@ -266,6 +266,19 @@ namespace FlaxEditor.GUI
|
||||
return AddChild(new MainMenuButton(text));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or adds a button.
|
||||
/// </summary>
|
||||
/// <param name="text">The button text</param>
|
||||
/// <returns>The existing or created button control.</returns>
|
||||
public MainMenuButton GetOrAddButton(string text)
|
||||
{
|
||||
MainMenuButton result = GetButton(text);
|
||||
if (result == null)
|
||||
result = AddButton(text);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button.
|
||||
/// </summary>
|
||||
|
||||
@@ -122,6 +122,14 @@ namespace FlaxEditor.GUI
|
||||
return this;
|
||||
}
|
||||
|
||||
private void OnClicked()
|
||||
{
|
||||
if (AutoCheck)
|
||||
Checked = !Checked;
|
||||
Clicked?.Invoke();
|
||||
(Parent as ToolStrip)?.OnButtonClicked(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
@@ -196,11 +204,7 @@ namespace FlaxEditor.GUI
|
||||
if (button == MouseButton.Left && _primaryMouseDown)
|
||||
{
|
||||
_primaryMouseDown = false;
|
||||
if (AutoCheck)
|
||||
Checked = !Checked;
|
||||
Clicked?.Invoke();
|
||||
(Parent as ToolStrip)?.OnButtonClicked(this);
|
||||
|
||||
OnClicked();
|
||||
return true;
|
||||
}
|
||||
if (button == MouseButton.Right && _secondaryMouseDown)
|
||||
@@ -215,6 +219,18 @@ namespace FlaxEditor.GUI
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
OnClicked();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
|
||||
@@ -73,6 +73,11 @@ namespace FlaxEditor.GUI.Tree
|
||||
/// </summary>
|
||||
public bool DrawRootTreeLine = true;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the deferred layout operation was performed.
|
||||
/// </summary>
|
||||
public event Action AfterDeferredLayout;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the margin for the child tree nodes.
|
||||
/// </summary>
|
||||
@@ -375,6 +380,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
if (_deferLayoutUpdate)
|
||||
{
|
||||
base.PerformLayout();
|
||||
AfterDeferredLayout?.Invoke();
|
||||
_deferLayoutUpdate = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,13 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when Editor will leave the play mode.
|
||||
/// </summary>
|
||||
public virtual void OnPlayEnding()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when Editor leaves the play mode.
|
||||
/// </summary>
|
||||
|
||||
@@ -1223,6 +1223,13 @@ namespace FlaxEditor.Modules
|
||||
Windows[i].OnPlayBegin();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPlayEnding()
|
||||
{
|
||||
for (int i = 0; i < Windows.Count; i++)
|
||||
Windows[i].OnPlayEnding();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPlayEnd()
|
||||
{
|
||||
|
||||
@@ -139,6 +139,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Common"), EditorOrder(240)]
|
||||
public InputBinding ToggleFullscreen = new InputBinding(KeyboardKeys.F11);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Ctrl+BackQuote")]
|
||||
[EditorDisplay("Common"), EditorOrder(250)]
|
||||
public InputBinding FocusConsoleCommand = new InputBinding(KeyboardKeys.BackQuote, KeyboardKeys.Control);
|
||||
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
@@ -647,5 +651,45 @@ namespace FlaxEditor.Options
|
||||
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Node editors
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+W")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4500)]
|
||||
public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+A")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4510)]
|
||||
public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+S")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4520)]
|
||||
public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+D")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4530)]
|
||||
public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Alt+Shift+W")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4540)]
|
||||
public InputBinding NodesAlignMiddle = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Alt+Shift+S")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4550)]
|
||||
public InputBinding NodesAlignCenter = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Q")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4560)]
|
||||
public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4570)]
|
||||
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4580)]
|
||||
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
43
Source/Editor/Surface/NodeAlignmentType.cs
Normal file
43
Source/Editor/Surface/NodeAlignmentType.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
{
|
||||
/// <summary>
|
||||
/// Node Alignment type
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum NodeAlignmentType
|
||||
{
|
||||
/// <summary>
|
||||
/// Align nodes vertically to top, matching top-most node
|
||||
/// </summary>
|
||||
Top,
|
||||
|
||||
/// <summary>
|
||||
/// Align nodes vertically to middle, using average of all nodes
|
||||
/// </summary>
|
||||
Middle,
|
||||
|
||||
/// <summary>
|
||||
/// Align nodes vertically to bottom, matching bottom-most node
|
||||
/// </summary>
|
||||
Bottom,
|
||||
|
||||
/// <summary>
|
||||
/// Align nodes horizontally to left, matching left-most node
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Align nodes horizontally to center, using average of all nodes
|
||||
/// </summary>
|
||||
Center,
|
||||
|
||||
/// <summary>
|
||||
/// Align nodes horizontally to right, matching right-most node
|
||||
/// </summary>
|
||||
Right,
|
||||
}
|
||||
}
|
||||
@@ -912,7 +912,7 @@ namespace FlaxEditor.Surface
|
||||
/// <inheritdoc />
|
||||
public override bool OnTestTooltipOverControl(ref Float2 location)
|
||||
{
|
||||
return _headerRect.Contains(ref location) && ShowTooltip;
|
||||
return _headerRect.Contains(ref location) && ShowTooltip && !Surface.IsConnecting && !Surface.IsBoxSelecting;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1070,7 +1070,7 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Header
|
||||
var headerColor = style.BackgroundHighlighted;
|
||||
if (_headerRect.Contains(ref _mousePosition))
|
||||
if (_headerRect.Contains(ref _mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting)
|
||||
headerColor *= 1.07f;
|
||||
Render2D.FillRectangle(_headerRect, headerColor);
|
||||
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
|
||||
@@ -1078,7 +1078,8 @@ namespace FlaxEditor.Surface
|
||||
// Close button
|
||||
if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit)
|
||||
{
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey);
|
||||
bool highlightClose = _closeButtonRect.Contains(_mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting;
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, highlightClose ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
|
||||
// Footer
|
||||
@@ -1123,8 +1124,9 @@ namespace FlaxEditor.Surface
|
||||
if (base.OnMouseUp(location, button))
|
||||
return true;
|
||||
|
||||
// Close
|
||||
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
||||
// Close/ delete
|
||||
bool canDelete = !Surface.IsConnecting && !Surface.WasBoxSelecting && !Surface.WasMovingSelection;
|
||||
if (button == MouseButton.Left && canDelete && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
||||
{
|
||||
Surface.Delete(this);
|
||||
return true;
|
||||
|
||||
@@ -191,7 +191,16 @@ namespace FlaxEditor.Surface
|
||||
|
||||
private ContextMenuButton _cmCopyButton;
|
||||
private ContextMenuButton _cmDuplicateButton;
|
||||
private ContextMenuChildMenu _cmFormatNodesMenu;
|
||||
private ContextMenuButton _cmFormatNodesConnectionButton;
|
||||
private ContextMenuButton _cmAlignNodesTopButton;
|
||||
private ContextMenuButton _cmAlignNodesMiddleButton;
|
||||
private ContextMenuButton _cmAlignNodesBottomButton;
|
||||
private ContextMenuButton _cmAlignNodesLeftButton;
|
||||
private ContextMenuButton _cmAlignNodesCenterButton;
|
||||
private ContextMenuButton _cmAlignNodesRightButton;
|
||||
private ContextMenuButton _cmDistributeNodesHorizontallyButton;
|
||||
private ContextMenuButton _cmDistributeNodesVerticallyButton;
|
||||
private ContextMenuButton _cmRemoveNodeConnectionsButton;
|
||||
private ContextMenuButton _cmRemoveBoxConnectionsButton;
|
||||
private readonly Float2 ContextMenuOffset = new Float2(5);
|
||||
@@ -399,8 +408,24 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
menu.AddSeparator();
|
||||
|
||||
_cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => { FormatGraph(SelectedNodes); });
|
||||
_cmFormatNodesConnectionButton.Enabled = CanEdit && HasNodesSelection;
|
||||
_cmFormatNodesMenu = menu.AddChildMenu("Format node(s)");
|
||||
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
|
||||
|
||||
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); });
|
||||
|
||||
_cmFormatNodesMenu.ContextMenu.AddSeparator();
|
||||
_cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", Editor.Instance.Options.Options.Input.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); });
|
||||
_cmAlignNodesMiddleButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align middle", Editor.Instance.Options.Options.Input.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); });
|
||||
_cmAlignNodesBottomButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align bottom", Editor.Instance.Options.Options.Input.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); });
|
||||
|
||||
_cmFormatNodesMenu.ContextMenu.AddSeparator();
|
||||
_cmAlignNodesLeftButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align left", Editor.Instance.Options.Options.Input.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); });
|
||||
_cmAlignNodesCenterButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align center", Editor.Instance.Options.Options.Input.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); });
|
||||
_cmAlignNodesRightButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align right", Editor.Instance.Options.Options.Input.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); });
|
||||
|
||||
_cmFormatNodesMenu.ContextMenu.AddSeparator();
|
||||
_cmDistributeNodesHorizontallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute horizontally", Editor.Instance.Options.Options.Input.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); });
|
||||
_cmDistributeNodesVerticallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute vertically", Editor.Instance.Options.Options.Input.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); });
|
||||
|
||||
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () =>
|
||||
{
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace FlaxEditor.Surface
|
||||
|
||||
_rootControl.DrawComments();
|
||||
|
||||
if (IsSelecting)
|
||||
if (IsBoxSelecting)
|
||||
{
|
||||
DrawSelection();
|
||||
}
|
||||
|
||||
@@ -282,5 +282,122 @@ namespace FlaxEditor.Surface
|
||||
|
||||
return maxOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Align given nodes on a graph using the given alignment type.
|
||||
/// Ignores any potential overlap.
|
||||
/// </summary>
|
||||
/// <param name="nodes">List of nodes</param>
|
||||
/// <param name="alignmentType">Alignemnt type</param>
|
||||
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
|
||||
{
|
||||
if(nodes.Count <= 1)
|
||||
return;
|
||||
|
||||
var undoActions = new List<MoveNodesAction>();
|
||||
var boundingBox = GetNodesBounds(nodes);
|
||||
for(int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var centerY = boundingBox.Center.Y - (nodes[i].Height / 2);
|
||||
var centerX = boundingBox.Center.X - (nodes[i].Width / 2);
|
||||
|
||||
var newLocation = alignmentType switch
|
||||
{
|
||||
NodeAlignmentType.Top => new Float2(nodes[i].Location.X, boundingBox.Top),
|
||||
NodeAlignmentType.Middle => new Float2(nodes[i].Location.X, centerY),
|
||||
NodeAlignmentType.Bottom => new Float2(nodes[i].Location.X, boundingBox.Bottom - nodes[i].Height),
|
||||
|
||||
NodeAlignmentType.Left => new Float2(boundingBox.Left, nodes[i].Location.Y),
|
||||
NodeAlignmentType.Center => new Float2(centerX, nodes[i].Location.Y),
|
||||
NodeAlignmentType.Right => new Float2(boundingBox.Right - nodes[i].Width, nodes[i].Location.Y),
|
||||
|
||||
_ => throw new NotImplementedException($"Unsupported node alignment type: {alignmentType}"),
|
||||
};
|
||||
|
||||
var locationDelta = newLocation - nodes[i].Location;
|
||||
nodes[i].Location = newLocation;
|
||||
|
||||
if(Undo != null)
|
||||
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
|
||||
}
|
||||
|
||||
MarkAsEdited(false);
|
||||
Undo?.AddAction(new MultiUndoAction(undoActions, $"Align nodes ({alignmentType})"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distribute the given nodes as equally as possible inside the bounding box, if no fit can be done it will use a default pad of 10 pixels between nodes.
|
||||
/// </summary>
|
||||
/// <param name="nodes">List of nodes</param>
|
||||
/// <param name="vertically">If false will be done horizontally, if true will be done vertically</param>
|
||||
public void DistributeNodes(List<SurfaceNode> nodes, bool vertically)
|
||||
{
|
||||
if(nodes.Count <= 1)
|
||||
return;
|
||||
|
||||
var undoActions = new List<MoveNodesAction>();
|
||||
var boundingBox = GetNodesBounds(nodes);
|
||||
float padding = 10;
|
||||
float totalSize = 0;
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (vertically)
|
||||
{
|
||||
totalSize += nodes[i].Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalSize += nodes[i].Width;
|
||||
}
|
||||
}
|
||||
|
||||
if(vertically)
|
||||
{
|
||||
nodes.Sort((leftValue, rightValue) => { return leftValue.Y.CompareTo(rightValue.Y); });
|
||||
|
||||
float position = boundingBox.Top;
|
||||
if(totalSize < boundingBox.Height)
|
||||
{
|
||||
padding = (boundingBox.Height - totalSize) / nodes.Count;
|
||||
}
|
||||
|
||||
for(int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var newLocation = new Float2(nodes[i].X, position);
|
||||
var locationDelta = newLocation - nodes[i].Location;
|
||||
nodes[i].Location = newLocation;
|
||||
|
||||
position += nodes[i].Height + padding;
|
||||
|
||||
if (Undo != null)
|
||||
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes.Sort((leftValue, rightValue) => { return leftValue.X.CompareTo(rightValue.X); });
|
||||
|
||||
float position = boundingBox.Left;
|
||||
if(totalSize < boundingBox.Width)
|
||||
{
|
||||
padding = (boundingBox.Width - totalSize) / nodes.Count;
|
||||
}
|
||||
|
||||
for(int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var newLocation = new Float2(position, nodes[i].Y);
|
||||
var locationDelta = newLocation - nodes[i].Location;
|
||||
nodes[i].Location = newLocation;
|
||||
|
||||
position += nodes[i].Width + padding;
|
||||
|
||||
if (Undo != null)
|
||||
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
|
||||
}
|
||||
}
|
||||
|
||||
MarkAsEdited(false);
|
||||
Undo?.AddAction(new MultiUndoAction(undoActions, vertically ? "Distribute nodes vertically" : "Distribute nodes horizontally"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace FlaxEditor.Surface
|
||||
private Float2 _movingNodesDelta;
|
||||
private Float2 _gridRoundingDelta;
|
||||
private HashSet<SurfaceNode> _movingNodes;
|
||||
private HashSet<SurfaceNode> _temporarySelectedNodes;
|
||||
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
|
||||
|
||||
private class InputBracket
|
||||
@@ -130,13 +131,34 @@ namespace FlaxEditor.Surface
|
||||
if (_rootControl.Children[i] is SurfaceControl control)
|
||||
{
|
||||
var select = control.IsSelectionIntersecting(ref selectionRect);
|
||||
if (select != control.IsSelected)
|
||||
|
||||
if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
control.IsSelected = select;
|
||||
selectionChanged = true;
|
||||
if (select == control.IsSelected && _temporarySelectedNodes.Contains(control))
|
||||
{
|
||||
control.IsSelected = !select;
|
||||
selectionChanged = true;
|
||||
}
|
||||
}
|
||||
else if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
if (select != control.IsSelected && !_temporarySelectedNodes.Contains(control))
|
||||
{
|
||||
control.IsSelected = select;
|
||||
selectionChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (select != control.IsSelected)
|
||||
{
|
||||
control.IsSelected = select;
|
||||
selectionChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectionChanged)
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
@@ -461,6 +483,19 @@ namespace FlaxEditor.Surface
|
||||
// Cache data
|
||||
_isMovingSelection = false;
|
||||
_mousePos = location;
|
||||
if(_temporarySelectedNodes == null)
|
||||
_temporarySelectedNodes = new HashSet<SurfaceNode>();
|
||||
else
|
||||
_temporarySelectedNodes.Clear();
|
||||
|
||||
for (int i = 0; i < _rootControl.Children.Count; i++)
|
||||
{
|
||||
if (_rootControl.Children[i] is SurfaceNode node && node.IsSelected)
|
||||
{
|
||||
_temporarySelectedNodes.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
_leftMouseDown = true;
|
||||
@@ -488,9 +523,11 @@ namespace FlaxEditor.Surface
|
||||
// Check if user is pressing control
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
// Add/remove from selection
|
||||
controlUnderMouse.IsSelected = !controlUnderMouse.IsSelected;
|
||||
SelectionChanged?.Invoke();
|
||||
AddToSelection(controlUnderMouse);
|
||||
}
|
||||
else if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
RemoveFromSelection(controlUnderMouse);
|
||||
}
|
||||
// Check if node isn't selected
|
||||
else if (!controlUnderMouse.IsSelected)
|
||||
@@ -500,10 +537,14 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// Start moving selected nodes
|
||||
StartMouseCapture();
|
||||
_movingSelectionViewPos = _rootControl.Location;
|
||||
_movingNodesDelta = Float2.Zero;
|
||||
OnGetNodesToMove();
|
||||
if (!Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
StartMouseCapture();
|
||||
_movingSelectionViewPos = _rootControl.Location;
|
||||
_movingNodesDelta = Float2.Zero;
|
||||
OnGetNodesToMove();
|
||||
}
|
||||
|
||||
Focus();
|
||||
return true;
|
||||
}
|
||||
@@ -515,7 +556,12 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
// Start selecting or commenting
|
||||
StartMouseCapture();
|
||||
ClearSelection();
|
||||
|
||||
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
Focus();
|
||||
return true;
|
||||
}
|
||||
@@ -544,6 +590,9 @@ namespace FlaxEditor.Surface
|
||||
// Cache flags and state
|
||||
if (_leftMouseDown && button == MouseButton.Left)
|
||||
{
|
||||
WasBoxSelecting = IsBoxSelecting;
|
||||
WasMovingSelection = _isMovingSelection;
|
||||
|
||||
_leftMouseDown = false;
|
||||
EndMouseCapture();
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
@@ -232,15 +232,25 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user is selecting nodes.
|
||||
/// Gets a value indicating whether user is box selecting nodes.
|
||||
/// </summary>
|
||||
public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null;
|
||||
public bool IsBoxSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user was previously box selecting nodes.
|
||||
/// </summary>
|
||||
public bool WasBoxSelecting { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user is moving selected nodes.
|
||||
/// </summary>
|
||||
public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator == null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user was previously moving selected nodes.
|
||||
/// </summary>
|
||||
public bool WasMovingSelection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user is connecting nodes.
|
||||
/// </summary>
|
||||
@@ -405,6 +415,15 @@ namespace FlaxEditor.Surface
|
||||
new InputActionsContainer.Binding(options => options.Paste, Paste),
|
||||
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
||||
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
|
||||
new InputActionsContainer.Binding(options => options.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }),
|
||||
new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }),
|
||||
});
|
||||
|
||||
Context.ControlSpawned += OnSurfaceControlSpawned;
|
||||
@@ -710,6 +729,18 @@ namespace FlaxEditor.Surface
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified control from the selection.
|
||||
/// </summary>
|
||||
/// <param name="control">The control.</param>
|
||||
public void RemoveFromSelection(SurfaceControl control)
|
||||
{
|
||||
if (!control.IsSelected)
|
||||
return;
|
||||
control.IsSelected = false;
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified control.
|
||||
/// </summary>
|
||||
|
||||
@@ -1518,6 +1518,7 @@ namespace FlaxEditor.Utilities
|
||||
inputActions.Add(options => options.OpenScriptsProject, () => Editor.Instance.CodeEditing.OpenSolution());
|
||||
inputActions.Add(options => options.GenerateScriptsProject, () => Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync());
|
||||
inputActions.Add(options => options.RecompileScripts, ScriptsBuilder.Compile);
|
||||
inputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
|
||||
}
|
||||
|
||||
internal static string ToPathProject(string path)
|
||||
|
||||
@@ -236,6 +236,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
var group = layout.Group("General");
|
||||
|
||||
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
|
||||
minScreenSize.ValueBox.SlideSpeed = 0.005f;
|
||||
minScreenSize.ValueBox.MinValue = 0.0f;
|
||||
minScreenSize.ValueBox.MaxValue = 1.0f;
|
||||
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
|
||||
@@ -476,12 +477,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
}
|
||||
|
||||
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs), VisibleIf("ShowUVs")]
|
||||
[Tooltip("Level Of Detail index to preview UVs layout.")]
|
||||
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs, 0.01f), VisibleIf("ShowUVs")]
|
||||
[Tooltip("Level Of Detail index to preview UVs layout at.")]
|
||||
public int LOD = 0;
|
||||
|
||||
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000), VisibleIf("ShowUVs")]
|
||||
[Tooltip("Mesh index to preview UVs layout. Use -1 for all meshes")]
|
||||
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000, 0.01f), VisibleIf("ShowUVs")]
|
||||
[Tooltip("Mesh index to show UVs layout for. Use -1 to display all UVs of all meshes")]
|
||||
public int Mesh = -1;
|
||||
|
||||
private bool ShowUVs => _uvChannel != UVChannel.None;
|
||||
|
||||
@@ -81,6 +81,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
var resolution = group.FloatValue("Resolution Scale", Window.Editor.CodeDocs.GetTooltip(typeof(ModelTool.Options), nameof(ModelImportSettings.Settings.SDFResolution)));
|
||||
resolution.ValueBox.SlideSpeed = 0.001f;
|
||||
resolution.ValueBox.MinValue = 0.0001f;
|
||||
resolution.ValueBox.MaxValue = 100.0f;
|
||||
resolution.ValueBox.Value = sdf.Texture != null ? sdf.ResolutionScale : 1.0f;
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
@@ -114,6 +115,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
private readonly PropertiesProxy _properties;
|
||||
private Tab _previewTab;
|
||||
private ToolStripButton _showSourceCodeButton;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ParticleEmitterWindow(Editor editor, AssetItem item)
|
||||
@@ -146,7 +148,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// Toolstrip
|
||||
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
|
||||
_showSourceCodeButton = _toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode);
|
||||
_showSourceCodeButton.LinkTooltip("Show generated shader source code");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
|
||||
}
|
||||
@@ -285,5 +288,15 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
/// <inheritdoc />
|
||||
public SearchAssetTypes AssetType => SearchAssetTypes.ParticleEmitter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_asset == null)
|
||||
return;
|
||||
_showSourceCodeButton.Enabled = _asset.HasShaderCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,10 +97,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// For single node selected scroll view so user can see it
|
||||
if (nodes.Count == 1)
|
||||
{
|
||||
nodes[0].ExpandAllParents(true);
|
||||
ScrollViewTo(nodes[0]);
|
||||
}
|
||||
ScrollToSelectedNode();
|
||||
}
|
||||
|
||||
// Update properties editor
|
||||
|
||||
@@ -318,7 +318,7 @@ namespace FlaxEditor.Windows
|
||||
private Color _colorWarning;
|
||||
private Color _colorError;
|
||||
private bool _colorDebugLogText;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DebugLogWindow"/> class.
|
||||
/// </summary>
|
||||
@@ -352,24 +352,12 @@ namespace FlaxEditor.Windows
|
||||
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);
|
||||
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");
|
||||
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => { OnGroupButtonPressed(0); })
|
||||
.SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
|
||||
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () => { OnGroupButtonPressed(1); })
|
||||
.SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
|
||||
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () => { OnGroupButtonPressed(2); })
|
||||
.SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
|
||||
UpdateCount();
|
||||
|
||||
// Split panel
|
||||
@@ -418,6 +406,27 @@ namespace FlaxEditor.Windows
|
||||
OnEditorOptionsChanged(Editor.Options.Options);
|
||||
}
|
||||
|
||||
private void OnGroupButtonPressed(int index)
|
||||
{
|
||||
UpdateLogTypeVisibility((LogGroup)index, _groupButtons[index].Checked);
|
||||
if (Input.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
for (int i = 0; i < (int)LogGroup.Max; i++)
|
||||
{
|
||||
if (i == index)
|
||||
continue;
|
||||
_groupButtons[i].Checked = !_groupButtons[index].Checked;
|
||||
UpdateLogTypeVisibility((LogGroup)i, _groupButtons[i].Checked);
|
||||
}
|
||||
}
|
||||
|
||||
var options = Editor.Options.Options.Interface;
|
||||
options.DebugLogShowErrorMessages = _groupButtons[0].Checked;
|
||||
options.DebugLogShowWarningMessages = _groupButtons[1].Checked;
|
||||
options.DebugLogShowInfoMessages = _groupButtons[2].Checked;
|
||||
Editor.Options.Apply(Editor.Options.Options);
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
_timestampsFormats = options.Interface.DebugLogTimestampsFormat;
|
||||
@@ -455,15 +464,9 @@ namespace FlaxEditor.Windows
|
||||
// Create new entry
|
||||
switch (_timestampsFormats)
|
||||
{
|
||||
case InterfaceOptions.TimestampsFormats.Utc:
|
||||
desc.Title = $"[{DateTime.UtcNow}] {desc.Title}";
|
||||
break;
|
||||
case InterfaceOptions.TimestampsFormats.LocalTime:
|
||||
desc.Title = $"[{DateTime.Now}] {desc.Title}";
|
||||
break;
|
||||
case InterfaceOptions.TimestampsFormats.TimeSinceStartup:
|
||||
desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title;
|
||||
break;
|
||||
case InterfaceOptions.TimestampsFormats.Utc: desc.Title = $"[{DateTime.UtcNow}] {desc.Title}"; break;
|
||||
case InterfaceOptions.TimestampsFormats.LocalTime: desc.Title = $"[{DateTime.Now}] {desc.Title}"; break;
|
||||
case InterfaceOptions.TimestampsFormats.TimeSinceStartup: desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title; break;
|
||||
}
|
||||
var newEntry = new LogEntry(this, ref desc);
|
||||
|
||||
|
||||
@@ -219,6 +219,13 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when Editor will leave the play mode.
|
||||
/// </summary>
|
||||
public virtual void OnPlayEnding()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when Editor leaves the play mode.
|
||||
/// </summary>
|
||||
|
||||
@@ -405,6 +405,7 @@ namespace FlaxEditor.Windows
|
||||
return;
|
||||
Editor.Instance.SceneEditing.Delete();
|
||||
});
|
||||
InputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
|
||||
}
|
||||
|
||||
private void ChangeViewportRatio(ViewportScaleOptions v)
|
||||
|
||||
@@ -830,6 +830,15 @@ namespace FlaxEditor.Windows
|
||||
OnOutputTextChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Focus the debug command line and ensure that the output log window is visible.
|
||||
/// </summary>
|
||||
public void FocusCommand()
|
||||
{
|
||||
FocusOrShow();
|
||||
_commandLineBox.Focus();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace FlaxEditor.Windows
|
||||
private DragScriptItems _dragScriptItems;
|
||||
private DragHandlers _dragHandlers;
|
||||
private bool _isDropping = false;
|
||||
private bool _forceScrollNodeToView = false;
|
||||
|
||||
/// <summary>
|
||||
/// Scene tree panel.
|
||||
@@ -91,6 +92,15 @@ namespace FlaxEditor.Windows
|
||||
_tree.SelectedChanged += Tree_OnSelectedChanged;
|
||||
_tree.RightClick += OnTreeRightClick;
|
||||
_tree.Parent = _sceneTreePanel;
|
||||
_tree.AfterDeferredLayout += () =>
|
||||
{
|
||||
if (_forceScrollNodeToView)
|
||||
{
|
||||
_forceScrollNodeToView = false;
|
||||
ScrollToSelectedNode();
|
||||
}
|
||||
};
|
||||
|
||||
headerPanel.Parent = this;
|
||||
|
||||
// Setup input actions
|
||||
@@ -142,6 +152,16 @@ namespace FlaxEditor.Windows
|
||||
root.TreeNode.UpdateFilter(query);
|
||||
|
||||
_tree.UnlockChildrenRecursive();
|
||||
|
||||
// When keep the selected nodes in a view
|
||||
var nodeSelection = _tree.Selection;
|
||||
if (nodeSelection.Count != 0)
|
||||
{
|
||||
var node = nodeSelection[nodeSelection.Count - 1];
|
||||
node.Expand(true);
|
||||
_forceScrollNodeToView = true;
|
||||
}
|
||||
|
||||
PerformLayout();
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace FlaxEditor.Windows.Search
|
||||
if (value == _selectedItem || (value != null && !_matchedItems.Contains(value)))
|
||||
return;
|
||||
|
||||
// Restore the previous selected item to the non-selected color
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
_selectedItem.BackgroundColor = Color.Transparent;
|
||||
@@ -54,6 +55,7 @@ namespace FlaxEditor.Windows.Search
|
||||
_selectedItem.BackgroundColor = Style.Current.BackgroundSelected;
|
||||
if (_matchedItems.Count > VisibleItemCount)
|
||||
{
|
||||
_selectedItem.Focus();
|
||||
_resultPanel.ScrollViewTo(_selectedItem, true);
|
||||
}
|
||||
}
|
||||
@@ -180,39 +182,17 @@ namespace FlaxEditor.Windows.Search
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.ArrowDown:
|
||||
{
|
||||
if (_matchedItems.Count == 0)
|
||||
return true;
|
||||
int currentPos;
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
currentPos = _matchedItems.IndexOf(_selectedItem) + 1;
|
||||
if (currentPos >= _matchedItems.Count)
|
||||
currentPos--;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPos = 0;
|
||||
}
|
||||
SelectedItem = _matchedItems[currentPos];
|
||||
return true;
|
||||
}
|
||||
case KeyboardKeys.ArrowUp:
|
||||
{
|
||||
if (_matchedItems.Count == 0)
|
||||
return true;
|
||||
int currentPos;
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
currentPos = _matchedItems.IndexOf(_selectedItem) - 1;
|
||||
if (currentPos < 0)
|
||||
currentPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPos = 0;
|
||||
}
|
||||
SelectedItem = _matchedItems[currentPos];
|
||||
|
||||
var focusedIndex = _matchedItems.IndexOf(_selectedItem);
|
||||
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
|
||||
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, _matchedItems.Count - 1);
|
||||
var nextItem = _matchedItems[nextIndex];
|
||||
|
||||
SelectedItem = nextItem;
|
||||
return true;
|
||||
}
|
||||
case KeyboardKeys.Return:
|
||||
@@ -234,6 +214,17 @@ namespace FlaxEditor.Windows.Search
|
||||
Hide();
|
||||
return true;
|
||||
}
|
||||
case KeyboardKeys.Backspace:
|
||||
{
|
||||
// Alow the user to quickly focus the searchbar
|
||||
if (_searchBox != null && !_searchBox.IsFocused)
|
||||
{
|
||||
_searchBox.Focus();
|
||||
_searchBox.SelectAll();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
|
||||
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
|
||||
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
|
||||
{
|
||||
@@ -118,6 +119,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
||||
}
|
||||
#else
|
||||
#define HANDLE_VORBIS(chunkIndex, dataPtr, dataSize) \
|
||||
MessageBox::Show(TEXT("Vorbis format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "Vorbis format is not supported."); \
|
||||
return CreateAssetResult::Error;
|
||||
#endif
|
||||
@@ -140,6 +142,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
||||
break; \
|
||||
default: \
|
||||
{ \
|
||||
MessageBox::Show(TEXT("Unknown audio format."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning); \
|
||||
LOG(Warning, "Unknown audio format."); \
|
||||
return CreateAssetResult::Error; \
|
||||
} \
|
||||
|
||||
@@ -16,6 +16,7 @@ API_CLASS(InBuild) class BitArray
|
||||
public:
|
||||
using ItemType = uint64;
|
||||
using AllocationData = typename AllocationType::template Data<ItemType>;
|
||||
static constexpr int32 ItemBitCount = 64;
|
||||
|
||||
private:
|
||||
int32 _count;
|
||||
@@ -209,8 +210,8 @@ public:
|
||||
bool Get(const int32 index) const
|
||||
{
|
||||
ASSERT(index >= 0 && index < _count);
|
||||
const ItemType offset = index / sizeof(ItemType);
|
||||
const ItemType bitMask = (ItemType)(int32)(1 << (index & ((int32)sizeof(ItemType) - 1)));
|
||||
const ItemType offset = index / ItemBitCount;
|
||||
const ItemType bitMask = (ItemType)(1 << (index & (ItemBitCount - 1)));
|
||||
const ItemType item = ((ItemType*)_allocation.Get())[offset];
|
||||
return (item & bitMask) != 0;
|
||||
}
|
||||
@@ -223,13 +224,13 @@ public:
|
||||
void Set(const int32 index, const bool value)
|
||||
{
|
||||
ASSERT(index >= 0 && index < _count);
|
||||
const ItemType offset = index / sizeof(ItemType);
|
||||
const ItemType bitMask = (ItemType)(int32)(1 << (index & ((int32)sizeof(ItemType) - 1)));
|
||||
const ItemType offset = index / ItemBitCount;
|
||||
const ItemType bitMask = (ItemType)(1 << (index & (ItemBitCount - 1)));
|
||||
ItemType& item = ((ItemType*)_allocation.Get())[offset];
|
||||
if (value)
|
||||
item |= bitMask;
|
||||
item |= bitMask; // Set the bit
|
||||
else
|
||||
item &= ~bitMask; // Clear the bit
|
||||
item &= ~bitMask; // Unset the bit
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS \
|
||||
_Pragma("clang diagnostic pop")
|
||||
#define PRAGMA_DISABLE_OPTIMIZATION
|
||||
#define PRAGMA_ENABLE_OPTIMIZATION
|
||||
|
||||
#pragma clang diagnostic ignored "-Wswitch"
|
||||
#pragma clang diagnostic ignored "-Wmacro-redefined"
|
||||
@@ -54,6 +56,8 @@
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
#define PRAGMA_DISABLE_OPTIMIZATION
|
||||
#define PRAGMA_ENABLE_OPTIMIZATION
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
@@ -86,6 +90,8 @@
|
||||
__pragma(warning(disable: 4996))
|
||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS \
|
||||
__pragma (warning(pop))
|
||||
#define PRAGMA_DISABLE_OPTIMIZATION __pragma(optimize("", off))
|
||||
#define PRAGMA_ENABLE_OPTIMIZATION __pragma(optimize("", on))
|
||||
|
||||
#pragma warning(disable: 4251)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
#define USE_MIKKTSPACE 1
|
||||
#include "ThirdParty/MikkTSpace/mikktspace.h"
|
||||
#if USE_ASSIMP
|
||||
@@ -181,6 +182,7 @@ bool MeshData::GenerateLightmapUVs()
|
||||
for (int32 i = 0; i < (int32)vb.size(); i++)
|
||||
lightmapChannel.Get()[i] = *(Float2*)&vb[i].uv;
|
||||
#else
|
||||
MessageBox::Show(TEXT("Model lightmap UVs generation is not supported on this platform."), TEXT("Import error"), MessageBoxButtons::OK, MessageBoxIcon::Error);
|
||||
LOG(Error, "Model lightmap UVs generation is not supported on this platform.");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -656,6 +656,23 @@ GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipInde
|
||||
return task;
|
||||
}
|
||||
|
||||
bool GPUTexture::UploadData(TextureData& data, bool copyData)
|
||||
{
|
||||
if (!IsAllocated())
|
||||
return true;
|
||||
if (data.Width != Width() || data.Height != Height() || data.Depth != Depth() || data.GetArraySize() != ArraySize() || data.Format != Format())
|
||||
return true;
|
||||
for (int32 arrayIndex = 0; arrayIndex < ArraySize(); arrayIndex++)
|
||||
{
|
||||
for (int32 mipLevel = 0; mipLevel < MipLevels(); mipLevel++)
|
||||
{
|
||||
TextureMipData* mip = data.GetData(arrayIndex, mipLevel);
|
||||
UploadMipMapAsync(mip->Data, mipLevel, mip->RowPitch, mip->DepthPitch, copyData);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class TextureDownloadDataTask : public ThreadPoolTask
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -499,7 +499,7 @@ public:
|
||||
/// <summary>
|
||||
/// Uploads mip map data to the GPU. Creates async GPU task.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing)</param>
|
||||
/// <param name="data">Data to upload, it must match texture dimensions. It must be valid for the next couple of frames due to GPU async task latency or use data copy.</param>
|
||||
/// <param name="mipIndex">Mip level index.</param>
|
||||
/// <param name="copyData">If true, the data will be copied to the async execution task instead of using the input pointer provided.</param>
|
||||
/// <returns>Created async task or null if cannot.</returns>
|
||||
@@ -508,7 +508,7 @@ public:
|
||||
/// <summary>
|
||||
/// Uploads mip map data to the GPU. Creates async GPU task.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing)</param>
|
||||
/// <param name="data">Data to upload, it must match texture dimensions. It must be valid for the next couple of frames due to GPU async task latency or use data copy.</param>
|
||||
/// <param name="mipIndex">Mip level index.</param>
|
||||
/// <param name="rowPitch">The data row pitch.</param>
|
||||
/// <param name="slicePitch">The data slice pitch.</param>
|
||||
@@ -516,6 +516,14 @@ public:
|
||||
/// <returns>Created async task or null if cannot.</returns>
|
||||
GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData = false);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads texture data to the GPU. Actual data copy to the GPU memory is performed via async task.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to upload, it must match texture dimensions. It must be valid for the next couple of frames due to GPU async task latency or use data copy.</param>
|
||||
/// <param name="copyData">If true, the data will be copied to the async execution task instead of using the input pointer provided.</param>
|
||||
/// <returns>True if cannot upload data, otherwise false.</returns>
|
||||
API_FUNCTION() bool UploadData(TextureData& data, bool copyData = false);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the texture data to be accessible from CPU. For frequent access, use staging textures, otherwise current thread will be stalled to wait for the GPU frame to copy data into staging buffer.
|
||||
/// </summary>
|
||||
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
/// <summary>
|
||||
/// The animation update delta timescale. Can be used to speed up animation playback or create slow motion effect.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
|
||||
float UpdateSpeed = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
/// <summary>
|
||||
/// The master scale parameter for the actor bounding box. Helps to reduce mesh flickering effect on screen edges.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
|
||||
float BoundsScale = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -443,4 +443,18 @@ bool ParticleEmitter::Save(const StringView& path)
|
||||
return SaveSurface(data);
|
||||
}
|
||||
|
||||
bool ParticleEmitter::HasShaderCode() const
|
||||
{
|
||||
if (SimulationMode != ParticlesSimulationMode::GPU)
|
||||
return false;
|
||||
|
||||
#if COMPILE_WITH_PARTICLE_GPU_GRAPH && COMPILE_WITH_SHADER_COMPILER
|
||||
if (_shaderHeader.ParticleEmitter.GraphVersion == PARTICLE_GPU_GRAPH_VERSION
|
||||
&& HasChunk(SHADER_FILE_CHUNK_SOURCE)
|
||||
&& !HasDependenciesModified())
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -173,6 +173,11 @@ public:
|
||||
#if USE_EDITOR
|
||||
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
|
||||
bool Save(const StringView& path = StringView::Empty) override;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the particle emitter has valid shader code present.
|
||||
/// </summary>
|
||||
API_PROPERTY() bool HasShaderCode() const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
@@ -262,16 +262,16 @@ void CharacterController::OnDebugDrawSelected()
|
||||
Quaternion rotation = Quaternion::Euler(90, 0, 0);
|
||||
const Vector3 position = GetControllerPosition();
|
||||
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow, 0, false);
|
||||
if (_contactOffset > 0)
|
||||
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius - _contactOffset, _height, Color::Blue.AlphaMultiplied(0.4f), 0, false);
|
||||
#if 1
|
||||
// More technical visuals debugging
|
||||
if (_controller)
|
||||
{
|
||||
// Physics backend capsule shape
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
Vector3 base = PhysicsBackend::GetControllerBasePosition(_controller);
|
||||
Vector3 pos = PhysicsBackend::GetControllerPosition(_controller);
|
||||
DEBUG_DRAW_WIRE_CAPSULE(pos, rotation, radius, height, Color::Blue.AlphaMultiplied(0.2f), 0, false);
|
||||
#if 0
|
||||
// More technical visuals debugging
|
||||
Vector3 base = PhysicsBackend::GetControllerBasePosition(_controller);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(base, 5.0f), Color::Red, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos, 4.0f), Color::Red, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false);
|
||||
@@ -279,17 +279,8 @@ void CharacterController::OnDebugDrawSelected()
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos - Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(pos, Quaternion::Identity, radius, height, Color::Red.AlphaMultiplied(0.2f), 0, false);
|
||||
}
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(position, 3.0f), Color::GreenYellow, 0, false);
|
||||
#else
|
||||
if (_controller)
|
||||
{
|
||||
// Physics backend capsule shape
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
DEBUG_DRAW_WIRE_CAPSULE(PhysicsBackend::GetControllerPosition(_controller), rotation, radius, height, Color::Blue.AlphaMultiplied(0.2f), 0, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Base
|
||||
Collider::OnDebugDrawSelected();
|
||||
|
||||
@@ -78,6 +78,32 @@ TEST_CASE("Array")
|
||||
|
||||
TEST_CASE("BitArray")
|
||||
{
|
||||
SECTION("Test Access")
|
||||
{
|
||||
BitArray<> a1;
|
||||
CHECK(a1.Count() == 0);
|
||||
for (int32 i = 0; i < 310; i++)
|
||||
{
|
||||
a1.Add(false);
|
||||
}
|
||||
CHECK(a1.Count() == 310);
|
||||
a1.Resize(300);
|
||||
CHECK(a1.Count() == 300);
|
||||
CHECK(a1.Capacity() >= 300);
|
||||
a1.SetAll(true);
|
||||
a1.SetAll(false);
|
||||
for (int32 i = 0; i < 300; i++)
|
||||
{
|
||||
a1.Set(i, true);
|
||||
for (int32 j = 0; j < 300; j++)
|
||||
{
|
||||
bool expected = j == i;
|
||||
CHECK(a1.Get(j) == expected);
|
||||
}
|
||||
a1.Set(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Test Allocators")
|
||||
{
|
||||
BitArray<> a1;
|
||||
@@ -142,7 +168,7 @@ TEST_CASE("BitArray")
|
||||
|
||||
// Generate some random data for testing
|
||||
BitArray<> testData;
|
||||
testData.Resize(32);
|
||||
testData.Resize(128);
|
||||
RandomStream rand(101);
|
||||
for (int32 i = 0; i < testData.Count(); i++)
|
||||
testData.Set(i, rand.GetBool());
|
||||
@@ -151,8 +177,8 @@ TEST_CASE("BitArray")
|
||||
{
|
||||
const BitArray<> a1(testData);
|
||||
const BitArray<InlinedAllocation<8>> a2(testData);
|
||||
const BitArray<InlinedAllocation<64>> a3(testData);
|
||||
const BitArray<FixedAllocation<64>> a4(testData);
|
||||
const BitArray<InlinedAllocation<256>> a3(testData);
|
||||
const BitArray<FixedAllocation<256>> a4(testData);
|
||||
CHECK(a1 == testData);
|
||||
CHECK(a2 == testData);
|
||||
CHECK(a3 == testData);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#endif
|
||||
#include "Engine/Utilities/AnsiPathTempFile.h"
|
||||
@@ -358,6 +359,9 @@ HRESULT LoadFromEXRFile(const StringView& path, DirectX::ScratchImage& image)
|
||||
free(pixels);
|
||||
return result;
|
||||
#else
|
||||
#if USE_EDITOR
|
||||
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
#endif
|
||||
LOG(Warning, "EXR format is not supported.");
|
||||
return E_FAIL;
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
#include "Engine/Utilities/AnsiPathTempFile.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
|
||||
#define STBI_ASSERT(x) ASSERT(x)
|
||||
#define STBI_MALLOC(sz) Allocator::Allocate(sz)
|
||||
@@ -286,21 +287,27 @@ bool TextureTool::ExportTextureStb(ImageType type, const StringView& path, const
|
||||
break;
|
||||
}
|
||||
case ImageType::GIF:
|
||||
MessageBox::Show(TEXT("GIF format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "GIF format is not supported.");
|
||||
break;
|
||||
case ImageType::TIFF:
|
||||
MessageBox::Show(TEXT("TIFF format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "GIF format is not supported.");
|
||||
break;
|
||||
case ImageType::DDS:
|
||||
MessageBox::Show(TEXT("DDS format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "DDS format is not supported.");
|
||||
break;
|
||||
case ImageType::RAW:
|
||||
MessageBox::Show(TEXT("RAW format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "RAW format is not supported.");
|
||||
break;
|
||||
case ImageType::EXR:
|
||||
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "EXR format is not supported.");
|
||||
break;
|
||||
default:
|
||||
MessageBox::Show(TEXT("Unknown format."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "Unknown format.");
|
||||
break;
|
||||
}
|
||||
@@ -434,17 +441,21 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu
|
||||
|
||||
free(pixels);
|
||||
#else
|
||||
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "EXR format is not supported.");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case ImageType::DDS:
|
||||
MessageBox::Show(TEXT("DDS format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "DDS format is not supported.");
|
||||
break;
|
||||
case ImageType::TIFF:
|
||||
MessageBox::Show(TEXT("TIFF format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "TIFF format is not supported.");
|
||||
break;
|
||||
default:
|
||||
MessageBox::Show(TEXT("Unknown format."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "Unknown format.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Ben Golus
|
||||
// https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8#3e73
|
||||
|
||||
#define USE_FORWARD true;
|
||||
#define MIN_DERIV 0.00001f
|
||||
|
||||
#include "./Flax/Common.hlsl"
|
||||
|
||||
@@ -61,11 +61,6 @@ float remap(float origFrom, float origTo, float targetFrom, float targetTo, floa
|
||||
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;
|
||||
@@ -73,7 +68,7 @@ float GetLine(float pos, float scale, float thickness)
|
||||
|
||||
float2 uvDDXY = float2(ddx(coord), ddy(coord));
|
||||
|
||||
float deriv = float(length(uvDDXY.xy));
|
||||
float deriv = max(float(length(uvDDXY.xy)), MIN_DERIV);
|
||||
float drawWidth = clamp(lineWidth, deriv, 0.5);
|
||||
float lineAA = deriv * 1.5;
|
||||
float gridUV = abs(coord);
|
||||
@@ -92,7 +87,7 @@ float GetGrid(float3 pos, float scale, float thickness)
|
||||
|
||||
float4 uvDDXY = float4(ddx(coord), ddy(coord));
|
||||
|
||||
float2 deriv = float2(length(uvDDXY.xz), length(uvDDXY.yw));
|
||||
float2 deriv = max(float2(length(uvDDXY.xz), length(uvDDXY.yw)), float2(MIN_DERIV, MIN_DERIV));
|
||||
float2 drawWidth = clamp(lineWidth, deriv, 0.5);
|
||||
float2 lineAA = deriv * 1.5;
|
||||
float2 gridUV = 1.0 - abs(frac(coord) * 2.0 - 1.0);
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Flax.Build
|
||||
private static Platform _buildPlatform;
|
||||
private static Platform[] _platforms;
|
||||
private Dictionary<TargetArchitecture, Toolchain> _toolchains;
|
||||
private uint _failedArchitectures = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current target platform that build tool runs on.
|
||||
@@ -251,7 +252,8 @@ namespace Flax.Build
|
||||
public Toolchain TryGetToolchain(TargetArchitecture targetArchitecture)
|
||||
{
|
||||
Toolchain result = null;
|
||||
if (HasRequiredSDKsInstalled)
|
||||
uint failedMask = 1u << (int)targetArchitecture; // Skip retrying if it already failed once on this arch
|
||||
if (HasRequiredSDKsInstalled && (_failedArchitectures & failedMask) != failedMask)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -259,6 +261,7 @@ namespace Flax.Build
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_failedArchitectures |= failedMask;
|
||||
Log.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user