Merge remote-tracking branch 'origin/master' into 1.9
# Conflicts: # Source/Engine/Content/Storage/FlaxStorage.cpp # Source/Engine/Renderer/GBufferPass.cpp
This commit is contained in:
@@ -467,26 +467,36 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Setup transform
|
||||
if (Presenter is LayoutElementsContainer l)
|
||||
{
|
||||
for (int i = 0; i < l.Children.Count; i++)
|
||||
{
|
||||
if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal))
|
||||
{
|
||||
l.Children.Remove(g);
|
||||
l.ContainerControl.Children.Remove(g.Panel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var transformGroup = l.Group("Transform");
|
||||
VerticalPanelElement mainHor = VerticalPanelWithoutMargin(transformGroup);
|
||||
CreateTransformElements(mainHor, ValuesTypes);
|
||||
|
||||
|
||||
ScriptMemberInfo scaleInfo = ValuesTypes[0].GetProperty("Scale");
|
||||
ItemInfo scaleItem = new ItemInfo(scaleInfo);
|
||||
transformGroup.Property("Scale", scaleItem.GetValues(Values));
|
||||
|
||||
|
||||
ScriptMemberInfo pivotInfo = ValuesTypes[0].GetProperty("Pivot");
|
||||
ItemInfo pivotItem = new ItemInfo(pivotInfo);
|
||||
transformGroup.Property("Pivot", pivotItem.GetValues(Values));
|
||||
|
||||
|
||||
ScriptMemberInfo shearInfo = ValuesTypes[0].GetProperty("Shear");
|
||||
ItemInfo shearItem = new ItemInfo(shearInfo);
|
||||
transformGroup.Property("Shear", shearItem.GetValues(Values));
|
||||
|
||||
|
||||
ScriptMemberInfo rotationInfo = ValuesTypes[0].GetProperty("Rotation");
|
||||
ItemInfo rotationItem = new ItemInfo(rotationInfo);
|
||||
transformGroup.Property("Rotation", rotationItem.GetValues(Values));
|
||||
|
||||
|
||||
// Get position of general tab
|
||||
for (int i = 0; i < l.Children.Count; i++)
|
||||
{
|
||||
|
||||
@@ -57,17 +57,18 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
menu.ItemsContainer.RemoveChildren();
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste;
|
||||
var b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
menu.AddSeparator();
|
||||
var moveUpButton = menu.AddButton("Move up", OnMoveUpClicked);
|
||||
moveUpButton.Enabled = Index > 0;
|
||||
b = menu.AddButton("Move up", OnMoveUpClicked);
|
||||
b.Enabled = Index > 0 && !Editor._readOnly;
|
||||
|
||||
var moveDownButton = menu.AddButton("Move down", OnMoveDownClicked);
|
||||
moveDownButton.Enabled = Index + 1 < Editor.Count;
|
||||
|
||||
menu.AddButton("Remove", OnRemoveClicked);
|
||||
b = menu.AddButton("Move down", OnMoveDownClicked);
|
||||
b.Enabled = Index + 1 < Editor.Count && !Editor._readOnly;
|
||||
|
||||
b = menu.AddButton("Remove", OnRemoveClicked);
|
||||
b.Enabled = !Editor._readOnly;
|
||||
}
|
||||
|
||||
private void OnMoveUpClicked()
|
||||
@@ -177,6 +178,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
private IntValueBox _sizeBox;
|
||||
private Color _background;
|
||||
private int _elementsCount, _minCount, _maxCount;
|
||||
private bool _readOnly;
|
||||
private bool _canResize;
|
||||
private bool _canReorderItems;
|
||||
private CollectionAttribute.DisplayType _displayType;
|
||||
@@ -209,6 +211,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return;
|
||||
|
||||
var size = Count;
|
||||
_readOnly = false;
|
||||
_canResize = true;
|
||||
_canReorderItems = true;
|
||||
_minCount = 0;
|
||||
@@ -225,6 +228,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (collection != null)
|
||||
{
|
||||
_canResize = !collection.ReadOnly;
|
||||
_readOnly = collection.ReadOnly;
|
||||
_minCount = collection.MinCount;
|
||||
_maxCount = collection.MaxCount;
|
||||
_canReorderItems = collection.CanReorderItems;
|
||||
@@ -235,6 +239,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
spacing = collection.Spacing;
|
||||
_displayType = collection.Display;
|
||||
}
|
||||
if (attributes != null && attributes.Any(x => x is ReadOnlyAttribute))
|
||||
{
|
||||
_readOnly = true;
|
||||
_canResize = false;
|
||||
_canReorderItems = false;
|
||||
}
|
||||
if (_maxCount == 0)
|
||||
_maxCount = ushort.MaxValue;
|
||||
_canResize &= _minCount < _maxCount;
|
||||
@@ -243,8 +253,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
dragArea.CustomControl.Editor = this;
|
||||
dragArea.CustomControl.ElementType = ElementType;
|
||||
|
||||
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter
|
||||
// which scripts can be dragged over and dropped on this collection editor.
|
||||
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter which scripts can be dragged over and dropped on this collection editor
|
||||
var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute);
|
||||
if (assetReference != null)
|
||||
{
|
||||
@@ -333,6 +342,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var property = panel.AddPropertyItem(itemLabel);
|
||||
var itemLayout = (LayoutElementsContainer)property;
|
||||
itemLabel.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
|
||||
if (_readOnly && itemLayout.Children.Count > 0)
|
||||
GenericEditor.OnReadOnlyProperty(itemLayout);
|
||||
}
|
||||
else if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single))
|
||||
{
|
||||
@@ -340,13 +351,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
cdp.CustomControl.Setup(this, i, _canReorderItems);
|
||||
var itemLayout = cdp.VerticalPanel();
|
||||
cdp.CustomControl.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
|
||||
if (_readOnly && itemLayout.Children.Count > 0)
|
||||
GenericEditor.OnReadOnlyProperty(itemLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
_elementsCount = size;
|
||||
|
||||
// Add/Remove buttons
|
||||
if (_canResize)
|
||||
if (_canResize && !_readOnly)
|
||||
{
|
||||
var panel = dragArea.HorizontalPanel();
|
||||
panel.Panel.Size = new Float2(0, 20);
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
if (button == MouseButton.Left && _editor._canEditKeys)
|
||||
{
|
||||
OnEditClicked(null);
|
||||
return true;
|
||||
@@ -197,6 +197,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
spacing = collection.Spacing;
|
||||
_displayType = collection.Display;
|
||||
}
|
||||
if (attributes != null && attributes.Any(x => x is ReadOnlyAttribute))
|
||||
{
|
||||
_readOnly = true;
|
||||
_canEditKeys = false;
|
||||
}
|
||||
|
||||
// Size
|
||||
if (layout.ContainerControl is DropPanel dropPanel)
|
||||
@@ -239,14 +244,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType<object>();
|
||||
var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray();
|
||||
var valuesType = new ScriptType(valueType);
|
||||
|
||||
bool single = valuesType.IsPrimitive ||
|
||||
valuesType.Equals(new ScriptType(typeof(string))) ||
|
||||
valuesType.IsEnum ||
|
||||
(valuesType.GetFields().Length == 1 && valuesType.GetProperties().Length == 0) ||
|
||||
(valuesType.GetProperties().Length == 1 && valuesType.GetFields().Length == 0) ||
|
||||
valuesType.Equals(new ScriptType(typeof(JsonAsset))) ||
|
||||
valuesType.Equals(new ScriptType(typeof(SettingsBase)));
|
||||
|
||||
// Use separate layout cells for each collection items to improve layout updates for them in separation
|
||||
var useSharedLayout = valueType.IsPrimitive || valueType.IsEnum;
|
||||
@@ -263,6 +260,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var property = panel.AddPropertyItem(new DictionaryItemLabel(this, key));
|
||||
var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel();
|
||||
itemLayout.Object(new DictionaryValueContainer(valuesType, key, Values), overrideEditor);
|
||||
if (_readOnly && itemLayout.Children.Count > 0)
|
||||
GenericEditor.OnReadOnlyProperty(itemLayout);
|
||||
}
|
||||
}
|
||||
_elementsCount = size;
|
||||
|
||||
@@ -581,6 +581,43 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return layout;
|
||||
}
|
||||
|
||||
internal static void OnReadOnlyProperty(LayoutElementsContainer itemLayout, int labelIndex = -1)
|
||||
{
|
||||
PropertiesListElement list = null;
|
||||
int firstChildControlIndex = 0;
|
||||
bool disableSingle = true;
|
||||
var control = itemLayout.Children[itemLayout.Children.Count - 1];
|
||||
if (control is GroupElement group && group.Children.Count > 0)
|
||||
{
|
||||
list = group.Children[0] as PropertiesListElement;
|
||||
disableSingle = false; // Disable all nested editors
|
||||
}
|
||||
else if (control is PropertiesListElement list1 && labelIndex != -1)
|
||||
{
|
||||
list = list1;
|
||||
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
|
||||
}
|
||||
else if (control?.Control != null)
|
||||
{
|
||||
control.Control.Enabled = false;
|
||||
}
|
||||
|
||||
if (list != null)
|
||||
{
|
||||
// Disable controls added to the editor
|
||||
var count = list.Properties.Children.Count;
|
||||
for (int j = firstChildControlIndex; j < count; j++)
|
||||
{
|
||||
var child = list.Properties.Children[j];
|
||||
if (disableSingle && child is PropertyNameLabel)
|
||||
break;
|
||||
|
||||
if (child != null)
|
||||
child.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate the <see cref="VisibleIfAttribute"/> cache for a given property item.
|
||||
/// </summary>
|
||||
@@ -660,35 +697,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (item.IsReadOnly && itemLayout.Children.Count > 0)
|
||||
{
|
||||
PropertiesListElement list = null;
|
||||
int firstChildControlIndex = 0;
|
||||
bool disableSingle = true;
|
||||
var control = itemLayout.Children[itemLayout.Children.Count - 1];
|
||||
if (control is GroupElement group && group.Children.Count > 0)
|
||||
{
|
||||
list = group.Children[0] as PropertiesListElement;
|
||||
disableSingle = false; // Disable all nested editors
|
||||
}
|
||||
else if (control is PropertiesListElement list1)
|
||||
{
|
||||
list = list1;
|
||||
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
|
||||
}
|
||||
|
||||
if (list != null)
|
||||
{
|
||||
// Disable controls added to the editor
|
||||
var count = list.Properties.Children.Count;
|
||||
for (int j = firstChildControlIndex; j < count; j++)
|
||||
{
|
||||
var child = list.Properties.Children[j];
|
||||
if (disableSingle && child is PropertyNameLabel)
|
||||
break;
|
||||
|
||||
if (child != null)
|
||||
child.Enabled = false;
|
||||
}
|
||||
}
|
||||
OnReadOnlyProperty(itemLayout, labelIndex);
|
||||
}
|
||||
|
||||
EvaluateVisibleIf(itemLayout, item, labelIndex);
|
||||
|
||||
@@ -371,9 +371,25 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
|
||||
// Color difference
|
||||
var newRect = new Rectangle(_cOK.X, _cHex.Bottom + PickerMargin, _cCancel.Right - _cOK.Left, 0);
|
||||
newRect.Size.Y = _cValue.Bottom - newRect.Y;
|
||||
Render2D.FillRectangle(newRect, _value * _value.A);
|
||||
var newRect = new Rectangle(_cOK.X - 3, _cHex.Bottom + PickerMargin, 130, 0);
|
||||
newRect.Size.Y = 50;
|
||||
Render2D.FillRectangle(newRect, Color.White);
|
||||
var smallRectSize = 10;
|
||||
var numHor = Mathf.FloorToInt(newRect.Width / smallRectSize);
|
||||
var numVer = Mathf.FloorToInt(newRect.Height / smallRectSize);
|
||||
// Draw checkerboard for background of color to help with transparency
|
||||
for (int i = 0; i < numHor; i++)
|
||||
{
|
||||
for (int j = 0; j < numVer; j++)
|
||||
{
|
||||
if ((i + j) % 2 == 0 )
|
||||
{
|
||||
var rect = new Rectangle(newRect.X + smallRectSize * i, newRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
Render2D.FillRectangle(newRect, _value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -418,9 +418,19 @@ namespace FlaxEditor.GUI.Tabs
|
||||
{
|
||||
// If scroll bar is visible it covers part of the tab header so include this in tab size to improve usability
|
||||
if (_orientation == Orientation.Horizontal && TabsPanel.HScrollBar.Visible)
|
||||
{
|
||||
tabsSize.Y += TabsPanel.HScrollBar.Height;
|
||||
var style = Style.Current;
|
||||
TabsPanel.HScrollBar.TrackColor = style.Background;
|
||||
TabsPanel.HScrollBar.ThumbColor = style.ForegroundGrey;
|
||||
}
|
||||
else if (_orientation == Orientation.Vertical && TabsPanel.VScrollBar.Visible)
|
||||
{
|
||||
tabsSize.X += TabsPanel.VScrollBar.Width;
|
||||
var style = Style.Current;
|
||||
TabsPanel.VScrollBar.TrackColor = style.Background;
|
||||
TabsPanel.VScrollBar.ThumbColor = style.ForegroundGrey;
|
||||
}
|
||||
}
|
||||
|
||||
// Fit the tabs panel
|
||||
|
||||
@@ -698,6 +698,38 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
}
|
||||
|
||||
// Show tree guide lines
|
||||
if (Editor.Instance.Options.Options.Interface.ShowTreeLines)
|
||||
{
|
||||
TreeNode parentNode = Parent as TreeNode;
|
||||
bool thisNodeIsLast = false;
|
||||
while (parentNode != null && parentNode != ParentTree.Children[0])
|
||||
{
|
||||
float bottomOffset = 0;
|
||||
float topOffset = 0;
|
||||
|
||||
if (Parent == parentNode && this == Parent.Children[0])
|
||||
topOffset = 2;
|
||||
|
||||
if (thisNodeIsLast && parentNode.Children.Count == 1)
|
||||
bottomOffset = topOffset != 0 ? 4 : 2;
|
||||
|
||||
if (Parent == parentNode && this == Parent.Children[Parent.Children.Count - 1] && !_opened)
|
||||
{
|
||||
thisNodeIsLast = true;
|
||||
bottomOffset = topOffset != 0 ? 4 : 2;
|
||||
}
|
||||
|
||||
float leftOffset = 9;
|
||||
// Adjust offset for icon image
|
||||
if (_iconCollaped.IsValid)
|
||||
leftOffset += 18;
|
||||
var lineRect1 = new Rectangle(parentNode.TextRect.Left - leftOffset, parentNode.HeaderRect.Top + topOffset, 1, parentNode.HeaderRect.Height - bottomOffset);
|
||||
Render2D.FillRectangle(lineRect1, isSelected ? style.ForegroundGrey : style.LightBackground);
|
||||
parentNode = parentNode.Parent as TreeNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
if (_opened)
|
||||
{
|
||||
@@ -729,7 +761,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
|
||||
// Try to estimate the rough location of the first node, assuming the node height is constant
|
||||
var firstChildGlobalRect = GetChildGlobalRectangle(children[0], ref globalTransform);
|
||||
var firstVisibleChild = Math.Clamp((int)Math.Floor((globalClipping.Y - firstChildGlobalRect.Top) / firstChildGlobalRect.Height) + 1, 0, children.Count - 1);
|
||||
var firstVisibleChild = Math.Clamp((int)Math.Floor((globalClipping.Y - firstChildGlobalRect.Top) / _headerHeight) + 1, 0, children.Count - 1);
|
||||
if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top || !children[firstVisibleChild].Visible)
|
||||
{
|
||||
// Estimate overshoot, either it's partially visible or hidden in the tree
|
||||
|
||||
@@ -499,6 +499,15 @@ namespace FlaxEditor
|
||||
bool drawAnySelectedControl = false;
|
||||
var transformGizmo = TransformGizmo;
|
||||
var mousePos = PointFromWindow(RootWindow.MousePosition);
|
||||
if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver)
|
||||
{
|
||||
// Highlight control under mouse for easier selecting (except if already selected)
|
||||
if (RayCastControl(ref mousePos, out var hitControl) &&
|
||||
(transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl)))
|
||||
{
|
||||
DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl);
|
||||
}
|
||||
}
|
||||
if (transformGizmo != null)
|
||||
{
|
||||
// Selected UI controls outline
|
||||
@@ -511,15 +520,6 @@ namespace FlaxEditor
|
||||
}
|
||||
}
|
||||
}
|
||||
if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver)
|
||||
{
|
||||
// Highlight control under mouse for easier selecting (except if already selected)
|
||||
if (RayCastControl(ref mousePos, out var hitControl) &&
|
||||
(transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl)))
|
||||
{
|
||||
DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl);
|
||||
}
|
||||
}
|
||||
if (drawAnySelectedControl)
|
||||
Render2D.PopTransform();
|
||||
|
||||
@@ -617,40 +617,39 @@ namespace FlaxEditor
|
||||
// Draw sizing widgets
|
||||
if (_widgets == null)
|
||||
_widgets = new List<Widget>();
|
||||
var widgetSize = 8.0f;
|
||||
var widgetSize = 10.0f;
|
||||
var viewScale = ViewScale;
|
||||
if (viewScale < 0.7f)
|
||||
widgetSize *= viewScale;
|
||||
var controlSize = control.Size.Absolute.MinValue / 50.0f;
|
||||
if (controlSize < 1.0f)
|
||||
widgetSize *= Mathf.Clamp(controlSize + 0.1f, 0.1f, 1.0f);
|
||||
var cornerSize = new Float2(widgetSize);
|
||||
DrawControlWidget(uiControl, ref ul, ref mousePos, ref cornerSize, new Float2(-1, -1), CursorType.SizeNWSE);
|
||||
DrawControlWidget(uiControl, ref ur, ref mousePos, ref cornerSize, new Float2(1, -1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref bl, ref mousePos, ref cornerSize, new Float2(-1, 1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref br, ref mousePos, ref cornerSize, new Float2(1, 1), CursorType.SizeNWSE);
|
||||
var edgeSizeV = new Float2(widgetSize * 2, widgetSize);
|
||||
var edgeSizeH = new Float2(edgeSizeV.Y, edgeSizeV.X);
|
||||
var widgetHandleSize = new Float2(widgetSize);
|
||||
DrawControlWidget(uiControl, ref ul, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, -1), CursorType.SizeNWSE);
|
||||
DrawControlWidget(uiControl, ref ur, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, -1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref bl, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, 1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref br, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, 1), CursorType.SizeNWSE);
|
||||
Float2.Lerp(ref ul, ref bl, 0.5f, out var el);
|
||||
Float2.Lerp(ref ur, ref br, 0.5f, out var er);
|
||||
Float2.Lerp(ref ul, ref ur, 0.5f, out var eu);
|
||||
Float2.Lerp(ref bl, ref br, 0.5f, out var eb);
|
||||
DrawControlWidget(uiControl, ref el, ref mousePos, ref edgeSizeH, new Float2(-1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref er, ref mousePos, ref edgeSizeH, new Float2(1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref eu, ref mousePos, ref edgeSizeV, new Float2(0, -1), CursorType.SizeNS);
|
||||
DrawControlWidget(uiControl, ref eb, ref mousePos, ref edgeSizeV, new Float2(0, 1), CursorType.SizeNS);
|
||||
DrawControlWidget(uiControl, ref el, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref er, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref eu, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, -1), CursorType.SizeNS);
|
||||
DrawControlWidget(uiControl, ref eb, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, 1), CursorType.SizeNS);
|
||||
|
||||
// TODO: draw anchors
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, Float2 resizeAxis, CursorType cursor)
|
||||
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size,float scale, Float2 resizeAxis, CursorType cursor)
|
||||
{
|
||||
var style = Style.Current;
|
||||
var rect = new Rectangle(pos - size * 0.5f, size);
|
||||
var rect = new Rectangle((pos + resizeAxis * 10 * scale) - size * 0.5f, size);
|
||||
if (rect.Contains(ref mousePos))
|
||||
{
|
||||
Render2D.FillRectangle(rect, style.Foreground);
|
||||
Render2D.DrawRectangle(rect, style.SelectionBorder);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -210,6 +210,13 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface"), EditorOrder(310)]
|
||||
public bool SeparateValueAndUnit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the option to put a space between numbers and units for unit formatting.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Interface"), EditorOrder(320)]
|
||||
public bool ShowTreeLines { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamps prefix mode for output log messages.
|
||||
/// </summary>
|
||||
|
||||
@@ -15,6 +15,7 @@ using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
@@ -625,6 +626,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
var item = _dragScriptItems.Objects[i];
|
||||
var actorType = Editor.Instance.CodeEditing.Actors.Get(item);
|
||||
var scriptType = Editor.Instance.CodeEditing.Scripts.Get(item);
|
||||
if (actorType != ScriptType.Null)
|
||||
{
|
||||
var actor = actorType.CreateInstance() as Actor;
|
||||
@@ -639,6 +641,18 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
ActorNode.Root.Spawn(actor, spawnParent);
|
||||
actor.OrderInParent = newOrder;
|
||||
}
|
||||
else if (scriptType != ScriptType.Null)
|
||||
{
|
||||
if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn script of type " + actorType.TypeName);
|
||||
continue;
|
||||
}
|
||||
IUndoAction action = new AddRemoveScript(true, newParent, scriptType);
|
||||
Select();
|
||||
ActorNode.Root.Undo?.AddAction(action);
|
||||
action.Do();
|
||||
}
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
@@ -699,9 +713,9 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
private bool ValidateDragScriptItem(ScriptItem script)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null || Editor.Instance.CodeEditing.Scripts.Get(script) != ScriptType.Null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -351,6 +351,8 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private void OnCollectDrawCalls(ref RenderContext renderContext)
|
||||
{
|
||||
if (renderContext.View.Pass == DrawPass.Depth)
|
||||
return;
|
||||
DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext);
|
||||
if (ShowNavigation)
|
||||
Editor.Internal_DrawNavMesh();
|
||||
@@ -620,12 +622,12 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return Level.IsAnySceneLoaded;
|
||||
return Level.IsAnySceneLoaded && Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
return Level.IsAnySceneLoaded && Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -98,7 +98,6 @@ namespace FlaxEditor.Viewport
|
||||
ShowDebugDraw = true;
|
||||
ShowEditorPrimitives = true;
|
||||
Gizmos = new GizmosCollection(this);
|
||||
var inputOptions = window.Editor.Options.Options.Input;
|
||||
|
||||
// Prepare rendering task
|
||||
Task.ActorsSource = ActorsSources.CustomActors;
|
||||
@@ -219,6 +218,8 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private void OnCollectDrawCalls(ref RenderContext renderContext)
|
||||
{
|
||||
if (renderContext.View.Pass == DrawPass.Depth)
|
||||
return;
|
||||
DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext);
|
||||
_debugDrawData.OnDraw(ref renderContext);
|
||||
}
|
||||
@@ -498,7 +499,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return true;
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
|
||||
@@ -102,8 +102,8 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
|
||||
Swap(eventTimeMin, eventTimeMax);
|
||||
}
|
||||
}
|
||||
const float eventTime = animPos / static_cast<float>(anim->Data.FramesPerSecond);
|
||||
const float eventDeltaTime = (animPos - animPrevPos) / static_cast<float>(anim->Data.FramesPerSecond);
|
||||
const float eventTime = (float)(animPos / anim->Data.FramesPerSecond);
|
||||
const float eventDeltaTime = (float)((animPos - animPrevPos) / anim->Data.FramesPerSecond);
|
||||
for (const auto& track : anim->Events)
|
||||
{
|
||||
for (const auto& k : track.Second.GetKeyframes())
|
||||
@@ -211,7 +211,7 @@ float GetAnimSamplePos(float length, Animation* anim, float pos, float speed)
|
||||
}
|
||||
if (animPos < 0)
|
||||
animPos = animLength + animPos;
|
||||
animPos *= static_cast<float>(anim->Data.FramesPerSecond);
|
||||
animPos = (float)(animPos * anim->Data.FramesPerSecond);
|
||||
return animPos;
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
float nestedAnimPrevPos = animPrevPos - nestedAnim.Time;
|
||||
const float nestedAnimLength = nestedAnim.Anim->GetLength();
|
||||
const float nestedAnimSpeed = nestedAnim.Speed * speed;
|
||||
const float frameRateMatchScale = nestedAnimSpeed / (float)anim->Data.FramesPerSecond;
|
||||
const float frameRateMatchScale = (float)(nestedAnimSpeed / anim->Data.FramesPerSecond);
|
||||
nestedAnimPos = nestedAnimPos * frameRateMatchScale;
|
||||
nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale;
|
||||
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
|
||||
@@ -363,8 +363,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
// Check if animation looped
|
||||
if (animPos < animPrevPos)
|
||||
{
|
||||
const float endPos = anim->GetLength() * static_cast<float>(anim->Data.FramesPerSecond);
|
||||
const float timeToEnd = endPos - animPrevPos;
|
||||
const float endPos = (float)(anim->GetLength() * anim->Data.FramesPerSecond);
|
||||
|
||||
Transform rootBegin = refPose;
|
||||
rootChannel.Evaluate(0, &rootBegin, false);
|
||||
@@ -372,16 +371,13 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
Transform rootEnd = refPose;
|
||||
rootChannel.Evaluate(endPos, &rootEnd, false);
|
||||
|
||||
//rootChannel.Evaluate(animPos - timeToEnd, &rootNow, true);
|
||||
|
||||
// Complex motion calculation to preserve the looped movement
|
||||
// (end - before + now - begin)
|
||||
// It sums the motion since the last update to anim end and since the start to now
|
||||
if (motionPosition)
|
||||
srcNode.Translation = (rootEnd.Translation - rootBefore.Translation + rootNode.Translation - rootBegin.Translation) * motionPositionMask;
|
||||
if (motionRotation)
|
||||
srcNode.Orientation = rootEnd.Orientation * rootBefore.Orientation.Conjugated() * (rootNode.Orientation * rootBegin.Orientation.Conjugated());
|
||||
//srcNode.Orientation = Quaternion::Identity;
|
||||
srcNode.Orientation = (rootBefore.Orientation.Conjugated() * rootEnd.Orientation) * (rootBegin.Orientation.Conjugated() * rootNode.Orientation);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1165,21 +1161,21 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
const auto nodes = node->GetNodes(this);
|
||||
const auto basePoseNodes = static_cast<AnimGraphImpulse*>(valueA.AsPointer);
|
||||
const auto blendPoseNodes = static_cast<AnimGraphImpulse*>(valueB.AsPointer);
|
||||
const auto& refrenceNodes = _graph.BaseModel.Get()->GetNodes();
|
||||
Transform t, basePoseTransform, blendPoseTransform, refrenceTransform;
|
||||
const auto& refNodes = _graph.BaseModel.Get()->GetNodes();
|
||||
Transform t, basePoseTransform, blendPoseTransform, refTransform;
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
basePoseTransform = basePoseNodes->Nodes[i];
|
||||
blendPoseTransform = blendPoseNodes->Nodes[i];
|
||||
refrenceTransform = refrenceNodes[i].LocalTransform;
|
||||
refTransform = refNodes[i].LocalTransform;
|
||||
|
||||
// base + (blend - refrence) = transform
|
||||
t.Translation = basePoseTransform.Translation + (blendPoseTransform.Translation - refrenceTransform.Translation);
|
||||
auto diff = Quaternion::Invert(refrenceTransform.Orientation) * blendPoseTransform.Orientation;
|
||||
// base + (blend - reference)
|
||||
t.Translation = basePoseTransform.Translation + (blendPoseTransform.Translation - refTransform.Translation);
|
||||
auto diff = Quaternion::Invert(refTransform.Orientation) * blendPoseTransform.Orientation;
|
||||
t.Orientation = basePoseTransform.Orientation * diff;
|
||||
t.Scale = basePoseTransform.Scale + (blendPoseTransform.Scale - refrenceTransform.Scale);
|
||||
t.Scale = basePoseTransform.Scale + (blendPoseTransform.Scale - refTransform.Scale);
|
||||
|
||||
//lerp base and transform
|
||||
// Lerp base and transform
|
||||
Transform::Lerp(basePoseTransform, t, alpha, nodes->Nodes[i]);
|
||||
}
|
||||
Transform::Lerp(basePoseNodes->RootMotion, basePoseNodes->RootMotion + blendPoseNodes->RootMotion, alpha, nodes->RootMotion);
|
||||
|
||||
@@ -886,7 +886,8 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
instance = eatBox(node, box->FirstConnection());
|
||||
else
|
||||
instance.SetObject(object);
|
||||
if (!instance.AsObject)
|
||||
ScriptingObject* instanceObj = (ScriptingObject*)instance;
|
||||
if (!instanceObj)
|
||||
{
|
||||
LOG(Error, "Cannot bind event to null object.");
|
||||
PrintStack(LogType::Error);
|
||||
@@ -928,13 +929,13 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
}
|
||||
eventBinding->BindedMethods.Add(method);
|
||||
if (eventBinding->BindedMethods.Count() == 1)
|
||||
(*eventBinder)(instance.AsObject, object, true);
|
||||
(*eventBinder)(instanceObj, object, true);
|
||||
}
|
||||
else if (eventBinding)
|
||||
{
|
||||
// Unbind from the event
|
||||
if (eventBinding->BindedMethods.Count() == 1)
|
||||
(*eventBinder)(instance.AsObject, object, false);
|
||||
(*eventBinder)(instanceObj, object, false);
|
||||
eventBinding->BindedMethods.Remove(method);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,10 +218,7 @@ const Char* TypeId2TypeName(const uint32 typeId)
|
||||
}
|
||||
|
||||
FlaxStorage::FlaxStorage(const StringView& path)
|
||||
: _refCount(0)
|
||||
, _chunksLock(0)
|
||||
, _version(0)
|
||||
, _path(path)
|
||||
: _path(path)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -242,6 +239,7 @@ FlaxStorage::~FlaxStorage()
|
||||
if (stream)
|
||||
Delete(stream);
|
||||
}
|
||||
Platform::AtomicStore(&_files, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1327,6 +1325,7 @@ FileReadStream* FlaxStorage::OpenFile()
|
||||
LOG(Error, "Cannot open Flax Storage file \'{0}\'.", _path);
|
||||
return nullptr;
|
||||
}
|
||||
Platform::InterlockedIncrement(&_files);
|
||||
|
||||
// Create file reading stream
|
||||
stream = New<FileReadStream>(file);
|
||||
@@ -1336,11 +1335,13 @@ FileReadStream* FlaxStorage::OpenFile()
|
||||
|
||||
bool FlaxStorage::CloseFileHandles()
|
||||
{
|
||||
// Early out if no handles are opened
|
||||
Array<FileReadStream*> streams;
|
||||
_file.GetValues(streams);
|
||||
if (streams.IsEmpty() && Platform::AtomicRead(&_chunksLock) == 0)
|
||||
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
|
||||
{
|
||||
Array<FileReadStream*, InlinedAllocation<8>> streams;
|
||||
_file.GetValues(streams);
|
||||
ASSERT(streams.Count() == 0);
|
||||
return false;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
|
||||
// Note: this is usually called by the content manager when this file is not used or on exit
|
||||
@@ -1372,7 +1373,7 @@ bool FlaxStorage::CloseFileHandles()
|
||||
return true; // Failed, someone is still accessing the file
|
||||
|
||||
// Close file handles (from all threads)
|
||||
streams.Clear();
|
||||
Array<FileReadStream*, InlinedAllocation<8>> streams;
|
||||
_file.GetValues(streams);
|
||||
for (FileReadStream* stream : streams)
|
||||
{
|
||||
@@ -1380,6 +1381,7 @@ bool FlaxStorage::CloseFileHandles()
|
||||
Delete(stream);
|
||||
}
|
||||
_file.Clear();
|
||||
Platform::AtomicStore(&_files, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,8 +87,9 @@ public:
|
||||
|
||||
protected:
|
||||
// State
|
||||
int64 _refCount;
|
||||
int64 _chunksLock;
|
||||
int64 _refCount = 0;
|
||||
int64 _chunksLock = 0;
|
||||
int64 _files = 0;
|
||||
double _lastRefLostTime;
|
||||
CriticalSection _loadLocker;
|
||||
|
||||
@@ -97,7 +98,7 @@ protected:
|
||||
Array<FlaxChunk*> _chunks;
|
||||
|
||||
// Metadata
|
||||
uint32 _version;
|
||||
uint32 _version = 0;
|
||||
String _path;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -404,7 +404,7 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
|
||||
drawCall.Material = material;
|
||||
drawCall.World = world;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = _sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue();
|
||||
drawCall.ObjectRadius = (float)_sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue();
|
||||
drawCall.Surface.GeometrySize = _box.GetSize();
|
||||
drawCall.Surface.PrevWorld = world;
|
||||
drawCall.Surface.Lightmap = nullptr;
|
||||
@@ -472,7 +472,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
|
||||
drawCall.Material = material;
|
||||
drawCall.World = *info.World;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.Surface.GeometrySize = _box.GetSize();
|
||||
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
|
||||
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
|
||||
@@ -534,7 +534,7 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
|
||||
drawCall.Material = material;
|
||||
drawCall.World = *info.World;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.Surface.GeometrySize = _box.GetSize();
|
||||
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
|
||||
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
|
||||
|
||||
@@ -246,7 +246,7 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
|
||||
drawCall.Material = material;
|
||||
drawCall.World = *info.World;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.Surface.GeometrySize = _box.GetSize();
|
||||
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
|
||||
drawCall.Surface.Lightmap = nullptr;
|
||||
@@ -289,7 +289,7 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
|
||||
drawCall.Material = material;
|
||||
drawCall.World = *info.World;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
|
||||
drawCall.Surface.GeometrySize = _box.GetSize();
|
||||
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
|
||||
drawCall.Surface.Lightmap = nullptr;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Math/Vector4.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Content/SoftAssetReference.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Content/Assets/Texture.h"
|
||||
#include "Engine/Content/Assets/MaterialBase.h"
|
||||
|
||||
@@ -526,13 +526,13 @@ API_STRUCT() struct FLAXENGINE_API ToneMappingSettings : ISerializable
|
||||
ToneMappingSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the white balance in relation to the temperature of the light in the scene. When the light temperature and this one match the light will appear white. When a value is used that is higher than the light in the scene it will yield a "warm" or yellow color, and, conversely, if the value is lower, it would yield a "cool" or blue color. The default value is `6500`.
|
||||
/// Adjusts the white balance in relation to the temperature of the light in the scene. When the light temperature and this one match the light will appear white. When a value is used that is higher than the light in the scene it will yield a "warm" or yellow color, and, conversely, if the value is lower, it would yield a "cool" or blue color.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(1500, 15000), EditorOrder(0), PostProcessSetting((int)ToneMappingSettingsOverride.WhiteTemperature)")
|
||||
float WhiteTemperature = 6500.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temperature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural. The default value is `0`.
|
||||
/// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temperature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(-1, 1, 0.001f), EditorOrder(1), PostProcessSetting((int)ToneMappingSettingsOverride.WhiteTint)")
|
||||
float WhiteTint = 0.0f;
|
||||
@@ -1079,7 +1079,7 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
|
||||
CameraArtifactsSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// Strength of the vignette effect. Value 0 hides it. The default value is 0.4.
|
||||
/// Strength of the vignette effect. Value 0 hides it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)")
|
||||
float VignetteIntensity = 0.4f;
|
||||
@@ -1091,19 +1091,19 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
|
||||
Float3 VignetteColor = Float3(0, 0, 0.001f);
|
||||
|
||||
/// <summary>
|
||||
/// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125.
|
||||
/// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0001f, 2.0f, 0.001f), EditorOrder(2), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteShapeFactor)")
|
||||
float VignetteShapeFactor = 0.125f;
|
||||
|
||||
/// <summary>
|
||||
/// Intensity of the grain filter. A value of 0 hides it. The default value is 0.005.
|
||||
/// Intensity of the grain filter. A value of 0 hides it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0f, 2.0f, 0.005f), EditorOrder(3), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainAmount)")
|
||||
float GrainAmount = 0.006f;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the grain particles. The default value is 1.6.
|
||||
/// Size of the grain particles.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(1.0f, 3.0f, 0.01f), EditorOrder(4), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainParticleSize)")
|
||||
float GrainParticleSize = 1.6f;
|
||||
@@ -1731,49 +1731,49 @@ API_STRUCT() struct FLAXENGINE_API ScreenSpaceReflectionsSettings : ISerializabl
|
||||
ReflectionsTraceMode TraceMode = ReflectionsTraceMode::ScreenTracing;
|
||||
|
||||
/// <summary>
|
||||
/// The depth buffer downscale option to optimize raycast performance. Full gives better quality, but half improves performance. The default value is half.
|
||||
/// The depth buffer downscale option to optimize raycast performance. Full gives better quality, but half improves performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(2), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.DepthResolution)")
|
||||
ResolutionMode DepthResolution = ResolutionMode::Half;
|
||||
|
||||
/// <summary>
|
||||
/// The raycast resolution. Full gives better quality, but half improves performance. The default value is half.
|
||||
/// The raycast resolution. Full gives better quality, but half improves performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(3), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RayTracePassResolution)")
|
||||
ResolutionMode RayTracePassResolution = ResolutionMode::Half;
|
||||
|
||||
/// <summary>
|
||||
/// The reflection spread parameter. This value controls source roughness effect on reflections blur. Smaller values produce wider reflections spread but also introduce more noise. Higher values provide more mirror-like reflections. Default value is 0.82.
|
||||
/// The reflection spread parameter. This value controls source roughness effect on reflections blur. Smaller values produce wider reflections spread but also introduce more noise. Higher values provide more mirror-like reflections.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(10), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.BRDFBias), EditorDisplay(null, \"BRDF Bias\")")
|
||||
float BRDFBias = 0.82f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of roughness a material must have to reflect the scene. For example, if this value is set to 0.4, only materials with a roughness value of 0.4 or below reflect the scene. The default value is 0.45.
|
||||
/// The maximum amount of roughness a material must have to reflect the scene. For example, if this value is set to 0.4, only materials with a roughness value of 0.4 or below reflect the scene.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(15), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RoughnessThreshold)")
|
||||
float RoughnessThreshold = 0.45f;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of the raycast origin. Lower values produce more correct reflection placement, but produce more artifacts. We recommend values of 0.3 or lower. The default value is 0.1.
|
||||
/// The offset of the raycast origin. Lower values produce more correct reflection placement, but produce more artifacts. We recommend values of 0.3 or lower.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(20), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.WorldAntiSelfOcclusionBias)")
|
||||
float WorldAntiSelfOcclusionBias = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// The raycast resolution. Full gives better quality, but half improves performance. The default value is half.
|
||||
/// The raycast resolution. Full gives better quality, but half improves performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(25), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolvePassResolution)")
|
||||
ResolutionMode ResolvePassResolution = ResolutionMode::Full;
|
||||
|
||||
/// <summary>
|
||||
/// The number of rays used to resolve the reflection color. Higher values provide better quality but reduce effect performance. Default value is 4. Use 1 for the highest speed.
|
||||
/// The number of rays used to resolve the reflection color. Higher values provide better quality but reduce effect performance. Use value of 1 for the best performance at cost of quality.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(1, 8), EditorOrder(26), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolveSamples)")
|
||||
int32 ResolveSamples = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The point at which the far edges of the reflection begin to fade. Has no effect on performance. The default value is 0.1.
|
||||
/// The point at which the far edges of the reflection begin to fade. Has no effect on performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 1.0f, 0.02f), EditorOrder(30), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.EdgeFadeFactor)")
|
||||
float EdgeFadeFactor = 0.1f;
|
||||
@@ -1803,13 +1803,13 @@ API_STRUCT() struct FLAXENGINE_API ScreenSpaceReflectionsSettings : ISerializabl
|
||||
bool TemporalEffect = true;
|
||||
|
||||
/// <summary>
|
||||
/// The intensity of the temporal effect. Lower values produce reflections faster, but more noise. The default value is 8.
|
||||
/// The intensity of the temporal effect. Lower values produce reflections faster, but more noise.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 20.0f, 0.5f), EditorOrder(55), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalScale)")
|
||||
float TemporalScale = 8.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Defines how quickly reflections blend between the reflection in the current frame and the history buffer. Lower values produce reflections faster, but with more jittering. If the camera in your game doesn't move much, we recommend values closer to 1. The default value is 0.8.
|
||||
/// Defines how quickly reflections blend between the reflection in the current frame and the history buffer. Lower values produce reflections faster, but with more jittering. If the camera in your game doesn't move much, we recommend values closer to 1.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.05f, 1.0f, 0.01f), EditorOrder(60), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalResponse)")
|
||||
float TemporalResponse = 0.8f;
|
||||
@@ -2015,7 +2015,7 @@ API_STRUCT() struct FLAXENGINE_API PostProcessSettings : ISerializable
|
||||
ScreenSpaceReflectionsSettings ScreenSpaceReflections;
|
||||
|
||||
/// <summary>
|
||||
/// The anti-aliasing effect settings.
|
||||
/// The antialiasing effect settings.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorDisplay(\"Anti Aliasing\"), EditorOrder(1100), JsonProperty(\"AA\")")
|
||||
AntiAliasingSettings AntiAliasing;
|
||||
|
||||
@@ -354,9 +354,9 @@ public:
|
||||
// Applies the render origin to the transformation instance matrix.
|
||||
FORCE_INLINE void GetWorldMatrix(Matrix& world) const
|
||||
{
|
||||
world.M41 -= Origin.X;
|
||||
world.M42 -= Origin.Y;
|
||||
world.M43 -= Origin.Z;
|
||||
world.M41 -= (float)Origin.X;
|
||||
world.M42 -= (float)Origin.Y;
|
||||
world.M43 -= (float)Origin.Z;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -609,7 +609,10 @@ void Actor::SetIsActive(bool value)
|
||||
|
||||
void Actor::SetStaticFlags(StaticFlags value)
|
||||
{
|
||||
if (_staticFlags == value)
|
||||
return;
|
||||
_staticFlags = value;
|
||||
OnStaticFlagsChanged();
|
||||
}
|
||||
|
||||
void Actor::SetTransform(const Transform& value)
|
||||
@@ -1229,6 +1232,14 @@ void Actor::OnOrderInParentChanged()
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorOrderInParentChanged, this, nullptr);
|
||||
}
|
||||
|
||||
void Actor::OnStaticFlagsChanged()
|
||||
{
|
||||
}
|
||||
|
||||
void Actor::OnLayerChanged()
|
||||
{
|
||||
}
|
||||
|
||||
BoundingBox Actor::GetBoxWithChildren() const
|
||||
{
|
||||
BoundingBox result = GetBox();
|
||||
|
||||
@@ -886,14 +886,12 @@ public:
|
||||
/// Gets rotation of the actor oriented towards the specified world position with upwards direction.
|
||||
/// </summary>
|
||||
/// <param name="worldPos">The world position to orient towards.</param>
|
||||
/// <param name="worldUp">The up direction that Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param>
|
||||
/// <param name="worldUp">The up direction that constrains up axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param>
|
||||
API_FUNCTION() Quaternion LookingAt(const Vector3& worldPos, const Vector3& worldUp) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Execute custom action on actors tree.
|
||||
/// Action should returns false to stop calling deeper.
|
||||
/// First action argument is current actor object.
|
||||
/// </summary>
|
||||
/// <param name="action">Actor to call on every actor in the tree. Returns true if keep calling deeper.</param>
|
||||
/// <param name="args">Custom arguments for the function</param>
|
||||
@@ -903,14 +901,12 @@ public:
|
||||
if (action(this, args...))
|
||||
{
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
Children[i]->TreeExecute<Params...>(action, args...);
|
||||
Children.Get()[i]->TreeExecute<Params...>(action, args...);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute custom action on actor children tree.
|
||||
/// Action should returns false to stop calling deeper.
|
||||
/// First action argument is current actor object.
|
||||
/// </summary>
|
||||
/// <param name="action">Actor to call on every actor in the tree. Returns true if keep calling deeper.</param>
|
||||
/// <param name="args">Custom arguments for the function</param>
|
||||
@@ -918,7 +914,7 @@ public:
|
||||
void TreeExecuteChildren(Function<bool(Actor*, Params ...)>& action, Params ... args)
|
||||
{
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
Children[i]->TreeExecute<Params...>(action, args...);
|
||||
Children.Get()[i]->TreeExecute<Params...>(action, args...);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1016,12 +1012,15 @@ public:
|
||||
/// </summary>
|
||||
virtual void OnOrderInParentChanged();
|
||||
|
||||
/// <summary>
|
||||
/// Called when actor static flag gets changed.
|
||||
/// </summary>
|
||||
virtual void OnStaticFlagsChanged();
|
||||
|
||||
/// <summary>
|
||||
/// Called when layer gets changed.
|
||||
/// </summary>
|
||||
virtual void OnLayerChanged()
|
||||
{
|
||||
}
|
||||
virtual void OnLayerChanged();
|
||||
|
||||
/// <summary>
|
||||
/// Called when adding object to the game.
|
||||
|
||||
@@ -33,7 +33,7 @@ void PostFxVolume::Collect(RenderContext& renderContext)
|
||||
}
|
||||
}
|
||||
|
||||
if (weight > ZeroTolerance)
|
||||
if (weight > ZeroTolerance && renderContext.View.RenderLayersMask.HasLayer(GetLayer()))
|
||||
{
|
||||
const float totalSizeSqrt = (_transform.Scale * _size).LengthSquared();
|
||||
renderContext.List->AddSettingsBlend((IPostFxSettingsProvider*)this, weight, _priority, totalSizeSqrt);
|
||||
|
||||
@@ -99,7 +99,7 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M
|
||||
DrawCall drawCall;
|
||||
drawCall.World = world;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = _sphere.Radius;
|
||||
drawCall.ObjectRadius = (float)_sphere.Radius;
|
||||
drawCall.Surface.GeometrySize = _box.GetSize();
|
||||
drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1);
|
||||
drawCall.PerInstanceRandom = GetPerInstanceRandom();
|
||||
|
||||
@@ -410,7 +410,7 @@ void SplineModel::Draw(RenderContext& renderContext)
|
||||
const Transform splineTransform = GetTransform();
|
||||
renderContext.View.GetWorldMatrix(splineTransform, drawCall.World);
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation();
|
||||
drawCall.ObjectRadius = _sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline
|
||||
drawCall.ObjectRadius = (float)_sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline
|
||||
const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant();
|
||||
for (int32 segment = 0; segment < _instances.Count(); segment++)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,12 @@ bool SceneAsset::IsInternalType() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneNavigation::Clear()
|
||||
{
|
||||
Volumes.Clear();
|
||||
Actors.Clear();
|
||||
}
|
||||
|
||||
BoundingBox SceneNavigation::GetNavigationBounds()
|
||||
{
|
||||
if (Volumes.IsEmpty())
|
||||
@@ -373,6 +379,7 @@ void Scene::EndPlay()
|
||||
// Improve scene cleanup performance by removing all data from scene rendering and ticking containers
|
||||
Ticking.Clear();
|
||||
Rendering.Clear();
|
||||
Navigation.Clear();
|
||||
|
||||
// Base
|
||||
Actor::EndPlay();
|
||||
|
||||
@@ -23,6 +23,17 @@ public:
|
||||
/// </summary>
|
||||
Array<NavMesh*> Meshes;
|
||||
|
||||
/// <summary>
|
||||
/// The list of registered navigation-relevant actors (on the scene).
|
||||
/// </summary>
|
||||
Array<Actor*> Actors;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Clears this instance data.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total navigation volumes bounds.
|
||||
/// </summary>
|
||||
|
||||
@@ -18,7 +18,7 @@ class FLAXENGINE_API SceneQuery
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Try to find actor hit by the given ray
|
||||
/// Try to find actor hit by the given ray.
|
||||
/// </summary>
|
||||
/// <param name="ray">Ray to test</param>
|
||||
/// <returns>Hit actor or nothing</returns>
|
||||
@@ -55,19 +55,16 @@ public:
|
||||
public:
|
||||
/// <summary>
|
||||
/// Execute custom action on actors tree.
|
||||
/// Action should returns false to stop calling deeper.
|
||||
/// First action argument is current actor object.
|
||||
/// </summary>
|
||||
/// <param name="action">Actor to call on every actor in the tree. Returns true if keep calling deeper.</param>
|
||||
/// <param name="args">Custom arguments for the function</param>
|
||||
template<typename... Params>
|
||||
static void TreeExecute(Function<bool(Actor*, Params ...)>& action, Params ... args)
|
||||
static void TreeExecute(Function<bool(Actor*, Params...)>& action, Params... args)
|
||||
{
|
||||
#if SCENE_QUERIES_WITH_LOCK
|
||||
ScopeLock lock(Level::ScenesLock);
|
||||
#endif
|
||||
|
||||
for (int32 i = 0; i < Level::Scenes.Count(); i++)
|
||||
Level::Scenes[i]->TreeExecute<Params...>(action, args...);
|
||||
Level::Scenes.Get()[i]->TreeExecute<Params...>(action, args...);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "NavLink.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
|
||||
NavLink::NavLink(const SpawnParams& params)
|
||||
@@ -62,6 +63,20 @@ void NavLink::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
|
||||
DESERIALIZE(BiDirectional);
|
||||
}
|
||||
|
||||
void NavLink::OnEnable()
|
||||
{
|
||||
GetScene()->Navigation.Actors.Add(this);
|
||||
|
||||
Actor::OnEnable();
|
||||
}
|
||||
|
||||
void NavLink::OnDisable()
|
||||
{
|
||||
Actor::OnDisable();
|
||||
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
}
|
||||
|
||||
void NavLink::OnTransformChanged()
|
||||
{
|
||||
// Base
|
||||
|
||||
@@ -46,6 +46,8 @@ public:
|
||||
#endif
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
|
||||
protected:
|
||||
// [Actor]
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Level/SceneQuery.h"
|
||||
#include <ThirdParty/recastnavigation/Recast.h>
|
||||
#include <ThirdParty/recastnavigation/DetourNavMeshBuilder.h>
|
||||
#include <ThirdParty/recastnavigation/DetourNavMesh.h>
|
||||
@@ -68,7 +67,7 @@ struct Modifier
|
||||
NavAreaProperties* NavArea;
|
||||
};
|
||||
|
||||
struct NavigationSceneRasterization
|
||||
struct NavSceneRasterizer
|
||||
{
|
||||
NavMesh* NavMesh;
|
||||
BoundingBox TileBoundsNavMesh;
|
||||
@@ -83,7 +82,7 @@ struct NavigationSceneRasterization
|
||||
Array<Modifier>* Modifiers;
|
||||
const bool IsWorldToNavMeshIdentity;
|
||||
|
||||
NavigationSceneRasterization(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
|
||||
NavSceneRasterizer(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
|
||||
: TileBoundsNavMesh(tileBoundsNavMesh)
|
||||
, WorldToNavMesh(worldToNavMesh)
|
||||
, IsWorldToNavMeshIdentity(worldToNavMesh.IsIdentity())
|
||||
@@ -103,35 +102,20 @@ struct NavigationSceneRasterization
|
||||
auto& ib = IndexBuffer;
|
||||
if (vb.IsEmpty() || ib.IsEmpty())
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
|
||||
// Rasterize triangles
|
||||
const Float3* vbData = vb.Get();
|
||||
const int32* ibData = ib.Get();
|
||||
Float3 v0, v1, v2;
|
||||
if (IsWorldToNavMeshIdentity)
|
||||
{
|
||||
// Faster path
|
||||
for (int32 i0 = 0; i0 < ib.Count();)
|
||||
{
|
||||
auto v0 = vb[ib[i0++]];
|
||||
auto v1 = vb[ib[i0++]];
|
||||
auto v2 = vb[ib[i0++]];
|
||||
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
|
||||
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
|
||||
#endif
|
||||
|
||||
auto n = Float3::Cross(v0 - v1, v0 - v2);
|
||||
n.Normalize();
|
||||
const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : 0;
|
||||
rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Transform vertices from world space into the navmesh space
|
||||
const Matrix worldToNavMesh = WorldToNavMesh;
|
||||
for (int32 i0 = 0; i0 < ib.Count();)
|
||||
{
|
||||
auto v0 = Float3::Transform(vb[ib[i0++]], worldToNavMesh);
|
||||
auto v1 = Float3::Transform(vb[ib[i0++]], worldToNavMesh);
|
||||
auto v2 = Float3::Transform(vb[ib[i0++]], worldToNavMesh);
|
||||
v0 = vbData[ibData[i0++]];
|
||||
v1 = vbData[ibData[i0++]];
|
||||
v2 = vbData[ibData[i0++]];
|
||||
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
|
||||
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
|
||||
#endif
|
||||
@@ -142,6 +126,29 @@ struct NavigationSceneRasterization
|
||||
rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Transform vertices from world space into the navmesh space
|
||||
const Matrix worldToNavMesh = WorldToNavMesh;
|
||||
for (int32 i0 = 0; i0 < ib.Count();)
|
||||
{
|
||||
Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v0);
|
||||
Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v1);
|
||||
Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v2);
|
||||
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
|
||||
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
|
||||
#endif
|
||||
|
||||
auto n = Float3::Cross(v0 - v1, v0 - v2);
|
||||
n.Normalize();
|
||||
const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : RC_NULL_AREA;
|
||||
rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear after use
|
||||
vb.Clear();
|
||||
ib.Clear();
|
||||
}
|
||||
|
||||
static void TriangulateBox(Array<Float3>& vb, Array<int32>& ib, const OrientedBoundingBox& box)
|
||||
@@ -215,88 +222,67 @@ struct NavigationSceneRasterization
|
||||
}
|
||||
}
|
||||
|
||||
static bool Walk(Actor* actor, NavigationSceneRasterization& e)
|
||||
void Rasterize(Actor* actor)
|
||||
{
|
||||
// Early out if object is not intersecting with the tile bounds or is not using navigation
|
||||
if (!actor->GetIsActive() || !(actor->GetStaticFlags() & StaticFlags::Navigation))
|
||||
return true;
|
||||
BoundingBox actorBoxNavMesh;
|
||||
BoundingBox::Transform(actor->GetBox(), e.WorldToNavMesh, actorBoxNavMesh);
|
||||
if (!actorBoxNavMesh.Intersects(e.TileBoundsNavMesh))
|
||||
return true;
|
||||
|
||||
// Prepare buffers (for triangles)
|
||||
auto& vb = e.VertexBuffer;
|
||||
auto& ib = e.IndexBuffer;
|
||||
vb.Clear();
|
||||
ib.Clear();
|
||||
|
||||
// Extract data from the actor
|
||||
if (const auto* boxCollider = dynamic_cast<BoxCollider*>(actor))
|
||||
{
|
||||
if (boxCollider->GetIsTrigger())
|
||||
return true;
|
||||
return;
|
||||
PROFILE_CPU_NAMED("BoxCollider");
|
||||
|
||||
const OrientedBoundingBox box = boxCollider->GetOrientedBox();
|
||||
TriangulateBox(vb, ib, box);
|
||||
|
||||
e.RasterizeTriangles();
|
||||
TriangulateBox(VertexBuffer, IndexBuffer, box);
|
||||
RasterizeTriangles();
|
||||
}
|
||||
else if (const auto* sphereCollider = dynamic_cast<SphereCollider*>(actor))
|
||||
{
|
||||
if (sphereCollider->GetIsTrigger())
|
||||
return true;
|
||||
return;
|
||||
PROFILE_CPU_NAMED("SphereCollider");
|
||||
|
||||
const BoundingSphere sphere = sphereCollider->GetSphere();
|
||||
TriangulateSphere(vb, ib, sphere);
|
||||
|
||||
e.RasterizeTriangles();
|
||||
TriangulateSphere(VertexBuffer, IndexBuffer, sphere);
|
||||
RasterizeTriangles();
|
||||
}
|
||||
else if (const auto* capsuleCollider = dynamic_cast<CapsuleCollider*>(actor))
|
||||
{
|
||||
if (capsuleCollider->GetIsTrigger())
|
||||
return true;
|
||||
return;
|
||||
PROFILE_CPU_NAMED("CapsuleCollider");
|
||||
|
||||
const BoundingBox box = capsuleCollider->GetBox();
|
||||
TriangulateBox(vb, ib, box);
|
||||
|
||||
e.RasterizeTriangles();
|
||||
TriangulateBox(VertexBuffer, IndexBuffer, box);
|
||||
RasterizeTriangles();
|
||||
}
|
||||
else if (const auto* meshCollider = dynamic_cast<MeshCollider*>(actor))
|
||||
{
|
||||
if (meshCollider->GetIsTrigger())
|
||||
return true;
|
||||
return;
|
||||
PROFILE_CPU_NAMED("MeshCollider");
|
||||
|
||||
auto collisionData = meshCollider->CollisionData.Get();
|
||||
if (!collisionData || collisionData->WaitForLoaded())
|
||||
return true;
|
||||
|
||||
collisionData->ExtractGeometry(vb, ib);
|
||||
return;
|
||||
|
||||
collisionData->ExtractGeometry(VertexBuffer, IndexBuffer);
|
||||
Matrix meshColliderToWorld;
|
||||
meshCollider->GetLocalToWorldMatrix(meshColliderToWorld);
|
||||
for (auto& v : vb)
|
||||
for (auto& v : VertexBuffer)
|
||||
Float3::Transform(v, meshColliderToWorld, v);
|
||||
|
||||
e.RasterizeTriangles();
|
||||
RasterizeTriangles();
|
||||
}
|
||||
else if (const auto* splineCollider = dynamic_cast<SplineCollider*>(actor))
|
||||
{
|
||||
if (splineCollider->GetIsTrigger())
|
||||
return true;
|
||||
return;
|
||||
PROFILE_CPU_NAMED("SplineCollider");
|
||||
|
||||
auto collisionData = splineCollider->CollisionData.Get();
|
||||
if (!collisionData || collisionData->WaitForLoaded())
|
||||
return true;
|
||||
return;
|
||||
|
||||
splineCollider->ExtractGeometry(vb, ib);
|
||||
|
||||
e.RasterizeTriangles();
|
||||
splineCollider->ExtractGeometry(VertexBuffer, IndexBuffer);
|
||||
RasterizeTriangles();
|
||||
}
|
||||
else if (const auto* terrain = dynamic_cast<Terrain*>(actor))
|
||||
{
|
||||
@@ -306,13 +292,13 @@ struct NavigationSceneRasterization
|
||||
{
|
||||
const auto patch = terrain->GetPatch(patchIndex);
|
||||
BoundingBox patchBoundsNavMesh;
|
||||
BoundingBox::Transform(patch->GetBounds(), e.WorldToNavMesh, patchBoundsNavMesh);
|
||||
if (!patchBoundsNavMesh.Intersects(e.TileBoundsNavMesh))
|
||||
BoundingBox::Transform(patch->GetBounds(), WorldToNavMesh, patchBoundsNavMesh);
|
||||
if (!patchBoundsNavMesh.Intersects(TileBoundsNavMesh))
|
||||
continue;
|
||||
|
||||
patch->ExtractCollisionGeometry(vb, ib);
|
||||
|
||||
e.RasterizeTriangles();
|
||||
// TODO: get collision only from tile area
|
||||
patch->ExtractCollisionGeometry(VertexBuffer, IndexBuffer);
|
||||
RasterizeTriangles();
|
||||
}
|
||||
}
|
||||
else if (const auto* navLink = dynamic_cast<NavLink*>(actor))
|
||||
@@ -321,44 +307,33 @@ struct NavigationSceneRasterization
|
||||
|
||||
OffMeshLink link;
|
||||
link.Start = navLink->GetTransform().LocalToWorld(navLink->Start);
|
||||
Float3::Transform(link.Start, e.WorldToNavMesh, link.Start);
|
||||
Float3::Transform(link.Start, WorldToNavMesh, link.Start);
|
||||
link.End = navLink->GetTransform().LocalToWorld(navLink->End);
|
||||
Float3::Transform(link.End, e.WorldToNavMesh, link.End);
|
||||
Float3::Transform(link.End, WorldToNavMesh, link.End);
|
||||
link.Radius = navLink->Radius;
|
||||
link.BiDir = navLink->BiDirectional;
|
||||
link.Id = GetHash(navLink->GetID());
|
||||
|
||||
e.OffMeshLinks->Add(link);
|
||||
OffMeshLinks->Add(link);
|
||||
}
|
||||
else if (const auto* navModifierVolume = dynamic_cast<NavModifierVolume*>(actor))
|
||||
{
|
||||
if (navModifierVolume->AgentsMask.IsNavMeshSupported(e.NavMesh->Properties))
|
||||
if (navModifierVolume->AgentsMask.IsNavMeshSupported(NavMesh->Properties))
|
||||
{
|
||||
PROFILE_CPU_NAMED("NavModifierVolume");
|
||||
|
||||
Modifier modifier;
|
||||
OrientedBoundingBox bounds = navModifierVolume->GetOrientedBox();
|
||||
bounds.Transform(e.WorldToNavMesh);
|
||||
bounds.Transform(WorldToNavMesh);
|
||||
bounds.GetBoundingBox(modifier.Bounds);
|
||||
modifier.NavArea = navModifierVolume->GetNavArea();
|
||||
|
||||
e.Modifiers->Add(modifier);
|
||||
Modifiers->Add(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void RasterizeGeometry(NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
|
||||
{
|
||||
PROFILE_CPU_NAMED("RasterizeGeometry");
|
||||
|
||||
NavigationSceneRasterization rasterization(navMesh, tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks, modifiers);
|
||||
Function<bool(Actor*, NavigationSceneRasterization&)> treeWalkFunction(NavigationSceneRasterization::Walk);
|
||||
SceneQuery::TreeExecute<NavigationSceneRasterization&>(treeWalkFunction, rasterization);
|
||||
}
|
||||
|
||||
// Builds navmesh tile bounds and check if there are any valid navmesh volumes at that tile location
|
||||
// Returns true if tile is intersecting with any navmesh bounds volume actor - which means tile is in use
|
||||
bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, float tileSize, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh)
|
||||
@@ -455,11 +430,44 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
|
||||
|
||||
Array<OffMeshLink> offMeshLinks;
|
||||
Array<Modifier> modifiers;
|
||||
RasterizeGeometry(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers);
|
||||
{
|
||||
PROFILE_CPU_NAMED("RasterizeGeometry");
|
||||
NavSceneRasterizer rasterizer(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers);
|
||||
|
||||
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield);
|
||||
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield);
|
||||
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, *heightfield);
|
||||
// Collect actors to rasterize
|
||||
Array<Actor*> actors;
|
||||
{
|
||||
PROFILE_CPU_NAMED("CollectActors");
|
||||
ScopeLock lock(Level::ScenesLock);
|
||||
for (Scene* scene : Level::Scenes)
|
||||
{
|
||||
for (Actor* actor : scene->Navigation.Actors)
|
||||
{
|
||||
BoundingBox actorBoxNavMesh;
|
||||
BoundingBox::Transform(actor->GetBox(), rasterizer.WorldToNavMesh, actorBoxNavMesh);
|
||||
if (actorBoxNavMesh.Intersects(rasterizer.TileBoundsNavMesh) &&
|
||||
actor->IsActiveInHierarchy() &&
|
||||
EnumHasAllFlags(actor->GetStaticFlags(), StaticFlags::Navigation))
|
||||
{
|
||||
actors.Add(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rasterize actors
|
||||
for (Actor* actor : actors)
|
||||
{
|
||||
rasterizer.Rasterize(actor);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_CPU_NAMED("FilterHeightfield");
|
||||
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield);
|
||||
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield);
|
||||
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, *heightfield);
|
||||
}
|
||||
|
||||
rcCompactHeightfield* compactHeightfield = rcAllocCompactHeightfield();
|
||||
if (!compactHeightfield)
|
||||
@@ -467,39 +475,51 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
|
||||
LOG(Warning, "Could not generate navmesh: Out of memory compact heightfield.");
|
||||
return true;
|
||||
}
|
||||
if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build compact data.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("CompactHeightfield");
|
||||
if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build compact data.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcFreeHeightField(heightfield);
|
||||
|
||||
if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not erode.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("ErodeWalkableArea");
|
||||
if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not erode.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark areas
|
||||
for (auto& modifier : modifiers)
|
||||
{
|
||||
const unsigned char areaId = modifier.NavArea ? modifier.NavArea->Id : RC_NULL_AREA;
|
||||
Float3 bMin = modifier.Bounds.Minimum;
|
||||
Float3 bMax = modifier.Bounds.Maximum;
|
||||
rcMarkBoxArea(&context, &bMin.X, &bMax.X, areaId, *compactHeightfield);
|
||||
PROFILE_CPU_NAMED("MarkModifiers");
|
||||
for (auto& modifier : modifiers)
|
||||
{
|
||||
const unsigned char areaId = modifier.NavArea ? modifier.NavArea->Id : RC_NULL_AREA;
|
||||
Float3 bMin = modifier.Bounds.Minimum;
|
||||
Float3 bMax = modifier.Bounds.Maximum;
|
||||
rcMarkBoxArea(&context, &bMin.X, &bMax.X, areaId, *compactHeightfield);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rcBuildDistanceField(&context, *compactHeightfield))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build distance field.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("BuildDistanceField");
|
||||
if (!rcBuildDistanceField(&context, *compactHeightfield))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build distance field.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rcBuildRegions(&context, *compactHeightfield, config.borderSize, config.minRegionArea, config.mergeRegionArea))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build regions.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("BuildRegions");
|
||||
if (!rcBuildRegions(&context, *compactHeightfield, config.borderSize, config.minRegionArea, config.mergeRegionArea))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build regions.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcContourSet* contourSet = rcAllocContourSet();
|
||||
@@ -508,10 +528,13 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
|
||||
LOG(Warning, "Could not generate navmesh: Out of memory for contour set.");
|
||||
return true;
|
||||
}
|
||||
if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not create contours.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("BuildContours");
|
||||
if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not create contours.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcPolyMesh* polyMesh = rcAllocPolyMesh();
|
||||
@@ -520,10 +543,13 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
|
||||
LOG(Warning, "Could not generate navmesh: Out of memory for poly mesh.");
|
||||
return true;
|
||||
}
|
||||
if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not triangulate contours.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("BuildPolyMesh");
|
||||
if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not triangulate contours.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcPolyMeshDetail* detailMesh = rcAllocPolyMeshDetail();
|
||||
@@ -532,20 +558,20 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
|
||||
LOG(Warning, "Could not generate navmesh: Out of memory for detail mesh.");
|
||||
return true;
|
||||
}
|
||||
if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build detail mesh.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("BuildPolyMeshDetail");
|
||||
if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh))
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh: Could not build detail mesh.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcFreeCompactHeightfield(compactHeightfield);
|
||||
rcFreeContourSet(contourSet);
|
||||
|
||||
for (int i = 0; i < polyMesh->npolys; i++)
|
||||
{
|
||||
polyMesh->flags[i] = polyMesh->areas[i] != RC_NULL_AREA ? 1 : 0;
|
||||
}
|
||||
|
||||
if (polyMesh->nverts == 0)
|
||||
{
|
||||
// Empty tile
|
||||
@@ -623,15 +649,18 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
|
||||
// Generate navmesh tile data
|
||||
unsigned char* navData = nullptr;
|
||||
int navDataSize = 0;
|
||||
if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize))
|
||||
{
|
||||
LOG(Warning, "Could not build Detour navmesh.");
|
||||
return true;
|
||||
PROFILE_CPU_NAMED("CreateNavMeshData");
|
||||
if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize))
|
||||
{
|
||||
LOG(Warning, "Could not build Detour navmesh.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ASSERT_LOW_LAYER(navDataSize > 4 && *(uint32*)navData == DT_NAVMESH_MAGIC); // Sanity check for Detour header
|
||||
|
||||
{
|
||||
PROFILE_CPU_NAMED("Navigation.CreateTile");
|
||||
PROFILE_CPU_NAMED("CreateTiles");
|
||||
|
||||
ScopeLock lock(runtime->Locker);
|
||||
|
||||
@@ -729,17 +758,13 @@ public:
|
||||
bool Run() override
|
||||
{
|
||||
PROFILE_CPU_NAMED("BuildNavMeshTile");
|
||||
|
||||
const auto navMesh = NavMesh.Get();
|
||||
if (!navMesh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (GenerateTile(NavMesh, Runtime, X, Y, TileBoundsNavMesh, WorldToNavMesh, TileSize, Config))
|
||||
{
|
||||
LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -776,7 +801,7 @@ void OnSceneUnloading(Scene* scene, const Guid& sceneId)
|
||||
{
|
||||
NavBuildTasksLocker.Unlock();
|
||||
|
||||
// Cancel task but without locking queue from this thread to prevent dead-locks
|
||||
// Cancel task but without locking queue from this thread to prevent deadlocks
|
||||
task->Cancel();
|
||||
|
||||
NavBuildTasksLocker.Lock();
|
||||
@@ -815,7 +840,7 @@ float NavMeshBuilder::GetNavMeshBuildingProgress()
|
||||
return result;
|
||||
}
|
||||
|
||||
void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize)
|
||||
void BuildTileAsync(NavMesh* navMesh, const int32 x, const int32 y, const rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize)
|
||||
{
|
||||
NavMeshRuntime* runtime = navMesh->GetRuntime();
|
||||
NavBuildTasksLocker.Lock();
|
||||
@@ -1108,7 +1133,7 @@ void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float t
|
||||
if (!scene)
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh without scene.");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
// Early out if scene is not using navigation
|
||||
|
||||
@@ -47,6 +47,20 @@ void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifie
|
||||
DESERIALIZE(AreaName);
|
||||
}
|
||||
|
||||
void NavModifierVolume::OnEnable()
|
||||
{
|
||||
GetScene()->Navigation.Actors.Add(this);
|
||||
|
||||
BoxVolume::OnEnable();
|
||||
}
|
||||
|
||||
void NavModifierVolume::OnDisable()
|
||||
{
|
||||
BoxVolume::OnDisable();
|
||||
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
}
|
||||
|
||||
void NavModifierVolume::OnBoundsChanged(const BoundingBox& prevBounds)
|
||||
{
|
||||
#if COMPILE_WITH_NAV_MESH_BUILDER
|
||||
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
// [BoxVolume]
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
|
||||
protected:
|
||||
// [BoxVolume]
|
||||
|
||||
@@ -939,7 +939,7 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
|
||||
DrawCall drawCall;
|
||||
drawCall.PerInstanceRandom = effect->GetPerInstanceRandom();
|
||||
drawCall.ObjectPosition = effect->GetSphere().Center - view.Origin;
|
||||
drawCall.ObjectRadius = effect->GetSphere().Radius;
|
||||
drawCall.ObjectRadius = (float)effect->GetSphere().Radius;
|
||||
|
||||
// Draw all emitters
|
||||
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Collider.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Level/Scene/SceneRendering.h"
|
||||
#endif
|
||||
@@ -35,6 +36,13 @@ void Collider::SetIsTrigger(bool value)
|
||||
_isTrigger = value;
|
||||
if (_shape)
|
||||
PhysicsBackend::SetShapeState(_shape, IsActiveInHierarchy(), _isTrigger && CanBeTrigger());
|
||||
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && _isEnabled)
|
||||
{
|
||||
if (_isTrigger)
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
else
|
||||
GetScene()->Navigation.Actors.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Collider::SetCenter(const Vector3& value)
|
||||
@@ -43,13 +51,9 @@ void Collider::SetCenter(const Vector3& value)
|
||||
return;
|
||||
_center = value;
|
||||
if (_staticActor)
|
||||
{
|
||||
PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity);
|
||||
}
|
||||
else if (const RigidBody* rigidBody = GetAttachedRigidBody())
|
||||
{
|
||||
PhysicsBackend::SetShapeLocalPose(_shape, (_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale(), _localTransform.Orientation);
|
||||
}
|
||||
UpdateBounds();
|
||||
}
|
||||
|
||||
@@ -134,25 +138,27 @@ RigidBody* Collider::GetAttachedRigidBody() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void Collider::OnEnable()
|
||||
{
|
||||
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
|
||||
GetScene()->Navigation.Actors.Add(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddPhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
|
||||
#endif
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
PhysicsColliderActor::OnEnable();
|
||||
}
|
||||
|
||||
void Collider::OnDisable()
|
||||
{
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
PhysicsColliderActor::OnDisable();
|
||||
|
||||
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemovePhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void Collider::Attach(RigidBody* rigidBody)
|
||||
{
|
||||
@@ -432,6 +438,19 @@ void Collider::OnLayerChanged()
|
||||
UpdateLayerBits();
|
||||
}
|
||||
|
||||
void Collider::OnStaticFlagsChanged()
|
||||
{
|
||||
PhysicsColliderActor::OnStaticFlagsChanged();
|
||||
|
||||
if (!_isTrigger && _isEnabled)
|
||||
{
|
||||
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation))
|
||||
GetScene()->Navigation.Actors.AddUnique(this);
|
||||
else
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Collider::OnPhysicsSceneChanged(PhysicsScene* previous)
|
||||
{
|
||||
PhysicsColliderActor::OnPhysicsSceneChanged(previous);
|
||||
|
||||
@@ -171,15 +171,14 @@ public:
|
||||
|
||||
protected:
|
||||
// [PhysicsColliderActor]
|
||||
#if USE_EDITOR
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
#endif
|
||||
void BeginPlay(SceneBeginData* data) override;
|
||||
void EndPlay() override;
|
||||
void OnActiveInTreeChanged() override;
|
||||
void OnParentChanged() override;
|
||||
void OnTransformChanged() override;
|
||||
void OnLayerChanged() override;
|
||||
void OnStaticFlagsChanged() override;
|
||||
void OnPhysicsSceneChanged(PhysicsScene* previous) override;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Physics/PhysicsScene.h"
|
||||
#include "Engine/Physics/PhysicsBackend.h"
|
||||
#include "Engine/Physics/CollisionCooking.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
|
||||
REGISTER_BINARY_ASSET(CollisionData, "FlaxEngine.CollisionData", true);
|
||||
@@ -33,6 +34,7 @@ bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, i
|
||||
LOG(Error, "Cannot cook collision data for virtual models on a main thread (virtual models data is stored on GPU only). Use thread pool or async task.");
|
||||
return true;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
|
||||
// Prepare
|
||||
CollisionCooking::Argument arg;
|
||||
@@ -61,6 +63,7 @@ bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, i
|
||||
|
||||
bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& vertices, const Span<uint32>& triangles, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
CHECK_RETURN(vertices.Length() != 0, true);
|
||||
CHECK_RETURN(triangles.Length() != 0 && triangles.Length() % 3 == 0, true);
|
||||
ModelData modelData;
|
||||
@@ -74,6 +77,7 @@ bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& ve
|
||||
|
||||
bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& vertices, const Span<int32>& triangles, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
CHECK_RETURN(vertices.Length() != 0, true);
|
||||
CHECK_RETURN(triangles.Length() != 0 && triangles.Length() % 3 == 0, true);
|
||||
ModelData modelData;
|
||||
@@ -89,12 +93,12 @@ bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& ve
|
||||
|
||||
bool CollisionData::CookCollision(CollisionDataType type, ModelData* modelData, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
|
||||
{
|
||||
// Validate state
|
||||
if (!IsVirtual())
|
||||
{
|
||||
LOG(Warning, "Only virtual assets can be modified at runtime.");
|
||||
return true;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
|
||||
// Prepare
|
||||
CollisionCooking::Argument arg;
|
||||
@@ -107,18 +111,14 @@ bool CollisionData::CookCollision(CollisionDataType type, ModelData* modelData,
|
||||
SerializedOptions options;
|
||||
BytesContainer outputData;
|
||||
if (CollisionCooking::CookCollision(arg, options, outputData))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear state
|
||||
unload(true);
|
||||
|
||||
// Load data
|
||||
if (load(&options, outputData.Get(), outputData.Length()) != LoadResult::Ok)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mark as loaded (eg. Mesh Colliders using this asset will update shape for physics simulation)
|
||||
onLoaded();
|
||||
@@ -133,6 +133,7 @@ bool CollisionData::GetModelTriangle(uint32 faceIndex, MeshBase*& mesh, uint32&
|
||||
meshTriangleIndex = MAX_uint32;
|
||||
if (!IsLoaded())
|
||||
return false;
|
||||
PROFILE_CPU();
|
||||
ScopeLock lock(Locker);
|
||||
if (_triangleMesh)
|
||||
{
|
||||
@@ -178,6 +179,7 @@ bool CollisionData::GetModelTriangle(uint32 faceIndex, MeshBase*& mesh, uint32&
|
||||
|
||||
void CollisionData::ExtractGeometry(Array<Float3>& vertexBuffer, Array<int32>& indexBuffer) const
|
||||
{
|
||||
PROFILE_CPU();
|
||||
vertexBuffer.Clear();
|
||||
indexBuffer.Clear();
|
||||
|
||||
@@ -194,6 +196,7 @@ const Array<Float3>& CollisionData::GetDebugLines()
|
||||
{
|
||||
if (_hasMissingDebugLines && IsLoaded())
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ScopeLock lock(Locker);
|
||||
_hasMissingDebugLines = false;
|
||||
|
||||
|
||||
@@ -4265,6 +4265,7 @@ void* PhysicsBackend::CreateHeightField(byte* data, int32 dataSize)
|
||||
|
||||
void PhysicsBackend::GetConvexMeshTriangles(void* contextMesh, Array<Float3, HeapAllocation>& vertexBuffer, Array<int, HeapAllocation>& indexBuffer)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
auto contextMeshPhysX = (PxConvexMesh*)contextMesh;
|
||||
uint32 numIndices = 0;
|
||||
uint32 numVertices = contextMeshPhysX->getNbVertices();
|
||||
@@ -4304,6 +4305,7 @@ void PhysicsBackend::GetConvexMeshTriangles(void* contextMesh, Array<Float3, Hea
|
||||
|
||||
void PhysicsBackend::GetTriangleMeshTriangles(void* triangleMesh, Array<Float3>& vertexBuffer, Array<int32, HeapAllocation>& indexBuffer)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
auto triangleMeshPhysX = (PxTriangleMesh*)triangleMesh;
|
||||
uint32 numVertices = triangleMeshPhysX->getNbVertices();
|
||||
uint32 numIndices = triangleMeshPhysX->getNbTriangles() * 3;
|
||||
|
||||
@@ -134,7 +134,7 @@ public:
|
||||
|
||||
static ScriptingObject* ToNative(MObject* obj);
|
||||
|
||||
static MObject* ToManaged(ScriptingObject* obj)
|
||||
FORCE_INLINE static MObject* ToManaged(const ScriptingObject* obj)
|
||||
{
|
||||
return obj ? obj->GetOrCreateManagedInstance() : nullptr;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Physics/PhysicsScene.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
|
||||
@@ -804,6 +805,7 @@ RigidBody* Terrain::GetAttachedRigidBody() const
|
||||
|
||||
void Terrain::OnEnable()
|
||||
{
|
||||
GetScene()->Navigation.Actors.Add(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
GetSceneRendering()->AddPhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
|
||||
@@ -824,6 +826,7 @@ void Terrain::OnEnable()
|
||||
|
||||
void Terrain::OnDisable()
|
||||
{
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
GetSceneRendering()->RemovePhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
|
||||
|
||||
@@ -97,7 +97,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
|
||||
drawCall.Material = _cachedDrawMaterial;
|
||||
renderContext.View.GetWorldMatrix(_transform, drawCall.World);
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = _sphere.Radius;
|
||||
drawCall.ObjectRadius = (float)_sphere.Radius;
|
||||
drawCall.Terrain.Patch = _patch;
|
||||
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
|
||||
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * Terrain::ChunksCountEdge + _x), (float)(_patch->_z * Terrain::ChunksCountEdge + _z));
|
||||
@@ -155,7 +155,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
|
||||
drawCall.Material = material;
|
||||
renderContext.View.GetWorldMatrix(_transform, drawCall.World);
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = _sphere.Radius;
|
||||
drawCall.ObjectRadius = (float)_sphere.Radius;
|
||||
drawCall.Terrain.Patch = _patch;
|
||||
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
|
||||
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * Terrain::ChunksCountEdge + _x), (float)(_patch->_z * Terrain::ChunksCountEdge + _z));
|
||||
|
||||
@@ -2474,7 +2474,7 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<i
|
||||
if (_collisionVertices.IsEmpty())
|
||||
{
|
||||
// Prevent race conditions
|
||||
ScopeLock lock(Level::ScenesLock);
|
||||
ScopeLock sceneLock(Level::ScenesLock);
|
||||
if (_collisionVertices.IsEmpty())
|
||||
{
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
|
||||
|
||||
@@ -7,6 +7,16 @@
|
||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
TestNesting::TestNesting(const SpawnParams& params)
|
||||
: SerializableScriptingObject(params)
|
||||
{
|
||||
}
|
||||
|
||||
TestNesting2::TestNesting2(const SpawnParams& params)
|
||||
: SerializableScriptingObject(params)
|
||||
{
|
||||
}
|
||||
|
||||
TestClassNative::TestClassNative(const SpawnParams& params)
|
||||
: ScriptingObject(params)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,59 @@
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Scripting/SerializableScriptingObject.h"
|
||||
|
||||
// Test compilation with nested types.
|
||||
API_CLASS() class TestNesting : public SerializableScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(TestNesting);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
// Structure
|
||||
API_STRUCT() struct TestAttribute : public ISerializable
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(TestAttribute);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
// Enumeration
|
||||
API_ENUM() enum TestEnum
|
||||
{
|
||||
E1, E2,
|
||||
};
|
||||
|
||||
// Enum
|
||||
API_FIELD() TestEnum Enum = E1;
|
||||
};
|
||||
|
||||
// Attributes
|
||||
API_FIELD() Array<TestAttribute> Attributes;
|
||||
// Enum
|
||||
API_FIELD() TestAttribute::TestEnum Enum = TestAttribute::E1;
|
||||
};
|
||||
|
||||
// Test compilation with nested types.
|
||||
API_CLASS() class TestNesting2 : public SerializableScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(TestNesting2);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
// Structure
|
||||
API_STRUCT() struct TestAttribute
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(TestAttribute);
|
||||
|
||||
// Enumeration
|
||||
API_ENUM() enum TestEnum
|
||||
{
|
||||
E1, E2,
|
||||
};
|
||||
};
|
||||
|
||||
// Attributes
|
||||
API_FIELD() Array<TestNesting::TestAttribute> Attributes;
|
||||
// Enum
|
||||
API_FIELD() TestNesting::TestAttribute::TestEnum Enum = TestNesting::TestAttribute::E1;
|
||||
};
|
||||
|
||||
// Test structure.
|
||||
API_STRUCT(NoDefault) struct TestStruct : public ISerializable
|
||||
|
||||
@@ -4,6 +4,27 @@ using System.ComponentModel;
|
||||
|
||||
namespace FlaxEngine.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for text case
|
||||
/// </summary>
|
||||
public enum TextCaseOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// No text case.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Uppercase.
|
||||
/// </summary>
|
||||
Uppercase,
|
||||
|
||||
/// <summary>
|
||||
/// Lowercase
|
||||
/// </summary>
|
||||
Lowercase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The basic GUI label control.
|
||||
/// </summary>
|
||||
@@ -45,6 +66,24 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text case.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2000), Tooltip("The case of the text.")]
|
||||
public TextCaseOptions CaseOption { get; set; } = TextCaseOptions.None;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to bold the text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2001), Tooltip("Bold the text.")]
|
||||
public bool Bold { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to italicize the text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2002), Tooltip("Italicize the text.")]
|
||||
public bool Italic { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the text.
|
||||
/// </summary>
|
||||
@@ -234,18 +273,49 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale);
|
||||
Font font = GetFont();
|
||||
var text = ConvertedText();
|
||||
|
||||
Render2D.DrawText(font, Material, text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale);
|
||||
|
||||
if (ClipText)
|
||||
Render2D.PopClip();
|
||||
}
|
||||
|
||||
private Font GetFont()
|
||||
{
|
||||
Font font;
|
||||
if (Bold)
|
||||
font = Italic ? _font.GetBold().GetItalic().GetFont() : _font.GetBold().GetFont();
|
||||
else if (Italic)
|
||||
font = _font.GetItalic().GetFont();
|
||||
else
|
||||
font = _font.GetFont();
|
||||
return font;
|
||||
}
|
||||
|
||||
private LocalizedString ConvertedText()
|
||||
{
|
||||
LocalizedString text = _text;
|
||||
switch (CaseOption)
|
||||
{
|
||||
case TextCaseOptions.Uppercase:
|
||||
text = text.ToString().ToUpper();
|
||||
break;
|
||||
case TextCaseOptions.Lowercase:
|
||||
text = text.ToString().ToLower();
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
if (_autoWidth || _autoHeight || _autoFitText)
|
||||
{
|
||||
var font = _font.GetFont();
|
||||
Font font = GetFont();
|
||||
var text = ConvertedText();
|
||||
if (font)
|
||||
{
|
||||
// Calculate text size
|
||||
@@ -255,7 +325,7 @@ namespace FlaxEngine.GUI
|
||||
layout.Bounds.Size.X = Width - Margin.Width;
|
||||
else if (_autoWidth && !_autoHeight)
|
||||
layout.Bounds.Size.Y = Height - Margin.Height;
|
||||
_textSize = font.MeasureText(_text, ref layout);
|
||||
_textSize = font.MeasureText(text, ref layout);
|
||||
_textSize.Y *= BaseLinesGapScale;
|
||||
|
||||
// Check if size is controlled via text
|
||||
|
||||
@@ -24,11 +24,49 @@ namespace FlaxEngine.GUI
|
||||
get => _watermarkText;
|
||||
set => _watermarkText = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text case.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2000), Tooltip("The case of the text.")]
|
||||
public TextCaseOptions CaseOption { get; set; } = TextCaseOptions.None;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to bold the text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2001), Tooltip("Bold the text.")]
|
||||
public bool Bold { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to italicize the text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2002), Tooltip("Italicize the text.")]
|
||||
public bool Italic { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The vertical alignment of the text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2023), Tooltip("The vertical alignment of the text.")]
|
||||
public TextAlignment VerticalAlignment
|
||||
{
|
||||
get => _layout.VerticalAlignment;
|
||||
set => _layout.VerticalAlignment = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The vertical alignment of the text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2024), Tooltip("The horizontal alignment of the text.")]
|
||||
public TextAlignment HorizontalAlignment
|
||||
{
|
||||
get => _layout.HorizontalAlignment;
|
||||
set => _layout.HorizontalAlignment = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text wrapping within the control bounds.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2023), Tooltip("The text wrapping within the control bounds.")]
|
||||
[EditorDisplay("Text Style"), EditorOrder(2025), Tooltip("The text wrapping within the control bounds.")]
|
||||
public TextWrapping Wrapping
|
||||
{
|
||||
get => _layout.TextWrapping;
|
||||
@@ -38,13 +76,13 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the font.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2024)]
|
||||
[EditorDisplay("Text Style"), EditorOrder(2026)]
|
||||
public FontReference Font { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2025), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")]
|
||||
[EditorDisplay("Text Style"), EditorOrder(2027), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")]
|
||||
public MaterialBase TextMaterial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -98,19 +136,46 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override Float2 GetTextSize()
|
||||
{
|
||||
var font = Font.GetFont();
|
||||
var font = GetFont();
|
||||
if (font == null)
|
||||
{
|
||||
return Float2.Zero;
|
||||
}
|
||||
|
||||
return font.MeasureText(_text, ref _layout);
|
||||
return font.MeasureText(ConvertedText(), ref _layout);
|
||||
}
|
||||
|
||||
private Font GetFont()
|
||||
{
|
||||
Font font;
|
||||
if (Bold)
|
||||
font = Italic ? Font.GetBold().GetItalic().GetFont() : Font.GetBold().GetFont();
|
||||
else if (Italic)
|
||||
font = Font.GetItalic().GetFont();
|
||||
else
|
||||
font = Font.GetFont();
|
||||
return font;
|
||||
}
|
||||
|
||||
private string ConvertedText()
|
||||
{
|
||||
string text = _text;
|
||||
switch (CaseOption)
|
||||
{
|
||||
case TextCaseOptions.Uppercase:
|
||||
text = text.ToUpper();
|
||||
break;
|
||||
case TextCaseOptions.Lowercase:
|
||||
text = text.ToLower();
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 GetCharPosition(int index, out float height)
|
||||
{
|
||||
var font = Font.GetFont();
|
||||
var font = GetFont();
|
||||
if (font == null)
|
||||
{
|
||||
height = Height;
|
||||
@@ -118,19 +183,19 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
height = font.Height / DpiScale;
|
||||
return font.GetCharPosition(_text, index, ref _layout);
|
||||
return font.GetCharPosition(ConvertedText(), index, ref _layout);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int HitTestText(Float2 location)
|
||||
{
|
||||
var font = Font.GetFont();
|
||||
var font = GetFont();
|
||||
if (font == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return font.HitTestText(_text, location, ref _layout);
|
||||
return font.HitTestText(ConvertedText(), location, ref _layout);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -147,7 +212,7 @@ namespace FlaxEngine.GUI
|
||||
// Cache data
|
||||
var rect = new Rectangle(Float2.Zero, Size);
|
||||
bool enabled = EnabledInHierarchy;
|
||||
var font = Font.GetFont();
|
||||
var font = GetFont();
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
@@ -166,11 +231,13 @@ namespace FlaxEngine.GUI
|
||||
if (useViewOffset)
|
||||
Render2D.PushTransform(Matrix3x3.Translation2D(-_viewOffset));
|
||||
|
||||
var text = ConvertedText();
|
||||
|
||||
// Check if sth is selected to draw selection
|
||||
if (HasSelection)
|
||||
{
|
||||
var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout);
|
||||
var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout);
|
||||
var leftEdge = font.GetCharPosition(text, SelectionLeft, ref _layout);
|
||||
var rightEdge = font.GetCharPosition(text, SelectionRight, ref _layout);
|
||||
var fontHeight = font.Height;
|
||||
var textHeight = fontHeight / DpiScale;
|
||||
|
||||
@@ -207,12 +274,12 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
// Text or watermark
|
||||
if (_text.Length > 0)
|
||||
if (text.Length > 0)
|
||||
{
|
||||
var color = TextColor;
|
||||
if (!enabled)
|
||||
color *= 0.6f;
|
||||
Render2D.DrawText(font, _text, color, ref _layout, TextMaterial);
|
||||
Render2D.DrawText(font, text, color, ref _layout, TextMaterial);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(_watermarkText))
|
||||
{
|
||||
@@ -224,7 +291,25 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
float alpha = Mathf.Saturate(Mathf.Cos(_animateTime * CaretFlashSpeed) * 0.5f + 0.7f);
|
||||
alpha = alpha * alpha * alpha * alpha * alpha * alpha;
|
||||
Render2D.FillRectangle(CaretBounds, CaretColor * alpha);
|
||||
if (CaretPosition == 0)
|
||||
{
|
||||
var bounds = CaretBounds;
|
||||
if (_layout.VerticalAlignment == TextAlignment.Center)
|
||||
bounds.Y = _layout.Bounds.Y + _layout.Bounds.Height * 0.5f - bounds.Height * 0.5f;
|
||||
else if (_layout.VerticalAlignment == TextAlignment.Far)
|
||||
bounds.Y = _layout.Bounds.Y + _layout.Bounds.Height - bounds.Height;
|
||||
|
||||
if (_layout.HorizontalAlignment == TextAlignment.Center)
|
||||
bounds.X = _layout.Bounds.X + _layout.Bounds.Width * 0.5f - bounds.Width * 0.5f;
|
||||
else if (_layout.HorizontalAlignment == TextAlignment.Far)
|
||||
bounds.X = _layout.Bounds.X + _layout.Bounds.Width - bounds.Width;
|
||||
Render2D.FillRectangle(bounds, CaretColor * alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
Render2D.FillRectangle(CaretBounds, CaretColor * alpha);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Restore rendering state
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace FlaxEngine.GUI
|
||||
private ScrollBars _scrollBars;
|
||||
private float _scrollBarsSize = ScrollBar.DefaultSize;
|
||||
private Margin _scrollMargin;
|
||||
private Color _scrollbarTrackColor;
|
||||
private Color _scrollbarThumbColor;
|
||||
private Color _scrollbarThumbSelectedColor;
|
||||
|
||||
/// <summary>
|
||||
/// The cached scroll area bounds. Used to scroll contents of the panel control. Cached during performing layout.
|
||||
@@ -49,7 +52,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the scroll bars usage by this panel.
|
||||
/// </summary>
|
||||
[EditorOrder(0), Tooltip("The scroll bars usage.")]
|
||||
[EditorDisplay("Scrollbar Style"), EditorOrder(1500), Tooltip("The scroll bars usage.")]
|
||||
public ScrollBars ScrollBars
|
||||
{
|
||||
get => _scrollBars;
|
||||
@@ -73,6 +76,12 @@ namespace FlaxEngine.GUI
|
||||
//VScrollBar.X += VScrollBar.Width;
|
||||
VScrollBar.ValueChanged += () => SetViewOffset(Orientation.Vertical, VScrollBar.Value);
|
||||
}
|
||||
if (VScrollBar != null)
|
||||
{
|
||||
VScrollBar.TrackColor = _scrollbarTrackColor;
|
||||
VScrollBar.ThumbColor = _scrollbarThumbColor;
|
||||
VScrollBar.ThumbSelectedColor = _scrollbarThumbSelectedColor;
|
||||
}
|
||||
}
|
||||
else if (VScrollBar != null)
|
||||
{
|
||||
@@ -94,6 +103,12 @@ namespace FlaxEngine.GUI
|
||||
//HScrollBar.Offsets += new Margin(0, 0, HScrollBar.Height * 0.5f, 0);
|
||||
HScrollBar.ValueChanged += () => SetViewOffset(Orientation.Horizontal, HScrollBar.Value);
|
||||
}
|
||||
if (HScrollBar != null)
|
||||
{
|
||||
HScrollBar.TrackColor = _scrollbarTrackColor;
|
||||
HScrollBar.ThumbColor = _scrollbarThumbColor;
|
||||
HScrollBar.ThumbSelectedColor = _scrollbarThumbSelectedColor;
|
||||
}
|
||||
}
|
||||
else if (HScrollBar != null)
|
||||
{
|
||||
@@ -108,7 +123,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the scroll bars.
|
||||
/// </summary>
|
||||
[EditorOrder(5), Tooltip("Scroll bars size.")]
|
||||
[EditorDisplay("Scrollbar Style"), EditorOrder(1501), Tooltip("Scroll bars size.")]
|
||||
public float ScrollBarsSize
|
||||
{
|
||||
get => _scrollBarsSize;
|
||||
@@ -124,7 +139,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether always show scrollbars. Otherwise show them only if scrolling is available.
|
||||
/// </summary>
|
||||
[EditorOrder(10), Tooltip("Whether always show scrollbars. Otherwise show them only if scrolling is available.")]
|
||||
[EditorDisplay("Scrollbar Style"), EditorOrder(1502), Tooltip("Whether always show scrollbars. Otherwise show them only if scrolling is available.")]
|
||||
public bool AlwaysShowScrollbars
|
||||
{
|
||||
get => _alwaysShowScrollbars;
|
||||
@@ -157,7 +172,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the scroll margin applies to the child controls area. Can be used to expand the scroll area bounds by adding a margin.
|
||||
/// </summary>
|
||||
[EditorOrder(20), Tooltip("Scroll margin applies to the child controls area. Can be used to expand the scroll area bounds by adding a margin.")]
|
||||
[EditorDisplay("Scrollbar Style"), EditorOrder(1503), Tooltip("Scroll margin applies to the child controls area. Can be used to expand the scroll area bounds by adding a margin.")]
|
||||
public Margin ScrollMargin
|
||||
{
|
||||
get => _scrollMargin;
|
||||
@@ -171,6 +186,57 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color of the scroll bar track.
|
||||
/// </summary>
|
||||
[EditorDisplay("Scrollbar Style"), EditorOrder(1600), ExpandGroups]
|
||||
public Color ScrollbarTrackColor
|
||||
{
|
||||
get => _scrollbarTrackColor;
|
||||
set
|
||||
{
|
||||
_scrollbarTrackColor = value;
|
||||
if (VScrollBar != null)
|
||||
VScrollBar.TrackColor = _scrollbarTrackColor;
|
||||
if (HScrollBar != null)
|
||||
HScrollBar.TrackColor = _scrollbarTrackColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color of the scroll bar thumb.
|
||||
/// </summary>
|
||||
[EditorDisplay("Scrollbar Style"), EditorOrder(1601)]
|
||||
public Color ScrollbarThumbColor
|
||||
{
|
||||
get => _scrollbarThumbColor;
|
||||
set
|
||||
{
|
||||
_scrollbarThumbColor = value;
|
||||
if (VScrollBar != null)
|
||||
VScrollBar.ThumbColor = _scrollbarThumbColor;
|
||||
if (HScrollBar != null)
|
||||
HScrollBar.ThumbColor = _scrollbarThumbColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color of the scroll bar thumb when selected.
|
||||
/// </summary>
|
||||
[EditorDisplay("Scrollbar Style"), EditorOrder(1602)]
|
||||
public Color ScrollbarThumbSelectedColor
|
||||
{
|
||||
get => _scrollbarThumbSelectedColor;
|
||||
set
|
||||
{
|
||||
_scrollbarThumbSelectedColor = value;
|
||||
if (VScrollBar != null)
|
||||
VScrollBar.ThumbSelectedColor = _scrollbarThumbSelectedColor;
|
||||
if (HScrollBar != null)
|
||||
HScrollBar.ThumbSelectedColor = _scrollbarThumbSelectedColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Panel"/> class.
|
||||
/// </summary>
|
||||
@@ -187,6 +253,10 @@ namespace FlaxEngine.GUI
|
||||
public Panel(ScrollBars scrollBars, bool autoFocus = false)
|
||||
{
|
||||
AutoFocus = autoFocus;
|
||||
var style = Style.Current;
|
||||
_scrollbarTrackColor = style.BackgroundHighlighted;
|
||||
_scrollbarThumbColor = style.BackgroundNormal;
|
||||
_scrollbarThumbSelectedColor = style.BackgroundSelected;
|
||||
ScrollBars = scrollBars;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,21 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public bool EnableSmoothing { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The track color.
|
||||
/// </summary>
|
||||
public Color TrackColor;
|
||||
|
||||
/// <summary>
|
||||
/// The thumb color.
|
||||
/// </summary>
|
||||
public Color ThumbColor;
|
||||
|
||||
/// <summary>
|
||||
/// The selected thumb color.
|
||||
/// </summary>
|
||||
public Color ThumbSelectedColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum value.
|
||||
/// </summary>
|
||||
@@ -209,6 +224,10 @@ namespace FlaxEngine.GUI
|
||||
AutoFocus = false;
|
||||
|
||||
_orientation = orientation;
|
||||
var style = Style.Current;
|
||||
TrackColor = style.BackgroundHighlighted;
|
||||
ThumbColor = style.BackgroundNormal;
|
||||
ThumbSelectedColor = style.BackgroundSelected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -377,8 +396,8 @@ namespace FlaxEngine.GUI
|
||||
base.Draw();
|
||||
|
||||
var style = Style.Current;
|
||||
Render2D.FillRectangle(_trackRect, style.BackgroundHighlighted * _thumbOpacity);
|
||||
Render2D.FillRectangle(_thumbRect, (_thumbClicked ? style.BackgroundSelected : style.BackgroundNormal) * _thumbOpacity);
|
||||
Render2D.FillRectangle(_trackRect, TrackColor * _thumbOpacity);
|
||||
Render2D.FillRectangle(_thumbRect, (_thumbClicked ? ThumbSelectedColor : ThumbColor) * _thumbOpacity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -234,11 +234,16 @@ namespace FlaxEngine.GUI
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Color.Lerp(style.BackgroundSelected, style.Background, 0.6f));
|
||||
Render2D.FillRectangle(new Rectangle(1.1f, 1.1f, Width - 2, Height - 2), style.Background);
|
||||
|
||||
// Padding for text
|
||||
var textRect = GetClientArea();
|
||||
textRect.X += 5;
|
||||
textRect.Width -= 10;
|
||||
|
||||
// Tooltip text
|
||||
Render2D.DrawText(
|
||||
style.FontMedium,
|
||||
_currentText,
|
||||
GetClientArea(),
|
||||
textRect,
|
||||
style.Foreground,
|
||||
TextAlignment.Center,
|
||||
TextAlignment.Center,
|
||||
|
||||
@@ -366,7 +366,7 @@ void TextRender::Draw(RenderContext& renderContext)
|
||||
DrawCall drawCall;
|
||||
drawCall.World = world;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = _sphere.Radius;
|
||||
drawCall.ObjectRadius = (float)_sphere.Radius;
|
||||
drawCall.Surface.GeometrySize = _localBox.GetSize();
|
||||
drawCall.Surface.PrevWorld = _drawState.PrevWorld;
|
||||
drawCall.Surface.Lightmap = nullptr;
|
||||
|
||||
5
Source/ThirdParty/recastnavigation/Recast.h
vendored
5
Source/ThirdParty/recastnavigation/Recast.h
vendored
@@ -196,6 +196,10 @@ protected:
|
||||
class rcScopedTimer
|
||||
{
|
||||
public:
|
||||
#if 1
|
||||
// Disable timer functionality
|
||||
inline rcScopedTimer(rcContext* ctx, const rcTimerLabel label) { }
|
||||
#else
|
||||
/// Constructs an instance and starts the timer.
|
||||
/// @param[in] ctx The context to use.
|
||||
/// @param[in] label The category of the timer.
|
||||
@@ -209,6 +213,7 @@ private:
|
||||
|
||||
rcContext* const m_ctx;
|
||||
const rcTimerLabel m_label;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Specifies a configuration to use when performing Recast builds.
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
var result = NativeName;
|
||||
if (Parent != null && !(Parent is FileInfo))
|
||||
result = Parent.FullNameNative + '_' + result;
|
||||
result = Parent.FullNameNative.Replace("::", "_") + '_' + result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,13 +267,21 @@ namespace Flax.Build.Bindings
|
||||
if (value.Contains('(') && value.Contains(')'))
|
||||
return "new " + value;
|
||||
|
||||
// Special case for non-strongly typed enums
|
||||
if (valueType != null && !value.Contains('.', StringComparison.Ordinal))
|
||||
{
|
||||
apiType = FindApiTypeInfo(buildData, valueType, caller);
|
||||
if (apiType is EnumInfo)
|
||||
return $"{apiType.Name}.{value}";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string GenerateCSharpNativeToManaged(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, bool marshalling = false)
|
||||
{
|
||||
string result;
|
||||
if (typeInfo?.Type == null)
|
||||
if (typeInfo == null || typeInfo.Type == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Use dynamic array as wrapper container for fixed-size native arrays
|
||||
@@ -1641,8 +1649,9 @@ namespace Flax.Build.Bindings
|
||||
if (internalType)
|
||||
{
|
||||
// Marshal blittable array elements back to original non-blittable elements
|
||||
string originalElementTypeMarshaller = originalElementType + "Marshaller";
|
||||
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementType}Internal";
|
||||
string originalElementTypeMarshaller = $"{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(); }}");
|
||||
|
||||
@@ -1991,13 +1991,8 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
contents.Append(')').AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
if (buildData.Target.IsEditor && false)
|
||||
contents.Append(" MMethod* method = nullptr;").AppendLine(); // TODO: find a better way to cache event method in editor and handle C# hot-reload
|
||||
else
|
||||
contents.Append(" static MMethod* method = nullptr;").AppendLine();
|
||||
contents.Append(" if (!method)").AppendLine();
|
||||
contents.AppendFormat(" method = {1}::TypeInitializer.GetClass()->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine();
|
||||
contents.Append(" CHECK(method);").AppendLine();
|
||||
contents.Append(" static MMethod* method = nullptr;").AppendLine();
|
||||
contents.AppendFormat(" if (!method) {{ method = {1}::TypeInitializer.GetClass()->GetMethod(\"Internal_{0}_Invoke\", {2}); CHECK(method); }}", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine();
|
||||
contents.Append(" MObject* exception = nullptr;").AppendLine();
|
||||
if (paramsCount == 0)
|
||||
contents.AppendLine(" void** params = nullptr;");
|
||||
|
||||
Reference in New Issue
Block a user