Compare commits
171 Commits
1.8.6511.1
...
1.8
| Author | SHA1 | Date | |
|---|---|---|---|
| c486577b07 | |||
| a69c8ce6a2 | |||
| e527783e55 | |||
| 4d9c92dd49 | |||
| 9b01229e58 | |||
| e3a030fad8 | |||
| cb878294ea | |||
| 1bd1aca0f0 | |||
| b433312042 | |||
| 1041b1b86d | |||
| 1ace5fd10d | |||
| cfc9f73744 | |||
| 2418167182 | |||
| 52090d3a6b | |||
| 73f68c102d | |||
| 862dd1e5f1 | |||
| 58351d1989 | |||
| 6705205e2f | |||
| 2cdd0ff644 | |||
| 69ae841f64 | |||
| 7f8700288f | |||
| f87dec6ca6 | |||
| 65a6c0aed5 | |||
| f6dd0decfb | |||
| 816984542a | |||
| 3837e8b263 | |||
| 47b3141f18 | |||
| e10ee3e55a | |||
| 0a4e89e29b | |||
| 0765fa92b5 | |||
| 2529312152 | |||
| 3404643636 | |||
| 6b9f6ac82e | |||
| ab5bb79754 | |||
| 58f95d6ce3 | |||
| 1cd2f6a070 | |||
| 35ddfc2455 | |||
| 17d1d87268 | |||
| fd871ce830 | |||
| b4a4a8a591 | |||
| 24e4015425 | |||
| c670887b1a | |||
| d3cd6a461b | |||
| 2625a9d762 | |||
| 4fdd9a242b | |||
| a6e8e6f749 | |||
| 03eabbcf63 | |||
| 13f94dcf11 | |||
| 3b44062eb0 | |||
| 1457637707 | |||
| d28567111f | |||
| 1c7f06e570 | |||
| 63cc0fef2e | |||
| 57084b3d6c | |||
| fa23619f08 | |||
| 1f2456fc67 | |||
| 0b71e906a6 | |||
| 2e59c35a44 | |||
| 6f2bd0e932 | |||
| 6a883bc7c6 | |||
| 17de6388ca | |||
| e028d263f1 | |||
| 6962ed6730 | |||
| b66d50ae1b | |||
| 675ce71935 | |||
| 833f844d59 | |||
| af08dc1c69 | |||
| 07628d2ec7 | |||
| aac3dbfe09 | |||
| 185f24ce49 | |||
| 77e29109ee | |||
| 8d89b9efb0 | |||
| b2fee31a13 | |||
| fc7628e2ee | |||
| 2e3e4959d6 | |||
| f22105c2c3 | |||
| 533902d185 | |||
| 68653fa91f | |||
| dc0aa61a14 | |||
| ee790ff3a9 | |||
| a2a3926aee | |||
| 9a70344c1f | |||
| 44006dd533 | |||
| f6aabf2d14 | |||
| dc1f15f18d | |||
| 7d7808af8f | |||
| 5029584a9f | |||
| f353d3f114 | |||
| 667e8bc293 | |||
| 2edb9cc4d8 | |||
| 7018666a8c | |||
| f04926ad94 | |||
| 50f5f0acd9 | |||
| 3745979b81 | |||
| db15f6f08a | |||
| e1a2f51d5a | |||
| a8e1fd7a4a | |||
| d46ef6ac92 | |||
| 36d21b27c7 | |||
| b1636c27e7 | |||
| 5d32fc6c5e | |||
| 065dc474c0 | |||
| 1fb7b24aad | |||
| f0b72aa025 | |||
| 058077736b | |||
| b02f011627 | |||
| ea04c746fd | |||
| 97454fc82e | |||
| 4a6afdb108 | |||
| 65e852600a | |||
| fedd990c13 | |||
| c0329abe40 | |||
| d8850a56a8 | |||
| e171bb06ec | |||
| 3825e07adc | |||
| db8adf7d96 | |||
| e77ae12b9b | |||
| bf4e4aeaf6 | |||
| 2107b069db | |||
| ea2005dacb | |||
| d5cded8aaa | |||
| 430b22d5d7 | |||
| 9d830eb1e2 | |||
| 7e3f84f95e | |||
| cddee38d71 | |||
| e030d0461b | |||
| 4978c8e0d9 | |||
| dc7b7e6e10 | |||
| 1e3eb11b94 | |||
| b15b231b85 | |||
| 262992571a | |||
| 352bf3f9a7 | |||
| 9683868767 | |||
| 40284fbbf8 | |||
| 0c86a900da | |||
| c1e3eaeab1 | |||
| 3c487dff47 | |||
| 2260d79e26 | |||
| 3209320547 | |||
| d1db06a9bb | |||
| 1c1d2fd96f | |||
| 2e5ad8c48a | |||
| 9a6f866956 | |||
| c59bce3b58 | |||
| 1185a9c06c | |||
| 026b69c544 | |||
| df7ece7655 | |||
| 7e8f20bd9b | |||
| 20aef27439 | |||
| 12344cf725 | |||
| ebb2704726 | |||
| 5f9d0140c7 | |||
| 02a7f74993 | |||
| 452e12db45 | |||
| ac46c89904 | |||
| 88acc772b3 | |||
| fbec80c801 | |||
| 1ef487a5cc | |||
| 1e5861de25 | |||
| 07733a4efb | |||
| fee15846ba | |||
| 7040da9a44 | |||
| a8a621df3b | |||
| 18660140b0 | |||
| 58cc53b44d | |||
| d8bb831dd9 | |||
| 0d770e3909 | |||
| 3641886ebf | |||
| 611dd7bad1 | |||
| 7db20e0411 | |||
| e835b25637 |
+2
-2
@@ -3,8 +3,8 @@
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 8,
|
||||
"Revision": 1,
|
||||
"Build": 6511
|
||||
"Revision": 2,
|
||||
"Build": 6512
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION_005FMEMBER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ARGB/@EntryIndexedValue">ARGB</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LO/@EntryIndexedValue">LO</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RPC/@EntryIndexedValue">RPC</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SDK/@EntryIndexedValue">SDK</s:String>
|
||||
|
||||
@@ -365,7 +365,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
||||
Platform::CreateProcess(procSettings);
|
||||
}
|
||||
#endif
|
||||
const bool distributionPackage = buildSettings->ForDistribution;
|
||||
const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release;
|
||||
{
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
|
||||
|
||||
@@ -52,13 +52,18 @@ namespace FlaxEditor.CustomEditors
|
||||
// Check if use provided editor
|
||||
if (overrideEditor != null)
|
||||
return overrideEditor;
|
||||
ScriptType targetType = values.Type;
|
||||
|
||||
// Special case if property is a pure object type and all values are the same type
|
||||
if (values.Type.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes)
|
||||
if (targetType.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes)
|
||||
return CreateEditor(TypeUtils.GetObjectType(values[0]), canUseRefPicker);
|
||||
|
||||
// Special case if property is interface but the value is implemented as Scripting Object that should use reference picker
|
||||
if (targetType.IsInterface && canUseRefPicker && values.Count > 0 && values[0] is FlaxEngine.Object)
|
||||
return new DummyEditor();
|
||||
|
||||
// Use editor for the property type
|
||||
return CreateEditor(values.Type, canUseRefPicker);
|
||||
return CreateEditor(targetType, canUseRefPicker);
|
||||
}
|
||||
|
||||
internal static CustomEditor CreateEditor(ScriptType targetType, bool canUseRefPicker = true)
|
||||
|
||||
@@ -75,7 +75,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
// Selecting actor prefab asset
|
||||
var selectPrefab = panel.Button("Select Prefab");
|
||||
selectPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Select(prefab);
|
||||
selectPrefab.Button.Clicked += () =>
|
||||
{
|
||||
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||
Editor.Instance.Windows.ContentWin.Select(prefab);
|
||||
};
|
||||
|
||||
// Viewing changes applied to this actor
|
||||
var viewChanges = panel.Button("View Changes");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -12,7 +13,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
[CustomEditor(typeof(LayersMask)), DefaultEditor]
|
||||
internal class LayersMaskEditor : CustomEditor
|
||||
{
|
||||
private CheckBox[] _checkBoxes;
|
||||
private List<CheckBox> _checkBoxes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -24,16 +25,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
}
|
||||
|
||||
_checkBoxes = new CheckBox[layers.Length];
|
||||
_checkBoxes = new List<CheckBox>();
|
||||
for (int i = 0; i < layers.Length; i++)
|
||||
{
|
||||
var layer = layers[i];
|
||||
var property = layout.AddPropertyItem(layer);
|
||||
if (string.IsNullOrEmpty(layer))
|
||||
continue;
|
||||
var property = layout.AddPropertyItem($"{i}: {layer}");
|
||||
var checkbox = property.Checkbox().CheckBox;
|
||||
UpdateCheckbox(checkbox, i);
|
||||
checkbox.Tag = i;
|
||||
checkbox.StateChanged += OnCheckboxStateChanged;
|
||||
_checkBoxes[i] = checkbox;
|
||||
_checkBoxes.Add(checkbox);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +53,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (_checkBoxes != null)
|
||||
{
|
||||
for (int i = 0; i < _checkBoxes.Length; i++)
|
||||
for (int i = 0; i < _checkBoxes.Count; i++)
|
||||
{
|
||||
UpdateCheckbox(_checkBoxes[i], i);
|
||||
UpdateCheckbox(_checkBoxes[i], (int)_checkBoxes[i].Tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -456,14 +456,57 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
for (int i = 0; i < layout.Children.Count; i++)
|
||||
{
|
||||
if (layout.Children[i] is GroupElement group && group.Panel.HeaderText == "Transform")
|
||||
if (layout.Children[i] is GroupElement group && group.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal))
|
||||
{
|
||||
VerticalPanelElement mainHor = VerticalPanelWithoutMargin(group);
|
||||
CreateTransformElements(mainHor, ValuesTypes);
|
||||
group.ContainerControl.ChangeChildIndex(mainHor.Control, 0);
|
||||
layout.Children.Remove(group);
|
||||
layout.ContainerControl.Children.Remove(group.Panel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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++)
|
||||
{
|
||||
if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("General", StringComparison.Ordinal) && i + 1 <= l.Children.Count)
|
||||
{
|
||||
Presenter.ContainerControl.ChangeChildIndex(transformGroup.Control, i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTransformElements(LayoutElementsContainer main, ScriptType[] valueTypes)
|
||||
@@ -645,7 +688,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
var grid = UniformGridTwoByOne(el);
|
||||
grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
|
||||
var label = grid.Label(text);
|
||||
var label = grid.Label(text, TextAlignment.Far);
|
||||
var editor = grid.Object(values);
|
||||
if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -224,7 +227,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
|
||||
if (collection != null)
|
||||
{
|
||||
_canResize = !collection.ReadOnly;
|
||||
_canResize = collection.CanResize;
|
||||
_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;
|
||||
@@ -189,6 +189,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
|
||||
if (collection != null)
|
||||
{
|
||||
_canEditKeys &= collection.CanReorderItems;
|
||||
_readOnly = collection.ReadOnly;
|
||||
_notNullItems = collection.NotNullItems;
|
||||
if (collection.BackgroundColor.HasValue)
|
||||
@@ -197,6 +198,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 +245,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 +261,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;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
internal sealed class DummyEditor : CustomEditor
|
||||
{
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
string valueName;
|
||||
if (Values.Count != 0 && Values[0] != null)
|
||||
valueName = Values[0].ToString();
|
||||
else
|
||||
valueName = "null";
|
||||
layout.Label($"{valueName} ({Values.Type})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public sealed class StringEditor : CustomEditor
|
||||
{
|
||||
private TextBoxElement _element;
|
||||
private string _watermarkText;
|
||||
private Color _watermarkColor;
|
||||
private Color _defaultWatermarkColor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
@@ -21,15 +24,26 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
bool isMultiLine = false;
|
||||
_watermarkText = string.Empty;
|
||||
|
||||
var attributes = Values.GetAttributes();
|
||||
var multiLine = attributes?.FirstOrDefault(x => x is MultilineTextAttribute);
|
||||
var watermarkAttribute = attributes?.FirstOrDefault(x => x is WatermarkAttribute);
|
||||
if (multiLine != null)
|
||||
{
|
||||
isMultiLine = true;
|
||||
}
|
||||
|
||||
_element = layout.TextBox(isMultiLine);
|
||||
_defaultWatermarkColor = _element.TextBox.WatermarkTextColor;
|
||||
if (watermarkAttribute is WatermarkAttribute watermark)
|
||||
{
|
||||
_watermarkText = watermark.WatermarkText;
|
||||
var watermarkColor = watermark.WatermarkColor > 0 ? Color.FromRGB(watermark.WatermarkColor) : FlaxEngine.GUI.Style.Current.ForegroundDisabled;
|
||||
_watermarkColor = watermarkColor;
|
||||
_element.TextBox.WatermarkText = watermark.WatermarkText;
|
||||
_element.TextBox.WatermarkTextColor = watermarkColor;
|
||||
}
|
||||
_element.TextBox.EditEnd += () => SetValue(_element.Text);
|
||||
}
|
||||
|
||||
@@ -41,12 +55,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
_element.TextBox.Text = string.Empty;
|
||||
_element.TextBox.WatermarkTextColor = _defaultWatermarkColor;
|
||||
_element.TextBox.WatermarkText = "Different values";
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.TextBox.Text = (string)Values[0];
|
||||
_element.TextBox.WatermarkText = string.Empty;
|
||||
_element.TextBox.WatermarkTextColor = _watermarkColor;
|
||||
_element.TextBox.WatermarkText = _watermarkText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
float namesWidth = _splitterValue * Width;
|
||||
int count = _element.Labels.Count;
|
||||
float[] yStarts = new float[count + 1];
|
||||
for (int i = 1; i < count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var label = _element.Labels[i];
|
||||
|
||||
@@ -251,9 +251,13 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
else if (_children.Count <= label.FirstChildControlIndex)
|
||||
yStarts[i] = y;
|
||||
else
|
||||
{
|
||||
yStarts[i] = _children[label.FirstChildControlIndex].Top;
|
||||
if (i == count - 1)
|
||||
yStarts[i + 1] = _children[label.FirstChildControlIndex].Bottom;
|
||||
}
|
||||
|
||||
}
|
||||
yStarts[count] = y;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var label = _element.Labels[i];
|
||||
|
||||
@@ -318,7 +318,7 @@ namespace FlaxEditor.CustomEditors
|
||||
if (header.FontSize > 0)
|
||||
element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize);
|
||||
if (header.Color > 0)
|
||||
element.Label.TextColor = Color.FromRGBA(header.Color);
|
||||
element.Label.TextColor = Color.FromRGB(header.Color);
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
@@ -264,6 +264,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
TooltipText = "Save Color.",
|
||||
Tag = null,
|
||||
};
|
||||
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
|
||||
@@ -370,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 />
|
||||
@@ -498,6 +515,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
TooltipText = "Save Color.",
|
||||
Tag = null,
|
||||
};
|
||||
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
|
||||
|
||||
@@ -311,7 +311,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
// Alpha
|
||||
float alphaY = _slider2Rect.Height * (1 - _color.A);
|
||||
var alphaR = new Rectangle(_slider2Rect.X - slidersOffset, _slider2Rect.Y + alphaY - slidersThickness / 2, _slider2Rect.Width + slidersOffset * 2, slidersThickness);
|
||||
Render2D.FillRectangle(_slider2Rect, _color, _color, Color.Transparent, Color.Transparent);
|
||||
var color = _color;
|
||||
color.A = 1; // Keep slider 2 fill rect from changing color alpha while selecting.
|
||||
Render2D.FillRectangle(_slider2Rect, color, color, Color.Transparent, Color.Transparent);
|
||||
Render2D.DrawRectangle(_slider2Rect, _isMouseDownSlider2 ? style.BackgroundSelected : Color.Black);
|
||||
Render2D.DrawRectangle(alphaR, _isMouseDownSlider2 ? Color.White : Color.Gray);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.GUI.Drag;
|
||||
|
||||
/// <summary>
|
||||
/// Control type drag handler.
|
||||
/// </summary>
|
||||
public sealed class DragControlType : DragActorType<DragEventArgs>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DragControlType"/> class.
|
||||
/// </summary>
|
||||
/// <param name="validateFunction">The validation function</param>
|
||||
public DragControlType(Func<ScriptType, bool> validateFunction)
|
||||
: base(validateFunction)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for handling control type drag and drop (for spawning).
|
||||
/// </summary>
|
||||
/// <seealso cref="Control" />
|
||||
/// <seealso cref="ActorNode" />
|
||||
public class DragControlType<U> : DragHelper<ScriptType, U> where U : DragEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The default prefix for drag data used for actor type drag and drop.
|
||||
/// </summary>
|
||||
public const string DragPrefix = "CTYPE!?";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new DragHelper
|
||||
/// </summary>
|
||||
/// <param name="validateFunction">The validation function</param>
|
||||
public DragControlType(Func<ScriptType, bool> validateFunction)
|
||||
: base(validateFunction)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DragData ToDragData(ScriptType item) => GetDragData(item);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DragData ToDragData(IEnumerable<ScriptType> items)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag data.
|
||||
/// </summary>
|
||||
/// <param name="item">The control type.</param>
|
||||
/// <returns>The data</returns>
|
||||
public static DragData GetDragData(Type item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException();
|
||||
return new DragDataText(DragPrefix + item.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag data.
|
||||
/// </summary>
|
||||
/// <param name="item">The control type.</param>
|
||||
/// <returns>The data</returns>
|
||||
public static DragData GetDragData(ScriptType item)
|
||||
{
|
||||
if (item == ScriptType.Null)
|
||||
throw new ArgumentNullException();
|
||||
return new DragDataText(DragPrefix + item.TypeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse the drag data to extract <see cref="Type"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>Gathered objects or empty array if cannot get any valid.</returns>
|
||||
public override IEnumerable<ScriptType> FromDragData(DragData data)
|
||||
{
|
||||
if (data is DragDataText dataText)
|
||||
{
|
||||
if (dataText.Text.StartsWith(DragPrefix))
|
||||
{
|
||||
// Remove prefix and parse spitted names
|
||||
var types = dataText.Text.Remove(0, DragPrefix.Length).Split('\n');
|
||||
var results = new List<ScriptType>(types.Length);
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
{
|
||||
// Find type
|
||||
var obj = TypeUtils.GetType(types[i]);
|
||||
if (obj)
|
||||
results.Add(obj);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
return Utils.GetEmptyArray<ScriptType>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// The keyframes.
|
||||
/// </summary>
|
||||
[EditorDisplay("Keyframes", EditorDisplayAttribute.InlineStyle), ExpandGroups]
|
||||
[Collection(CanReorderItems = false, ReadOnly = true)]
|
||||
[Collection(CanReorderItems = false, CanResize = true)]
|
||||
public List<KeyValuePair<string, object>> Keyframes;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// The parameters values.
|
||||
/// </summary>
|
||||
[EditorDisplay("Parameters", EditorDisplayAttribute.InlineStyle), ExpandGroups]
|
||||
[Collection(CanReorderItems = false, ReadOnly = true)]
|
||||
[Collection(CanReorderItems = false, CanResize = true)]
|
||||
public object[] Parameters;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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,13 +761,16 @@ 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);
|
||||
if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top)
|
||||
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)
|
||||
{
|
||||
// Overshoot...
|
||||
// Estimate overshoot, either it's partially visible or hidden in the tree
|
||||
for (; firstVisibleChild > 0; firstVisibleChild--)
|
||||
{
|
||||
var child = children[firstVisibleChild];
|
||||
if (!child.Visible)
|
||||
continue;
|
||||
|
||||
if (GetChildGlobalRectangle(child, ref globalTransform).Top < globalClipping.Top)
|
||||
break;
|
||||
}
|
||||
@@ -744,18 +779,16 @@ namespace FlaxEditor.GUI.Tree
|
||||
for (int i = firstVisibleChild; i < children.Count; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child.Visible)
|
||||
{
|
||||
var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform);
|
||||
if (globalClipping.Intersects(ref childGlobalRect))
|
||||
{
|
||||
Render2D.PushTransform(ref child._cachedTransform);
|
||||
child.Draw();
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!child.Visible)
|
||||
continue;
|
||||
|
||||
var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform);
|
||||
if (!globalClipping.Intersects(ref childGlobalRect))
|
||||
break;
|
||||
|
||||
Render2D.PushTransform(ref child._cachedTransform);
|
||||
child.Draw();
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
|
||||
static Rectangle GetChildGlobalRectangle(Control control, ref Matrix3x3 globalTransform)
|
||||
|
||||
@@ -253,7 +253,11 @@ namespace FlaxEditor
|
||||
{
|
||||
// Select node (with additive mode)
|
||||
var selection = new List<SceneGraphNode>();
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
if (Root.GetKey(KeyboardKeys.Shift) && transformGizmo.Selection.Contains(uiControlNode))
|
||||
{
|
||||
// Move whole selection
|
||||
}
|
||||
else if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
// Add/remove from selection
|
||||
selection.AddRange(transformGizmo.Selection);
|
||||
@@ -261,13 +265,14 @@ namespace FlaxEditor
|
||||
selection.Remove(uiControlNode);
|
||||
else
|
||||
selection.Add(uiControlNode);
|
||||
owner.Select(selection);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select
|
||||
selection.Add(uiControlNode);
|
||||
owner.Select(selection);
|
||||
}
|
||||
owner.Select(selection);
|
||||
|
||||
// Initialize control movement
|
||||
_mouseMovesControl = true;
|
||||
@@ -499,6 +504,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 +525,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 +622,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
|
||||
{
|
||||
|
||||
@@ -202,6 +202,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
var prefabId = ((ActorNode)selection[0]).Actor.PrefabID;
|
||||
var prefab = FlaxEngine.Content.LoadAsync<Prefab>(prefabId);
|
||||
Editor.Windows.ContentWin.ClearItemsSearch();
|
||||
Editor.Windows.ContentWin.Select(prefab);
|
||||
}
|
||||
|
||||
|
||||
@@ -565,6 +565,7 @@ namespace FlaxEditor.Modules
|
||||
if (item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Scene
|
||||
MenuScene = MainMenu.AddButton("Scene");
|
||||
@@ -619,7 +620,6 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsTakeScreenshot = cm.AddButton("Take screenshot", inputOptions.TakeScreenshot, Editor.Windows.TakeScreenshot);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Plugins", () => Editor.Windows.PluginsWin.Show());
|
||||
cm.AddButton("Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Window
|
||||
MenuWindow = MainMenu.AddButton("Window");
|
||||
@@ -640,7 +640,7 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Visual Script Debugger", Editor.Windows.VisualScriptDebuggerWin.FocusOrShow);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Save window layout", Editor.Windows.SaveLayout);
|
||||
_menuWindowApplyWindowLayout = cm.AddChildMenu("Apply window layout");
|
||||
_menuWindowApplyWindowLayout = cm.AddChildMenu("Window layouts");
|
||||
cm.AddButton("Restore default layout", Editor.Windows.LoadDefaultLayout);
|
||||
|
||||
// Help
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -255,6 +255,17 @@ namespace FlaxEditor.Options
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure custom fonts are valid, reset if not
|
||||
var defaultInterfaceOptions = new InterfaceOptions();
|
||||
if (Style.Current.FontTitle == null)
|
||||
Style.Current.FontTitle = defaultInterfaceOptions.TitleFont.GetFont();
|
||||
if (Style.Current.FontSmall == null)
|
||||
Style.Current.FontSmall = defaultInterfaceOptions.SmallFont.GetFont();
|
||||
if (Style.Current.FontMedium == null)
|
||||
Style.Current.FontMedium = defaultInterfaceOptions.MediumFont.GetFont();
|
||||
if (Style.Current.FontLarge == null)
|
||||
Style.Current.FontLarge = defaultInterfaceOptions.LargeFont.GetFont();
|
||||
|
||||
// Set fallback fonts
|
||||
var fallbackFonts = Options.Interface.FallbackFonts;
|
||||
if (fallbackFonts == null || fallbackFonts.Length == 0 || fallbackFonts.All(x => x == null))
|
||||
|
||||
@@ -15,6 +15,7 @@ using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
@@ -29,6 +30,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
private DragScripts _dragScripts;
|
||||
private DragAssets _dragAssets;
|
||||
private DragActorType _dragActorType;
|
||||
private DragControlType _dragControlType;
|
||||
private DragScriptItems _dragScriptItems;
|
||||
private DragHandlers _dragHandlers;
|
||||
private List<Rectangle> _highlights;
|
||||
@@ -354,7 +356,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
private void OnRenamed(RenamePopup renamePopup)
|
||||
{
|
||||
using (new UndoBlock(ActorNode.Root.Undo, Actor, "Rename"))
|
||||
Actor.Name = renamePopup.Text;
|
||||
Actor.Name = renamePopup.Text.Trim();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -439,6 +441,17 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
}
|
||||
if (_dragActorType.OnDragEnter(data))
|
||||
return _dragActorType.Effect;
|
||||
|
||||
// Check if drag control type
|
||||
if (_dragControlType == null)
|
||||
{
|
||||
_dragControlType = new DragControlType(ValidateDragControlType);
|
||||
_dragHandlers.Add(_dragControlType);
|
||||
}
|
||||
if (_dragControlType.OnDragEnter(data))
|
||||
return _dragControlType.Effect;
|
||||
|
||||
// Check if drag script item
|
||||
if (_dragScriptItems == null)
|
||||
{
|
||||
_dragScriptItems = new DragScriptItems(ValidateDragScriptItem);
|
||||
@@ -572,10 +585,33 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
Editor.LogWarning("Failed to spawn actor of type " + item.TypeName);
|
||||
continue;
|
||||
}
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.StaticFlags = newParent.StaticFlags;
|
||||
actor.Name = item.Name;
|
||||
actor.Transform = Actor.Transform;
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
ActorNode.Root.Spawn(actor, newParent);
|
||||
actor.OrderInParent = newOrder;
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag control type
|
||||
else if (_dragControlType != null && _dragControlType.HasValidDrag)
|
||||
{
|
||||
for (int i = 0; i < _dragControlType.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragControlType.Objects[i];
|
||||
var control = item.CreateInstance() as Control;
|
||||
if (control == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName);
|
||||
continue;
|
||||
}
|
||||
var uiControl = new UIControl
|
||||
{
|
||||
Control = control,
|
||||
StaticFlags = newParent.StaticFlags,
|
||||
Name = item.Name,
|
||||
};
|
||||
ActorNode.Root.Spawn(uiControl, newParent);
|
||||
uiControl.OrderInParent = newOrder;
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
@@ -590,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;
|
||||
@@ -604,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;
|
||||
}
|
||||
@@ -656,12 +705,17 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return true;
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
private static bool ValidateDragControlType(ScriptType controlType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
}
|
||||
|
||||
private bool ValidateDragScriptItem(ScriptItem script)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null || Editor.Instance.CodeEditing.Scripts.Get(script) != ScriptType.Null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -704,6 +758,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
_dragScripts = null;
|
||||
_dragAssets = null;
|
||||
_dragActorType = null;
|
||||
_dragControlType = null;
|
||||
_dragScriptItems = null;
|
||||
_dragHandlers?.Clear();
|
||||
_dragHandlers = null;
|
||||
|
||||
@@ -186,6 +186,18 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnEndMouseCapture();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.Delete:
|
||||
_editor.SetAsset(_index, Guid.Empty);
|
||||
return true;
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -430,7 +430,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Smoothstep",
|
||||
Description = "Returns a smooth Hermite interpolation between 0 and 1, if value is in the range [min, max].",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(120, 60),
|
||||
Size = new Float2(200, 60),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0, 1, 2 },
|
||||
DependentBoxes = new[] { 3 },
|
||||
|
||||
@@ -66,6 +66,8 @@ namespace FlaxEditor.Surface.Undo
|
||||
// Initialize
|
||||
if (node.Values != null && node.Values.Length == _nodeValues.Length)
|
||||
Array.Copy(_nodeValues, node.Values, _nodeValues.Length);
|
||||
else if (_nodeValues != null && (node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
|
||||
node.Values = (object[])_nodeValues.Clone();
|
||||
else if (_nodeValues != null && _nodeValues.Length != 0)
|
||||
throw new InvalidOperationException("Invalid node values.");
|
||||
node.Location = _nodeLocation;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace FlaxEditor.Tools.Foliage
|
||||
}
|
||||
}
|
||||
|
||||
[EditorOrder(20), EditorDisplay("Model"), Collection(ReadOnly = true), Tooltip("Model materials override collection. Can be used to change a specific material of the mesh to the custom one without editing the asset.")]
|
||||
[EditorOrder(20), EditorDisplay("Model"), Collection(CanResize = true), Tooltip("Model materials override collection. Can be used to change a specific material of the mesh to the custom one without editing the asset.")]
|
||||
public MaterialBase[] Materials
|
||||
{
|
||||
get
|
||||
|
||||
@@ -155,8 +155,8 @@ namespace FlaxEditor.Utilities
|
||||
var scene = Level.LoadSceneFromBytes(data.Bytes);
|
||||
if (scene == null)
|
||||
{
|
||||
Profiler.EndEvent();
|
||||
throw new Exception("Failed to deserialize scene");
|
||||
Editor.LogError("Failed to restore scene");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Restore `dirty` state
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -302,8 +302,17 @@ namespace FlaxEditor.Windows.Assets
|
||||
// TODO: improve the UI
|
||||
layout.Space(40);
|
||||
var addParamType = layout.ComboBox().ComboBox;
|
||||
addParamType.Items = AllowedTypes.Select(CustomEditorsUtil.GetTypeNameUI).ToList();
|
||||
addParamType.SelectedIndex = 0;
|
||||
object lastValue = null;
|
||||
foreach (var e in _proxy.DefaultValues)
|
||||
lastValue = e.Value;
|
||||
|
||||
var allowedTypes = AllowedTypes.Select(CustomEditorsUtil.GetTypeNameUI).ToList();
|
||||
int index = 0;
|
||||
if (lastValue != null)
|
||||
index = allowedTypes.FindIndex(x => x.Equals(CustomEditorsUtil.GetTypeNameUI(lastValue.GetType()), StringComparison.Ordinal));
|
||||
|
||||
addParamType.Items = allowedTypes;
|
||||
addParamType.SelectedIndex = index;
|
||||
_addParamType = addParamType;
|
||||
var addParamButton = layout.Button("Add").Button;
|
||||
addParamButton.Clicked += OnAddParamButtonClicked;
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private PrefabWindow _window;
|
||||
private DragAssets _dragAssets;
|
||||
private DragActorType _dragActorType;
|
||||
private DragControlType _dragControlType;
|
||||
private DragScriptItems _dragScriptItems;
|
||||
private DragHandlers _dragHandlers;
|
||||
|
||||
@@ -83,7 +84,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return true;
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragControlType(ScriptType controlType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
@@ -113,6 +119,13 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
if (_dragActorType.OnDragEnter(data))
|
||||
return _dragActorType.Effect;
|
||||
if (_dragControlType == null)
|
||||
{
|
||||
_dragControlType = new DragControlType(ValidateDragControlType);
|
||||
_dragHandlers.Add(_dragControlType);
|
||||
}
|
||||
if (_dragControlType.OnDragEnter(data))
|
||||
return _dragControlType.Effect;
|
||||
if (_dragScriptItems == null)
|
||||
{
|
||||
_dragScriptItems = new DragScriptItems(ValidateDragScriptItem);
|
||||
@@ -176,6 +189,27 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag control type
|
||||
else if (_dragControlType != null && _dragControlType.HasValidDrag)
|
||||
{
|
||||
for (int i = 0; i < _dragControlType.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragControlType.Objects[i];
|
||||
var control = item.CreateInstance() as Control;
|
||||
if (control == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName);
|
||||
continue;
|
||||
}
|
||||
var uiControl = new UIControl
|
||||
{
|
||||
Control = control,
|
||||
Name = item.Name,
|
||||
};
|
||||
_window.Spawn(uiControl);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag script item
|
||||
else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag)
|
||||
{
|
||||
@@ -207,6 +241,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_window = null;
|
||||
_dragAssets = null;
|
||||
_dragActorType = null;
|
||||
_dragControlType = null;
|
||||
_dragScriptItems = null;
|
||||
_dragHandlers?.Clear();
|
||||
_dragHandlers = null;
|
||||
@@ -450,6 +485,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Create undo action
|
||||
var action = new CustomDeleteActorsAction(new List<SceneGraphNode>(1) { actorNode }, true);
|
||||
Undo.AddAction(action);
|
||||
Focus();
|
||||
Select(actorNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace FlaxEditor.Windows
|
||||
public void ClearItemsSearch()
|
||||
{
|
||||
// Skip if already cleared
|
||||
if (_itemsSearchBox.TextLength == 0 && !_viewDropdown.HasSelection)
|
||||
if (_itemsSearchBox.TextLength == 0)
|
||||
return;
|
||||
|
||||
IsLayoutLocked = true;
|
||||
|
||||
@@ -542,6 +542,8 @@ namespace FlaxEditor.Windows
|
||||
return;
|
||||
}
|
||||
|
||||
newShortName = newShortName.Trim();
|
||||
|
||||
// Cache data
|
||||
string extension = item.IsFolder ? "" : Path.GetExtension(item.Path);
|
||||
var newPath = StringUtils.CombinePaths(item.ParentFolder.Path, newShortName + extension);
|
||||
|
||||
@@ -470,6 +470,10 @@ namespace FlaxEditor.Windows
|
||||
IsMaximized = false;
|
||||
IsBorderless = false;
|
||||
Cursor = CursorType.Default;
|
||||
Screen.CursorLock = CursorLockMode.None;
|
||||
if (Screen.MainWindow.IsMouseTracking)
|
||||
Screen.MainWindow.EndTrackingMouse();
|
||||
RootControl.GameRoot.EndMouseCapture();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -478,7 +482,7 @@ namespace FlaxEditor.Windows
|
||||
base.OnMouseLeave();
|
||||
|
||||
// Remove focus from game window when mouse moves out and the cursor is hidden during game
|
||||
if ((IsFocused || ContainsFocus) && Parent != null && Editor.IsPlayMode && !Screen.CursorVisible)
|
||||
if (ContainsFocus && Parent != null && Editor.IsPlayMode && !Screen.CursorVisible && Screen.CursorLock == CursorLockMode.None)
|
||||
{
|
||||
Parent.Focus();
|
||||
}
|
||||
@@ -856,7 +860,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
var alpha = Mathf.Saturate(-animTime / fadeOutTime);
|
||||
var rect = new Rectangle(new Float2(6), Size - 12);
|
||||
var text = "Press Shift+F11 to unlock the mouse";
|
||||
var text = $"Press {Editor.Options.Options.Input.DebuggerUnlockMouse} to unlock the mouse";
|
||||
Render2D.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far);
|
||||
Render2D.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far);
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ namespace FlaxEditor.Windows
|
||||
if (IsLayoutLocked)
|
||||
return;
|
||||
|
||||
_hScroll.Maximum = _output.TextSize.X;
|
||||
_hScroll.Maximum = Mathf.Max(_output.TextSize.X, _hScroll.Minimum);
|
||||
_vScroll.Maximum = Mathf.Max(_output.TextSize.Y - _output.Height, _vScroll.Minimum);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private DragAssets _dragAssets;
|
||||
private DragActorType _dragActorType;
|
||||
private DragControlType _dragControlType;
|
||||
private DragScriptItems _dragScriptItems;
|
||||
private DragHandlers _dragHandlers;
|
||||
|
||||
@@ -275,7 +276,12 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return true;
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragControlType(ScriptType controlType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
@@ -390,6 +396,13 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
if (_dragActorType.OnDragEnter(data) && result == DragDropEffect.None)
|
||||
return _dragActorType.Effect;
|
||||
if (_dragControlType == null)
|
||||
{
|
||||
_dragControlType = new DragControlType(ValidateDragControlType);
|
||||
_dragHandlers.Add(_dragControlType);
|
||||
}
|
||||
if (_dragControlType.OnDragEnter(data) && result == DragDropEffect.None)
|
||||
return _dragControlType.Effect;
|
||||
if (_dragScriptItems == null)
|
||||
{
|
||||
_dragScriptItems = new DragScriptItems(ValidateDragScriptItem);
|
||||
@@ -462,6 +475,28 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag control type
|
||||
else if (_dragControlType != null && _dragControlType.HasValidDrag)
|
||||
{
|
||||
for (int i = 0; i < _dragControlType.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragControlType.Objects[i];
|
||||
var control = item.CreateInstance() as Control;
|
||||
if (control == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName);
|
||||
continue;
|
||||
}
|
||||
var uiControl = new UIControl
|
||||
{
|
||||
Control = control,
|
||||
Name = item.Name,
|
||||
};
|
||||
Level.SpawnActor(uiControl);
|
||||
Editor.Scene.MarkSceneEdited(uiControl.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag script item
|
||||
else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag)
|
||||
{
|
||||
@@ -495,6 +530,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
_dragAssets = null;
|
||||
_dragActorType = null;
|
||||
_dragControlType = null;
|
||||
_dragScriptItems = null;
|
||||
_dragHandlers?.Clear();
|
||||
_dragHandlers = null;
|
||||
|
||||
@@ -191,6 +191,52 @@ namespace FlaxEditor.Windows
|
||||
CreateGroupWithList(_actorGroups, "GUI");
|
||||
CreateGroupWithList(_actorGroups, "Other");
|
||||
|
||||
// Add control types to tabs
|
||||
foreach (var controlType in Editor.Instance.CodeEditing.Controls.Get())
|
||||
{
|
||||
if (controlType.IsAbstract)
|
||||
continue;
|
||||
_groupSearch.AddChild(CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType));
|
||||
ActorToolboxAttribute attribute = null;
|
||||
foreach (var e in controlType.GetAttributes(false))
|
||||
{
|
||||
if (e is ActorToolboxAttribute actorToolboxAttribute)
|
||||
{
|
||||
attribute = actorToolboxAttribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attribute == null)
|
||||
continue;
|
||||
var groupName = attribute.Group.Trim();
|
||||
|
||||
// Check if tab already exists and add it to the tab
|
||||
var actorTabExists = false;
|
||||
foreach (var child in _actorGroups.Children)
|
||||
{
|
||||
if (child is Tab tab)
|
||||
{
|
||||
if (string.Equals(tab.Text, groupName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var tree = tab.GetChild<Panel>().GetChild<Tree>();
|
||||
if (tree != null)
|
||||
{
|
||||
tree.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType) : CreateControlItem(attribute.Name, controlType));
|
||||
tree.SortChildren();
|
||||
}
|
||||
actorTabExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actorTabExists)
|
||||
continue;
|
||||
|
||||
var group = CreateGroupWithList(_actorGroups, groupName);
|
||||
group.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType) : CreateControlItem(attribute.Name, controlType));
|
||||
group.SortChildren();
|
||||
}
|
||||
|
||||
// Add other actor types to respective tab based on attribute
|
||||
foreach (var actorType in Editor.CodeEditing.Actors.Get())
|
||||
{
|
||||
@@ -304,6 +350,11 @@ namespace FlaxEditor.Windows
|
||||
return new ScriptTypeItem(name, type, GUI.Drag.DragActorType.GetDragData(type));
|
||||
}
|
||||
|
||||
private Item CreateControlItem(string name, ScriptType type)
|
||||
{
|
||||
return new ScriptTypeItem(name, type, GUI.Drag.DragControlType.GetDragData(type));
|
||||
}
|
||||
|
||||
private ContainerControl CreateGroupWithList(Tabs parentTabs, string title, float topOffset = 0)
|
||||
{
|
||||
var tab = parentTabs.AddTab(new Tab(title));
|
||||
@@ -316,6 +367,7 @@ namespace FlaxEditor.Windows
|
||||
var tree = new Tree(false)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Margin = new Margin(0, 0, 0, panel.ScrollBarsSize),
|
||||
IsScrollable = true,
|
||||
Parent = panel
|
||||
};
|
||||
|
||||
@@ -163,7 +163,10 @@ namespace Serialization
|
||||
{
|
||||
const auto& keyframesArray = mKeyframes->value.GetArray();
|
||||
auto& keyframes = v.GetKeyframes();
|
||||
const int32 newCount = keyframesArray.Size() - keyframes.Count();
|
||||
keyframes.Resize(keyframesArray.Size());
|
||||
for (int32 i = 0; i < newCount; i++)
|
||||
keyframes[keyframes.Count() + i - newCount] = KeyFrame(0.0f, AnimationUtils::GetZero<T>());
|
||||
for (rapidjson::SizeType i = 0; i < keyframesArray.Size(); i++)
|
||||
Deserialize(keyframesArray[i], keyframes[i], modifier);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -80,7 +80,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
// Calculate new positions for joint and end effector
|
||||
Vector3 newEndEffectorPos = targetPosition;
|
||||
Vector3 newMidJointPos = midJointPos;
|
||||
|
||||
if (toTargetLength >= totalLimbLength)
|
||||
{
|
||||
// Target is beyond the reach of the limb
|
||||
@@ -90,13 +89,9 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
Vector3 rootToPole = (poleVector - rootTransform.Translation).GetNormalized();
|
||||
Vector3 slightBendDirection = Vector3::Cross(rootToEnd, rootToPole);
|
||||
if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance)
|
||||
{
|
||||
slightBendDirection = Vector3::Up;
|
||||
}
|
||||
else
|
||||
{
|
||||
slightBendDirection.Normalize();
|
||||
}
|
||||
|
||||
// Calculate the direction from root to mid joint with a slight offset towards the pole vector
|
||||
Vector3 midJointDirection = Vector3::Cross(slightBendDirection, rootToEnd).GetNormalized();
|
||||
@@ -117,9 +112,16 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
projJointDist *= -1.0f;
|
||||
newMidJointPos = rootTransform.Translation + projJointDist * toTargetDir + jointLineDist * bendDirection;
|
||||
}
|
||||
|
||||
// TODO: fix the new IK impl (https://github.com/FlaxEngine/FlaxEngine/pull/2421) to properly work for character from https://github.com/PrecisionRender/CharacterControllerPro
|
||||
#define OLD 0
|
||||
// Update root joint orientation
|
||||
{
|
||||
#if OLD
|
||||
const Vector3 oldDir = (midJointPos - rootTransform.Translation).GetNormalized();
|
||||
const Vector3 newDir = (newMidJointPos - rootTransform.Translation).GetNormalized();
|
||||
const Quaternion deltaRotation = Quaternion::FindBetween(oldDir, newDir);
|
||||
rootTransform.Orientation = deltaRotation * rootTransform.Orientation;
|
||||
#else
|
||||
// Vector from root joint to mid joint (local Y-axis direction)
|
||||
Vector3 localY = (newMidJointPos - rootTransform.Translation).GetNormalized();
|
||||
|
||||
@@ -136,20 +138,23 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
localZ = Vector3::Cross(localX, localY).GetNormalized();
|
||||
|
||||
// Construct a rotation from the orthogonal basis vectors
|
||||
Quaternion newRootJointOrientation = Quaternion::LookRotation(localZ, localY);
|
||||
|
||||
// Apply the new rotation to the root joint
|
||||
rootTransform.Orientation = newRootJointOrientation;
|
||||
rootTransform.Orientation = Quaternion::LookRotation(localZ, localY);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Update mid joint orientation to point Y-axis towards the end effector and Z-axis perpendicular to the IK plane
|
||||
{
|
||||
#if OLD
|
||||
const Vector3 oldDir = (endEffectorTransform.Translation - midJointPos).GetNormalized();
|
||||
const Vector3 newDir = (newEndEffectorPos - newMidJointPos).GetNormalized();
|
||||
const Quaternion deltaRotation = Quaternion::FindBetween(oldDir, newDir);
|
||||
midJointTransform.Orientation = deltaRotation * midJointTransform.Orientation;
|
||||
#else
|
||||
// Vector from mid joint to end effector (local Y-axis direction after rotation)
|
||||
Vector3 midToEnd = (newEndEffectorPos - newMidJointPos).GetNormalized();
|
||||
|
||||
// Calculate the plane normal using the root, mid joint, and end effector positions (will be the local Z-axis direction)
|
||||
Vector3 rootToMid = (newMidJointPos - rootTransform.Translation).GetNormalized();
|
||||
Vector3 planeNormal = Vector3::Cross(rootToMid, midToEnd).GetNormalized();
|
||||
|
||||
// Vector from mid joint to end effector (local Y-axis direction)
|
||||
Vector3 localY = (newEndEffectorPos - newMidJointPos).GetNormalized();
|
||||
@@ -157,21 +162,18 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
// Calculate the plane normal using the root, mid joint, and end effector positions (local Z-axis direction)
|
||||
Vector3 localZ = Vector3::Cross(rootToMid, localY).GetNormalized();
|
||||
|
||||
//// Calculate the local X-axis direction, should be perpendicular to the Y and Z axes
|
||||
// Calculate the local X-axis direction, should be perpendicular to the Y and Z axes
|
||||
Vector3 localX = Vector3::Cross(localY, localZ).GetNormalized();
|
||||
|
||||
// Correct the local Z-axis direction based on the cross product of X and Y to ensure orthogonality
|
||||
localZ = Vector3::Cross(localX, localY).GetNormalized();
|
||||
|
||||
// Construct a rotation from the orthogonal basis vectors
|
||||
// The axes are used differently here than a standard LookRotation to align Z towards the end and Y perpendicular
|
||||
Quaternion newMidJointOrientation = Quaternion::LookRotation(localZ, localY); // Assuming FromLookRotation creates a rotation with the first vector as forward and the second as up
|
||||
|
||||
// Apply the new rotation to the mid joint
|
||||
midJointTransform.Orientation = newMidJointOrientation;
|
||||
midJointTransform.Translation = newMidJointPos;
|
||||
midJointTransform.Orientation = Quaternion::LookRotation(localZ, localY);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Update end effector transform
|
||||
// Update mid and end locations
|
||||
midJointTransform.Translation = newMidJointPos;
|
||||
endEffectorTransform.Translation = newEndEffectorPos;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,9 @@ Asset::LoadResult Material::load()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// Guard file with the lock during shader generation (prevents FlaxStorage::Tick from messing with the file)
|
||||
auto lock = Storage->Lock();
|
||||
|
||||
// Prepare
|
||||
MaterialGenerator generator;
|
||||
generator.Error.Bind(&OnGeneratorError);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +252,7 @@ void ContentStorageSystem::Job(int32 index)
|
||||
{
|
||||
PROFILE_CPU_NAMED("ContentStorage.Job");
|
||||
|
||||
const double time = Platform::GetTimeSeconds();
|
||||
ScopeLock lock(Locker);
|
||||
for (auto i = StorageMap.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
@@ -271,7 +272,7 @@ void ContentStorageSystem::Job(int32 index)
|
||||
}
|
||||
else
|
||||
{
|
||||
storage->Tick();
|
||||
storage->Tick(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 +1335,10 @@ FileReadStream* FlaxStorage::OpenFile()
|
||||
|
||||
bool FlaxStorage::CloseFileHandles()
|
||||
{
|
||||
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
|
||||
// Note: this is usually called by the content manager when this file is not used or on exit
|
||||
@@ -1367,7 +1370,7 @@ bool FlaxStorage::CloseFileHandles()
|
||||
return true; // Failed, someone is still accessing the file
|
||||
|
||||
// Close file handles (from all threads)
|
||||
Array<FileReadStream*> streams;
|
||||
Array<FileReadStream*, InlinedAllocation<8>> streams;
|
||||
_file.GetValues(streams);
|
||||
for (FileReadStream* stream : streams)
|
||||
{
|
||||
@@ -1375,6 +1378,7 @@ bool FlaxStorage::CloseFileHandles()
|
||||
Delete(stream);
|
||||
}
|
||||
_file.Clear();
|
||||
Platform::AtomicStore(&_files, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1394,19 +1398,18 @@ void FlaxStorage::Dispose()
|
||||
_version = 0;
|
||||
}
|
||||
|
||||
void FlaxStorage::Tick()
|
||||
void FlaxStorage::Tick(double time)
|
||||
{
|
||||
// Check if chunks are locked
|
||||
// Skip if file is in use
|
||||
if (Platform::AtomicRead(&_chunksLock) != 0)
|
||||
return;
|
||||
|
||||
const double now = Platform::GetTimeSeconds();
|
||||
bool wasAnyUsed = false;
|
||||
const float unusedDataChunksLifetime = ContentStorageManager::UnusedDataChunksLifetime.GetTotalSeconds();
|
||||
for (int32 i = 0; i < _chunks.Count(); i++)
|
||||
{
|
||||
auto chunk = _chunks.Get()[i];
|
||||
const bool wasUsed = (now - chunk->LastAccessTime) < unusedDataChunksLifetime;
|
||||
const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime;
|
||||
if (!wasUsed && chunk->IsLoaded())
|
||||
{
|
||||
chunk->Unload();
|
||||
@@ -1414,7 +1417,7 @@ void FlaxStorage::Tick()
|
||||
wasAnyUsed |= wasUsed;
|
||||
}
|
||||
|
||||
// Release file handles in none of chunks was not used
|
||||
// Release file handles in none of chunks is in use
|
||||
if (!wasAnyUsed && Platform::AtomicRead(&_chunksLock) == 0)
|
||||
{
|
||||
CloseFileHandles();
|
||||
|
||||
@@ -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:
|
||||
@@ -415,7 +416,7 @@ public:
|
||||
/// <summary>
|
||||
/// Ticks this instance.
|
||||
/// </summary>
|
||||
void Tick();
|
||||
void Tick(double time);
|
||||
|
||||
#if USE_EDITOR
|
||||
void OnRename(const StringView& newPath);
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
int32 ContentKey = 0;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).
|
||||
/// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett). Enabled by default for `Release` builds.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"General\")")
|
||||
bool ForDistribution = false;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace FlaxEditor.Content.Settings
|
||||
/// <summary>
|
||||
/// The layers names.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(ReadOnly = true, Display = CollectionAttribute.DisplayType.Inline)]
|
||||
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = true, Display = CollectionAttribute.DisplayType.Inline)]
|
||||
public string[] Layers = new string[32];
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -32,41 +32,39 @@ Color::Color(const Color32& color)
|
||||
{
|
||||
}
|
||||
|
||||
Color Color::FromHex(const String& hexString, bool& isValid)
|
||||
Color Color::FromHex(const String& hex, bool& isValid)
|
||||
{
|
||||
int32 r, g, b, a = 255;
|
||||
isValid = true;
|
||||
|
||||
int32 startIndex = !hexString.IsEmpty() && hexString[0] == Char('#') ? 1 : 0;
|
||||
if (hexString.Length() == 3 + startIndex)
|
||||
int32 startIndex = !hex.IsEmpty() && hex[0] == Char('#') ? 1 : 0;
|
||||
if (hex.Length() == 3 + startIndex)
|
||||
{
|
||||
r = StringUtils::HexDigit(hexString[startIndex++]);
|
||||
g = StringUtils::HexDigit(hexString[startIndex++]);
|
||||
b = StringUtils::HexDigit(hexString[startIndex]);
|
||||
r = StringUtils::HexDigit(hex[startIndex++]);
|
||||
g = StringUtils::HexDigit(hex[startIndex++]);
|
||||
b = StringUtils::HexDigit(hex[startIndex]);
|
||||
|
||||
r = (r << 4) + r;
|
||||
g = (g << 4) + g;
|
||||
b = (b << 4) + b;
|
||||
}
|
||||
else if (hexString.Length() == 6 + startIndex)
|
||||
else if (hex.Length() == 6 + startIndex)
|
||||
{
|
||||
r = (StringUtils::HexDigit(hexString[startIndex + 0]) << 4) + StringUtils::HexDigit(hexString[startIndex + 1]);
|
||||
g = (StringUtils::HexDigit(hexString[startIndex + 2]) << 4) + StringUtils::HexDigit(hexString[startIndex + 3]);
|
||||
b = (StringUtils::HexDigit(hexString[startIndex + 4]) << 4) + StringUtils::HexDigit(hexString[startIndex + 5]);
|
||||
r = (StringUtils::HexDigit(hex[startIndex + 0]) << 4) + StringUtils::HexDigit(hex[startIndex + 1]);
|
||||
g = (StringUtils::HexDigit(hex[startIndex + 2]) << 4) + StringUtils::HexDigit(hex[startIndex + 3]);
|
||||
b = (StringUtils::HexDigit(hex[startIndex + 4]) << 4) + StringUtils::HexDigit(hex[startIndex + 5]);
|
||||
}
|
||||
else if (hexString.Length() == 8 + startIndex)
|
||||
else if (hex.Length() == 8 + startIndex)
|
||||
{
|
||||
r = (StringUtils::HexDigit(hexString[startIndex + 0]) << 4) + StringUtils::HexDigit(hexString[startIndex + 1]);
|
||||
g = (StringUtils::HexDigit(hexString[startIndex + 2]) << 4) + StringUtils::HexDigit(hexString[startIndex + 3]);
|
||||
b = (StringUtils::HexDigit(hexString[startIndex + 4]) << 4) + StringUtils::HexDigit(hexString[startIndex + 5]);
|
||||
a = (StringUtils::HexDigit(hexString[startIndex + 6]) << 4) + StringUtils::HexDigit(hexString[startIndex + 7]);
|
||||
r = (StringUtils::HexDigit(hex[startIndex + 0]) << 4) + StringUtils::HexDigit(hex[startIndex + 1]);
|
||||
g = (StringUtils::HexDigit(hex[startIndex + 2]) << 4) + StringUtils::HexDigit(hex[startIndex + 3]);
|
||||
b = (StringUtils::HexDigit(hex[startIndex + 4]) << 4) + StringUtils::HexDigit(hex[startIndex + 5]);
|
||||
a = (StringUtils::HexDigit(hex[startIndex + 6]) << 4) + StringUtils::HexDigit(hex[startIndex + 7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = g = b = 0;
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
return FromBytes(r, g, b, a);
|
||||
}
|
||||
|
||||
@@ -122,8 +120,9 @@ String Color::ToHexString() const
|
||||
const byte r = static_cast<byte>(R * MAX_uint8);
|
||||
const byte g = static_cast<byte>(G * MAX_uint8);
|
||||
const byte b = static_cast<byte>(B * MAX_uint8);
|
||||
const byte a = static_cast<byte>(A * MAX_uint8);
|
||||
|
||||
Char result[6];
|
||||
Char result[8];
|
||||
|
||||
result[0] = digits[r >> 4 & 0x0f];
|
||||
result[1] = digits[r & 0x0f];
|
||||
@@ -134,7 +133,10 @@ String Color::ToHexString() const
|
||||
result[4] = digits[b >> 4 & 0x0f];
|
||||
result[5] = digits[b & 0x0f];
|
||||
|
||||
return String(result, 6);
|
||||
result[6] = digits[a >> 4 & 0x0f];
|
||||
result[7] = digits[a & 0x0f];
|
||||
|
||||
return String(result, 8);
|
||||
}
|
||||
|
||||
bool Color::IsTransparent() const
|
||||
|
||||
@@ -230,36 +230,93 @@ namespace FlaxEngine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="Color"/> from the RGB value and separate alpha channel.
|
||||
/// Creates <see cref="Color"/> from the RGB value (bottom bits contain Blue) and separate alpha channel.
|
||||
/// </summary>
|
||||
/// <param name="rgb">The packed RGB value.</param>
|
||||
/// <param name="rgb">The packed RGB value (bottom bits contain Blue).</param>
|
||||
/// <param name="a">The alpha channel value.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static Color FromRGB(uint rgb, float a = 1.0f)
|
||||
{
|
||||
return new Color(
|
||||
((rgb >> 16) & 0xff) / 255.0f,
|
||||
((rgb >> 8) & 0xff) / 255.0f,
|
||||
(rgb & 0xff) / 255.0f,
|
||||
a);
|
||||
return new Color(((rgb >> 16) & 0xff) / 255.0f, ((rgb >> 8) & 0xff) / 255.0f, (rgb & 0xff) / 255.0f, a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="Color"/> from the RGBA value.
|
||||
/// Creates <see cref="Color"/> from the ARGB value (bottom bits contain Blue).
|
||||
/// </summary>
|
||||
/// <param name="rgb">The packed RGBA value.</param>
|
||||
/// <param name="argb">The packed ARGB value (bottom bits contain Blue).</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static Color FromRGBA(uint rgb)
|
||||
public static Color FromARGB(uint argb)
|
||||
{
|
||||
return new Color(
|
||||
((rgb >> 16) & 0xff) / 255.0f,
|
||||
((rgb >> 8) & 0xff) / 255.0f,
|
||||
(rgb & 0xff) / 255.0f,
|
||||
((rgb >> 24) & 0xff) / 255.0f);
|
||||
return new Color(((argb >> 16) & 0xff) / 255.0f, ((argb >> 8) & 0xff) / 255.0f, ((argb >> 0) & 0xff) / 255.0f, ((argb >> 24) & 0xff) / 255.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color value as the hexadecimal string.
|
||||
/// Creates <see cref="Color"/> from the RGBA value (bottom bits contain Alpha).
|
||||
/// </summary>
|
||||
/// <param name="rgba">The packed RGBA value (bottom bits Alpha Red).</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static Color FromRGBA(uint rgba)
|
||||
{
|
||||
return new Color(((rgba >> 24) & 0xff) / 255.0f, ((rgba >> 16) & 0xff) / 255.0f, ((rgba >> 8) & 0xff) / 255.0f, ((rgba >> 0) & 0xff) / 255.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="Color"/> from the Hex string.
|
||||
/// </summary>
|
||||
/// <param name="hex">The hexadecimal color string.</param>
|
||||
/// <returns>The output color value.</returns>
|
||||
public static Color FromHex(string hex)
|
||||
{
|
||||
FromHex(hex, out var color);
|
||||
return color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="Color"/> from the Hex string.
|
||||
/// </summary>
|
||||
/// <param name="hex">The hexadecimal color string.</param>
|
||||
/// <param name="color">The output color value. Valid if method returns true.</param>
|
||||
/// <returns>True if method was able to convert color, otherwise false.</returns>
|
||||
public static bool FromHex(string hex, out Color color)
|
||||
{
|
||||
int r, g, b, a = 255;
|
||||
bool isValid = true;
|
||||
|
||||
int startIndex = hex.Length != 0 && hex[0] == '#' ? 1 : 0;
|
||||
if (hex.Length == 3 + startIndex)
|
||||
{
|
||||
r = StringUtils.HexDigit(hex[startIndex++]);
|
||||
g = StringUtils.HexDigit(hex[startIndex++]);
|
||||
b = StringUtils.HexDigit(hex[startIndex]);
|
||||
r = (r << 4) + r;
|
||||
g = (g << 4) + g;
|
||||
b = (b << 4) + b;
|
||||
}
|
||||
else if (hex.Length == 6 + startIndex)
|
||||
{
|
||||
r = (StringUtils.HexDigit(hex[startIndex + 0]) << 4) + StringUtils.HexDigit(hex[startIndex + 1]);
|
||||
g = (StringUtils.HexDigit(hex[startIndex + 2]) << 4) + StringUtils.HexDigit(hex[startIndex + 3]);
|
||||
b = (StringUtils.HexDigit(hex[startIndex + 4]) << 4) + StringUtils.HexDigit(hex[startIndex + 5]);
|
||||
}
|
||||
else if (hex.Length == 8 + startIndex)
|
||||
{
|
||||
r = (StringUtils.HexDigit(hex[startIndex + 0]) << 4) + StringUtils.HexDigit(hex[startIndex + 1]);
|
||||
g = (StringUtils.HexDigit(hex[startIndex + 2]) << 4) + StringUtils.HexDigit(hex[startIndex + 3]);
|
||||
b = (StringUtils.HexDigit(hex[startIndex + 4]) << 4) + StringUtils.HexDigit(hex[startIndex + 5]);
|
||||
a = (StringUtils.HexDigit(hex[startIndex + 6]) << 4) + StringUtils.HexDigit(hex[startIndex + 7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = g = b = 0;
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
color = new Color(r, g, b, a);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color value as the hexadecimal string (in RGBA order).
|
||||
/// </summary>
|
||||
/// <returns>Hex string.</returns>
|
||||
public string ToHexString()
|
||||
@@ -287,7 +344,7 @@ namespace FlaxEngine
|
||||
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="Color"/> from the text string (hex or color name).
|
||||
/// </summary>
|
||||
|
||||
@@ -126,9 +126,9 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes from packed RGB value of the color and separate alpha channel value.
|
||||
/// Initializes from packed RGB value (bottom bits contain Blue) of the color and separate alpha channel value.
|
||||
/// </summary>
|
||||
/// <param name="rgb">The packed RGB value.</param>
|
||||
/// <param name="rgb">The packed RGB value (bottom bits contain Blue).</param>
|
||||
/// <param name="a">The alpha channel.</param>
|
||||
/// <returns>The color.</returns>
|
||||
static Color FromRGB(uint32 rgb, float a = 1.0f)
|
||||
@@ -137,22 +137,32 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes from packed RGBA value.
|
||||
/// Initializes from packed ARGB value (bottom bits contain Blue).
|
||||
/// </summary>
|
||||
/// <param name="rgba">The packed RGBA value.</param>
|
||||
/// <param name="argb">The packed ARGB value (bottom bits contain Blue).</param>
|
||||
/// <returns>The color.</returns>
|
||||
static Color FromARGB(uint32 argb)
|
||||
{
|
||||
return Color((float)((argb >> 16) & 0xff) / 255.0f,(float)((argb >> 8) & 0xff) / 255.0f, (float)((argb >> 0) & 0xff) / 255.0f, (float)((argb >> 24) & 0xff) / 255.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes from packed RGBA value (bottom bits contain Alpha).
|
||||
/// </summary>
|
||||
/// <param name="rgba">The packed RGBA value (bottom bits contain Alpha).</param>
|
||||
/// <returns>The color.</returns>
|
||||
static Color FromRGBA(uint32 rgba)
|
||||
{
|
||||
return Color(static_cast<float>(rgba >> 16 & 0xff) / 255.0f, static_cast<float>(rgba >> 8 & 0xff) / 255.0f, static_cast<float>(rgba & 0xff) / 255.0f, static_cast<float>(rgba >> 24 & 0xff) / 255.0f);
|
||||
return Color((float)((rgba >> 24) & 0xff) / 255.0f,(float)((rgba >> 16) & 0xff) / 255.0f, (float)((rgba >> 8) & 0xff) / 255.0f, (float)((rgba >> 0) & 0xff) / 255.0f);
|
||||
}
|
||||
|
||||
static Color FromHex(const String& hexString)
|
||||
static Color FromHex(const String& hex)
|
||||
{
|
||||
bool isValid;
|
||||
return FromHex(hexString, isValid);
|
||||
return FromHex(hex, isValid);
|
||||
}
|
||||
|
||||
static Color FromHex(const String& hexString, bool& isValid);
|
||||
static Color FromHex(const String& hex, bool& isValid);
|
||||
|
||||
/// <summary>
|
||||
/// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1].
|
||||
|
||||
@@ -586,8 +586,17 @@ namespace FlaxEngine.Interop
|
||||
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
|
||||
{
|
||||
Type elementType = Unsafe.As<TypeHolder>(elementTypeHandle.Target);
|
||||
Type classType = ArrayFactory.GetArrayType(elementType);
|
||||
return GetTypeManagedHandle(classType);
|
||||
Type arrayType = ArrayFactory.GetArrayType(elementType);
|
||||
return GetTypeManagedHandle(arrayType);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetArrayTypeFromWrappedArray(ManagedHandle arrayHandle)
|
||||
{
|
||||
ManagedArray managedArray = Unsafe.As<ManagedArray>(arrayHandle.Target);
|
||||
Type elementType = managedArray.ArrayType.GetElementType();
|
||||
Type arrayType = ArrayFactory.GetArrayType(elementType);
|
||||
return GetTypeManagedHandle(arrayType);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -42,7 +42,7 @@ void RenderTask::DrawAll()
|
||||
// Sort tasks (by Order property)
|
||||
Sorting::QuickSortObj(Tasks.Get(), Tasks.Count());
|
||||
|
||||
// Render all that shit
|
||||
// Render all tasks
|
||||
for (auto task : Tasks)
|
||||
{
|
||||
if (task->CanDraw())
|
||||
|
||||
@@ -349,9 +349,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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -33,6 +33,14 @@
|
||||
#define VULKAN_USE_DEBUG_LAYER GPU_ENABLE_DIAGNOSTICS
|
||||
#define VULKAN_USE_DEBUG_DATA (GPU_ENABLE_DIAGNOSTICS && COMPILE_WITH_DEV_ENV)
|
||||
|
||||
#ifndef VULKAN_USE_VALIDATION_CACHE
|
||||
#ifdef VK_EXT_validation_cache
|
||||
#define VULKAN_USE_VALIDATION_CACHE VK_EXT_validation_cache
|
||||
#else
|
||||
#define VULKAN_USE_VALIDATION_CACHE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef VULKAN_USE_QUERIES
|
||||
#define VULKAN_USE_QUERIES 1
|
||||
#endif
|
||||
|
||||
@@ -39,7 +39,7 @@ static const char* GInstanceExtensions[] =
|
||||
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
|
||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
||||
#endif
|
||||
#if VK_EXT_validation_cache
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
VK_EXT_VALIDATION_CACHE_EXTENSION_NAME,
|
||||
#endif
|
||||
#if defined(VK_KHR_display) && 0
|
||||
@@ -57,7 +57,7 @@ static const char* GDeviceExtensions[] =
|
||||
#if VK_KHR_maintenance1
|
||||
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
|
||||
#endif
|
||||
#if VK_EXT_validation_cache
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
VK_EXT_VALIDATION_CACHE_EXTENSION_NAME,
|
||||
#endif
|
||||
#if VK_KHR_sampler_mirror_clamp_to_edge
|
||||
@@ -582,7 +582,7 @@ void GPUDeviceVulkan::ParseOptionalDeviceExtensions(const Array<const char*>& de
|
||||
OptionalDeviceExtensions.HasKHRMaintenance2 = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_MAINTENANCE2_EXTENSION_NAME);
|
||||
#endif
|
||||
OptionalDeviceExtensions.HasMirrorClampToEdge = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME);
|
||||
#if VK_EXT_validation_cache
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
OptionalDeviceExtensions.HasEXTValidationCache = RenderToolsVulkan::HasExtension(deviceExtensions, VK_EXT_VALIDATION_CACHE_EXTENSION_NAME);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1439,7 +1439,7 @@ bool GPUDeviceVulkan::SavePipelineCache()
|
||||
return File::WriteAllBytes(path, data);
|
||||
}
|
||||
|
||||
#if VK_EXT_validation_cache
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
|
||||
void GetValidationCachePath(String& path)
|
||||
{
|
||||
@@ -1900,7 +1900,7 @@ bool GPUDeviceVulkan::Init()
|
||||
const VkResult result = vkCreatePipelineCache(Device, &pipelineCacheCreateInfo, nullptr, &PipelineCache);
|
||||
LOG_VULKAN_RESULT(result);
|
||||
}
|
||||
#if VK_EXT_validation_cache
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
if (OptionalDeviceExtensions.HasEXTValidationCache && vkCreateValidationCacheEXT && vkDestroyValidationCacheEXT)
|
||||
{
|
||||
Array<uint8> data;
|
||||
@@ -1915,16 +1915,16 @@ bool GPUDeviceVulkan::Init()
|
||||
int32* dataPtr = (int32*)data.Get();
|
||||
if (*dataPtr > 0)
|
||||
{
|
||||
dataPtr++;
|
||||
const int32 version = *dataPtr++;
|
||||
const int32 versionExpected = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
|
||||
if (version == versionExpected)
|
||||
const int32 cacheSize = *dataPtr++;
|
||||
const int32 cacheVersion = *dataPtr++;
|
||||
const int32 cacheVersionExpected = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
|
||||
if (cacheVersion == cacheVersionExpected)
|
||||
{
|
||||
dataPtr += VK_UUID_SIZE / sizeof(int32);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Warning, "Bad validation cache file, version: {0}, expected: {1}", version, versionExpected);
|
||||
LOG(Warning, "Bad validation cache file, version: {0}, expected: {1}", cacheVersion, cacheVersionExpected);
|
||||
data.Clear();
|
||||
}
|
||||
}
|
||||
@@ -2003,7 +2003,7 @@ void GPUDeviceVulkan::Dispose()
|
||||
vkDestroyPipelineCache(Device, PipelineCache, nullptr);
|
||||
PipelineCache = VK_NULL_HANDLE;
|
||||
}
|
||||
#if VK_EXT_validation_cache
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
if (ValidationCache != VK_NULL_HANDLE)
|
||||
{
|
||||
if (SaveValidationCache())
|
||||
|
||||
@@ -400,7 +400,9 @@ public:
|
||||
uint32 HasKHRMaintenance1 : 1;
|
||||
uint32 HasKHRMaintenance2 : 1;
|
||||
uint32 HasMirrorClampToEdge : 1;
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
uint32 HasEXTValidationCache : 1;
|
||||
#endif
|
||||
};
|
||||
|
||||
static void GetInstanceLayersAndExtensions(Array<const char*>& outInstanceExtensions, Array<const char*>& outInstanceLayers, bool& outDebugUtils);
|
||||
@@ -496,13 +498,11 @@ public:
|
||||
/// </summary>
|
||||
VkPipelineCache PipelineCache = VK_NULL_HANDLE;
|
||||
|
||||
#if VK_EXT_validation_cache
|
||||
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
/// <summary>
|
||||
/// The optional validation cache.
|
||||
/// </summary>
|
||||
VkValidationCacheEXT ValidationCache = VK_NULL_HANDLE;
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
@@ -584,12 +584,10 @@ public:
|
||||
bool SavePipelineCache();
|
||||
|
||||
#if VK_EXT_validation_cache
|
||||
|
||||
/// <summary>
|
||||
/// Saves the validation cache.
|
||||
/// </summary>
|
||||
bool SaveValidationCache();
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
@@ -116,7 +116,7 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
|
||||
RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO);
|
||||
createInfo.codeSize = (size_t)spirv.Length();
|
||||
createInfo.pCode = (const uint32_t*)spirv.Get();
|
||||
#if VK_EXT_validation_cache
|
||||
#if VULKAN_USE_VALIDATION_CACHE
|
||||
VkShaderModuleValidationCacheCreateInfoEXT validationInfo;
|
||||
if (_device->ValidationCache != VK_NULL_HANDLE)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <vulkan/vulkan.h>
|
||||
#undef VK_EXT_debug_utils
|
||||
#undef VK_EXT_validation_cache
|
||||
#define VULKAN_USE_VALIDATION_CACHE 0
|
||||
#pragma clang diagnostic ignored "-Wpointer-bool-conversion"
|
||||
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
// Support more backbuffers in case driver decides to use more (https://gitlab.freedesktop.org/apinheiro/mesa/-/issues/9)
|
||||
#define VULKAN_BACK_BUFFERS_COUNT_MAX 8
|
||||
|
||||
// Prevent wierd error 'Invalid VkValidationCacheEXT Object'
|
||||
#define VULKAN_USE_VALIDATION_CACHE 0
|
||||
|
||||
/// <summary>
|
||||
/// The implementation for the Vulkan API support for Linux platform.
|
||||
/// </summary>
|
||||
|
||||
@@ -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)
|
||||
@@ -1226,6 +1229,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.
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the brush proxies per surface.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="Serialize, EditorOrder(100), EditorDisplay(\"Surfaces\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems = false, NotNullItems = true, ReadOnly = true)")
|
||||
API_PROPERTY(Attributes="Serialize, EditorOrder(100), EditorDisplay(\"Surfaces\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems = false, NotNullItems = true, CanResize = true)")
|
||||
Array<BrushSurface> GetSurfaces() const;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -349,6 +349,11 @@ void Camera::Draw(RenderContext& renderContext)
|
||||
_previewModel->Draw(renderContext, draw);
|
||||
}
|
||||
}
|
||||
// Load preview model if it doesnt exist. Ex: prefabs
|
||||
else if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::EditorSprites) && !_previewModel)
|
||||
{
|
||||
_previewModel = Content::LoadAsyncInternal<Model>(TEXT("Editor/Camera/O_Camera"));
|
||||
}
|
||||
}
|
||||
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the model entries collection. Each entry contains data how to render meshes using this entry (transformation, material, shadows casting, etc.).
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="Serialize, EditorOrder(1000), EditorDisplay(\"Entries\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems=false, NotNullItems=true, ReadOnly=true, Spacing=10)")
|
||||
API_PROPERTY(Attributes="Serialize, EditorOrder(1000), EditorDisplay(\"Entries\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems=false, NotNullItems=true, CanResize=false, Spacing=10)")
|
||||
FORCE_INLINE const Array<ModelInstanceEntry>& GetEntries() const
|
||||
{
|
||||
return Entries;
|
||||
|
||||
@@ -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())
|
||||
@@ -372,6 +378,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]
|
||||
|
||||
@@ -1392,12 +1392,20 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
|
||||
if (ownerClientId == NetworkManager::LocalClientId)
|
||||
{
|
||||
// Ensure local client owns that object actually
|
||||
CHECK(localRole == NetworkObjectRole::OwnedAuthoritative);
|
||||
if (localRole != NetworkObjectRole::OwnedAuthoritative)
|
||||
{
|
||||
LOG(Error, "Cannot change overship of object (Id={}) to the local client (Id={}) if the local role is not set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure local client doesn't own that object since it's owned by other client
|
||||
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
|
||||
if (localRole == NetworkObjectRole::OwnedAuthoritative)
|
||||
{
|
||||
LOG(Error, "Cannot change overship of object (Id={}) to the remote client (Id={}) if the local role is set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
item.HasOwnership = true;
|
||||
@@ -1421,7 +1429,13 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
|
||||
if (item.OwnerClientId != ownerClientId)
|
||||
{
|
||||
// Change role locally
|
||||
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
|
||||
#if !BUILD_RELEASE
|
||||
if (localRole == NetworkObjectRole::OwnedAuthoritative)
|
||||
{
|
||||
LOG(Error, "Cannot change overship of object (Id={}) to the remote client (Id={}) if the local role is set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative)
|
||||
Hierarchy->RemoveObject(obj);
|
||||
item.OwnerClientId = ownerClientId;
|
||||
@@ -1433,7 +1447,13 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
|
||||
else
|
||||
{
|
||||
// Allow to change local role of the object (except ownership)
|
||||
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
|
||||
#if !BUILD_RELEASE
|
||||
if (localRole == NetworkObjectRole::OwnedAuthoritative)
|
||||
{
|
||||
LOG(Error, "Cannot change overship of object (Id={}) to the remote client (Id={}) if the local role is set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (Hierarchy && it->Item.Role == NetworkObjectRole::OwnedAuthoritative)
|
||||
Hierarchy->RemoveObject(obj);
|
||||
item.Role = localRole;
|
||||
@@ -2211,7 +2231,6 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
|
||||
// Validate RPC
|
||||
if (info->Server && NetworkManager::IsClient())
|
||||
{
|
||||
@@ -2234,7 +2253,7 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
|
||||
// Execute RPC
|
||||
info->Execute(obj, stream, info->Tag);
|
||||
}
|
||||
else if(info->Channel != static_cast<uint8>(NetworkChannelType::Unreliable) && info->Channel != static_cast<uint8>(NetworkChannelType::UnreliableOrdered))
|
||||
else if (info->Channel != static_cast<uint8>(NetworkChannelType::Unreliable) && info->Channel != static_cast<uint8>(NetworkChannelType::UnreliableOrdered))
|
||||
{
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgData.ObjectId, String(msgData.RpcTypeName), String(msgData.RpcName));
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ void ParticleEffect::Sync()
|
||||
|
||||
SceneRenderTask* ParticleEffect::GetRenderTask() const
|
||||
{
|
||||
const uint64 minFrame = Engine::UpdateCount - 2;
|
||||
const uint64 minFrame = Engine::FrameCount - 2;
|
||||
|
||||
// Custom task
|
||||
const auto customViewRenderTask = CustomViewRenderTask.Get();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -770,14 +770,24 @@ void WindowsWindow::CheckForWindowResize()
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsWindow::UpdateCursor() const
|
||||
void WindowsWindow::UpdateCursor()
|
||||
{
|
||||
// Don't hide cursor when window is not focused
|
||||
if (_cursor == CursorType::Hidden && _focused)
|
||||
{
|
||||
if (!_lastCursorHidden)
|
||||
{
|
||||
_lastCursorHidden = true;
|
||||
::ShowCursor(FALSE);
|
||||
}
|
||||
::SetCursor(nullptr);
|
||||
return;
|
||||
}
|
||||
else if (_lastCursorHidden)
|
||||
{
|
||||
_lastCursorHidden = false;
|
||||
::ShowCursor(TRUE);
|
||||
}
|
||||
|
||||
int32 index = 0;
|
||||
switch (_cursor)
|
||||
|
||||
@@ -28,6 +28,7 @@ private:
|
||||
bool _isSwitchingFullScreen = false;
|
||||
bool _trackingMouse = false;
|
||||
bool _clipCursorSet = false;
|
||||
bool _lastCursorHidden = false;
|
||||
bool _isDuringMaximize = false;
|
||||
Windows::HANDLE _monitor = nullptr;
|
||||
Windows::LONG _clipCursorRect[4];
|
||||
@@ -90,7 +91,7 @@ public:
|
||||
private:
|
||||
|
||||
void CheckForWindowResize();
|
||||
void UpdateCursor() const;
|
||||
void UpdateCursor();
|
||||
void UpdateRegion();
|
||||
|
||||
public:
|
||||
|
||||
@@ -155,9 +155,9 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry)
|
||||
// Get the index to the glyph in the font face
|
||||
const FT_UInt glyphIndex = FT_Get_Char_Index(face, c);
|
||||
#if !BUILD_RELEASE
|
||||
if (glyphIndex == 0)
|
||||
if (glyphIndex == 0 && c >= '!')
|
||||
{
|
||||
LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c);
|
||||
LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font.", String(face->family_name), c);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1175,7 +1175,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
|
||||
drawCall.AsChar.Mat = nullptr;
|
||||
}
|
||||
Float2 pointer = location;
|
||||
for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++)
|
||||
for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++)
|
||||
{
|
||||
// Cache current character
|
||||
const Char currentChar = text[currentIndex];
|
||||
|
||||
@@ -464,7 +464,7 @@ void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* light
|
||||
transform.Scale *= decal->GetSize();
|
||||
renderContext.View.GetWorldMatrix(transform, drawCall.World);
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.ObjectRadius = decal->GetSphere().Radius;
|
||||
drawCall.ObjectRadius = (float)decal->GetSphere().Radius;
|
||||
|
||||
context->ResetRenderTarget();
|
||||
|
||||
|
||||
@@ -334,6 +334,32 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
}
|
||||
setup.UseTemporalAAJitter = aaMode == AntialiasingMode::TemporalAntialiasing;
|
||||
|
||||
// Disable TAA jitter in debug modes
|
||||
switch (renderContext.View.Mode)
|
||||
{
|
||||
case ViewMode::Unlit:
|
||||
case ViewMode::Diffuse:
|
||||
case ViewMode::Normals:
|
||||
case ViewMode::Depth:
|
||||
case ViewMode::Emissive:
|
||||
case ViewMode::AmbientOcclusion:
|
||||
case ViewMode::Metalness:
|
||||
case ViewMode::Roughness:
|
||||
case ViewMode::Specular:
|
||||
case ViewMode::SpecularColor:
|
||||
case ViewMode::SubsurfaceColor:
|
||||
case ViewMode::ShadingModel:
|
||||
case ViewMode::Reflections:
|
||||
case ViewMode::GlobalSDF:
|
||||
case ViewMode::GlobalSurfaceAtlas:
|
||||
case ViewMode::LightmapUVsDensity:
|
||||
case ViewMode::MaterialComplexity:
|
||||
case ViewMode::Wireframe:
|
||||
case ViewMode::NoPostFx:
|
||||
setup.UseTemporalAAJitter = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Customize setup (by postfx or custom gameplay effects)
|
||||
renderContext.Task->SetupRender(renderContext);
|
||||
for (PostProcessEffect* e : renderContext.List->PostFx)
|
||||
@@ -500,8 +526,13 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
EyeAdaptationPass::Instance()->Render(renderContext, lightBuffer);
|
||||
PostProcessingPass::Instance()->Render(renderContext, lightBuffer, tempBuffer, colorGradingLUT);
|
||||
RenderTargetPool::Release(colorGradingLUT);
|
||||
RenderTargetPool::Release(lightBuffer);
|
||||
context->ResetRenderTarget();
|
||||
if (aaMode == AntialiasingMode::TemporalAntialiasing)
|
||||
{
|
||||
TAA::Instance()->Render(renderContext, tempBuffer, lightBuffer->View());
|
||||
Swap(lightBuffer, tempBuffer);
|
||||
}
|
||||
RenderTargetPool::Release(lightBuffer);
|
||||
context->SetRenderTarget(task->GetOutputView());
|
||||
context->SetViewportAndScissors(task->GetOutputViewport());
|
||||
context->Draw(tempBuffer);
|
||||
|
||||
@@ -46,6 +46,11 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public bool CanReorderItems = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether items can be added or removed from this collection.
|
||||
/// </summary>
|
||||
public bool CanResize = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the items of this collection can be null. If <c>true</c>, applications using this collection should prevent user to add null items to the collection.
|
||||
/// </summary>
|
||||
|
||||
@@ -23,11 +23,14 @@ namespace FlaxEngine
|
||||
public int FontSize;
|
||||
|
||||
/// <summary>
|
||||
/// The custom header color (as 32-bit uint).
|
||||
/// The custom header color (as 32-bit uint in RGB order, bottom bits contain Blue).
|
||||
/// </summary>
|
||||
public uint Color;
|
||||
|
||||
private HeaderAttribute()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeaderAttribute"/> class.
|
||||
/// </summary>
|
||||
public HeaderAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Used to add a watermark to a string textbox in the editor field
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class WatermarkAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The watermark text.
|
||||
/// </summary>
|
||||
public string WatermarkText;
|
||||
|
||||
/// <summary>
|
||||
/// The watermark color (as 32-bit uint in RGB order, bottom bits contain Blue).
|
||||
/// </summary>
|
||||
public uint WatermarkColor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WatermarkAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The watermark text.</param>
|
||||
public WatermarkAttribute(string text)
|
||||
{
|
||||
WatermarkText = text;
|
||||
WatermarkColor = 0; // Default color of watermark in textbox
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WatermarkAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The watermark text.</param>
|
||||
/// <param name="color">The watermark color. 0 to use default.</param>
|
||||
public WatermarkAttribute(string text, uint color)
|
||||
{
|
||||
WatermarkText = text;
|
||||
WatermarkColor = color;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user