Merge remote-tracking branch 'origin/1.5' into dotnet7

This commit is contained in:
Wojciech Figat
2023-01-02 10:37:04 +01:00
29 changed files with 543 additions and 143 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -11,7 +11,10 @@ namespace FlaxEditor.Content
/// <seealso cref="FlaxEditor.Content.AssetItem" /> /// <seealso cref="FlaxEditor.Content.AssetItem" />
public class JsonAssetItem : AssetItem public class JsonAssetItem : AssetItem
{ {
private readonly SpriteHandle _thumbnail; /// <summary>
/// Asset icon.
/// </summary>
protected SpriteHandle _thumbnail;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonAssetItem"/> class. /// Initializes a new instance of the <see cref="JsonAssetItem"/> class.

View File

@@ -127,7 +127,7 @@ namespace FlaxEditor.Content
/// Generic Json assets proxy (supports all json assets that don't have dedicated proxy). /// Generic Json assets proxy (supports all json assets that don't have dedicated proxy).
/// </summary> /// </summary>
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" /> /// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
public sealed class GenericJsonAssetProxy : JsonAssetProxy public class GenericJsonAssetProxy : JsonAssetProxy
{ {
/// <inheritdoc /> /// <inheritdoc />
public override string TypeName => typeof(JsonAsset).FullName; public override string TypeName => typeof(JsonAsset).FullName;
@@ -161,7 +161,7 @@ namespace FlaxEditor.Content
/// Content proxy for a json assets of the given type that can be spawned in the editor. /// Content proxy for a json assets of the given type that can be spawned in the editor.
/// </summary> /// </summary>
/// <seealso cref="FlaxEditor.Content.JsonAssetProxy" /> /// <seealso cref="FlaxEditor.Content.JsonAssetProxy" />
public sealed class SpawnableJsonAssetProxy<T> : JsonAssetProxy where T : new() public class SpawnableJsonAssetProxy<T> : JsonAssetProxy where T : new()
{ {
/// <inheritdoc /> /// <inheritdoc />
public override string Name { get; } = Utilities.Utils.GetPropertyNameUI(typeof(T).Name); public override string Name { get; } = Utilities.Utils.GetPropertyNameUI(typeof(T).Name);

View File

@@ -55,8 +55,8 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
if (Values[0] is Tag asTag) if (Values[0] is Tag asTag)
return asTag; return asTag;
if (Values[0] is int asInt) if (Values[0] is uint asUInt)
return new Tag(asInt); return new Tag(asUInt);
if (Values[0] is string asString) if (Values[0] is string asString)
return Tags.Get(asString); return Tags.Get(asString);
return Tag.Default; return Tag.Default;
@@ -65,7 +65,7 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
if (Values[0] is Tag) if (Values[0] is Tag)
SetValue(value); SetValue(value);
if (Values[0] is int) if (Values[0] is uint)
SetValue(value.Index); SetValue(value.Index);
else if (Values[0] is string) else if (Values[0] is string)
SetValue(value.ToString()); SetValue(value.ToString());
@@ -114,7 +114,7 @@ namespace FlaxEditor.CustomEditors.Editors
private static void GetTags(TreeNode n, PickerData pickerData) private static void GetTags(TreeNode n, PickerData pickerData)
{ {
if (n is TreeNodeWithAddons a && a.Addons.Count != 0 && a.Addons[0] is CheckBox c && c.Checked) if (n is TreeNodeWithAddons a && a.Addons.Count != 0 && a.Addons[0] is CheckBox c && c.Checked)
pickerData.CachedTags.Add(new Tag((int)n.Tag)); pickerData.CachedTags.Add((Tag)n.Tag);
foreach (var child in n.Children) foreach (var child in n.Children)
{ {
if (child is TreeNode treeNode) if (child is TreeNode treeNode)
@@ -139,7 +139,7 @@ namespace FlaxEditor.CustomEditors.Editors
if (pickerData.IsSingle) if (pickerData.IsSingle)
{ {
UncheckAll(node.ParentTree, node); UncheckAll(node.ParentTree, node);
var value = new Tag(c.Checked ? (int)node.Tag : -1); var value = c.Checked ? (Tag)node.Tag : Tag.Default;
pickerData.SetValue?.Invoke(value); pickerData.SetValue?.Invoke(value);
pickerData.SetValues?.Invoke(new[] { value }); pickerData.SetValues?.Invoke(new[] { value });
} }
@@ -166,8 +166,8 @@ namespace FlaxEditor.CustomEditors.Editors
if (parentNode.CustomArrowRect.HasValue) if (parentNode.CustomArrowRect.HasValue)
{ {
indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1; indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1;
var parentIndex = (int)parentNode.Tag; var parentTagValue = (Tag)parentNode.Tag;
parentTag = Tags.List[parentIndex]; parentTag = parentTagValue.ToString();
} }
var node = new TreeNodeWithAddons var node = new TreeNodeWithAddons
{ {
@@ -244,7 +244,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Add tag // Add tag
var tag = Tags.Get(tagName); var tag = Tags.Get(tagName);
node.Text = name; node.Text = name;
node.Tag = tag.Index; node.Tag = tag;
var settingsAsset = GameSettings.LoadAsset<LayersAndTagsSettings>(); var settingsAsset = GameSettings.LoadAsset<LayersAndTagsSettings>();
if (settingsAsset && !settingsAsset.WaitForLoaded()) if (settingsAsset && !settingsAsset.WaitForLoaded())
{ {
@@ -283,7 +283,8 @@ namespace FlaxEditor.CustomEditors.Editors
for (var i = 0; i < tags.Length; i++) for (var i = 0; i < tags.Length; i++)
{ {
var tag = tags[i]; var tag = tags[i];
bool isSelected = pickerData.IsSingle ? value.Index == i : values.Contains(new Tag(i)); var tagValue = new Tag((uint)(i + 1));
bool isSelected = pickerData.IsSingle ? value == tagValue : values.Contains(tagValue);
// Count parent tags count // Count parent tags count
int indentation = 0; int indentation = 0;
@@ -296,7 +297,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Create node // Create node
var node = new TreeNodeWithAddons var node = new TreeNodeWithAddons
{ {
Tag = i, Tag = tagValue,
Text = tag, Text = tag,
ChildrenIndent = nodeIndent, ChildrenIndent = nodeIndent,
CullChildren = false, CullChildren = false,

View File

@@ -1205,6 +1205,7 @@ namespace FlaxEditor
{ {
public byte AutoReloadScriptsOnMainWindowFocus; public byte AutoReloadScriptsOnMainWindowFocus;
public byte ForceScriptCompilationOnStartup; public byte ForceScriptCompilationOnStartup;
public byte UseAssetImportPathRelative;
public byte AutoRebuildCSG; public byte AutoRebuildCSG;
public float AutoRebuildCSGTimeoutMs; public float AutoRebuildCSGTimeoutMs;
public byte AutoRebuildNavMesh; public byte AutoRebuildNavMesh;

View File

@@ -356,10 +356,15 @@ namespace FlaxEditor.GUI.ContextMenu
private void OnWindowGotFocus() private void OnWindowGotFocus()
{ {
if (_childCM != null && _window && _window.IsForegroundWindow) var child = _childCM;
if (child != null && _window && _window.IsForegroundWindow)
{ {
// Hide child if user clicked over parent (do it next frame to process other events before - eg. child windows focus loss) // Hide child if user clicked over parent (do it next frame to process other events before - eg. child windows focus loss)
FlaxEngine.Scripting.InvokeOnUpdate(HideChild); FlaxEngine.Scripting.InvokeOnUpdate(() =>
{
if (child == _childCM)
HideChild();
});
} }
} }

View File

@@ -830,6 +830,9 @@ public:
{ {
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetOptions") SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetOptions")
ManagedEditor::ManagedEditorOptions = *options; ManagedEditor::ManagedEditorOptions = *options;
// Apply options
AssetsImportingManager::UseImportPathRelative = ManagedEditor::ManagedEditorOptions.UseAssetImportPathRelative != 0;
} }
static void DrawNavMesh() static void DrawNavMesh()

View File

@@ -26,6 +26,7 @@ public:
{ {
byte AutoReloadScriptsOnMainWindowFocus = 1; byte AutoReloadScriptsOnMainWindowFocus = 1;
byte ForceScriptCompilationOnStartup = 1; byte ForceScriptCompilationOnStartup = 1;
byte UseAssetImportPathRelative = 1;
byte AutoRebuildCSG = 1; byte AutoRebuildCSG = 1;
float AutoRebuildCSGTimeoutMs = 50; float AutoRebuildCSGTimeoutMs = 50;
byte AutoRebuildNavMesh = 1; byte AutoRebuildNavMesh = 1;

View File

@@ -148,6 +148,13 @@ namespace FlaxEditor.Options
[EditorDisplay("Scripting", "Auto Save Visual Script On Play Start"), EditorOrder(505), Tooltip("Determines whether automatically save the Visual Script asset editors when starting the play mode in editor.")] [EditorDisplay("Scripting", "Auto Save Visual Script On Play Start"), EditorOrder(505), Tooltip("Determines whether automatically save the Visual Script asset editors when starting the play mode in editor.")]
public bool AutoSaveVisualScriptOnPlayStart { get; set; } = true; public bool AutoSaveVisualScriptOnPlayStart { get; set; } = true;
/// <summary>
/// If checked, imported file path will be stored relative to the project folder within imported asset metadata. Otherwise will use absolute path.
/// </summary>
[DefaultValue(true)]
[EditorDisplay("Content"), EditorOrder(550)]
public bool UseAssetImportPathRelative { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether perform automatic CSG rebuild on brush change. /// Gets or sets a value indicating whether perform automatic CSG rebuild on brush change.
/// </summary> /// </summary>

View File

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

View File

@@ -119,7 +119,8 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = IntegerValue.Get(box.ParentNode, box.Archetype, box.Value); var value = IntegerValue.Get(box.ParentNode, box.Archetype, box.Value);
var control = new IntValueBox(value, bounds.X, bounds.Y, 40, int.MinValue, int.MaxValue, 0.01f) var width = 40;
var control = new IntValueBox(value, bounds.X, bounds.Y, width + 12, int.MinValue, int.MaxValue, 0.01f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = box.Parent, Parent = box.Parent,
@@ -164,7 +165,8 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype, box.Value); var value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype, box.Value);
var control = new UIntValueBox(value, bounds.X, bounds.Y, 40, uint.MinValue, uint.MaxValue, 0.01f) var width = 40;
var control = new UIntValueBox(value, bounds.X, bounds.Y, width + 12, uint.MinValue, uint.MaxValue, 0.01f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = box.Parent, Parent = box.Parent,
@@ -209,7 +211,8 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = FloatValue.Get(box.ParentNode, box.Archetype, box.Value); var value = FloatValue.Get(box.ParentNode, box.Archetype, box.Value);
var control = new FloatValueBox(value, bounds.X, bounds.Y, 40, float.MinValue, float.MaxValue, 0.01f) var width = 40;
var control = new FloatValueBox(value, bounds.X, bounds.Y, width + 12, float.MinValue, float.MaxValue, 0.01f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = box.Parent, Parent = box.Parent,
@@ -254,7 +257,7 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = box.Value as string; var value = box.Value as string;
var control = new TextBox(false, bounds.X, bounds.Y, 40) var control = new TextBox(false, bounds.X, bounds.Y, 50)
{ {
Text = value, Text = value,
Height = bounds.Height, Height = bounds.Height,
@@ -299,20 +302,21 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 2 - 2, bounds.Height) var width = 30;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 2 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new RealValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new RealValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new RealValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new RealValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -372,26 +376,27 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 3 - 2, bounds.Height) var width = 30;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 3 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new RealValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new RealValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new RealValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new RealValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatY.BoxValueChanged += OnValueChanged; floatY.BoxValueChanged += OnValueChanged;
var floatZ = new RealValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatZ = new RealValueBox(value.Z, width * 2 + 4, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -454,32 +459,33 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 4 - 2, bounds.Height) var width = 20;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 4 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new RealValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new RealValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new RealValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new RealValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatY.BoxValueChanged += OnValueChanged; floatY.BoxValueChanged += OnValueChanged;
var floatZ = new RealValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatZ = new RealValueBox(value.Z, width * 2 + 4, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatZ.BoxValueChanged += OnValueChanged; floatZ.BoxValueChanged += OnValueChanged;
var floatW = new RealValueBox(value.W, 66, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatW = new RealValueBox(value.W, width * 3 + 6, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -546,20 +552,21 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 2 - 2, bounds.Height) var width = 30;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 2 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new FloatValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new FloatValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -619,26 +626,27 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 3 - 2, bounds.Height) var width = 30;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 3 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new FloatValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new FloatValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatY.BoxValueChanged += OnValueChanged; floatY.BoxValueChanged += OnValueChanged;
var floatZ = new FloatValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatZ = new FloatValueBox(value.Z, width * 2 + 4, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -701,32 +709,33 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 4 - 2, bounds.Height) var width = 20;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 4 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new FloatValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new FloatValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatY.BoxValueChanged += OnValueChanged; floatY.BoxValueChanged += OnValueChanged;
var floatZ = new FloatValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatZ = new FloatValueBox(value.Z, width * 2 + 4, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatZ.BoxValueChanged += OnValueChanged; floatZ.BoxValueChanged += OnValueChanged;
var floatW = new FloatValueBox(value.W, 66, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatW = new FloatValueBox(value.W, width * 3 + 6, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -793,20 +802,21 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 2 - 2, bounds.Height) var width = 30;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 2 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new DoubleValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new DoubleValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new DoubleValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new DoubleValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -866,26 +876,27 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 3 - 2, bounds.Height) var width = 30;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 3 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new DoubleValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new DoubleValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new DoubleValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new DoubleValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatY.BoxValueChanged += OnValueChanged; floatY.BoxValueChanged += OnValueChanged;
var floatZ = new DoubleValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatZ = new DoubleValueBox(value.Z, width * 2 + 4, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -948,32 +959,33 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box); var value = GetValue(box);
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 4 - 2, bounds.Height) var width = 20;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 4 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new DoubleValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new DoubleValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new DoubleValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new DoubleValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatY.BoxValueChanged += OnValueChanged; floatY.BoxValueChanged += OnValueChanged;
var floatZ = new DoubleValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatZ = new DoubleValueBox(value.Z, width * 2 + 4, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatZ.BoxValueChanged += OnValueChanged; floatZ.BoxValueChanged += OnValueChanged;
var floatW = new DoubleValueBox(value.W, 66, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatW = new DoubleValueBox(value.W, width * 3 + 6, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
@@ -1040,26 +1052,27 @@ namespace FlaxEditor.Surface.Elements
public Control Create(InputBox box, ref Rectangle bounds) public Control Create(InputBox box, ref Rectangle bounds)
{ {
var value = GetValue(box).EulerAngles; var value = GetValue(box).EulerAngles;
var control = new ContainerControl(bounds.X, bounds.Y, 22 * 3 - 2, bounds.Height) var width = 20;
var control = new ContainerControl(bounds.X, bounds.Y, (width + 2) * 3 - 2, bounds.Height)
{ {
ClipChildren = false, ClipChildren = false,
AutoFocus = false, AutoFocus = false,
Parent = box.Parent, Parent = box.Parent,
Tag = box, Tag = box,
}; };
var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatX = new FloatValueBox(value.X, 0, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatX.BoxValueChanged += OnValueChanged; floatX.BoxValueChanged += OnValueChanged;
var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatY = new FloatValueBox(value.Y, width + 2, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,
}; };
floatY.BoxValueChanged += OnValueChanged; floatY.BoxValueChanged += OnValueChanged;
var floatZ = new FloatValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f) var floatZ = new FloatValueBox(value.Z, width * 2 + 4, 0, width, float.MinValue, float.MaxValue, 0.0f)
{ {
Height = bounds.Height, Height = bounds.Height,
Parent = control, Parent = control,

View File

@@ -147,17 +147,8 @@ namespace FlaxEditor.Tools.Terrain.Sculpt
Apply(ref p); Apply(ref p);
} }
var editorOptions = Editor.Instance.Options.Options;
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
// Auto NavMesh rebuild // Auto NavMesh rebuild
if (!isPlayMode && editorOptions.General.AutoRebuildNavMesh) gizmo.CurrentEditUndoAction.AddDirtyBounds(ref brushBounds);
{
if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation))
{
Navigation.BuildNavMesh(terrain.Scene, brushBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs);
}
}
} }
/// <summary> /// <summary>

View File

@@ -41,33 +41,24 @@ namespace FlaxEditor.Tools.Terrain.Undo
public object Tag; public object Tag;
} }
/// <summary>
/// The terrain (actor Id).
/// </summary>
[Serialize] [Serialize]
protected readonly Guid _terrain; protected readonly Guid _terrain;
/// <summary>
/// The heightmap length (vertex count).
/// </summary>
[Serialize] [Serialize]
protected readonly int _heightmapLength; protected readonly int _heightmapLength;
/// <summary>
/// The heightmap data size (in bytes).
/// </summary>
[Serialize] [Serialize]
protected readonly int _heightmapDataSize; protected readonly int _heightmapDataSize;
/// <summary>
/// The terrain patches
/// </summary>
[Serialize] [Serialize]
protected readonly List<PatchData> _patches; protected readonly List<PatchData> _patches;
/// <summary> [Serialize]
/// Gets a value indicating whether this action has any modification to the terrain (recorded patches changes). protected readonly List<BoundingBox> _navmeshBoundsModifications;
/// </summary>
[Serialize]
protected readonly float _dirtyNavMeshTimeoutMs;
[NoSerialize] [NoSerialize]
public bool HasAnyModification => _patches.Count > 0; public bool HasAnyModification => _patches.Count > 0;
@@ -98,6 +89,27 @@ namespace FlaxEditor.Tools.Terrain.Undo
var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1; var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
_heightmapLength = heightmapSize * heightmapSize; _heightmapLength = heightmapSize * heightmapSize;
_heightmapDataSize = _heightmapLength * stride; _heightmapDataSize = _heightmapLength * stride;
// Auto NavMesh rebuild
var editorOptions = Editor.Instance.Options.Options;
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
if (!isPlayMode && editorOptions.General.AutoRebuildNavMesh)
{
if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation))
{
_navmeshBoundsModifications = new List<BoundingBox>();
_dirtyNavMeshTimeoutMs = editorOptions.General.AutoRebuildNavMeshTimeoutMs;
}
}
}
/// <summary>
/// Adds modified bounds to the undo actor modifications list.
/// </summary>
/// <param name="bounds">The world-space bounds.</param>
public void AddDirtyBounds(ref BoundingBox bounds)
{
_navmeshBoundsModifications?.Add(bounds);
} }
/// <summary> /// <summary>
@@ -155,7 +167,15 @@ namespace FlaxEditor.Tools.Terrain.Undo
_patches[i] = patch; _patches[i] = patch;
} }
Editor.Instance.Scene.MarkSceneEdited(Terrain.Scene); // Update navmesh
var scene = Terrain.Scene;
if (_navmeshBoundsModifications != null)
{
foreach (var bounds in _navmeshBoundsModifications)
Navigation.BuildNavMesh(scene, bounds, _dirtyNavMeshTimeoutMs);
}
Editor.Instance.Scene.MarkSceneEdited(scene);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -183,10 +203,12 @@ namespace FlaxEditor.Tools.Terrain.Undo
Marshal.FreeHGlobal(_patches[i].After); Marshal.FreeHGlobal(_patches[i].After);
} }
_patches.Clear(); _patches.Clear();
_navmeshBoundsModifications?.Clear();
} }
private void Set(Func<PatchData, IntPtr> dataGetter) private void Set(Func<PatchData, IntPtr> dataGetter)
{ {
// Update patches
for (int i = 0; i < _patches.Count; i++) for (int i = 0; i < _patches.Count; i++)
{ {
var patch = _patches[i]; var patch = _patches[i];
@@ -194,6 +216,14 @@ namespace FlaxEditor.Tools.Terrain.Undo
SetData(ref patch.PatchCoord, data, patch.Tag); SetData(ref patch.PatchCoord, data, patch.Tag);
} }
// Update navmesh
var scene = Terrain.Scene;
if (_navmeshBoundsModifications != null)
{
foreach (var bounds in _navmeshBoundsModifications)
Navigation.BuildNavMesh(scene, bounds, _dirtyNavMeshTimeoutMs);
}
Editor.Instance.Scene.MarkSceneEdited(Terrain.Scene); Editor.Instance.Scene.MarkSceneEdited(Terrain.Scene);
} }

View File

@@ -155,7 +155,7 @@ namespace FlaxEditor.Windows
var scriptType = new ScriptType(typeof(Script)); var scriptType = new ScriptType(typeof(Script));
foreach (var type in Editor.CodeEditing.All.Get()) foreach (var type in Editor.CodeEditing.All.Get())
{ {
if (type.IsAbstract) if (type.IsAbstract || type.Type == null)
continue; continue;
if (actorType.IsAssignableFrom(type) || scriptType.IsAssignableFrom(type)) if (actorType.IsAssignableFrom(type) || scriptType.IsAssignableFrom(type))
continue; continue;

View File

@@ -4,15 +4,16 @@
#include "Cache/AssetsCache.h" #include "Cache/AssetsCache.h"
#include "Storage/ContentStorageManager.h" #include "Storage/ContentStorageManager.h"
#include "Loading/Tasks/LoadAssetDataTask.h" #include "Loading/Tasks/LoadAssetDataTask.h"
#include "Factories/BinaryAssetFactory.h"
#include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/ContentImporters/AssetsImportingManager.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonTools.h"
#include "Engine/Debug/Exceptions/JsonParseException.h" #include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Factories/BinaryAssetFactory.h"
#include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Threading/ThreadPoolTask.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Engine/Globals.h"
#endif #endif
REGISTER_BINARY_ASSET_ABSTRACT(BinaryAsset, "FlaxEngine.BinaryAsset"); REGISTER_BINARY_ASSET_ABSTRACT(BinaryAsset, "FlaxEngine.BinaryAsset");
@@ -123,6 +124,12 @@ void BinaryAsset::GetImportMetadata(String& path, String& username) const
{ {
path = JsonTools::GetString(document, "ImportPath"); path = JsonTools::GetString(document, "ImportPath");
username = JsonTools::GetString(document, "ImportUsername"); username = JsonTools::GetString(document, "ImportUsername");
if (path.HasChars() && FileSystem::IsRelative(path))
{
// Convert path back to thr absolute (eg. if stored in relative format)
path = Globals::ProjectFolder / path;
StringUtils::PathRemoveRelativeParts(path);
}
} }
else else
{ {

View File

@@ -2,6 +2,8 @@
#include "Content.h" #include "Content.h"
#include "JsonAsset.h" #include "JsonAsset.h"
#include "SceneReference.h"
#include "Engine/Serialization/Serialization.h"
#include "Cache/AssetsCache.h" #include "Cache/AssetsCache.h"
#include "Storage/ContentStorageManager.h" #include "Storage/ContentStorageManager.h"
#include "Storage/JsonStorageProxy.h" #include "Storage/JsonStorageProxy.h"
@@ -39,6 +41,16 @@ String AssetInfo::ToString() const
return String::Format(TEXT("ID: {0}, TypeName: {1}, Path: \'{2}\'"), ID, TypeName, Path); return String::Format(TEXT("ID: {0}, TypeName: {1}, Path: \'{2}\'"), ID, TypeName, Path);
} }
void FLAXENGINE_API Serialization::Serialize(ISerializable::SerializeStream& stream, const SceneReference& v, const void* otherObj)
{
Serialize(stream, v.ID, otherObj);
}
void FLAXENGINE_API Serialization::Deserialize(ISerializable::DeserializeStream& stream, SceneReference& v, ISerializeModifier* modifier)
{
Deserialize(stream, v.ID, modifier);
}
namespace namespace
{ {
// Assets // Assets

View File

@@ -1,8 +1,9 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once #pragma once
#include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/Guid.h"
#include "Engine/Core/ISerializable.h"
/// <summary> /// <summary>
/// Represents the reference to the scene asset. Stores the unique ID of the scene to reference. Can be used to load the selected scene. /// Represents the reference to the scene asset. Stores the unique ID of the scene to reference. Can be used to load the selected scene.
@@ -15,4 +16,37 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API SceneReference
/// The identifier of the scene asset (and the scene object). /// The identifier of the scene asset (and the scene object).
/// </summary> /// </summary>
API_FIELD() Guid ID; API_FIELD() Guid ID;
FORCE_INLINE bool operator==(const SceneReference& other) const
{
return ID == other.ID;
}
FORCE_INLINE bool operator!=(const SceneReference& other) const
{
return ID != other.ID;
}
}; };
template<>
struct TIsPODType<SceneReference>
{
enum { Value = true };
};
inline uint32 GetHash(const SceneReference& key)
{
return GetHash(key.ID);
}
// @formatter:off
namespace Serialization
{
inline bool ShouldSerialize(const SceneReference& v, const void* otherObj)
{
return !otherObj || v != *(SceneReference*)otherObj;
}
void FLAXENGINE_API Serialize(ISerializable::SerializeStream& stream, const SceneReference& v, const void* otherObj);
void FLAXENGINE_API Deserialize(ISerializable::DeserializeStream& stream, SceneReference& v, ISerializeModifier* modifier);
}
// @formatter:on

View File

@@ -3,6 +3,7 @@
#if COMPILE_WITH_ASSETS_IMPORTER #if COMPILE_WITH_ASSETS_IMPORTER
#include "AssetsImportingManager.h" #include "AssetsImportingManager.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Utilities.h" #include "Engine/Core/Utilities.h"
#include "Engine/Serialization/JsonWriters.h" #include "Engine/Serialization/JsonWriters.h"
#include "Engine/Threading/MainThreadTask.h" #include "Engine/Threading/MainThreadTask.h"
@@ -10,9 +11,9 @@
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Content/Cache/AssetsCache.h" #include "Engine/Content/Cache/AssetsCache.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Core/Log.h"
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/Platform.h" #include "Engine/Platform/Platform.h"
#include "Engine/Engine/Globals.h"
#include "ImportTexture.h" #include "ImportTexture.h"
#include "ImportModelFile.h" #include "ImportModelFile.h"
#include "ImportAudio.h" #include "ImportAudio.h"
@@ -71,6 +72,7 @@ AssetsImportingManagerService AssetsImportingManagerServiceInstance;
Array<AssetImporter> AssetsImportingManager::Importers; Array<AssetImporter> AssetsImportingManager::Importers;
Array<AssetCreator> AssetsImportingManager::Creators; Array<AssetCreator> AssetsImportingManager::Creators;
bool AssetsImportingManager::UseImportPathRelative = false;
CreateAssetContext::CreateAssetContext(const StringView& inputPath, const StringView& outputPath, const Guid& id, void* arg) CreateAssetContext::CreateAssetContext(const StringView& inputPath, const StringView& outputPath, const Guid& id, void* arg)
{ {
@@ -161,7 +163,15 @@ bool CreateAssetContext::AllocateChunk(int32 index)
void CreateAssetContext::AddMeta(JsonWriter& writer) const void CreateAssetContext::AddMeta(JsonWriter& writer) const
{ {
writer.JKEY("ImportPath"); writer.JKEY("ImportPath");
writer.String(InputPath); if (AssetsImportingManager::UseImportPathRelative && !FileSystem::IsRelative(InputPath))
{
const String relativePath = FileSystem::ConvertAbsolutePathToRelative(Globals::ProjectFolder, InputPath);
writer.String(relativePath);
}
else
{
writer.String(InputPath);
}
writer.JKEY("ImportUsername"); writer.JKEY("ImportUsername");
writer.String(Platform::GetUserName()); writer.String(Platform::GetUserName());
} }

View File

@@ -22,6 +22,11 @@ public:
/// </summary> /// </summary>
static Array<AssetCreator> Creators; static Array<AssetCreator> Creators;
/// <summary>
/// If true store asset import path relative to the current workspace, otherwise will store absolute path.
/// </summary>
static bool UseImportPathRelative;
public: public:
/// <summary> /// <summary>
/// The create texture tag (using internal import pipeline to crate texture asset from custom image source). /// The create texture tag (using internal import pipeline to crate texture asset from custom image source).

View File

@@ -12,7 +12,8 @@ FLAXENGINE_API String* TagsListDebug = nullptr;
const String& Tag::ToString() const const String& Tag::ToString() const
{ {
return Index >= 0 && Index < Tags::List.Count() ? Tags::List.Get()[Index] : String::Empty; const int32 index = (int32)Index - 1;
return index >= 0 && index < Tags::List.Count() ? Tags::List.Get()[index] : String::Empty;
} }
bool Tag::operator==(const StringView& other) const bool Tag::operator==(const StringView& other) const
@@ -27,7 +28,7 @@ bool Tag::operator!=(const StringView& other) const
void FLAXENGINE_API Serialization::Serialize(ISerializable::SerializeStream& stream, const Tag& v, const void* otherObj) void FLAXENGINE_API Serialization::Serialize(ISerializable::SerializeStream& stream, const Tag& v, const void* otherObj)
{ {
if (v.Index != -1) if (v.Index != 0)
stream.String(v.ToString()); stream.String(v.ToString());
else else
stream.String("", 0); stream.String("", 0);
@@ -42,11 +43,11 @@ Tag Tags::Get(const StringView& tagName)
{ {
if (tagName.IsEmpty()) if (tagName.IsEmpty())
return Tag(); return Tag();
Tag tag = List.Find(tagName); Tag tag(List.Find(tagName) + 1);
if (tag.Index == -1 && tagName.HasChars()) if (tag.Index == 0 && tagName.HasChars())
{ {
tag.Index = List.Count();
List.AddOne() = tagName; List.AddOne() = tagName;
tag.Index = List.Count();
#if !BUILD_RELEASE #if !BUILD_RELEASE
TagsListDebug = List.Get(); TagsListDebug = List.Get();
#endif #endif
@@ -56,7 +57,7 @@ Tag Tags::Get(const StringView& tagName)
bool Tags::HasTag(const Array<Tag>& list, const Tag& tag) bool Tags::HasTag(const Array<Tag>& list, const Tag& tag)
{ {
if (tag.Index == -1) if (tag.Index == 0)
return false; return false;
const String& tagName = tag.ToString(); const String& tagName = tag.ToString();
for (const Tag& e : list) for (const Tag& e : list)
@@ -70,7 +71,7 @@ bool Tags::HasTag(const Array<Tag>& list, const Tag& tag)
bool Tags::HasTagExact(const Array<Tag>& list, const Tag& tag) bool Tags::HasTagExact(const Array<Tag>& list, const Tag& tag)
{ {
if (tag.Index == -1) if (tag.Index == 0)
return false; return false;
return list.Contains(tag); return list.Contains(tag);
} }
@@ -119,7 +120,7 @@ bool Tags::HasAllExact(const Array<Tag>& list, const Array<Tag>& tags)
return true; return true;
} }
const String& Tags::GetTagName(int32 tag) const String& Tags::GetTagName(uint32 tag)
{ {
return Tag(tag).ToString(); return Tag(tag).ToString();
} }

View File

@@ -1,33 +1,29 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System; using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace FlaxEngine namespace FlaxEngine
{ {
[TypeConverter(typeof(TypeConverters.TagConverter))]
partial struct Tag : IEquatable<Tag>, IEquatable<string>, IComparable, IComparable<Tag>, IComparable<string> partial struct Tag : IEquatable<Tag>, IEquatable<string>, IComparable, IComparable<Tag>, IComparable<string>
{ {
/// <summary> /// <summary>
/// The default <see cref="Tag"/>. /// The default <see cref="Tag"/>.
/// </summary> /// </summary>
public static Tag Default => new Tag(-1); public static Tag Default => new Tag(0);
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Tag" /> struct. /// Initializes a new instance of the <see cref="Tag" /> struct.
/// </summary> /// </summary>
/// <param name="index">The tag index.</param> /// <param name="index">The tag index.</param>
public Tag(int index) public Tag(uint index)
{ {
Index = index; Index = index;
} }
[System.Runtime.Serialization.OnDeserializing]
internal void OnDeserializing(System.Runtime.Serialization.StreamingContext context)
{
// Initialize structure with default values to replicate C++ deserialization behavior
Index = -1;
}
/// <summary> /// <summary>
/// Compares two tags. /// Compares two tags.
/// </summary> /// </summary>
@@ -50,6 +46,28 @@ namespace FlaxEngine
return left.Index == right.Index; return left.Index == right.Index;
} }
/// <summary>
/// Compares two tags.
/// </summary>
/// <param name="left">The lft tag.</param>
/// <param name="right">The right tag name.</param>
/// <returns>True if both values are equal, otherwise false.</returns>
public static bool operator ==(Tag left, string right)
{
return string.Equals(left.ToString(), right, StringComparison.Ordinal);
}
/// <summary>
/// Compares two tags.
/// </summary>
/// <param name="left">The lft tag.</param>
/// <param name="right">The right tag name.</param>
/// <returns>True if both values are not equal, otherwise false.</returns>
public static bool operator !=(Tag left, string right)
{
return !string.Equals(left.ToString(), right, StringComparison.Ordinal);
}
/// <summary> /// <summary>
/// Checks if tag is valid. /// Checks if tag is valid.
/// </summary> /// </summary>
@@ -58,7 +76,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator bool(Tag tag) public static implicit operator bool(Tag tag)
{ {
return tag.Index != -1; return tag.Index != 0;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -104,7 +122,7 @@ namespace FlaxEngine
/// <inheritdoc /> /// <inheritdoc />
public override int GetHashCode() public override int GetHashCode()
{ {
return Index; return (int)Index;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -124,7 +142,7 @@ namespace FlaxEngine
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns> /// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
public static bool HasTag(this Tag[] list, Tag tag) public static bool HasTag(this Tag[] list, Tag tag)
{ {
if (tag.Index == -1) if (tag.Index == 0)
return false; return false;
string tagName = tag.ToString(); string tagName = tag.ToString();
foreach (Tag e in list) foreach (Tag e in list)
@@ -144,7 +162,7 @@ namespace FlaxEngine
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns> /// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
public static bool HasTagExact(this Tag[] list, Tag tag) public static bool HasTagExact(this Tag[] list, Tag tag)
{ {
if (tag.Index == -1) if (tag.Index == 0)
return false; return false;
if (list == null) if (list == null)
return false; return false;
@@ -233,3 +251,40 @@ namespace FlaxEngine
} }
} }
} }
namespace FlaxEngine.TypeConverters
{
internal class TagConverter : TypeConverter
{
/// <inheritdoc />
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
if (str.Length == 0)
return Tag.Default;
return Tags.Get(str);
}
return base.ConvertFrom(context, culture, value);
}
/// <inheritdoc />
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
var v = (Tag)value;
return v.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}

View File

@@ -14,9 +14,9 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API Tag
DECLARE_SCRIPTING_TYPE_MINIMAL(Tag); DECLARE_SCRIPTING_TYPE_MINIMAL(Tag);
/// <summary> /// <summary>
/// Index of the tag (in global Level.Tags list). /// Index of the tag (in global Level.Tags list). Index 0 is invalid. 1 is the first index.
/// </summary> /// </summary>
API_FIELD() int32 Index = -1; API_FIELD() uint32 Index = 0;
/// <summary> /// <summary>
/// Gets the tag name. /// Gets the tag name.
@@ -26,14 +26,14 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API Tag
public: public:
Tag() = default; Tag() = default;
FORCE_INLINE Tag(int32 index) FORCE_INLINE explicit Tag(uint32 index)
: Index(index) : Index(index)
{ {
} }
FORCE_INLINE operator bool() const FORCE_INLINE operator bool() const
{ {
return Index != -1; return Index != 0;
} }
FORCE_INLINE bool operator==(const Tag& other) const FORCE_INLINE bool operator==(const Tag& other) const
@@ -142,7 +142,7 @@ public:
static bool HasAllExact(const Array<Tag>& list, const Array<Tag>& tags); static bool HasAllExact(const Array<Tag>& list, const Array<Tag>& tags);
private: private:
API_FUNCTION(NoProxy) static const String& GetTagName(int32 tag); API_FUNCTION(NoProxy) static const String& GetTagName(uint32 tag);
}; };
#if !BUILD_RELEASE #if !BUILD_RELEASE

View File

@@ -4,6 +4,7 @@
#include "Types.h" #include "Types.h"
#include "Engine/Core/ISerializable.h" #include "Engine/Core/ISerializable.h"
#include "Engine/Level/Tags.h"
/// <summary> /// <summary>
/// Physical materials are used to define the response of a physical object when interacting dynamically with the world. /// Physical materials are used to define the response of a physical object when interacting dynamically with the world.
@@ -69,6 +70,12 @@ public:
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Physical Material\")") API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Physical Material\")")
float Density = 1000.0f; float Density = 1000.0f;
/// <summary>
/// Physical material tag used to identify it (eg. `Surface.Wood`). Can be used to play proper footstep sounds when walking over object with that material.
/// </summary>
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Physical Material\")")
Tag Tag;
public: public:
/// <summary> /// <summary>
/// Gets the PhysX material. /// Gets the PhysX material.

View File

@@ -119,6 +119,11 @@ public:
// Tries to cast native interface object to scripting object instance. Returns null if fails. // Tries to cast native interface object to scripting object instance. Returns null if fails.
static ScriptingObject* FromInterface(void* interfaceObj, const ScriptingTypeHandle& interfaceType); static ScriptingObject* FromInterface(void* interfaceObj, const ScriptingTypeHandle& interfaceType);
template<typename T>
static ScriptingObject* FromInterface(T* interfaceObj)
{
return FromInterface(interfaceObj, T::TypeInitializer);
}
static void* ToInterface(ScriptingObject* obj, const ScriptingTypeHandle& interfaceType); static void* ToInterface(ScriptingObject* obj, const ScriptingTypeHandle& interfaceType);
template<typename T> template<typename T>
static T* ToInterface(ScriptingObject* obj) static T* ToInterface(ScriptingObject* obj)

View File

@@ -21,7 +21,9 @@ TEST_CASE("Scripting")
CHECK(object->Is<TestClassNative>()); CHECK(object->Is<TestClassNative>());
TestClassNative* testClass = (TestClassNative*)object; TestClassNative* testClass = (TestClassNative*)object;
CHECK(testClass->SimpleField == 1); CHECK(testClass->SimpleField == 1);
int32 methodResult = testClass->Test(TEXT("123")); CHECK(testClass->SimpleStruct.Object == nullptr);
CHECK(testClass->SimpleStruct.Vector == Float3::One);
int32 methodResult = testClass->TestMethod(TEXT("123"));
CHECK(methodResult == 3); CHECK(methodResult == 3);
// Test managed class // Test managed class
@@ -34,7 +36,80 @@ TEST_CASE("Scripting")
MObject* managed = testClass->GetOrCreateManagedInstance(); // Ensure to create C# object and run it's ctor MObject* managed = testClass->GetOrCreateManagedInstance(); // Ensure to create C# object and run it's ctor
CHECK(managed); CHECK(managed);
CHECK(testClass->SimpleField == 2); CHECK(testClass->SimpleField == 2);
methodResult = testClass->Test(TEXT("123")); CHECK(testClass->SimpleStruct.Object == testClass);
CHECK(testClass->SimpleStruct.Vector == Float3::UnitX);
methodResult = testClass->TestMethod(TEXT("123"));
CHECK(methodResult == 6); CHECK(methodResult == 6);
} }
SECTION("Test Event")
{
ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged");
CHECK(type);
ScriptingObject* object = Scripting::NewObject(type.GetType().ManagedClass);
CHECK(object);
MObject* managed = object->GetOrCreateManagedInstance(); // Ensure to create C# object and run it's ctor
CHECK(managed);
TestClassNative* testClass = (TestClassNative*)object;
CHECK(testClass->SimpleField == 2);
String str1 = TEXT("1");
String str2 = TEXT("2");
Array<TestStruct> arr1 = { testClass->SimpleStruct };
Array<TestStruct> arr2 = { testClass->SimpleStruct };
testClass->SimpleEvent(1, Float3::One, str1, str2, arr1, arr2);
CHECK(testClass->SimpleField == 4);
CHECK(str2 == TEXT("4"));
CHECK(arr2.Count() == 2);
CHECK(arr2[0].Vector == Float3::Half);
CHECK(arr2[0].Object == nullptr);
CHECK(arr2[1].Vector == testClass->SimpleStruct.Vector);
CHECK(arr2[1].Object == testClass);
}
SECTION("Test Interface")
{
// Test native interface implementation
ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassNative");
CHECK(type);
ScriptingObject* object = Scripting::NewObject(type.GetType().ManagedClass);
CHECK(object);
TestClassNative* testClass = (TestClassNative*)object;
int32 methodResult = testClass->TestInterfaceMethod(TEXT("123"));
CHECK(methodResult == 3);
ITestInterface* interface = ScriptingObject::ToInterface<ITestInterface>(object);
CHECK(interface);
methodResult = interface->TestInterfaceMethod(TEXT("1234"));
CHECK(methodResult == 4);
ScriptingObject* interfaceObject = ScriptingObject::FromInterface<ITestInterface>(interface);
CHECK(interfaceObject);
CHECK(interfaceObject == object);
// Test managed interface override
type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged");
CHECK(type);
object = Scripting::NewObject(type.GetType().ManagedClass);
CHECK(object);
testClass = (TestClassNative*)object;
methodResult = testClass->TestInterfaceMethod(TEXT("123"));
CHECK(methodResult == 6);
interface = ScriptingObject::ToInterface<ITestInterface>(object);
CHECK(interface);
methodResult = interface->TestInterfaceMethod(TEXT("1234"));
CHECK(methodResult == 8);
interfaceObject = ScriptingObject::FromInterface<ITestInterface>(interface);
CHECK(interfaceObject);
CHECK(interfaceObject == object);
// Test managed interface implementation
type = Scripting::FindScriptingType("FlaxEngine.TestInterfaceManaged");
CHECK(type);
object = Scripting::NewObject(type.GetType().ManagedClass);
CHECK(object);
interface = ScriptingObject::ToInterface<ITestInterface>(object);
CHECK(interface);
methodResult = interface->TestInterfaceMethod(TEXT("1234"));
CHECK(methodResult == 4);
interfaceObject = ScriptingObject::FromInterface<ITestInterface>(interface);
CHECK(interfaceObject);
CHECK(interfaceObject == object);
}
} }

View File

@@ -3,6 +3,48 @@
#if FLAX_TESTS #if FLAX_TESTS
namespace FlaxEngine namespace FlaxEngine
{ {
partial struct TestStruct : System.IEquatable<TestStruct>
{
/// <summary></summary>
public static bool operator ==(TestStruct left, TestStruct right)
{
return left.Equals(right);
}
/// <summary></summary>
public static bool operator !=(TestStruct left, TestStruct right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public bool Equals(TestStruct other)
{
return Vector.Equals(other.Vector) && Equals(Object, other.Object);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is TestStruct other && Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (Vector.GetHashCode() * 397) ^ (Object != null ? Object.GetHashCode() : 0);
}
}
/// <inheritdoc />
public override string ToString()
{
return $"Vector={Vector}, Object={Object?.ToString() ?? "null"}";
}
}
/// <summary> /// <summary>
/// Test class. /// Test class.
/// </summary> /// </summary>
@@ -10,13 +52,65 @@ namespace FlaxEngine
{ {
TestClassManaged() TestClassManaged()
{ {
// Test setting C++ values from C#
SimpleField = 2; SimpleField = 2;
SimpleStruct = new TestStruct
{
Vector = Float3.UnitX,
Object = this,
};
SimpleEvent += OnSimpleEvent;
} }
/// <inheritdoc /> /// <inheritdoc />
public override int Test(string str) public override int TestMethod(string str)
{ {
return str.Length + base.Test(str); // Test C++ base method invocation
return str.Length + base.TestMethod(str);
}
/// <inheritdoc />
public override int TestInterfaceMethod(string str)
{
// Test C++ base method invocation
return str.Length + base.TestInterfaceMethod(str);
}
private void OnSimpleEvent(int arg1, Float3 arg2, string arg3, ref string arg4, TestStruct[] arg5, ref TestStruct[] arg6)
{
// Verify that C++ passed proper data to C# via event bindings
if (arg1 == 1 &&
arg2 == Float3.One &&
arg3 == "1" &&
arg4 == "2" &&
arg5 != null && arg5.Length == 1 && arg5[0] == SimpleStruct &&
arg6 != null && arg6.Length == 1 && arg6[0] == SimpleStruct)
{
// Test passing data back from C# to C++
SimpleField = 4;
arg4 = "4";
arg6 = new TestStruct[2]
{
new TestStruct
{
Vector = Float3.Half,
Object = null,
},
SimpleStruct,
};
}
}
}
/// <summary>
/// Test interface in C#.
/// </summary>
public class TestInterfaceManaged : Object, ITestInterface
{
/// <inheritdoc />
public int TestInterfaceMethod(string str)
{
return str.Length;
} }
} }
} }

View File

@@ -3,10 +3,34 @@
#pragma once #pragma once
#include "Engine/Core/ISerializable.h" #include "Engine/Core/ISerializable.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingObject.h"
// Test structure.
API_STRUCT(NoDefault) struct TestStruct : public ISerializable
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE_MINIMAL(TestStruct);
// Var
API_FIELD() Float3 Vector = Float3::One;
// Ref
API_FIELD() ScriptingObject* Object = nullptr;
};
// Test interface.
API_INTERFACE() class ITestInterface
{
DECLARE_SCRIPTING_TYPE_MINIMAL(ITestInterface);
~ITestInterface() = default;
// Test abstract method
API_FUNCTION() virtual int32 TestInterfaceMethod(const String& str) = 0;
};
// Test class. // Test class.
API_CLASS() class TestClassNative : public ScriptingObject, public ISerializable API_CLASS() class TestClassNative : public ScriptingObject, public ISerializable, public ITestInterface
{ {
API_AUTO_SERIALIZATION(); API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(TestClassNative); DECLARE_SCRIPTING_TYPE(TestClassNative);
@@ -15,8 +39,19 @@ public:
// Test value // Test value
API_FIELD() int32 SimpleField = 1; API_FIELD() int32 SimpleField = 1;
// Test struct
API_FIELD() TestStruct SimpleStruct;
// Test event
API_EVENT() Delegate<int32, Float3, const String&, String&, const Array<TestStruct>&, Array<TestStruct>&> SimpleEvent;
// Test virtual method // Test virtual method
API_FUNCTION() virtual int32 Test(const String& str) API_FUNCTION() virtual int32 TestMethod(const String& str)
{
return str.Length();
}
int32 TestInterfaceMethod(const String& str) override
{ {
return str.Length(); return str.Length();
} }

View File

@@ -99,14 +99,15 @@ namespace Flax.Build.Bindings
return sb.ToString(); return sb.ToString();
} }
private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller, bool isOut) private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller, bool isOut, out bool useLocalVar)
{ {
useLocalVar = false;
var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null); var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null);
string result; string result;
if (!string.IsNullOrEmpty(nativeToManaged)) if (!string.IsNullOrEmpty(nativeToManaged))
{ {
result = string.Format(nativeToManaged, paramName); result = string.Format(nativeToManaged, paramName);
if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*') if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' && !isOut)
{ {
// Pass pointer value // Pass pointer value
} }
@@ -117,6 +118,7 @@ namespace Flax.Build.Bindings
result = string.Format(nativeToManaged, '*' + paramName); result = string.Format(nativeToManaged, '*' + paramName);
contents.Append($" auto __param_{paramName} = {result};").AppendLine(); contents.Append($" auto __param_{paramName} = {result};").AppendLine();
result = $"&__param_{paramName}"; result = $"&__param_{paramName}";
useLocalVar = true;
} }
} }
else else
@@ -588,12 +590,12 @@ namespace Flax.Build.Bindings
} }
} }
private static string GenerateCppWrapperManagedToNative(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, out string type, FunctionInfo functionInfo, out bool needLocalVariable) private static string GenerateCppWrapperManagedToNative(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, out string type, out ApiTypeInfo apiType, FunctionInfo functionInfo, out bool needLocalVariable)
{ {
needLocalVariable = false; needLocalVariable = false;
// Register any API types usage // Register any API types usage
var apiType = FindApiTypeInfo(buildData, typeInfo, caller); apiType = FindApiTypeInfo(buildData, typeInfo, caller);
CppReferencesFiles.Add(apiType?.File); CppReferencesFiles.Add(apiType?.File);
if (typeInfo.GenericArgs != null) if (typeInfo.GenericArgs != null)
{ {
@@ -608,7 +610,7 @@ namespace Flax.Build.Bindings
if (typeInfo.IsArray) if (typeInfo.IsArray)
{ {
var arrayType = new TypeInfo { Type = "Array", GenericArgs = new List<TypeInfo> { new TypeInfo(typeInfo) { IsArray = false } } }; var arrayType = new TypeInfo { Type = "Array", GenericArgs = new List<TypeInfo> { new TypeInfo(typeInfo) { IsArray = false } } };
var result = GenerateCppWrapperManagedToNative(buildData, arrayType, caller, out type, functionInfo, out needLocalVariable); var result = GenerateCppWrapperManagedToNative(buildData, arrayType, caller, out type, out _, functionInfo, out needLocalVariable);
return result + ".Get()"; return result + ".Get()";
} }
@@ -967,7 +969,7 @@ namespace Flax.Build.Bindings
separator = true; separator = true;
CppParamsThatNeedConversion[i] = false; CppParamsThatNeedConversion[i] = false;
CppParamsWrappersCache[i] = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, out var managedType, functionInfo, out CppParamsThatNeedLocalVariable[i]); CppParamsWrappersCache[i] = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, out var managedType, out var apiType, functionInfo, out CppParamsThatNeedLocalVariable[i]);
// Out parameters that need additional converting will be converted at the native side (eg. object reference) // Out parameters that need additional converting will be converted at the native side (eg. object reference)
var isOutWithManagedConverter = parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)); var isOutWithManagedConverter = parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller));
@@ -985,7 +987,6 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut || isRefOut) if (parameterInfo.IsOut || isRefOut)
{ {
bool convertOutputParameter = false; bool convertOutputParameter = false;
var apiType = FindApiTypeInfo(buildData, parameterInfo.Type, caller);
if (apiType != null) if (apiType != null)
{ {
// Non-POD structure passed as value (eg. it contains string or array inside) // Non-POD structure passed as value (eg. it contains string or array inside)
@@ -1038,7 +1039,7 @@ namespace Flax.Build.Bindings
contents.Append(", "); contents.Append(", ");
separator = true; separator = true;
GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, out var managedType, functionInfo, out _); GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, out var managedType, out _, functionInfo, out _);
contents.Append(managedType); contents.Append(managedType);
if (parameterInfo.IsRef || parameterInfo.IsOut || UsePassByReference(buildData, parameterInfo.Type, caller)) if (parameterInfo.IsRef || parameterInfo.IsOut || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append('*'); contents.Append('*');
@@ -1336,7 +1337,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < functionInfo.Parameters.Count; i++) for (var i = 0; i < functionInfo.Parameters.Count; i++)
{ {
var parameterInfo = functionInfo.Parameters[i]; var parameterInfo = functionInfo.Parameters[i];
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, parameterInfo.IsOut); var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, parameterInfo.IsOut, out _);
contents.Append($" params[{i}] = {paramValue};").AppendLine(); contents.Append($" params[{i}] = {paramValue};").AppendLine();
} }
@@ -1728,7 +1729,8 @@ namespace Flax.Build.Bindings
{ {
var paramType = eventInfo.Type.GenericArgs[i]; var paramType = eventInfo.Type.GenericArgs[i];
var paramName = "arg" + i; var paramName = "arg" + i;
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, false); var paramIsOut = paramType.IsRef && !paramType.IsConst;
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, paramIsOut, out CppParamsThatNeedConversion[i]);
contents.Append($" params[{i}] = {paramValue};").AppendLine(); contents.Append($" params[{i}] = {paramValue};").AppendLine();
} }
if (eventInfo.IsStatic) if (eventInfo.IsStatic)
@@ -1741,13 +1743,15 @@ namespace Flax.Build.Bindings
for (var i = 0; i < paramsCount; i++) for (var i = 0; i < paramsCount; i++)
{ {
var paramType = eventInfo.Type.GenericArgs[i]; var paramType = eventInfo.Type.GenericArgs[i];
if (paramType.IsRef && !paramType.IsConst) var paramIsOut = paramType.IsRef && !paramType.IsConst;
if (paramIsOut)
{ {
// Convert value back from managed to native (could be modified there) // Convert value back from managed to native (could be modified there)
paramType.IsRef = false; paramType.IsRef = false;
var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, null, out _); var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, out var apiType, null, out _);
var passAsParamPtr = managedType.EndsWith("*"); var passAsParamPtr = managedType.EndsWith("*");
var paramValue = $"({managedType}{(passAsParamPtr ? "" : "*")})params[{i}]"; var useLocalVarPointer = CppParamsThatNeedConversion[i] && !apiType.IsValueType;
var paramValue = useLocalVarPointer ? $"*({managedType}{(passAsParamPtr ? "" : "*")}*)params[{i}]" : $"({managedType}{(passAsParamPtr ? "" : "*")})params[{i}]";
if (!string.IsNullOrEmpty(managedToNative)) if (!string.IsNullOrEmpty(managedToNative))
{ {
if (!passAsParamPtr) if (!passAsParamPtr)
@@ -2468,7 +2472,7 @@ namespace Flax.Build.Bindings
if (typeInfo.IsArray) if (typeInfo.IsArray)
{ {
typeInfo.IsArray = false; typeInfo.IsArray = false;
header.Append($"{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}Array(const {typeInfo}* v, const int32 length)").AppendLine(); header.Append($"{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}Array({(typeInfo.IsConst ? "const " : "")}{typeInfo}* v, const int32 length)").AppendLine();
header.Append('{').AppendLine(); header.Append('{').AppendLine();
header.Append(" Variant result;").AppendLine(); header.Append(" Variant result;").AppendLine();
header.Append(" result.SetType(VariantType(VariantType::Array));").AppendLine(); header.Append(" result.SetType(VariantType(VariantType::Array));").AppendLine();
@@ -2481,7 +2485,7 @@ namespace Flax.Build.Bindings
else if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null) else if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null)
{ {
var valueType = typeInfo.GenericArgs[0]; var valueType = typeInfo.GenericArgs[0];
header.Append($"{GenerateCppWrapperNativeToVariantMethodName(valueType)}Array(const {valueType}* v, const int32 length)").AppendLine(); header.Append($"{GenerateCppWrapperNativeToVariantMethodName(valueType)}Array({(typeInfo.IsConst ? "const " : "")}{valueType}* v, const int32 length)").AppendLine();
header.Append('{').AppendLine(); header.Append('{').AppendLine();
header.Append(" Variant result;").AppendLine(); header.Append(" Variant result;").AppendLine();
header.Append(" result.SetType(VariantType(VariantType::Array));").AppendLine(); header.Append(" result.SetType(VariantType(VariantType::Array));").AppendLine();
@@ -2646,7 +2650,7 @@ namespace Flax.Build.Bindings
continue; continue;
CppNonPodTypesConvertingGeneration = true; CppNonPodTypesConvertingGeneration = true;
var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out _, null, out _); var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out _, out _, null, out _);
CppNonPodTypesConvertingGeneration = false; CppNonPodTypesConvertingGeneration = false;
if (fieldInfo.Type.IsArray) if (fieldInfo.Type.IsArray)
@@ -2730,7 +2734,7 @@ namespace Flax.Build.Bindings
continue; continue;
CppNonPodTypesConvertingGeneration = true; CppNonPodTypesConvertingGeneration = true;
var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out _, null, out _); var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out _, out _, null, out _);
CppNonPodTypesConvertingGeneration = false; CppNonPodTypesConvertingGeneration = false;
if (fieldInfo.Type.IsArray) if (fieldInfo.Type.IsArray)