Merge remote-tracking branch 'origin/master' into 1.7
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,5 +1,5 @@
|
|||||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||||
* text=auto
|
* text=auto eol=lf
|
||||||
|
|
||||||
# Explicitly declare text files you want to always be normalized and converted to native line endings on checkout.
|
# Explicitly declare text files you want to always be normalized and converted to native line endings on checkout.
|
||||||
*.c text diff=cpp
|
*.c text diff=cpp
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"Version": {
|
"Version": {
|
||||||
"Major": 1,
|
"Major": 1,
|
||||||
"Minor": 6,
|
"Minor": 6,
|
||||||
"Build": 6342
|
"Build": 6343
|
||||||
},
|
},
|
||||||
"Company": "Flax",
|
"Company": "Flax",
|
||||||
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
||||||
|
|||||||
@@ -113,7 +113,15 @@ namespace FlaxEditor.Content
|
|||||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Other;
|
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Other;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanRename => ParentFolder != null; // Deny rename action for root folders
|
public override bool CanRename
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var hasParentFolder = ParentFolder != null;
|
||||||
|
var isContentFolder = Node is MainContentTreeNode;
|
||||||
|
return hasParentFolder && !isContentFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanDrag => ParentFolder != null; // Deny rename action for root folders
|
public override bool CanDrag => ParentFolder != null; // Deny rename action for root folders
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine;
|
|
||||||
using DockState = FlaxEditor.GUI.Docking.DockState;
|
using DockState = FlaxEditor.GUI.Docking.DockState;
|
||||||
|
|
||||||
namespace FlaxEditor
|
namespace FlaxEditor
|
||||||
@@ -86,8 +85,12 @@ namespace FlaxEditor
|
|||||||
if (!FlaxEngine.Scripting.IsTypeFromGameScripts(type))
|
if (!FlaxEngine.Scripting.IsTypeFromGameScripts(type))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!Window.IsHidden)
|
||||||
|
{
|
||||||
Editor.Instance.Windows.AddToRestore(this);
|
Editor.Instance.Windows.AddToRestore(this);
|
||||||
|
}
|
||||||
Window.Close();
|
Window.Close();
|
||||||
|
Window.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
Presenter.Undo?.AddAction(new MultiUndoAction(actions));
|
Presenter.Undo?.AddAction(new MultiUndoAction(actions));
|
||||||
|
|
||||||
// Build ragdoll
|
// Build ragdoll
|
||||||
SceneGraph.Actors.AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
|
AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRebuildBone(Button button)
|
private void OnRebuildBone(Button button)
|
||||||
@@ -191,7 +191,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build ragdoll
|
// Build ragdoll
|
||||||
SceneGraph.Actors.AnimatedModelNode.BuildRagdoll(animatedModel, new AnimatedModelNode.RebuildOptions(), ragdoll, name);
|
AnimatedModelNode.BuildRagdoll(animatedModel, new AnimatedModelNode.RebuildOptions(), ragdoll, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemoveBone(Button button)
|
private void OnRemoveBone(Button button)
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ public class Editor : EditorModule
|
|||||||
{
|
{
|
||||||
base.Setup(options);
|
base.Setup(options);
|
||||||
|
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.Private.Xml");
|
options.ScriptingAPI.SystemReferences.Add("System.Xml");
|
||||||
|
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
|
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ bool Editor::CheckProjectUpgrade()
|
|||||||
" BuildNativeCode = false;\n"
|
" BuildNativeCode = false;\n"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"}}\n"
|
"}}\n"
|
||||||
), codeName), Encoding::Unicode);
|
), codeName), Encoding::UTF8);
|
||||||
if (useEditorModule)
|
if (useEditorModule)
|
||||||
{
|
{
|
||||||
File::WriteAllText(gameEditorModuleFolder / String::Format(TEXT("{0}Editor.Build.cs"), codeName), String::Format(TEXT(
|
File::WriteAllText(gameEditorModuleFolder / String::Format(TEXT("{0}Editor.Build.cs"), codeName), String::Format(TEXT(
|
||||||
@@ -211,7 +211,7 @@ bool Editor::CheckProjectUpgrade()
|
|||||||
" BuildNativeCode = false;\n"
|
" BuildNativeCode = false;\n"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"}}\n"
|
"}}\n"
|
||||||
), codeName), Encoding::Unicode);
|
), codeName), Encoding::UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate target files
|
// Generate target files
|
||||||
@@ -229,7 +229,7 @@ bool Editor::CheckProjectUpgrade()
|
|||||||
" Modules.Add(\"{0}\");\n"
|
" Modules.Add(\"{0}\");\n"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"}}\n"
|
"}}\n"
|
||||||
), codeName), Encoding::Unicode);
|
), codeName), Encoding::UTF8);
|
||||||
const String editorTargetGameEditorModule = useEditorModule ? String::Format(TEXT(" Modules.Add(\"{0}Editor\");\n"), codeName) : String::Empty;
|
const String editorTargetGameEditorModule = useEditorModule ? String::Format(TEXT(" Modules.Add(\"{0}Editor\");\n"), codeName) : String::Empty;
|
||||||
File::WriteAllText(sourceFolder / String::Format(TEXT("{0}EditorTarget.Build.cs"), codeName), String::Format(TEXT(
|
File::WriteAllText(sourceFolder / String::Format(TEXT("{0}EditorTarget.Build.cs"), codeName), String::Format(TEXT(
|
||||||
"using Flax.Build;\n"
|
"using Flax.Build;\n"
|
||||||
@@ -246,7 +246,7 @@ bool Editor::CheckProjectUpgrade()
|
|||||||
"{1}"
|
"{1}"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"}}\n"
|
"}}\n"
|
||||||
), codeName, editorTargetGameEditorModule), Encoding::Unicode);
|
), codeName, editorTargetGameEditorModule), Encoding::UTF8);
|
||||||
|
|
||||||
// Generate new project file
|
// Generate new project file
|
||||||
Project->ProjectPath = root / String::Format(TEXT("{0}.flaxproj"), codeName);
|
Project->ProjectPath = root / String::Format(TEXT("{0}.flaxproj"), codeName);
|
||||||
@@ -454,7 +454,7 @@ int32 Editor::LoadProduct()
|
|||||||
" // Reference the modules for game\n"
|
" // Reference the modules for game\n"
|
||||||
" Modules.Add(\"Game\");\n"
|
" Modules.Add(\"Game\");\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n"), Encoding::Unicode);
|
"}\n"), Encoding::UTF8);
|
||||||
failed |= File::WriteAllText(projectPath / TEXT("Source/GameEditorTarget.Build.cs"),TEXT(
|
failed |= File::WriteAllText(projectPath / TEXT("Source/GameEditorTarget.Build.cs"),TEXT(
|
||||||
"using Flax.Build;\n"
|
"using Flax.Build;\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -468,7 +468,7 @@ int32 Editor::LoadProduct()
|
|||||||
" // Reference the modules for editor\n"
|
" // Reference the modules for editor\n"
|
||||||
" Modules.Add(\"Game\");\n"
|
" Modules.Add(\"Game\");\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n"), Encoding::Unicode);
|
"}\n"), Encoding::UTF8);
|
||||||
failed |= File::WriteAllText(projectPath / TEXT("Source/Game/Game.Build.cs"),TEXT(
|
failed |= File::WriteAllText(projectPath / TEXT("Source/Game/Game.Build.cs"),TEXT(
|
||||||
"using Flax.Build;\n"
|
"using Flax.Build;\n"
|
||||||
"using Flax.Build.NativeCpp;\n"
|
"using Flax.Build.NativeCpp;\n"
|
||||||
@@ -496,7 +496,7 @@ int32 Editor::LoadProduct()
|
|||||||
" // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n"
|
" // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n"
|
||||||
" // To learn more see scripting documentation.\n"
|
" // To learn more see scripting documentation.\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n"), Encoding::Unicode);
|
"}\n"), Encoding::UTF8);
|
||||||
if (failed)
|
if (failed)
|
||||||
return 12;
|
return 12;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,17 @@ namespace FlaxEditor.GUI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ScriptType Type => _type;
|
public ScriptType Type => _type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TypeItemView"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public TypeItemView()
|
||||||
|
{
|
||||||
|
_type = ScriptType.Null;
|
||||||
|
Name = "<null>";
|
||||||
|
TooltipText = "Unset value.";
|
||||||
|
Tag = _type;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TypeItemView"/> class.
|
/// Initializes a new instance of the <see cref="TypeItemView"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -83,6 +94,7 @@ namespace FlaxEditor.GUI
|
|||||||
|
|
||||||
// TODO: use async thread to search types without UI stall
|
// TODO: use async thread to search types without UI stall
|
||||||
var allTypes = Editor.Instance.CodeEditing.All.Get();
|
var allTypes = Editor.Instance.CodeEditing.All.Get();
|
||||||
|
AddItem(new TypeItemView());
|
||||||
for (int i = 0; i < allTypes.Count; i++)
|
for (int i = 0; i < allTypes.Count; i++)
|
||||||
{
|
{
|
||||||
var type = allTypes[i];
|
var type = allTypes[i];
|
||||||
|
|||||||
@@ -12,45 +12,37 @@ namespace FlaxEditor.Gizmo
|
|||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public class GridGizmo : GizmoBase
|
public class GridGizmo : GizmoBase
|
||||||
{
|
{
|
||||||
private bool _enabled = true;
|
[HideInEditor]
|
||||||
|
private sealed class Renderer : PostProcessEffect
|
||||||
|
{
|
||||||
|
private IntPtr _debugDrawContext;
|
||||||
|
|
||||||
/// <summary>
|
public Renderer()
|
||||||
/// Gets or sets a value indicating whether this <see cref="GridGizmo"/> is enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool Enabled
|
|
||||||
{
|
{
|
||||||
get => _enabled;
|
Order = -100;
|
||||||
set
|
UseSingleTarget = true;
|
||||||
{
|
Location = PostProcessEffectLocation.BeforeForwardPass;
|
||||||
if (_enabled != value)
|
|
||||||
{
|
|
||||||
_enabled = value;
|
|
||||||
EnabledChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
~Renderer()
|
||||||
/// Occurs when enabled state gets changed.
|
|
||||||
/// </summary>
|
|
||||||
public event Action<GridGizmo> EnabledChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="GridGizmo"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="owner">The gizmos owner.</param>
|
|
||||||
public GridGizmo(IGizmoOwner owner)
|
|
||||||
: base(owner)
|
|
||||||
{
|
{
|
||||||
|
if (_debugDrawContext != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DebugDraw.FreeContext(_debugDrawContext);
|
||||||
|
_debugDrawContext = IntPtr.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
|
||||||
public override void Draw(ref RenderContext renderContext)
|
|
||||||
{
|
{
|
||||||
if (!Enabled)
|
Profiler.BeginEventGPU("Editor Grid");
|
||||||
return;
|
|
||||||
|
|
||||||
var viewPos = Owner.ViewPosition;
|
if (_debugDrawContext == IntPtr.Zero)
|
||||||
|
_debugDrawContext = DebugDraw.AllocateContext();
|
||||||
|
DebugDraw.SetContext(_debugDrawContext);
|
||||||
|
DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1));
|
||||||
|
|
||||||
|
var viewPos = (Vector3)renderContext.View.Position;
|
||||||
var plane = new Plane(Vector3.Zero, Vector3.UnitY);
|
var plane = new Plane(Vector3.Zero, Vector3.UnitY);
|
||||||
var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos);
|
var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos);
|
||||||
|
|
||||||
@@ -91,6 +83,54 @@ namespace FlaxEditor.Gizmo
|
|||||||
start.Z = end.Z = i * space + start.X;
|
start.Z = end.Z = i * space + start.X;
|
||||||
DebugDraw.DrawLine(start, end, color);
|
DebugDraw.DrawLine(start, end, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DebugDraw.Draw(ref renderContext, input.View(), null, true);
|
||||||
|
DebugDraw.SetContext(IntPtr.Zero);
|
||||||
|
|
||||||
|
Profiler.EndEventGPU();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Renderer _renderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this <see cref="GridGizmo"/> is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled
|
||||||
|
{
|
||||||
|
get => _renderer.Enabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_renderer.Enabled != value)
|
||||||
|
{
|
||||||
|
_renderer.Enabled = value;
|
||||||
|
EnabledChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when enabled state gets changed.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<GridGizmo> EnabledChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GridGizmo"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="owner">The gizmos owner.</param>
|
||||||
|
public GridGizmo(IGizmoOwner owner)
|
||||||
|
: base(owner)
|
||||||
|
{
|
||||||
|
_renderer = new Renderer();
|
||||||
|
owner.RenderTask.AddCustomPostFx(_renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor.
|
||||||
|
/// </summary>
|
||||||
|
~GridGizmo()
|
||||||
|
{
|
||||||
|
FlaxEngine.Object.Destroy(ref _renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace FlaxEditor
|
|||||||
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
|
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
@@ -44,7 +44,7 @@ namespace FlaxEditor
|
|||||||
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
|
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
/// <returns>The object value.</returns>
|
/// <returns>The object value.</returns>
|
||||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.TokenType == JsonToken.Null)
|
if (reader.TokenType == JsonToken.Null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public:
|
|||||||
{
|
{
|
||||||
Version = ::Version(1, 0);
|
Version = ::Version(1, 0);
|
||||||
DefaultSceneSpawn = Ray(Vector3::Zero, Vector3::Forward);
|
DefaultSceneSpawn = Ray(Vector3::Zero, Vector3::Forward);
|
||||||
|
DefaultScene = Guid::Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -832,7 +832,7 @@ namespace FlaxEditor.Scripting
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_managed != null)
|
if (_managed != null)
|
||||||
return _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) != null;
|
return _managed.IsValueType || _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) != null;
|
||||||
return _custom?.CanCreateInstance ?? false;
|
return _custom?.CanCreateInstance ?? false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -893,9 +893,16 @@ namespace FlaxEditor.Scripting
|
|||||||
{
|
{
|
||||||
if (_managed != null)
|
if (_managed != null)
|
||||||
{
|
{
|
||||||
var ctor = _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
|
|
||||||
object value = RuntimeHelpers.GetUninitializedObject(_managed);
|
object value = RuntimeHelpers.GetUninitializedObject(_managed);
|
||||||
|
if (!_managed.IsValueType)
|
||||||
|
{
|
||||||
|
var ctor = _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
|
||||||
|
#if !BUILD_RELEASE
|
||||||
|
if (ctor == null)
|
||||||
|
throw new Exception($"Missing empty constructor for type {_managed.FullName}.");
|
||||||
|
#endif
|
||||||
ctor.Invoke(value, null);
|
ctor.Invoke(value, null);
|
||||||
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return _custom.CreateInstance();
|
return _custom.CreateInstance();
|
||||||
|
|||||||
@@ -613,6 +613,28 @@ bool ScriptsBuilderService::Init()
|
|||||||
const String targetOutput = Globals::ProjectFolder / TEXT("Binaries") / target / platform / architecture / configuration;
|
const String targetOutput = Globals::ProjectFolder / TEXT("Binaries") / target / platform / architecture / configuration;
|
||||||
Array<String> files;
|
Array<String> files;
|
||||||
FileSystem::DirectoryGetFiles(files, targetOutput, TEXT("*.HotReload.*"), DirectorySearchOption::TopDirectoryOnly);
|
FileSystem::DirectoryGetFiles(files, targetOutput, TEXT("*.HotReload.*"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
|
|
||||||
|
for (const auto& reference : Editor::Project->References)
|
||||||
|
{
|
||||||
|
if (reference.Project->Name == TEXT("Flax"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String referenceTarget;
|
||||||
|
if (reference.Project->EditorTarget.HasChars())
|
||||||
|
{
|
||||||
|
referenceTarget = reference.Project->EditorTarget.Get();
|
||||||
|
}
|
||||||
|
else if (reference.Project->GameTarget.HasChars())
|
||||||
|
{
|
||||||
|
referenceTarget = reference.Project->GameTarget.Get();
|
||||||
|
}
|
||||||
|
if (referenceTarget.IsEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const String referenceTargetOutput = reference.Project->ProjectFolderPath / TEXT("Binaries") / referenceTarget / platform / architecture / configuration;
|
||||||
|
FileSystem::DirectoryGetFiles(files, referenceTargetOutput, TEXT("*.HotReload.*"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
|
}
|
||||||
|
|
||||||
if (files.HasItems())
|
if (files.HasItems())
|
||||||
LOG(Info, "Removing {0} files from previous Editor run hot-reloads", files.Count());
|
LOG(Info, "Removing {0} files from previous Editor run hot-reloads", files.Count());
|
||||||
for (auto& file : files)
|
for (auto& file : files)
|
||||||
|
|||||||
@@ -1125,7 +1125,9 @@ namespace FlaxEditor.Utilities
|
|||||||
public static string GetAssetNamePathWithExt(string path)
|
public static string GetAssetNamePathWithExt(string path)
|
||||||
{
|
{
|
||||||
var projectFolder = Globals.ProjectFolder;
|
var projectFolder = Globals.ProjectFolder;
|
||||||
if (path.StartsWith(projectFolder))
|
if (path == projectFolder)
|
||||||
|
path = string.Empty;
|
||||||
|
else if (path.StartsWith(projectFolder))
|
||||||
path = path.Substring(projectFolder.Length + 1);
|
path = path.Substring(projectFolder.Length + 1);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1168,14 +1168,15 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
offset = Float2.Zero;
|
offset = Float2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mouseDelta = Float2.Zero;
|
||||||
|
if (_useMouseFiltering)
|
||||||
|
{
|
||||||
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
||||||
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
||||||
_mouseDelta = offset / size;
|
_mouseDelta = offset / size;
|
||||||
_mouseDelta.Y *= size.Y / size.X;
|
_mouseDelta.Y *= size.Y / size.X;
|
||||||
|
|
||||||
var mouseDelta = Float2.Zero;
|
|
||||||
if (_useMouseFiltering)
|
|
||||||
{
|
|
||||||
// Update delta filtering buffer
|
// Update delta filtering buffer
|
||||||
_deltaFilteringBuffer[_deltaFilteringStep] = _mouseDelta;
|
_deltaFilteringBuffer[_deltaFilteringStep] = _mouseDelta;
|
||||||
_deltaFilteringStep++;
|
_deltaFilteringStep++;
|
||||||
@@ -1192,6 +1193,8 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_mouseDelta = offset / size;
|
||||||
|
_mouseDelta.Y *= size.Y / size.X;
|
||||||
mouseDelta = _mouseDelta;
|
mouseDelta = _mouseDelta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEditor.GUI.Input;
|
|
||||||
using FlaxEngine.GUI;
|
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Previews
|
namespace FlaxEditor.Viewport.Previews
|
||||||
@@ -15,11 +13,10 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <seealso cref="AssetPreview" />
|
/// <seealso cref="AssetPreview" />
|
||||||
public class AnimatedModelPreview : AssetPreview
|
public class AnimatedModelPreview : AssetPreview
|
||||||
{
|
{
|
||||||
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
|
||||||
private bool _showNodes, _showBounds, _showFloor, _showCurrentLOD, _showNodesNames;
|
|
||||||
private AnimatedModel _previewModel;
|
private AnimatedModel _previewModel;
|
||||||
|
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
||||||
|
private bool _showNodes, _showBounds, _showFloor, _showNodesNames;
|
||||||
private StaticModel _floorModel;
|
private StaticModel _floorModel;
|
||||||
private ContextMenuButton _showCurrentLODButton;
|
|
||||||
private bool _playAnimation, _playAnimationOnce;
|
private bool _playAnimation, _playAnimationOnce;
|
||||||
private float _playSpeed = 1.0f;
|
private float _playSpeed = 1.0f;
|
||||||
|
|
||||||
@@ -207,26 +204,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
// Show Floor
|
// Show Floor
|
||||||
_showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor);
|
_showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor);
|
||||||
_showFloorButton.IndexInParent = 1;
|
_showFloorButton.IndexInParent = 1;
|
||||||
|
|
||||||
// Show Current LOD
|
|
||||||
_showCurrentLODButton = ViewWidgetShowMenu.AddButton("Current LOD", button =>
|
|
||||||
{
|
|
||||||
_showCurrentLOD = !_showCurrentLOD;
|
|
||||||
_showCurrentLODButton.Icon = _showCurrentLOD ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
||||||
});
|
|
||||||
_showCurrentLODButton.IndexInParent = 2;
|
|
||||||
|
|
||||||
// Preview LOD
|
|
||||||
{
|
|
||||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
|
||||||
previewLOD.CloseMenuOnClick = false;
|
|
||||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
|
||||||
{
|
|
||||||
Parent = previewLOD
|
|
||||||
};
|
|
||||||
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
|
|
||||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable shadows
|
// Enable shadows
|
||||||
@@ -339,44 +316,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
_previewModel.ResetAnimation();
|
_previewModel.ResetAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ComputeLODIndex(SkinnedModel model)
|
|
||||||
{
|
|
||||||
if (PreviewActor.ForcedLOD != -1)
|
|
||||||
return PreviewActor.ForcedLOD;
|
|
||||||
|
|
||||||
// Based on RenderTools::ComputeModelLOD
|
|
||||||
CreateProjectionMatrix(out var projectionMatrix);
|
|
||||||
float screenMultiple = 0.5f * Mathf.Max(projectionMatrix.M11, projectionMatrix.M22);
|
|
||||||
var sphere = PreviewActor.Sphere;
|
|
||||||
var viewOrigin = ViewPosition;
|
|
||||||
var distSqr = Vector3.DistanceSquared(ref sphere.Center, ref viewOrigin);
|
|
||||||
var screenRadiusSquared = Mathf.Square(screenMultiple * sphere.Radius) / Mathf.Max(1.0f, distSqr);
|
|
||||||
|
|
||||||
// Check if model is being culled
|
|
||||||
if (Mathf.Square(model.MinScreenSize * 0.5f) > screenRadiusSquared)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Skip if no need to calculate LOD
|
|
||||||
if (model.LoadedLODs == 0)
|
|
||||||
return -1;
|
|
||||||
var lods = model.LODs;
|
|
||||||
if (lods.Length == 0)
|
|
||||||
return -1;
|
|
||||||
if (lods.Length == 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Iterate backwards and return the first matching LOD
|
|
||||||
for (int lodIndex = lods.Length - 1; lodIndex >= 0; lodIndex--)
|
|
||||||
{
|
|
||||||
if (Mathf.Square(lods[lodIndex].ScreenSize * 0.5f) >= screenRadiusSquared)
|
|
||||||
{
|
|
||||||
return lodIndex + PreviewActor.LODBias;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext)
|
protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext)
|
||||||
{
|
{
|
||||||
@@ -440,45 +379,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
base.Draw();
|
|
||||||
|
|
||||||
var skinnedModel = _previewModel.SkinnedModel;
|
|
||||||
if (skinnedModel == null || !skinnedModel.IsLoaded)
|
|
||||||
return;
|
|
||||||
var lods = skinnedModel.LODs;
|
|
||||||
if (lods.Length == 0)
|
|
||||||
{
|
|
||||||
// Force show skeleton for models without geometry
|
|
||||||
ShowNodes = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_showCurrentLOD)
|
|
||||||
{
|
|
||||||
var lodIndex = ComputeLODIndex(skinnedModel);
|
|
||||||
string text = string.Format("Current LOD: {0}", lodIndex);
|
|
||||||
if (lodIndex != -1)
|
|
||||||
{
|
|
||||||
lodIndex = Mathf.Clamp(lodIndex + PreviewActor.LODBias, 0, lods.Length - 1);
|
|
||||||
var lod = lods[lodIndex];
|
|
||||||
int triangleCount = 0, vertexCount = 0;
|
|
||||||
for (int meshIndex = 0; meshIndex < lod.Meshes.Length; meshIndex++)
|
|
||||||
{
|
|
||||||
var mesh = lod.Meshes[meshIndex];
|
|
||||||
triangleCount += mesh.TriangleCount;
|
|
||||||
vertexCount += mesh.VertexCount;
|
|
||||||
}
|
|
||||||
text += string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}", triangleCount, vertexCount);
|
|
||||||
}
|
|
||||||
var font = Style.Current.FontMedium;
|
|
||||||
var pos = new Float2(10, 50);
|
|
||||||
Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black);
|
|
||||||
Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Update(float deltaTime)
|
public override void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
@@ -498,14 +398,21 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the camera to focus on a object.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetCamera()
|
||||||
|
{
|
||||||
|
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnKeyDown(KeyboardKeys key)
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
{
|
{
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case KeyboardKeys.F:
|
case KeyboardKeys.F:
|
||||||
// Pay respect..
|
ResetCamera();
|
||||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
|
||||||
return true;
|
return true;
|
||||||
case KeyboardKeys.Spacebar:
|
case KeyboardKeys.Spacebar:
|
||||||
PlayAnimation = !PlayAnimation;
|
PlayAnimation = !PlayAnimation;
|
||||||
@@ -525,7 +432,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
_showNodesButton = null;
|
_showNodesButton = null;
|
||||||
_showBoundsButton = null;
|
_showBoundsButton = null;
|
||||||
_showFloorButton = null;
|
_showFloorButton = null;
|
||||||
_showCurrentLODButton = null;
|
|
||||||
_showNodesNamesButton = null;
|
_showNodesNamesButton = null;
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using System;
|
|||||||
using FlaxEditor.Surface;
|
using FlaxEditor.Surface;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEditor.Viewport.Widgets;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Previews
|
namespace FlaxEditor.Viewport.Previews
|
||||||
@@ -46,6 +48,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
private int _selectedModelIndex;
|
private int _selectedModelIndex;
|
||||||
private Image _guiMaterialControl;
|
private Image _guiMaterialControl;
|
||||||
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
||||||
|
private ContextMenu _modelWidgetButtonMenu;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the material asset to preview. It can be <see cref="FlaxEngine.Material"/> or <see cref="FlaxEngine.MaterialInstance"/>.
|
/// Gets or sets the material asset to preview. It can be <see cref="FlaxEngine.Material"/> or <see cref="FlaxEngine.MaterialInstance"/>.
|
||||||
@@ -95,20 +98,33 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
Task.AddCustomActor(_previewModel);
|
Task.AddCustomActor(_previewModel);
|
||||||
|
|
||||||
// Create context menu for primitive switching
|
// Create context menu for primitive switching
|
||||||
if (useWidgets && ViewWidgetButtonMenu != null)
|
if (useWidgets)
|
||||||
{
|
{
|
||||||
ViewWidgetButtonMenu.AddSeparator();
|
// Model mode widget
|
||||||
var modelSelect = ViewWidgetButtonMenu.AddChildMenu("Model").ContextMenu;
|
var modelMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
_modelWidgetButtonMenu = new ContextMenu();
|
||||||
|
_modelWidgetButtonMenu.VisibleChanged += control =>
|
||||||
|
{
|
||||||
|
if (!control.Visible)
|
||||||
|
return;
|
||||||
|
_modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||||
|
|
||||||
// Fill out all models
|
// Fill out all models
|
||||||
for (int i = 0; i < Models.Length; i++)
|
for (int i = 0; i < Models.Length; i++)
|
||||||
{
|
{
|
||||||
var button = modelSelect.AddButton(Models[i]);
|
var index = i;
|
||||||
button.Tag = i;
|
var button = _modelWidgetButtonMenu.AddButton(Models[index]);
|
||||||
|
button.ButtonClicked += _ => SelectedModelIndex = index;
|
||||||
|
button.Checked = SelectedModelIndex == index;
|
||||||
|
button.Tag = index;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// Link the action
|
new ViewportWidgetButton("Model", SpriteHandle.Invalid, _modelWidgetButtonMenu)
|
||||||
modelSelect.ButtonClicked += (button) => SelectedModelIndex = (int)button.Tag;
|
{
|
||||||
|
TooltipText = "Change material model",
|
||||||
|
Parent = modelMode,
|
||||||
|
};
|
||||||
|
modelMode.Parent = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using FlaxEditor.GUI.Input;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
@@ -56,25 +55,14 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
// Link actors for rendering
|
// Link actors for rendering
|
||||||
Task.AddCustomActor(StaticModel);
|
Task.AddCustomActor(StaticModel);
|
||||||
Task.AddCustomActor(AnimatedModel);
|
Task.AddCustomActor(AnimatedModel);
|
||||||
|
}
|
||||||
|
|
||||||
if (useWidgets)
|
/// <summary>
|
||||||
|
/// Resets the camera to focus on a object.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetCamera()
|
||||||
{
|
{
|
||||||
// Preview LOD
|
ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
|
||||||
{
|
|
||||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
|
||||||
previewLOD.CloseMenuOnClick = false;
|
|
||||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
|
||||||
{
|
|
||||||
Parent = previewLOD
|
|
||||||
};
|
|
||||||
previewLODValue.ValueChanged += () =>
|
|
||||||
{
|
|
||||||
StaticModel.ForcedLOD = previewLODValue.Value;
|
|
||||||
AnimatedModel.ForcedLOD = previewLODValue.Value;
|
|
||||||
};
|
|
||||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = StaticModel.ForcedLOD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBegin(RenderTask task, GPUContext context)
|
private void OnBegin(RenderTask task, GPUContext context)
|
||||||
@@ -103,8 +91,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case KeyboardKeys.F:
|
case KeyboardKeys.F:
|
||||||
// Pay respect..
|
ResetCamera();
|
||||||
ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return base.OnKeyDown(key);
|
return base.OnKeyDown(key);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Input;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
|
using FlaxEditor.Viewport.Widgets;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Previews
|
namespace FlaxEditor.Viewport.Previews
|
||||||
@@ -16,10 +16,27 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
public class ModelPreview : AssetPreview
|
public class ModelPreview : AssetPreview
|
||||||
{
|
{
|
||||||
private ContextMenuButton _showBoundsButton, _showCurrentLODButton, _showNormalsButton, _showTangentsButton, _showBitangentsButton, _showFloorButton;
|
private ContextMenuButton _showBoundsButton, _showCurrentLODButton, _showNormalsButton, _showTangentsButton, _showBitangentsButton, _showFloorButton;
|
||||||
|
private ContextMenu _previewLODsWidgetButtonMenu;
|
||||||
private StaticModel _previewModel, _floorModel;
|
private StaticModel _previewModel, _floorModel;
|
||||||
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor;
|
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor;
|
||||||
private MeshDataCache _meshDatas;
|
private MeshDataCache _meshDatas;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that shows LOD statistics
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowCurrentLOD
|
||||||
|
{
|
||||||
|
get => _showCurrentLOD;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showCurrentLOD == value)
|
||||||
|
return;
|
||||||
|
_showCurrentLOD = value;
|
||||||
|
if (_showCurrentLODButton != null)
|
||||||
|
_showCurrentLODButton.Checked = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the model asset to preview.
|
/// Gets or sets the model asset to preview.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -198,18 +215,37 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
});
|
});
|
||||||
_showCurrentLODButton.IndexInParent = 2;
|
_showCurrentLODButton.IndexInParent = 2;
|
||||||
|
|
||||||
// Preview LOD
|
// Preview LODs mode widget
|
||||||
|
var PreviewLODsMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
_previewLODsWidgetButtonMenu = new ContextMenu();
|
||||||
|
_previewLODsWidgetButtonMenu.VisibleChanged += control =>
|
||||||
{
|
{
|
||||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
if (!control.Visible)
|
||||||
previewLOD.CloseMenuOnClick = false;
|
return;
|
||||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
var model = _previewModel.Model;
|
||||||
|
if (model && !model.WaitForLoaded())
|
||||||
{
|
{
|
||||||
Parent = previewLOD
|
_previewLODsWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||||
};
|
var lods = model.LODs.Length;
|
||||||
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
|
for (int i = -1; i < lods; i++)
|
||||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
|
{
|
||||||
|
var index = i;
|
||||||
|
var button = _previewLODsWidgetButtonMenu.AddButton("LOD " + (index == -1 ? "Auto" : index));
|
||||||
|
button.ButtonClicked += _ => _previewModel.ForcedLOD = index;
|
||||||
|
button.Checked = _previewModel.ForcedLOD == index;
|
||||||
|
button.Tag = index;
|
||||||
|
if (lods <= 1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
new ViewportWidgetButton("Preview LOD", SpriteHandle.Invalid, _previewLODsWidgetButtonMenu)
|
||||||
|
{
|
||||||
|
TooltipText = "Preview LOD properties",
|
||||||
|
Parent = PreviewLODsMode,
|
||||||
|
};
|
||||||
|
PreviewLODsMode.Parent = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBegin(RenderTask task, GPUContext context)
|
private void OnBegin(RenderTask task, GPUContext context)
|
||||||
@@ -347,7 +383,10 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
{
|
{
|
||||||
var asset = Model;
|
var asset = Model;
|
||||||
var lodIndex = ComputeLODIndex(asset, out var screenSize);
|
var lodIndex = ComputeLODIndex(asset, out var screenSize);
|
||||||
string text = string.Format("Current LOD: {0}\nScreen Size: {1:F2}", lodIndex, screenSize);
|
var auto = _previewModel.ForcedLOD == -1;
|
||||||
|
string text = auto ? "LOD Automatic" : "";
|
||||||
|
text += auto ? string.Format("\nScreen Size: {0:F2}", screenSize) : "";
|
||||||
|
text += string.Format("\nCurrent LOD: {0}", lodIndex);
|
||||||
if (lodIndex != -1)
|
if (lodIndex != -1)
|
||||||
{
|
{
|
||||||
var lods = asset.LODs;
|
var lods = asset.LODs;
|
||||||
@@ -369,14 +408,21 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the camera to focus on a object.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetCamera()
|
||||||
|
{
|
||||||
|
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnKeyDown(KeyboardKeys key)
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
{
|
{
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case KeyboardKeys.F:
|
case KeyboardKeys.F:
|
||||||
// Pay respect..
|
ResetCamera();
|
||||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return base.OnKeyDown(key);
|
return base.OnKeyDown(key);
|
||||||
@@ -389,6 +435,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
Object.Destroy(ref _previewModel);
|
Object.Destroy(ref _previewModel);
|
||||||
_showBoundsButton = null;
|
_showBoundsButton = null;
|
||||||
_showCurrentLODButton = null;
|
_showCurrentLODButton = null;
|
||||||
|
_previewLODsWidgetButtonMenu = null;
|
||||||
_showNormalsButton = null;
|
_showNormalsButton = null;
|
||||||
_showTangentsButton = null;
|
_showTangentsButton = null;
|
||||||
_showBitangentsButton = null;
|
_showBitangentsButton = null;
|
||||||
|
|||||||
177
Source/Editor/Viewport/Previews/SkinnedModelPreview.cs
Normal file
177
Source/Editor/Viewport/Previews/SkinnedModelPreview.cs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEditor.Viewport.Widgets;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Viewport.Previews
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Animation asset preview editor viewport.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="AnimatedModelPreview" />
|
||||||
|
public class SkinnedModelPreview : AnimatedModelPreview
|
||||||
|
{
|
||||||
|
private bool _showCurrentLOD;
|
||||||
|
private ContextMenuButton _showCurrentLODButton;
|
||||||
|
private ContextMenu _previewLODsWidgetButtonMenu;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that shows LOD statistics
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowCurrentLOD
|
||||||
|
{
|
||||||
|
get => _showCurrentLOD;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showCurrentLOD == value)
|
||||||
|
return;
|
||||||
|
_showCurrentLOD = value;
|
||||||
|
if (_showCurrentLODButton != null)
|
||||||
|
_showCurrentLODButton.Checked = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SkinnedModelPreview"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||||
|
public SkinnedModelPreview(bool useWidgets)
|
||||||
|
: base(useWidgets)
|
||||||
|
{
|
||||||
|
if (useWidgets)
|
||||||
|
{
|
||||||
|
// Show Current LOD
|
||||||
|
_showCurrentLODButton = ViewWidgetShowMenu.AddButton("Current LOD", button =>
|
||||||
|
{
|
||||||
|
_showCurrentLOD = !_showCurrentLOD;
|
||||||
|
_showCurrentLODButton.Icon = _showCurrentLOD ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
|
});
|
||||||
|
_showCurrentLODButton.IndexInParent = 2;
|
||||||
|
|
||||||
|
// PreviewLODS mode widget
|
||||||
|
var PreviewLODSMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
_previewLODsWidgetButtonMenu = new ContextMenu();
|
||||||
|
_previewLODsWidgetButtonMenu.VisibleChanged += control =>
|
||||||
|
{
|
||||||
|
if (!control.Visible)
|
||||||
|
return;
|
||||||
|
var skinned = PreviewActor.SkinnedModel;
|
||||||
|
if (skinned && !skinned.WaitForLoaded())
|
||||||
|
{
|
||||||
|
_previewLODsWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||||
|
var lods = skinned.LODs.Length;
|
||||||
|
for (int i = -1; i < lods; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
var button = _previewLODsWidgetButtonMenu.AddButton("LOD " + (index == -1 ? "Auto" : index));
|
||||||
|
button.ButtonClicked += (button) => PreviewActor.ForcedLOD = index;
|
||||||
|
button.Checked = PreviewActor.ForcedLOD == index;
|
||||||
|
button.Tag = index;
|
||||||
|
if (lods <= 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
new ViewportWidgetButton("Preview LOD", SpriteHandle.Invalid, _previewLODsWidgetButtonMenu)
|
||||||
|
{
|
||||||
|
TooltipText = "Preview LOD properties",
|
||||||
|
Parent = PreviewLODSMode,
|
||||||
|
};
|
||||||
|
PreviewLODSMode.Parent = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ComputeLODIndex(SkinnedModel model, out float screenSize)
|
||||||
|
{
|
||||||
|
screenSize = 1.0f;
|
||||||
|
if (PreviewActor.ForcedLOD != -1)
|
||||||
|
return PreviewActor.ForcedLOD;
|
||||||
|
|
||||||
|
// Based on RenderTools::ComputeModelLOD
|
||||||
|
CreateProjectionMatrix(out var projectionMatrix);
|
||||||
|
float screenMultiple = 0.5f * Mathf.Max(projectionMatrix.M11, projectionMatrix.M22);
|
||||||
|
var sphere = PreviewActor.Sphere;
|
||||||
|
var viewOrigin = ViewPosition;
|
||||||
|
var distSqr = Vector3.DistanceSquared(ref sphere.Center, ref viewOrigin);
|
||||||
|
var screenRadiusSquared = Mathf.Square(screenMultiple * sphere.Radius) / Mathf.Max(1.0f, distSqr);
|
||||||
|
screenSize = Mathf.Sqrt((float)screenRadiusSquared) * 2.0f;
|
||||||
|
|
||||||
|
// Check if model is being culled
|
||||||
|
if (Mathf.Square(model.MinScreenSize * 0.5f) > screenRadiusSquared)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Skip if no need to calculate LOD
|
||||||
|
if (model.LoadedLODs == 0)
|
||||||
|
return -1;
|
||||||
|
var lods = model.LODs;
|
||||||
|
if (lods.Length == 0)
|
||||||
|
return -1;
|
||||||
|
if (lods.Length == 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Iterate backwards and return the first matching LOD
|
||||||
|
for (int lodIndex = lods.Length - 1; lodIndex >= 0; lodIndex--)
|
||||||
|
{
|
||||||
|
if (Mathf.Square(lods[lodIndex].ScreenSize * 0.5f) >= screenRadiusSquared)
|
||||||
|
{
|
||||||
|
return lodIndex + PreviewActor.LODBias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
base.Draw();
|
||||||
|
|
||||||
|
var skinnedModel = PreviewActor.SkinnedModel;
|
||||||
|
if (skinnedModel == null || !skinnedModel.IsLoaded)
|
||||||
|
return;
|
||||||
|
var lods = skinnedModel.LODs;
|
||||||
|
if (lods.Length == 0)
|
||||||
|
{
|
||||||
|
// Force show skeleton for models without geometry
|
||||||
|
ShowNodes = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_showCurrentLOD)
|
||||||
|
{
|
||||||
|
var lodIndex = ComputeLODIndex(skinnedModel, out var screenSize);
|
||||||
|
var auto = PreviewActor.ForcedLOD == -1;
|
||||||
|
string text = auto ? "LOD Automatic" : "";
|
||||||
|
text += auto ? string.Format("\nScreen Size: {0:F2}", screenSize) : "";
|
||||||
|
text += string.Format("\nCurrent LOD: {0}", lodIndex);
|
||||||
|
if (lodIndex != -1)
|
||||||
|
{
|
||||||
|
lodIndex = Mathf.Clamp(lodIndex + PreviewActor.LODBias, 0, lods.Length - 1);
|
||||||
|
var lod = lods[lodIndex];
|
||||||
|
int triangleCount = 0, vertexCount = 0;
|
||||||
|
for (int meshIndex = 0; meshIndex < lod.Meshes.Length; meshIndex++)
|
||||||
|
{
|
||||||
|
var mesh = lod.Meshes[meshIndex];
|
||||||
|
triangleCount += mesh.TriangleCount;
|
||||||
|
vertexCount += mesh.VertexCount;
|
||||||
|
}
|
||||||
|
text += string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}", triangleCount, vertexCount);
|
||||||
|
}
|
||||||
|
var font = Style.Current.FontMedium;
|
||||||
|
var pos = new Float2(10, 50);
|
||||||
|
Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black);
|
||||||
|
Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
_showCurrentLODButton = null;
|
||||||
|
_previewLODsWidgetButtonMenu = null;
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -182,6 +182,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
|
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole collision");
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
// Split Panel
|
// Split Panel
|
||||||
|
|||||||
@@ -779,6 +779,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
private MeshDataCache _meshData;
|
private MeshDataCache _meshData;
|
||||||
private ModelImportSettings _importSettings = new ModelImportSettings();
|
private ModelImportSettings _importSettings = new ModelImportSettings();
|
||||||
private float _backfacesThreshold = 0.6f;
|
private float _backfacesThreshold = 0.6f;
|
||||||
|
private ToolStripButton _showCurrentLODButton;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ModelWindow(Editor editor, AssetItem item)
|
public ModelWindow(Editor editor, AssetItem item)
|
||||||
@@ -786,6 +787,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
|
_showCurrentLODButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64, () => _preview.ShowCurrentLOD = !_preview.ShowCurrentLOD).LinkTooltip("Show LOD statistics");
|
||||||
|
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole model");
|
||||||
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/models/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/models/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
// Model preview
|
// Model preview
|
||||||
@@ -869,6 +873,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showCurrentLODButton.Checked = _preview.ShowCurrentLOD;
|
||||||
|
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,6 +952,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
|
|
||||||
Object.Destroy(ref _highlightActor);
|
Object.Destroy(ref _highlightActor);
|
||||||
|
_showCurrentLODButton = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
||||||
public sealed class SkinnedModelWindow : ModelBaseWindow<SkinnedModel, SkinnedModelWindow>
|
public sealed class SkinnedModelWindow : ModelBaseWindow<SkinnedModel, SkinnedModelWindow>
|
||||||
{
|
{
|
||||||
private sealed class Preview : AnimatedModelPreview
|
private sealed class Preview : SkinnedModelPreview
|
||||||
{
|
{
|
||||||
private readonly SkinnedModelWindow _window;
|
private readonly SkinnedModelWindow _window;
|
||||||
|
|
||||||
@@ -1105,6 +1105,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
private Preview _preview;
|
private Preview _preview;
|
||||||
private AnimatedModel _highlightActor;
|
private AnimatedModel _highlightActor;
|
||||||
private ToolStripButton _showNodesButton;
|
private ToolStripButton _showNodesButton;
|
||||||
|
private ToolStripButton _showCurrentLODButton;
|
||||||
|
|
||||||
private MeshData[][] _meshDatas;
|
private MeshData[][] _meshDatas;
|
||||||
private bool _meshDatasInProgress;
|
private bool _meshDatasInProgress;
|
||||||
@@ -1116,7 +1117,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
|
_showCurrentLODButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64, () => _preview.ShowCurrentLOD = !_preview.ShowCurrentLOD).LinkTooltip("Show LOD statistics");
|
||||||
_showNodesButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Bone64, () => _preview.ShowNodes = !_preview.ShowNodes).LinkTooltip("Show animated model nodes debug view");
|
_showNodesButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Bone64, () => _preview.ShowNodes = !_preview.ShowNodes).LinkTooltip("Show animated model nodes debug view");
|
||||||
|
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole model");
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/skinned-model/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/skinned-model/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
@@ -1265,6 +1268,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showCurrentLODButton.Checked = _preview.ShowCurrentLOD;
|
||||||
_showNodesButton.Checked = _preview.ShowNodes;
|
_showNodesButton.Checked = _preview.ShowNodes;
|
||||||
|
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
@@ -1349,6 +1353,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
Object.Destroy(ref _highlightActor);
|
Object.Destroy(ref _highlightActor);
|
||||||
_preview = null;
|
_preview = null;
|
||||||
_showNodesButton = null;
|
_showNodesButton = null;
|
||||||
|
_showCurrentLODButton = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,6 +304,6 @@ void SplashScreen::OnFontLoaded(Asset* asset)
|
|||||||
|
|
||||||
// Create fonts
|
// Create fonts
|
||||||
const float s = _dpiScale;
|
const float s = _dpiScale;
|
||||||
_titleFont = font->CreateFont((uint32)(35 * s));
|
_titleFont = font->CreateFont(35 * s);
|
||||||
_subtitleFont = font->CreateFont((uint32)(9 * s));
|
_subtitleFont = font->CreateFont(9 * s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assets Importing service allows to import or create new assets
|
/// Assets Importing service allows to import or create new assets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class AssetsExportingManager
|
class FLAXENGINE_API AssetsExportingManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ typedef Function<ExportAssetResult(ExportAssetContext&)> ExportAssetFunction;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exporting asset context structure
|
/// Exporting asset context structure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ExportAssetContext : public NonCopyable
|
class FLAXENGINE_API ExportAssetContext : public NonCopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assets Importing service allows to import or create new assets
|
/// Assets Importing service allows to import or create new assets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class AssetsImportingManager
|
class FLAXENGINE_API AssetsImportingManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ typedef Function<CreateAssetResult(CreateAssetContext&)> CreateAssetFunction;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Importing/creating asset context structure
|
/// Importing/creating asset context structure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class CreateAssetContext : public NonCopyable
|
class FLAXENGINE_API CreateAssetContext : public NonCopyable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
CreateAssetResult _applyChangesResult;
|
CreateAssetResult _applyChangesResult;
|
||||||
|
|||||||
@@ -357,8 +357,8 @@ public:
|
|||||||
{
|
{
|
||||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||||
{
|
{
|
||||||
if (i->Value)
|
if (i->Item)
|
||||||
::Delete(i->Value);
|
::Delete(i->Item);
|
||||||
}
|
}
|
||||||
Clear();
|
Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
#include "Enums.h"
|
#include "Enums.h"
|
||||||
|
|
||||||
DECLARE_ENUM_3(Encoding, ANSI, Unicode, UnicodeBigEndian);
|
DECLARE_ENUM_4(Encoding, ANSI, Unicode, UnicodeBigEndian, UTF8);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum
|
API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum
|
||||||
{
|
{
|
||||||
|
friend CollisionsHelper;
|
||||||
private:
|
private:
|
||||||
Matrix _matrix;
|
Matrix _matrix;
|
||||||
|
|
||||||
|
|||||||
@@ -939,7 +939,6 @@ bool CollisionsHelper::RayIntersectsSphere(const Ray& ray, const BoundingSphere&
|
|||||||
normal = Vector3::Up;
|
normal = Vector3::Up;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vector3 point = ray.Position + ray.Direction * distance;
|
const Vector3 point = ray.Position + ray.Direction * distance;
|
||||||
normal = Vector3::Normalize(point - sphere.Center);
|
normal = Vector3::Normalize(point - sphere.Center);
|
||||||
return true;
|
return true;
|
||||||
@@ -953,22 +952,17 @@ bool CollisionsHelper::RayIntersectsSphere(const Ray& ray, const BoundingSphere&
|
|||||||
point = Vector3::Zero;
|
point = Vector3::Zero;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
point = ray.Position + ray.Direction * distance;
|
point = ray.Position + ray.Direction * distance;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaneIntersectionType CollisionsHelper::PlaneIntersectsPoint(const Plane& plane, const Vector3& point)
|
PlaneIntersectionType CollisionsHelper::PlaneIntersectsPoint(const Plane& plane, const Vector3& point)
|
||||||
{
|
{
|
||||||
Real distance = Vector3::Dot(plane.Normal, point);
|
const Real distance = Vector3::Dot(plane.Normal, point) + plane.D;
|
||||||
distance += plane.D;
|
|
||||||
|
|
||||||
if (distance > Plane::DistanceEpsilon)
|
if (distance > Plane::DistanceEpsilon)
|
||||||
return PlaneIntersectionType::Front;
|
return PlaneIntersectionType::Front;
|
||||||
|
|
||||||
if (distance < Plane::DistanceEpsilon)
|
if (distance < Plane::DistanceEpsilon)
|
||||||
return PlaneIntersectionType::Back;
|
return PlaneIntersectionType::Back;
|
||||||
|
|
||||||
return PlaneIntersectionType::Intersecting;
|
return PlaneIntersectionType::Intersecting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1169,7 +1163,6 @@ ContainmentType CollisionsHelper::SphereContainsPoint(const BoundingSphere& sphe
|
|||||||
{
|
{
|
||||||
if (Vector3::DistanceSquared(point, sphere.Center) <= sphere.Radius * sphere.Radius)
|
if (Vector3::DistanceSquared(point, sphere.Center) <= sphere.Radius * sphere.Radius)
|
||||||
return ContainmentType::Contains;
|
return ContainmentType::Contains;
|
||||||
|
|
||||||
return ContainmentType::Disjoint;
|
return ContainmentType::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1254,13 +1247,10 @@ ContainmentType CollisionsHelper::SphereContainsBox(const BoundingSphere& sphere
|
|||||||
ContainmentType CollisionsHelper::SphereContainsSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
|
ContainmentType CollisionsHelper::SphereContainsSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
|
||||||
{
|
{
|
||||||
const Real distance = Vector3::Distance(sphere1.Center, sphere2.Center);
|
const Real distance = Vector3::Distance(sphere1.Center, sphere2.Center);
|
||||||
|
|
||||||
if (sphere1.Radius + sphere2.Radius < distance)
|
if (sphere1.Radius + sphere2.Radius < distance)
|
||||||
return ContainmentType::Disjoint;
|
return ContainmentType::Disjoint;
|
||||||
|
|
||||||
if (sphere1.Radius - sphere2.Radius < distance)
|
if (sphere1.Radius - sphere2.Radius < distance)
|
||||||
return ContainmentType::Intersects;
|
return ContainmentType::Intersects;
|
||||||
|
|
||||||
return ContainmentType::Contains;
|
return ContainmentType::Contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1274,7 +1264,8 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
|||||||
auto result = ContainmentType::Contains;
|
auto result = ContainmentType::Contains;
|
||||||
for (int32 i = 0; i < 6; i++)
|
for (int32 i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
Plane plane = frustum.GetPlane(i);
|
Plane plane = frustum._planes[i];
|
||||||
|
|
||||||
Vector3 p = box.Minimum;
|
Vector3 p = box.Minimum;
|
||||||
if (plane.Normal.X >= 0)
|
if (plane.Normal.X >= 0)
|
||||||
p.X = box.Maximum.X;
|
p.X = box.Maximum.X;
|
||||||
@@ -1282,7 +1273,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
|||||||
p.Y = box.Maximum.Y;
|
p.Y = box.Maximum.Y;
|
||||||
if (plane.Normal.Z >= 0)
|
if (plane.Normal.Z >= 0)
|
||||||
p.Z = box.Maximum.Z;
|
p.Z = box.Maximum.Z;
|
||||||
if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
|
if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
|
||||||
return ContainmentType::Disjoint;
|
return ContainmentType::Disjoint;
|
||||||
|
|
||||||
p = box.Maximum;
|
p = box.Maximum;
|
||||||
@@ -1292,7 +1283,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
|||||||
p.Y = box.Minimum.Y;
|
p.Y = box.Minimum.Y;
|
||||||
if (plane.Normal.Z >= 0)
|
if (plane.Normal.Z >= 0)
|
||||||
p.Z = box.Minimum.Z;
|
p.Z = box.Minimum.Z;
|
||||||
if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
|
if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
|
||||||
result = ContainmentType::Intersects;
|
result = ContainmentType::Intersects;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -36,6 +36,32 @@ namespace AllocatorExt
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reallocates block of the memory.
|
||||||
|
/// </summary>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A pointer to the memory block to reallocate.</param>
|
||||||
|
/// <param name="newSize">The size of the new allocation (in bytes).</param>
|
||||||
|
/// <param name="alignment">The memory alignment (in bytes). Must be an integer power of 2.</param>
|
||||||
|
/// <returns>The pointer to the allocated chunk of the memory. The pointer is a multiple of alignment.</returns>
|
||||||
|
inline void* ReallocAligned(void* ptr, uint64 newSize, uint64 alignment)
|
||||||
|
{
|
||||||
|
if (newSize == 0)
|
||||||
|
{
|
||||||
|
Allocator::Free(ptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!ptr)
|
||||||
|
return Allocator::Allocate(newSize, alignment);
|
||||||
|
void* result = Allocator::Allocate(newSize, alignment);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
Platform::MemoryCopy(result, ptr, newSize);
|
||||||
|
Allocator::Free(ptr);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reallocates block of the memory.
|
/// Reallocates block of the memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -114,3 +114,14 @@ inline Span<T> ToSpan(const T* ptr, int32 length)
|
|||||||
{
|
{
|
||||||
return Span<T>(ptr, length);
|
return Span<T>(ptr, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool SpanContains(const Span<T> span, const T& value)
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < span.Length(); i++)
|
||||||
|
{
|
||||||
|
if (span.Get()[i] == value)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ void String::Set(const char* chars, int32 length)
|
|||||||
_length = length;
|
_length = length;
|
||||||
}
|
}
|
||||||
if (chars)
|
if (chars)
|
||||||
StringUtils::ConvertANSI2UTF16(chars, _data, length);
|
StringUtils::ConvertANSI2UTF16(chars, _data, length, _length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::SetUTF8(const char* chars, int32 length)
|
void String::SetUTF8(const char* chars, int32 length)
|
||||||
@@ -112,7 +112,7 @@ void String::Append(const char* chars, int32 count)
|
|||||||
_data = (Char*)Platform::Allocate((_length + 1) * sizeof(Char), 16);
|
_data = (Char*)Platform::Allocate((_length + 1) * sizeof(Char), 16);
|
||||||
|
|
||||||
Platform::MemoryCopy(_data, oldData, oldLength * sizeof(Char));
|
Platform::MemoryCopy(_data, oldData, oldLength * sizeof(Char));
|
||||||
StringUtils::ConvertANSI2UTF16(chars, _data + oldLength, count * sizeof(Char));
|
StringUtils::ConvertANSI2UTF16(chars, _data + oldLength, count, _length);
|
||||||
_data[_length] = 0;
|
_data[_length] = 0;
|
||||||
|
|
||||||
Platform::Free(oldData);
|
Platform::Free(oldData);
|
||||||
|
|||||||
@@ -125,7 +125,8 @@ public:
|
|||||||
const int32 length = str && *str ? StringUtils::Length(str) : 0;
|
const int32 length = str && *str ? StringUtils::Length(str) : 0;
|
||||||
const int32 prevCnt = _data.Count();
|
const int32 prevCnt = _data.Count();
|
||||||
_data.AddDefault(length);
|
_data.AddDefault(length);
|
||||||
StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length);
|
int32 tmp;
|
||||||
|
StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length, tmp);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -660,7 +660,8 @@ Variant::Variant(const StringAnsiView& v)
|
|||||||
const int32 length = v.Length() * sizeof(Char) + 2;
|
const int32 length = v.Length() * sizeof(Char) + 2;
|
||||||
AsBlob.Data = Allocator::Allocate(length);
|
AsBlob.Data = Allocator::Allocate(length);
|
||||||
AsBlob.Length = length;
|
AsBlob.Length = length;
|
||||||
StringUtils::ConvertANSI2UTF16(v.Get(), (Char*)AsBlob.Data, v.Length());
|
int32 tmp;
|
||||||
|
StringUtils::ConvertANSI2UTF16(v.Get(), (Char*)AsBlob.Data, v.Length(), tmp);
|
||||||
((Char*)AsBlob.Data)[v.Length()] = 0;
|
((Char*)AsBlob.Data)[v.Length()] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2578,7 +2579,8 @@ void Variant::SetString(const StringAnsiView& str)
|
|||||||
AsBlob.Data = Allocator::Allocate(length);
|
AsBlob.Data = Allocator::Allocate(length);
|
||||||
AsBlob.Length = length;
|
AsBlob.Length = length;
|
||||||
}
|
}
|
||||||
StringUtils::ConvertANSI2UTF16(str.Get(), (Char*)AsBlob.Data, str.Length());
|
int32 tmp;
|
||||||
|
StringUtils::ConvertANSI2UTF16(str.Get(), (Char*)AsBlob.Data, str.Length(), tmp);
|
||||||
((Char*)AsBlob.Data)[str.Length()] = 0;
|
((Char*)AsBlob.Data)[str.Length()] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ inline void DrawText3D(const DebugText3D& t, const RenderContext& renderContext,
|
|||||||
Matrix::Multiply(fw, vp, m);
|
Matrix::Multiply(fw, vp, m);
|
||||||
Render2D::Begin(context, target, depthBuffer, viewport, m);
|
Render2D::Begin(context, target, depthBuffer, viewport, m);
|
||||||
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
||||||
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, Vector2::Zero);
|
Render2D::DrawText(DebugDrawFont->CreateFont((float)t.Size), text, t.Color, Vector2::Zero);
|
||||||
Render2D::End();
|
Render2D::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,7 +777,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
|||||||
context->BindSR(0, renderContext.Buffers->DepthBuffer);
|
context->BindSR(0, renderContext.Buffers->DepthBuffer);
|
||||||
const bool enableDepthWrite = data.EnableDepthTest;
|
const bool enableDepthWrite = data.EnableDepthTest;
|
||||||
|
|
||||||
context->SetRenderTarget(depthBuffer ? depthBuffer : *renderContext.Buffers->DepthBuffer, target);
|
context->SetRenderTarget(depthBuffer ? depthBuffer : (data.EnableDepthTest ? nullptr : renderContext.Buffers->DepthBuffer->View()), target);
|
||||||
|
|
||||||
// Lines
|
// Lines
|
||||||
if (depthTestLines.VertexCount)
|
if (depthTestLines.VertexCount)
|
||||||
@@ -859,12 +859,12 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
|||||||
for (auto& t : Context->DebugDrawDefault.DefaultText2D)
|
for (auto& t : Context->DebugDrawDefault.DefaultText2D)
|
||||||
{
|
{
|
||||||
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
||||||
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
|
Render2D::DrawText(DebugDrawFont->CreateFont((float)t.Size), text, t.Color, t.Position);
|
||||||
}
|
}
|
||||||
for (auto& t : Context->DebugDrawDefault.OneFrameText2D)
|
for (auto& t : Context->DebugDrawDefault.OneFrameText2D)
|
||||||
{
|
{
|
||||||
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
||||||
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
|
Render2D::DrawText(DebugDrawFont->CreateFont((float)t.Size), text, t.Color, t.Position);
|
||||||
}
|
}
|
||||||
Render2D::End();
|
Render2D::End();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ namespace FlaxEngine.Interop
|
|||||||
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
||||||
public static ManagedArray WrapPooledArray(Array arr, Type arrayType)
|
public static ManagedArray WrapPooledArray(Array arr, Type arrayType)
|
||||||
{
|
{
|
||||||
ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * Marshal.SizeOf(arr.GetType().GetElementType()));
|
ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * NativeInterop.GetTypeSize(arr.GetType().GetElementType()));
|
||||||
managedArray.WrapArray(arr, arrayType);
|
managedArray.WrapArray(arr, arrayType);
|
||||||
return managedArray;
|
return managedArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType)
|
internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType)
|
||||||
=> new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, arrayType, elementType);
|
=> new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, NativeInterop.GetTypeSize(elementType)), length, arrayType, elementType);
|
||||||
|
|
||||||
internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType)
|
internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType)
|
||||||
=> new ManagedArray(ptr, length, arrayType, elementType);
|
=> new ManagedArray(ptr, length, arrayType, elementType);
|
||||||
@@ -86,7 +86,7 @@ namespace FlaxEngine.Interop
|
|||||||
_length = arr.Length;
|
_length = arr.Length;
|
||||||
_arrayType = arrayType;
|
_arrayType = arrayType;
|
||||||
_elementType = arr.GetType().GetElementType();
|
_elementType = arr.GetType().GetElementType();
|
||||||
_elementSize = Marshal.SizeOf(_elementType);
|
_elementSize = NativeInterop.GetTypeSize(_elementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Allocate<T>(int length) where T : unmanaged
|
internal void Allocate<T>(int length) where T : unmanaged
|
||||||
@@ -117,7 +117,7 @@ namespace FlaxEngine.Interop
|
|||||||
_length = length;
|
_length = length;
|
||||||
_arrayType = arrayType;
|
_arrayType = arrayType;
|
||||||
_elementType = elementType;
|
_elementType = elementType;
|
||||||
_elementSize = Marshal.SizeOf(elementType);
|
_elementSize = NativeInterop.GetTypeSize(_elementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ManagedArray()
|
~ManagedArray()
|
||||||
|
|||||||
@@ -350,14 +350,14 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
public static class NativeToManaged
|
public static class NativeToManaged
|
||||||
{
|
{
|
||||||
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
|
public static T[] AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
if (unmanaged is null)
|
if (unmanaged is null)
|
||||||
return null;
|
return null;
|
||||||
return new T[numElements];
|
return new T[numElements];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Span<T> GetManagedValuesDestination(T[]? managed) => managed;
|
public static Span<T> GetManagedValuesDestination(T[] managed) => managed;
|
||||||
|
|
||||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
@@ -390,7 +390,7 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
public static class ManagedToNative
|
public static class ManagedToNative
|
||||||
{
|
{
|
||||||
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
|
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||||
{
|
{
|
||||||
if (managed is null)
|
if (managed is null)
|
||||||
{
|
{
|
||||||
@@ -402,7 +402,7 @@ namespace FlaxEngine.Interop
|
|||||||
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
|
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed) => managed;
|
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
|
||||||
|
|
||||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
@@ -431,7 +431,7 @@ namespace FlaxEngine.Interop
|
|||||||
ManagedArray unmanagedArray;
|
ManagedArray unmanagedArray;
|
||||||
ManagedHandle handle;
|
ManagedHandle handle;
|
||||||
|
|
||||||
public void FromManaged(T[]? managed)
|
public void FromManaged(T[] managed)
|
||||||
{
|
{
|
||||||
if (managed == null)
|
if (managed == null)
|
||||||
return;
|
return;
|
||||||
@@ -476,7 +476,7 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
|
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||||
{
|
{
|
||||||
if (managed is null)
|
if (managed is null)
|
||||||
{
|
{
|
||||||
@@ -489,7 +489,7 @@ namespace FlaxEngine.Interop
|
|||||||
return (TUnmanagedElement*)handle;
|
return (TUnmanagedElement*)handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed) => managed;
|
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
|
||||||
|
|
||||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
@@ -499,9 +499,9 @@ namespace FlaxEngine.Interop
|
|||||||
return unmanagedArray.ToSpan<TUnmanagedElement>();
|
return unmanagedArray.ToSpan<TUnmanagedElement>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements];
|
public static T[] AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements];
|
||||||
|
|
||||||
public static Span<T> GetManagedValuesDestination(T[]? managed) => managed;
|
public static Span<T> GetManagedValuesDestination(T[] managed) => managed;
|
||||||
|
|
||||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -885,6 +885,7 @@ namespace FlaxEngine.Interop
|
|||||||
handle.Free();
|
handle.Free();
|
||||||
fieldHandleCacheCollectible.Clear();
|
fieldHandleCacheCollectible.Clear();
|
||||||
#endif
|
#endif
|
||||||
|
_typeSizeCache.Clear();
|
||||||
|
|
||||||
foreach (var pair in classAttributesCacheCollectible)
|
foreach (var pair in classAttributesCacheCollectible)
|
||||||
pair.Value.Free();
|
pair.Value.Free();
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
private static Dictionary<object, ManagedHandle> classAttributesCacheCollectible = new();
|
private static Dictionary<object, ManagedHandle> classAttributesCacheCollectible = new();
|
||||||
private static Dictionary<Assembly, ManagedHandle> assemblyHandles = new();
|
private static Dictionary<Assembly, ManagedHandle> assemblyHandles = new();
|
||||||
|
private static Dictionary<Type, int> _typeSizeCache = new();
|
||||||
|
|
||||||
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
||||||
internal static Dictionary<string, string> nativeLibraryPaths = new();
|
internal static Dictionary<string, string> nativeLibraryPaths = new();
|
||||||
@@ -58,19 +59,8 @@ namespace FlaxEngine.Interop
|
|||||||
if (!loadedNativeLibraries.TryGetValue(libraryName, out IntPtr nativeLibrary))
|
if (!loadedNativeLibraries.TryGetValue(libraryName, out IntPtr nativeLibrary))
|
||||||
{
|
{
|
||||||
if (!nativeLibraryPaths.TryGetValue(libraryName, out var nativeLibraryPath))
|
if (!nativeLibraryPaths.TryGetValue(libraryName, out var nativeLibraryPath))
|
||||||
{
|
|
||||||
nativeLibraryPath = libraryName;
|
nativeLibraryPath = libraryName;
|
||||||
|
|
||||||
// Check if any of the loaded assemblies has matching native module filename
|
|
||||||
foreach (var e in nativeLibraryPaths)
|
|
||||||
{
|
|
||||||
if (string.Equals(Path.GetFileNameWithoutExtension(e.Value), libraryName, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
nativeLibraryPath = e.Value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nativeLibrary = NativeLibrary.Load(nativeLibraryPath, assembly, dllImportSearchPath);
|
nativeLibrary = NativeLibrary.Load(nativeLibraryPath, assembly, dllImportSearchPath);
|
||||||
loadedNativeLibraries.Add(libraryName, nativeLibrary);
|
loadedNativeLibraries.Add(libraryName, nativeLibrary);
|
||||||
assemblyOwnedNativeLibraries.Add(assembly, libraryName);
|
assemblyOwnedNativeLibraries.Add(assembly, libraryName);
|
||||||
@@ -107,14 +97,17 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if FLAX_EDITOR
|
#if FLAX_EDITOR
|
||||||
private static Assembly? OnScriptingAssemblyLoadContextResolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
|
private static Assembly OnScriptingAssemblyLoadContextResolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
|
||||||
{
|
{
|
||||||
// FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored
|
// FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored
|
||||||
string editorTargetPath = Path.GetDirectoryName(nativeLibraryPaths.Keys.First(x => x != "FlaxEngine"));
|
foreach (string nativeLibraryPath in nativeLibraryPaths.Values)
|
||||||
|
{
|
||||||
|
string editorTargetPath = Path.GetDirectoryName(nativeLibraryPath);
|
||||||
|
|
||||||
var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll");
|
var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll");
|
||||||
if (File.Exists(assemblyPath))
|
if (File.Exists(assemblyPath))
|
||||||
return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -592,7 +585,7 @@ namespace FlaxEngine.Interop
|
|||||||
else if (fieldType.IsClass || fieldType.IsPointer)
|
else if (fieldType.IsClass || fieldType.IsPointer)
|
||||||
fieldAlignment = IntPtr.Size;
|
fieldAlignment = IntPtr.Size;
|
||||||
else
|
else
|
||||||
fieldAlignment = Marshal.SizeOf(fieldType);
|
fieldAlignment = GetTypeSize(fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset)
|
internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset)
|
||||||
@@ -1096,6 +1089,26 @@ namespace FlaxEngine.Interop
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static int GetTypeSize(Type type)
|
||||||
|
{
|
||||||
|
if (!_typeSizeCache.TryGetValue(type, out var size))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
size = Marshal.SizeOf(type);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Workaround the issue where structure defined within generic type instance (eg. MyType<int>.MyStruct) fails to get size
|
||||||
|
// https://github.com/dotnet/runtime/issues/46426
|
||||||
|
var obj = Activator.CreateInstance(type);
|
||||||
|
size = Marshal.SizeOf(obj);
|
||||||
|
}
|
||||||
|
_typeSizeCache.Add(type, size);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
private static class DelegateHelpers
|
private static class DelegateHelpers
|
||||||
{
|
{
|
||||||
#if USE_AOT
|
#if USE_AOT
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ bool GPUShader::Create(MemoryReadStream& stream)
|
|||||||
|
|
||||||
// Create CB
|
// Create CB
|
||||||
#if GPU_ENABLE_RESOURCE_NAMING
|
#if GPU_ENABLE_RESOURCE_NAMING
|
||||||
String name = ToString() + TEXT(".CB") + i;
|
String name = String::Format(TEXT("{}.CB{}"), ToString(), i);
|
||||||
#else
|
#else
|
||||||
String name;
|
String name;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ void AnimatedModel::SetCurrentPose(const Array<Matrix>& nodesTransformation, boo
|
|||||||
Matrix invWorld;
|
Matrix invWorld;
|
||||||
Matrix::Invert(world, invWorld);
|
Matrix::Invert(world, invWorld);
|
||||||
for (auto& m : GraphInstance.NodesPose)
|
for (auto& m : GraphInstance.NodesPose)
|
||||||
m = invWorld * m;
|
m = m * invWorld;
|
||||||
}
|
}
|
||||||
OnAnimationUpdated();
|
OnAnimationUpdated();
|
||||||
}
|
}
|
||||||
@@ -774,7 +774,13 @@ void AnimatedModel::OnAnimationUpdated_Sync()
|
|||||||
// Update synchronous stuff
|
// Update synchronous stuff
|
||||||
UpdateSockets();
|
UpdateSockets();
|
||||||
ApplyRootMotion(GraphInstance.RootMotion);
|
ApplyRootMotion(GraphInstance.RootMotion);
|
||||||
|
if (!_isDuringUpdateEvent)
|
||||||
|
{
|
||||||
|
// Prevent stack-overflow when gameplay modifies the pose within the event
|
||||||
|
_isDuringUpdateEvent = true;
|
||||||
AnimationUpdated();
|
AnimationUpdated();
|
||||||
|
_isDuringUpdateEvent = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedModel::OnAnimationUpdated()
|
void AnimatedModel::OnAnimationUpdated()
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ private:
|
|||||||
AnimationUpdateMode _actualMode;
|
AnimationUpdateMode _actualMode;
|
||||||
uint32 _counter;
|
uint32 _counter;
|
||||||
Real _lastMinDstSqr;
|
Real _lastMinDstSqr;
|
||||||
|
bool _isDuringUpdateEvent = false;
|
||||||
uint64 _lastUpdateFrame;
|
uint64 _lastUpdateFrame;
|
||||||
mutable MeshDeformation* _deformation = nullptr;
|
mutable MeshDeformation* _deformation = nullptr;
|
||||||
ScriptingObjectReference<AnimatedModel> _masterPose;
|
ScriptingObjectReference<AnimatedModel> _masterPose;
|
||||||
|
|||||||
@@ -740,16 +740,20 @@ Actor* FindActorRecursive(Actor* node, const Tag& tag)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindActorsRecursive(Actor* node, const Tag& tag, Array<Actor*>& result)
|
void FindActorsRecursive(Actor* node, const Tag& tag, const bool activeOnly, Array<Actor*>& result)
|
||||||
{
|
{
|
||||||
|
if (activeOnly && !node->GetIsActive())
|
||||||
|
return;
|
||||||
if (node->HasTag(tag))
|
if (node->HasTag(tag))
|
||||||
result.Add(node);
|
result.Add(node);
|
||||||
for (Actor* child : node->Children)
|
for (Actor* child : node->Children)
|
||||||
FindActorsRecursive(child, tag, result);
|
FindActorsRecursive(child, tag, activeOnly, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindActorsRecursiveByParentTags(Actor* node, const Array<Tag>& tags, Array<Actor*>& result)
|
void FindActorsRecursiveByParentTags(Actor* node, const Array<Tag>& tags, const bool activeOnly, Array<Actor*>& result)
|
||||||
{
|
{
|
||||||
|
if (activeOnly && !node->GetIsActive())
|
||||||
|
return;
|
||||||
for (Tag tag : tags)
|
for (Tag tag : tags)
|
||||||
{
|
{
|
||||||
if (node->HasTag(tag))
|
if (node->HasTag(tag))
|
||||||
@@ -759,7 +763,7 @@ void FindActorsRecursiveByParentTags(Actor* node, const Array<Tag>& tags, Array<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Actor* child : node->Children)
|
for (Actor* child : node->Children)
|
||||||
FindActorsRecursiveByParentTags(child, tags, result);
|
FindActorsRecursiveByParentTags(child, tags, activeOnly, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Actor* Level::FindActor(const Tag& tag, Actor* root)
|
Actor* Level::FindActor(const Tag& tag, Actor* root)
|
||||||
@@ -785,24 +789,24 @@ void FindActorRecursive(Actor* node, const Tag& tag, Array<Actor*>& result)
|
|||||||
FindActorRecursive(child, tag, result);
|
FindActorRecursive(child, tag, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Array<Actor*> Level::FindActors(const Tag& tag, Actor* root)
|
Array<Actor*> Level::FindActors(const Tag& tag, const bool activeOnly, Actor* root)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
Array<Actor*> result;
|
Array<Actor*> result;
|
||||||
if (root)
|
if (root)
|
||||||
{
|
{
|
||||||
FindActorsRecursive(root, tag, result);
|
FindActorsRecursive(root, tag, activeOnly, result);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ScopeLock lock(ScenesLock);
|
ScopeLock lock(ScenesLock);
|
||||||
for (Scene* scene : Scenes)
|
for (Scene* scene : Scenes)
|
||||||
FindActorsRecursive(scene, tag, result);
|
FindActorsRecursive(scene, tag, activeOnly, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Array<Actor*> Level::FindActorsByParentTag(const Tag& parentTag, Actor* root)
|
Array<Actor*> Level::FindActorsByParentTag(const Tag& parentTag, const bool activeOnly, Actor* root)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
Array<Actor*> result;
|
Array<Actor*> result;
|
||||||
@@ -814,19 +818,19 @@ Array<Actor*> Level::FindActorsByParentTag(const Tag& parentTag, Actor* root)
|
|||||||
}
|
}
|
||||||
if (subTags.Count() == 1)
|
if (subTags.Count() == 1)
|
||||||
{
|
{
|
||||||
result = FindActors(subTags[0], root);
|
result = FindActors(subTags[0], activeOnly, root);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root)
|
if (root)
|
||||||
{
|
{
|
||||||
FindActorsRecursiveByParentTags(root, subTags, result);
|
FindActorsRecursiveByParentTags(root, subTags, activeOnly, result);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ScopeLock lock(ScenesLock);
|
ScopeLock lock(ScenesLock);
|
||||||
for (Scene* scene : Scenes)
|
for (Scene* scene : Scenes)
|
||||||
FindActorsRecursiveByParentTags(scene, subTags, result);
|
FindActorsRecursiveByParentTags(scene, subTags, activeOnly, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -494,17 +494,19 @@ public:
|
|||||||
/// Tries to find the actors with the given tag (returns all found).
|
/// Tries to find the actors with the given tag (returns all found).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tag">The tag of the actor to search for.</param>
|
/// <param name="tag">The tag of the actor to search for.</param>
|
||||||
|
/// <param name="activeOnly">Find only active actors.</param>
|
||||||
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
/// <returns>Found actors or empty if none.</returns>
|
/// <returns>Found actors or empty if none.</returns>
|
||||||
API_FUNCTION() static Array<Actor*> FindActors(const Tag& tag, Actor* root = nullptr);
|
API_FUNCTION() static Array<Actor*> FindActors(const Tag& tag, const bool activeOnly = false, Actor* root = nullptr);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Search actors using a parent parentTag.
|
/// Search actors using a parent parentTag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parentTag">The tag to search actors with subtags belonging to this tag</param>
|
/// <param name="parentTag">The tag to search actors with subtags belonging to this tag</param>
|
||||||
|
/// <param name="activeOnly">Find only active actors.</param>
|
||||||
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
/// <returns>Returns all actors that have subtags belonging to the given parent parentTag</returns>
|
/// <returns>Returns all actors that have subtags belonging to the given parent parentTag</returns>
|
||||||
API_FUNCTION() static Array<Actor*> FindActorsByParentTag(const Tag& parentTag, Actor* root = nullptr);
|
API_FUNCTION() static Array<Actor*> FindActorsByParentTag(const Tag& parentTag, const bool activeOnly = false, Actor* root = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Actor API
|
// Actor API
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ namespace FlaxEngine
|
|||||||
if (_keyframes == null || _keyframes.Length != count)
|
if (_keyframes == null || _keyframes.Length != count)
|
||||||
_keyframes = new BezierCurve<Transform>.Keyframe[count];
|
_keyframes = new BezierCurve<Transform>.Keyframe[count];
|
||||||
#if !BUILD_RELEASE
|
#if !BUILD_RELEASE
|
||||||
if (Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) != Transform.SizeInBytes * 3 + sizeof(float))
|
if (System.Runtime.CompilerServices.Unsafe.SizeOf<BezierCurve<Transform>.Keyframe>() != Transform.SizeInBytes * 3 + sizeof(float))
|
||||||
throw new Exception("Invalid size of BezierCurve keyframe " + Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) + " bytes.");
|
throw new Exception("Invalid size of BezierCurve keyframe " + System.Runtime.CompilerServices.Unsafe.SizeOf<BezierCurve<Transform>.Keyframe>() + " bytes.");
|
||||||
#endif
|
#endif
|
||||||
Internal_GetKeyframes(__unmanagedPtr, _keyframes);
|
Internal_GetKeyframes(__unmanagedPtr, _keyframes);
|
||||||
return _keyframes;
|
return _keyframes;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ void NetworkReplicationHierarchyUpdateResult::Init()
|
|||||||
{
|
{
|
||||||
_clientsHaveLocation = false;
|
_clientsHaveLocation = false;
|
||||||
_clients.Resize(NetworkManager::Clients.Count());
|
_clients.Resize(NetworkManager::Clients.Count());
|
||||||
_clientsMask = NetworkClientsMask();
|
_clientsMask = NetworkManager::Mode == NetworkManagerMode::Client ? NetworkClientsMask::All : NetworkClientsMask();
|
||||||
for (int32 i = 0; i < _clients.Count(); i++)
|
for (int32 i = 0; i < _clients.Count(); i++)
|
||||||
_clientsMask.SetBit(i);
|
_clientsMask.SetBit(i);
|
||||||
_entries.Clear();
|
_entries.Clear();
|
||||||
@@ -49,7 +49,7 @@ bool NetworkReplicationHierarchyUpdateResult::GetClientLocation(int32 clientInde
|
|||||||
|
|
||||||
void NetworkReplicationNode::AddObject(NetworkReplicationHierarchyObject obj)
|
void NetworkReplicationNode::AddObject(NetworkReplicationHierarchyObject obj)
|
||||||
{
|
{
|
||||||
if (obj.ReplicationFPS > 0.0f)
|
if (obj.ReplicationFPS > ZeroTolerance) // > 0
|
||||||
{
|
{
|
||||||
// Randomize initial replication update to spread rep rates more evenly for large scenes that register all objects within the same frame
|
// Randomize initial replication update to spread rep rates more evenly for large scenes that register all objects within the same frame
|
||||||
obj.ReplicationUpdatesLeft = NetworkReplicationNodeObjectCounter++ % Math::Clamp(Math::RoundToInt(NetworkManager::NetworkFPS / obj.ReplicationFPS), 1, 60);
|
obj.ReplicationUpdatesLeft = NetworkReplicationNodeObjectCounter++ % Math::Clamp(Math::RoundToInt(NetworkManager::NetworkFPS / obj.ReplicationFPS), 1, 60);
|
||||||
@@ -63,6 +63,17 @@ bool NetworkReplicationNode::RemoveObject(ScriptingObject* obj)
|
|||||||
return !Objects.Remove(obj);
|
return !Objects.Remove(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NetworkReplicationNode::GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result)
|
||||||
|
{
|
||||||
|
const int32 index = Objects.Find(obj);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
result = Objects[index];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool NetworkReplicationNode::DirtyObject(ScriptingObject* obj)
|
bool NetworkReplicationNode::DirtyObject(ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
const int32 index = Objects.Find(obj);
|
const int32 index = Objects.Find(obj);
|
||||||
@@ -80,7 +91,11 @@ void NetworkReplicationNode::Update(NetworkReplicationHierarchyUpdateResult* res
|
|||||||
const float networkFPS = NetworkManager::NetworkFPS / result->ReplicationScale;
|
const float networkFPS = NetworkManager::NetworkFPS / result->ReplicationScale;
|
||||||
for (NetworkReplicationHierarchyObject& obj : Objects)
|
for (NetworkReplicationHierarchyObject& obj : Objects)
|
||||||
{
|
{
|
||||||
if (obj.ReplicationFPS <= 0.0f)
|
if (obj.ReplicationFPS < -ZeroTolerance) // < 0
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (obj.ReplicationFPS < ZeroTolerance) // == 0
|
||||||
{
|
{
|
||||||
// Always relevant
|
// Always relevant
|
||||||
result->AddObject(obj.Object);
|
result->AddObject(obj.Object);
|
||||||
@@ -152,6 +167,7 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
|
|||||||
cell->MinCullDistance = obj.CullDistance;
|
cell->MinCullDistance = obj.CullDistance;
|
||||||
}
|
}
|
||||||
cell->Node->AddObject(obj);
|
cell->Node->AddObject(obj);
|
||||||
|
_objectToCell[obj.Object] = coord;
|
||||||
|
|
||||||
// Cache minimum culling distance for a whole cell to skip it at once
|
// Cache minimum culling distance for a whole cell to skip it at once
|
||||||
cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance);
|
cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance);
|
||||||
@@ -159,14 +175,35 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
|
|||||||
|
|
||||||
bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj)
|
bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
for (const auto& e : _children)
|
Int3 coord;
|
||||||
|
|
||||||
|
if (!_objectToCell.TryGet(obj, coord))
|
||||||
{
|
{
|
||||||
if (e.Value.Node->RemoveObject(obj))
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_children[coord].Node->RemoveObject(obj))
|
||||||
{
|
{
|
||||||
|
_objectToCell.Remove(obj);
|
||||||
// TODO: remove empty cells?
|
// TODO: remove empty cells?
|
||||||
// TODO: update MinCullDistance for cell?
|
// TODO: update MinCullDistance for cell?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkReplicationGridNode::GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result)
|
||||||
|
{
|
||||||
|
Int3 coord;
|
||||||
|
|
||||||
|
if (!_objectToCell.TryGet(obj, coord))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_children[coord].Node->GetObject(obj, result))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API
|
|||||||
|
|
||||||
// The object to replicate.
|
// The object to replicate.
|
||||||
API_FIELD() ScriptingObjectReference<ScriptingObject> Object;
|
API_FIELD() ScriptingObjectReference<ScriptingObject> Object;
|
||||||
// The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. Use 0 for 'always relevant' object.
|
// The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. Use 0 for 'always relevant' object and less than 0 (eg. -1) for 'never relevant' objects that would only get synched on client join once.
|
||||||
API_FIELD() float ReplicationFPS = 60;
|
API_FIELD() float ReplicationFPS = 60;
|
||||||
// The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data. Use 0 if unused.
|
// The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data. Use 0 if unused.
|
||||||
API_FIELD() float CullDistance = 15000;
|
API_FIELD() float CullDistance = 15000;
|
||||||
@@ -200,6 +200,14 @@ API_CLASS(Abstract, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API Ne
|
|||||||
/// <returns>True on successful removal, otherwise false.</returns>
|
/// <returns>True on successful removal, otherwise false.</returns>
|
||||||
API_FUNCTION() virtual bool RemoveObject(ScriptingObject* obj);
|
API_FUNCTION() virtual bool RemoveObject(ScriptingObject* obj);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets object from the hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to get.</param>
|
||||||
|
/// <param name="result">The hierarchy object to retrieve.</param>
|
||||||
|
/// <returns>True on successful retrieval, otherwise false.</returns>
|
||||||
|
API_FUNCTION() virtual bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization.
|
/// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -238,6 +246,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Dictionary<Int3, Cell> _children;
|
Dictionary<Int3, Cell> _children;
|
||||||
|
Dictionary<ScriptingObject*, Int3> _objectToCell;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -247,6 +256,7 @@ public:
|
|||||||
|
|
||||||
void AddObject(NetworkReplicationHierarchyObject obj) override;
|
void AddObject(NetworkReplicationHierarchyObject obj) override;
|
||||||
bool RemoveObject(ScriptingObject* obj) override;
|
bool RemoveObject(ScriptingObject* obj) override;
|
||||||
|
bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result) override;
|
||||||
void Update(NetworkReplicationHierarchyUpdateResult* result) override;
|
void Update(NetworkReplicationHierarchyUpdateResult* result) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -700,9 +700,9 @@ void NetworkReplicator::AddRPC(const ScriptingTypeHandle& typeHandle, const Stri
|
|||||||
NetworkRpcInfo::RPCsTable[rpcName] = rpcInfo;
|
NetworkRpcInfo::RPCsTable[rpcName] = rpcInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds)
|
bool NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds)
|
||||||
{
|
{
|
||||||
EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan<uint32>(targetIds));
|
return EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan<uint32>(targetIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name)
|
StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name)
|
||||||
@@ -1113,12 +1113,12 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
|
|||||||
return CachedWriteStream;
|
return CachedWriteStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
|
bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
|
||||||
{
|
{
|
||||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||||
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
||||||
if (!info || !obj || NetworkManager::IsOffline())
|
if (!info || !obj || NetworkManager::IsOffline())
|
||||||
return;
|
return false;
|
||||||
ObjectsLock.Lock();
|
ObjectsLock.Lock();
|
||||||
auto& rpc = RpcQueue.AddOne();
|
auto& rpc = RpcQueue.AddOne();
|
||||||
rpc.Object = obj;
|
rpc.Object = obj;
|
||||||
@@ -1135,12 +1135,23 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
ObjectsLock.Unlock();
|
ObjectsLock.Unlock();
|
||||||
|
|
||||||
|
// Check if skip local execution (eg. server rpc called from client or client rpc with specific targets)
|
||||||
|
const NetworkManagerMode networkMode = NetworkManager::Mode;
|
||||||
|
if (info->Server && networkMode == NetworkManagerMode::Client)
|
||||||
|
return true;
|
||||||
|
if (info->Client && networkMode == NetworkManagerMode::Server)
|
||||||
|
return true;
|
||||||
|
if (info->Client && networkMode == NetworkManagerMode::Host && targetIds.IsValid() && !SpanContains(targetIds, NetworkManager::LocalClientId))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
||||||
{
|
{
|
||||||
ScopeLock lock(ObjectsLock);
|
ScopeLock lock(ObjectsLock);
|
||||||
NewClients.Add(client);
|
NewClients.Add(client);
|
||||||
|
ASSERT(sizeof(NetworkClientsMask) * 8 >= (uint32)NetworkManager::Clients.Count()); // Ensure that clients mask can hold all of clients
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkInternal::NetworkReplicatorClientDisconnected(NetworkClient* client)
|
void NetworkInternal::NetworkReplicatorClientDisconnected(NetworkClient* client)
|
||||||
@@ -1193,6 +1204,7 @@ void NetworkInternal::NetworkReplicatorClear()
|
|||||||
Objects.Remove(it);
|
Objects.Remove(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Objects.Clear();
|
||||||
RpcQueue.Clear();
|
RpcQueue.Clear();
|
||||||
SpawnQueue.Clear();
|
SpawnQueue.Clear();
|
||||||
DespawnQueue.Clear();
|
DespawnQueue.Clear();
|
||||||
|
|||||||
@@ -120,10 +120,11 @@ namespace FlaxEngine.Networking
|
|||||||
/// <param name="name">The RPC name.</param>
|
/// <param name="name">The RPC name.</param>
|
||||||
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
||||||
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
||||||
|
/// <returns>True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).</returns>
|
||||||
[Unmanaged]
|
[Unmanaged]
|
||||||
public static void EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null)
|
public static bool EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null)
|
||||||
{
|
{
|
||||||
Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds);
|
return Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -195,13 +195,14 @@ public:
|
|||||||
/// <param name="name">The RPC name.</param>
|
/// <param name="name">The RPC name.</param>
|
||||||
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
||||||
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
||||||
static void EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds = Span<uint32>());
|
/// <returns>True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).</returns>
|
||||||
|
static bool EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds = Span<uint32>());
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
|
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
|
||||||
API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function<void(void*, void*)>& execute, bool isServer, bool isClient, NetworkChannelType channel);
|
API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function<void(void*, void*)>& execute, bool isServer, bool isClient, NetworkChannelType channel);
|
||||||
API_FUNCTION(NoProxy) static void CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds);
|
API_FUNCTION(NoProxy) static bool CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds);
|
||||||
static StringAnsiView GetCSharpCachedName(const StringAnsiView& name);
|
static StringAnsiView GetCSharpCachedName(const StringAnsiView& name);
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct FLAXENGINE_API NetworkRpcInfo
|
|||||||
uint8 Client : 1;
|
uint8 Client : 1;
|
||||||
uint8 Channel : 4;
|
uint8 Channel : 4;
|
||||||
void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag);
|
void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag);
|
||||||
void (*Invoke)(ScriptingObject* obj, void** args);
|
bool (*Invoke)(ScriptingObject* obj, void** args);
|
||||||
void* Tag;
|
void* Tag;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -83,10 +83,7 @@ FORCE_INLINE void NetworkRpcInitArg(Array<void*, FixedAllocation<16>>& args, con
|
|||||||
{ \
|
{ \
|
||||||
Array<void*, FixedAllocation<16>> args; \
|
Array<void*, FixedAllocation<16>> args; \
|
||||||
NetworkRpcInitArg(args, __VA_ARGS__); \
|
NetworkRpcInitArg(args, __VA_ARGS__); \
|
||||||
rpcInfo.Invoke(this, args.Get()); \
|
if (rpcInfo.Invoke(this, args.Get())) \
|
||||||
if (rpcInfo.Server && networkMode == NetworkManagerMode::Client) \
|
|
||||||
return; \
|
|
||||||
if (rpcInfo.Client && networkMode == NetworkManagerMode::Server) \
|
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,17 +299,19 @@ void ParticleEffect::Stop()
|
|||||||
void ParticleEffect::UpdateBounds()
|
void ParticleEffect::UpdateBounds()
|
||||||
{
|
{
|
||||||
BoundingBox bounds = BoundingBox::Empty;
|
BoundingBox bounds = BoundingBox::Empty;
|
||||||
if (ParticleSystem && Instance.LastUpdateTime >= 0)
|
const auto particleSystem = ParticleSystem.Get();
|
||||||
|
if (particleSystem && Instance.LastUpdateTime >= 0)
|
||||||
{
|
{
|
||||||
for (int32 j = 0; j < ParticleSystem->Tracks.Count(); j++)
|
for (int32 j = 0; j < particleSystem->Tracks.Count(); j++)
|
||||||
{
|
{
|
||||||
const auto& track = ParticleSystem->Tracks[j];
|
const auto& track = particleSystem->Tracks[j];
|
||||||
if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled)
|
if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled)
|
||||||
continue;
|
continue;
|
||||||
auto& emitter = ParticleSystem->Emitters[track.AsEmitter.Index];
|
const int32 emitterIndex = track.AsEmitter.Index;
|
||||||
auto& data = Instance.Emitters[track.AsEmitter.Index];
|
ParticleEmitter* emitter = particleSystem->Emitters[emitterIndex].Get();
|
||||||
if (!emitter || emitter->Capacity == 0 || emitter->Graph.Layout.Size == 0)
|
if (!emitter || emitter->Capacity == 0 || emitter->Graph.Layout.Size == 0 || Instance.Emitters.Count() <= emitterIndex)
|
||||||
continue;
|
continue;
|
||||||
|
auto& data = Instance.Emitters[emitterIndex];
|
||||||
|
|
||||||
BoundingBox emitterBounds;
|
BoundingBox emitterBounds;
|
||||||
if (emitter->GraphExecutorCPU.ComputeBounds(emitter, this, data, emitterBounds))
|
if (emitter->GraphExecutorCPU.ComputeBounds(emitter, this, data, emitterBounds))
|
||||||
|
|||||||
@@ -230,10 +230,11 @@ void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
|
|||||||
{
|
{
|
||||||
const Vector3 currentPos = wheel.Collider->GetPosition();
|
const Vector3 currentPos = wheel.Collider->GetPosition();
|
||||||
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
||||||
|
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, true);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, true);
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, true);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, true);
|
||||||
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, true);
|
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, true);
|
||||||
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.8f, 0, true);
|
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheelDebugOrientation, wheel.Radius, wheel.Width, Color::Red * 0.8f, 0, true);
|
||||||
if (!data.State.IsInAir)
|
if (!data.State.IsInAir)
|
||||||
{
|
{
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.TireContactPoint, 5.0f), Color::Green, 0, true);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.TireContactPoint, 5.0f), Color::Green, 0, true);
|
||||||
@@ -260,6 +261,7 @@ void WheeledVehicle::OnDebugDrawSelected()
|
|||||||
{
|
{
|
||||||
const Vector3 currentPos = wheel.Collider->GetPosition();
|
const Vector3 currentPos = wheel.Collider->GetPosition();
|
||||||
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
||||||
|
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
|
||||||
Transform actorPose = Transform::Identity, shapePose = Transform::Identity;
|
Transform actorPose = Transform::Identity, shapePose = Transform::Identity;
|
||||||
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
|
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
|
||||||
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
|
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
|
||||||
@@ -267,7 +269,7 @@ void WheeledVehicle::OnDebugDrawSelected()
|
|||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(actorPose.LocalToWorld(shapePose.Translation), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(actorPose.LocalToWorld(shapePose.Translation), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
|
||||||
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, false);
|
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, false);
|
||||||
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.4f, 0, false);
|
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheelDebugOrientation, wheel.Radius, wheel.Width, Color::Red * 0.4f, 0, false);
|
||||||
if (!data.State.SuspensionTraceStart.IsZero())
|
if (!data.State.SuspensionTraceStart.IsZero())
|
||||||
{
|
{
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.SuspensionTraceStart, 5.0f), Color::AliceBlue, 0, false);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.SuspensionTraceStart, 5.0f), Color::AliceBlue, 0, false);
|
||||||
|
|||||||
@@ -279,6 +279,60 @@ class CharacterControllerFilterPhysX : public PxControllerFilterCallback
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CharacterControllerHitReportPhysX : public PxUserControllerHitReport
|
||||||
|
{
|
||||||
|
void onHit(const PxControllerHit& hit, Collision& c)
|
||||||
|
{
|
||||||
|
ASSERT_LOW_LAYER(c.ThisActor && c.OtherActor);
|
||||||
|
c.Impulse = Vector3::Zero;
|
||||||
|
c.ThisVelocity = P2C(hit.dir) * hit.length;
|
||||||
|
c.OtherVelocity = Vector3::Zero;
|
||||||
|
c.ContactsCount = 1;
|
||||||
|
ContactPoint& contact = c.Contacts[0];
|
||||||
|
contact.Point = P2C(hit.worldPos);
|
||||||
|
contact.Normal = P2C(hit.worldNormal);
|
||||||
|
contact.Separation = 0.0f;
|
||||||
|
|
||||||
|
//auto simulationEventCallback = static_cast<SimulationEventCallback*>(hit.controller->getScene()->getSimulationEventCallback());
|
||||||
|
//simulationEventCallback->Collisions[SimulationEventCallback::CollidersPair(c.ThisActor, c.OtherActor)] = c;
|
||||||
|
// TODO: build additional list for hit-only events to properly send enter/exit pairs instead of spamming every frame whether controller executes move
|
||||||
|
|
||||||
|
// Single-hit collision
|
||||||
|
c.ThisActor->OnCollisionEnter(c);
|
||||||
|
c.SwapObjects();
|
||||||
|
c.ThisActor->OnCollisionEnter(c);
|
||||||
|
c.SwapObjects();
|
||||||
|
c.ThisActor->OnCollisionExit(c);
|
||||||
|
c.SwapObjects();
|
||||||
|
c.ThisActor->OnCollisionExit(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onShapeHit(const PxControllerShapeHit& hit) override
|
||||||
|
{
|
||||||
|
Collision c;
|
||||||
|
PxShape* controllerShape;
|
||||||
|
hit.controller->getActor()->getShapes(&controllerShape, 1);
|
||||||
|
c.ThisActor = static_cast<PhysicsColliderActor*>(controllerShape->userData);
|
||||||
|
c.OtherActor = static_cast<PhysicsColliderActor*>(hit.shape->userData);
|
||||||
|
onHit(hit, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onControllerHit(const PxControllersHit& hit) override
|
||||||
|
{
|
||||||
|
Collision c;
|
||||||
|
PxShape* controllerShape;
|
||||||
|
hit.controller->getActor()->getShapes(&controllerShape, 1);
|
||||||
|
c.ThisActor = static_cast<PhysicsColliderActor*>(controllerShape->userData);
|
||||||
|
hit.other->getActor()->getShapes(&controllerShape, 1);
|
||||||
|
c.OtherActor = static_cast<PhysicsColliderActor*>(controllerShape->userData);
|
||||||
|
onHit(hit, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onObstacleHit(const PxControllerObstacleHit& hit) override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#if WITH_VEHICLE
|
#if WITH_VEHICLE
|
||||||
|
|
||||||
class WheelFilterPhysX : public PxQueryFilterCallback
|
class WheelFilterPhysX : public PxQueryFilterCallback
|
||||||
@@ -463,6 +517,7 @@ namespace
|
|||||||
QueryFilterPhysX QueryFilter;
|
QueryFilterPhysX QueryFilter;
|
||||||
CharacterQueryFilterPhysX CharacterQueryFilter;
|
CharacterQueryFilterPhysX CharacterQueryFilter;
|
||||||
CharacterControllerFilterPhysX CharacterControllerFilter;
|
CharacterControllerFilterPhysX CharacterControllerFilter;
|
||||||
|
CharacterControllerHitReportPhysX CharacterControllerHitReport;
|
||||||
Dictionary<PxScene*, Vector3, InlinedAllocation<32>> SceneOrigins;
|
Dictionary<PxScene*, Vector3, InlinedAllocation<32>> SceneOrigins;
|
||||||
|
|
||||||
CriticalSection FlushLocker;
|
CriticalSection FlushLocker;
|
||||||
@@ -2811,6 +2866,7 @@ void* PhysicsBackend::CreateController(void* scene, IPhysicsActor* actor, Physic
|
|||||||
const Vector3 sceneOrigin = SceneOrigins[scenePhysX->Scene];
|
const Vector3 sceneOrigin = SceneOrigins[scenePhysX->Scene];
|
||||||
PxCapsuleControllerDesc desc;
|
PxCapsuleControllerDesc desc;
|
||||||
desc.userData = actor;
|
desc.userData = actor;
|
||||||
|
desc.reportCallback = &CharacterControllerHitReport;
|
||||||
desc.contactOffset = Math::Max(contactOffset, ZeroTolerance);
|
desc.contactOffset = Math::Max(contactOffset, ZeroTolerance);
|
||||||
desc.position = PxExtendedVec3(position.X - sceneOrigin.X, position.Y - sceneOrigin.Y, position.Z - sceneOrigin.Z);
|
desc.position = PxExtendedVec3(position.X - sceneOrigin.X, position.Y - sceneOrigin.Y, position.Z - sceneOrigin.Z);
|
||||||
desc.slopeLimit = Math::Cos(slopeLimit * DegreesToRadians);
|
desc.slopeLimit = Math::Cos(slopeLimit * DegreesToRadians);
|
||||||
|
|||||||
@@ -309,6 +309,45 @@ bool FileBase::WriteAllText(const StringView& path, const Char* data, int32 leng
|
|||||||
|
|
||||||
return WriteAllBytes(path, tmp.Get(), tmp.Count());
|
return WriteAllBytes(path, tmp.Get(), tmp.Count());
|
||||||
}
|
}
|
||||||
|
case Encoding::UTF8:
|
||||||
|
{
|
||||||
|
Array<byte> tmp;
|
||||||
|
tmp.SetCapacity(length);
|
||||||
|
|
||||||
|
for (int32 i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
Char c = data[i];
|
||||||
|
if (c < 0x0080)
|
||||||
|
{
|
||||||
|
tmp.Add((byte)c);
|
||||||
|
}
|
||||||
|
else if (c < 0x0800)
|
||||||
|
{
|
||||||
|
tmp.Add(0xC0 | (c >> 6));
|
||||||
|
tmp.Add(0x80 | (c & 0x3F));
|
||||||
|
}
|
||||||
|
else if (c < 0xD800 || c >= 0xE000)
|
||||||
|
{
|
||||||
|
tmp.Add(0xE0 | (c >> 12));
|
||||||
|
tmp.Add(0x80 | ((c >> 6) & 0x3F));
|
||||||
|
tmp.Add(0x80 | (c & 0x3F));
|
||||||
|
}
|
||||||
|
else // Surrogate pair
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i >= length)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32 p = 0x10000 + (((c & 0x3FF) << 10) | (data[i] & 0x3FF));
|
||||||
|
tmp.Add(0xF0 | (p >> 18));
|
||||||
|
tmp.Add(0x80 | ((p >> 12) & 0x3F));
|
||||||
|
tmp.Add(0x80 | ((p >> 6) & 0x3F));
|
||||||
|
tmp.Add(0x80 | (p & 0x3F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteAllBytes(path, tmp.Get(), tmp.Count());
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Converts characters from ANSI to UTF-16
|
// Converts characters from ANSI to UTF-16
|
||||||
static void ConvertANSI2UTF16(const char* from, Char* to, int32 len);
|
static void ConvertANSI2UTF16(const char* from, Char* to, int32 fromLength, int32& toLength);
|
||||||
|
|
||||||
// Converts characters from UTF-16 to ANSI
|
// Converts characters from UTF-16 to ANSI
|
||||||
static void ConvertUTF162ANSI(const Char* from, char* to, int32 len);
|
static void ConvertUTF162ANSI(const Char* from, char* to, int32 len);
|
||||||
|
|||||||
@@ -311,14 +311,14 @@ static inline uint32 Utf8ToUtf32Codepoint(const char* src, int32 length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringUtils::ConvertANSI2UTF16(const char* from, Char* to, int32 len)
|
void StringUtils::ConvertANSI2UTF16(const char* from, Char* to, int32 fromLength, int32& toLength)
|
||||||
{
|
{
|
||||||
const char* const u8end = from + len;
|
const char* const u8end = from + fromLength;
|
||||||
const char* u8cur = from;
|
const char* u8cur = from;
|
||||||
char16_t* u16cur = to;
|
char16_t* u16cur = to;
|
||||||
while (u8cur < u8end)
|
while (u8cur < u8end)
|
||||||
{
|
{
|
||||||
len = Utf8CodepointLength(*u8cur);
|
int32 len = Utf8CodepointLength(*u8cur);
|
||||||
uint32 codepoint = Utf8ToUtf32Codepoint(u8cur, len);
|
uint32 codepoint = Utf8ToUtf32Codepoint(u8cur, len);
|
||||||
|
|
||||||
// Convert the UTF32 codepoint to one or more UTF16 codepoints
|
// Convert the UTF32 codepoint to one or more UTF16 codepoints
|
||||||
@@ -336,6 +336,7 @@ void StringUtils::ConvertANSI2UTF16(const char* from, Char* to, int32 len)
|
|||||||
}
|
}
|
||||||
u8cur += len;
|
u8cur += len;
|
||||||
}
|
}
|
||||||
|
toLength = (int32)(u16cur - to);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char32_t kByteMask = 0x000000BF;
|
static const char32_t kByteMask = 0x000000BF;
|
||||||
|
|||||||
@@ -179,10 +179,12 @@ const char* StringUtils::Find(const char* str, const char* toFind)
|
|||||||
return strstr(str, toFind);
|
return strstr(str, toFind);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringUtils::ConvertANSI2UTF16(const char* from, Char* to, int32 len)
|
void StringUtils::ConvertANSI2UTF16(const char* from, Char* to, int32 fromLength, int32& toLength)
|
||||||
{
|
{
|
||||||
if (len)
|
if (fromLength)
|
||||||
mbstowcs(to, from, len);
|
toLength = mbstowcs(to, from, fromLength);
|
||||||
|
else
|
||||||
|
toLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringUtils::ConvertUTF162ANSI(const Char* from, char* to, int32 len)
|
void StringUtils::ConvertUTF162ANSI(const Char* from, char* to, int32 len)
|
||||||
|
|||||||
@@ -992,7 +992,8 @@ void ReadPipe(HANDLE pipe, Array<char>& rawData, Array<Char>& logData, LogType l
|
|||||||
// Log contents
|
// Log contents
|
||||||
logData.Clear();
|
logData.Clear();
|
||||||
logData.Resize(rawData.Count() + 1);
|
logData.Resize(rawData.Count() + 1);
|
||||||
StringUtils::ConvertANSI2UTF16(rawData.Get(), logData.Get(), rawData.Count());
|
int32 tmp;
|
||||||
|
StringUtils::ConvertANSI2UTF16(rawData.Get(), logData.Get(), rawData.Count(), tmp);
|
||||||
logData.Last() = '\0';
|
logData.Last() = '\0';
|
||||||
if (settings.LogOutput)
|
if (settings.LogOutput)
|
||||||
Log::Logger::Write(logType, StringView(logData.Get(), rawData.Count()));
|
Log::Logger::Write(logType, StringView(logData.Get(), rawData.Count()));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "Engine/Threading/Threading.h"
|
#include "Engine/Threading/Threading.h"
|
||||||
#include "IncludeFreeType.h"
|
#include "IncludeFreeType.h"
|
||||||
|
|
||||||
Font::Font(FontAsset* parentAsset, int32 size)
|
Font::Font(FontAsset* parentAsset, float size)
|
||||||
: ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer))
|
: ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer))
|
||||||
, _asset(parentAsset)
|
, _asset(parentAsset)
|
||||||
, _size(size)
|
, _size(size)
|
||||||
@@ -436,7 +436,7 @@ void Font::FlushFaceSize() const
|
|||||||
{
|
{
|
||||||
// Set the character size
|
// Set the character size
|
||||||
const FT_Face face = _asset->GetFTFace();
|
const FT_Face face = _asset->GetFTFace();
|
||||||
const FT_Error error = FT_Set_Char_Size(face, 0, ConvertPixelTo26Dot6<FT_F26Dot6>((float)_size * FontManager::FontScale), DefaultDPI, DefaultDPI);
|
const FT_Error error = FT_Set_Char_Size(face, 0, ConvertPixelTo26Dot6<FT_F26Dot6>(_size * FontManager::FontScale), DefaultDPI, DefaultDPI);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
LOG_FT_ERROR(error);
|
LOG_FT_ERROR(error);
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font);
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
FontAsset* _asset;
|
FontAsset* _asset;
|
||||||
int32 _size;
|
float _size;
|
||||||
int32 _height;
|
int32 _height;
|
||||||
int32 _ascender;
|
int32 _ascender;
|
||||||
int32 _descender;
|
int32 _descender;
|
||||||
@@ -244,7 +244,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parentAsset">The parent asset.</param>
|
/// <param name="parentAsset">The parent asset.</param>
|
||||||
/// <param name="size">The size.</param>
|
/// <param name="size">The size.</param>
|
||||||
Font(FontAsset* parentAsset, int32 size);
|
Font(FontAsset* parentAsset, float size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finalizes an instance of the <see cref="Font"/> class.
|
/// Finalizes an instance of the <see cref="Font"/> class.
|
||||||
@@ -264,7 +264,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets font size.
|
/// Gets font size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY() FORCE_INLINE int32 GetSize() const
|
API_PROPERTY() FORCE_INLINE float GetSize() const
|
||||||
{
|
{
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void FontAsset::SetOptions(const FontOptions& value)
|
|||||||
_options = value;
|
_options = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font* FontAsset::CreateFont(int32 size)
|
Font* FontAsset::CreateFont(float size)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="size">The font characters size.</param>
|
/// <param name="size">The font characters size.</param>
|
||||||
/// <returns>The created font object.</returns>
|
/// <returns>The created font object.</returns>
|
||||||
API_FUNCTION() Font* CreateFont(int32 size);
|
API_FUNCTION() Font* CreateFont(float size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the font with bold style. Returns itself or creates a new virtual font asset using this font but with bold option enabled.
|
/// Gets the font with bold style. Returns itself or creates a new virtual font asset using this font but with bold option enabled.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace FlaxEngine
|
|||||||
private FontAsset _font;
|
private FontAsset _font;
|
||||||
|
|
||||||
[NoSerialize]
|
[NoSerialize]
|
||||||
private int _size;
|
private float _size;
|
||||||
|
|
||||||
[NoSerialize]
|
[NoSerialize]
|
||||||
private Font _cachedFont;
|
private Font _cachedFont;
|
||||||
@@ -33,7 +33,7 @@ namespace FlaxEngine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="font">The font.</param>
|
/// <param name="font">The font.</param>
|
||||||
/// <param name="size">The font size.</param>
|
/// <param name="size">The font size.</param>
|
||||||
public FontReference(FontAsset font, int size)
|
public FontReference(FontAsset font, float size)
|
||||||
{
|
{
|
||||||
_font = font;
|
_font = font;
|
||||||
_size = size;
|
_size = size;
|
||||||
@@ -91,7 +91,7 @@ namespace FlaxEngine
|
|||||||
/// The size of the font characters.
|
/// The size of the font characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EditorOrder(10), Limit(1, 500, 0.1f), Tooltip("The size of the font characters.")]
|
[EditorOrder(10), Limit(1, 500, 0.1f), Tooltip("The size of the font characters.")]
|
||||||
public int Size
|
public float Size
|
||||||
{
|
{
|
||||||
get => _size;
|
get => _size;
|
||||||
set
|
set
|
||||||
@@ -187,7 +187,7 @@ namespace FlaxEngine
|
|||||||
unchecked
|
unchecked
|
||||||
{
|
{
|
||||||
int hashCode = _font ? _font.GetHashCode() : 0;
|
int hashCode = _font ? _font.GetHashCode() : 0;
|
||||||
hashCode = (hashCode * 397) ^ _size;
|
hashCode = (hashCode * 397) ^ _size.GetHashCode();
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -712,7 +712,8 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa
|
|||||||
// Provide new path of hot-reloaded native library path for managed DllImport
|
// Provide new path of hot-reloaded native library path for managed DllImport
|
||||||
if (nativePath.HasChars())
|
if (nativePath.HasChars())
|
||||||
{
|
{
|
||||||
RegisterNativeLibrary(assemblyPathAnsi.Get(), StringAnsi(nativePath).Get());
|
StringAnsi nativeName = _name.EndsWith(".CSharp") ? StringAnsi(_name.Get(), _name.Length() - 7) : StringAnsi(_name);
|
||||||
|
RegisterNativeLibrary(nativeName.Get(), StringAnsi(nativePath).Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasCachedClasses = false;
|
_hasCachedClasses = false;
|
||||||
@@ -845,7 +846,7 @@ bool MClass::IsSubClassOf(const MClass* klass, bool checkInterfaces) const
|
|||||||
bool MClass::HasInterface(const MClass* klass) const
|
bool MClass::HasInterface(const MClass* klass) const
|
||||||
{
|
{
|
||||||
static void* TypeIsAssignableFrom = GetStaticMethodPointer(TEXT("TypeIsAssignableFrom"));
|
static void* TypeIsAssignableFrom = GetStaticMethodPointer(TEXT("TypeIsAssignableFrom"));
|
||||||
return klass && CallStaticMethod<bool, void*, void*>(TypeIsAssignableFrom, _handle, klass->_handle);
|
return klass && CallStaticMethod<bool, void*, void*>(TypeIsAssignableFrom, klass->_handle, _handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MClass::IsInstanceOfType(MObject* object) const
|
bool MClass::IsInstanceOfType(MObject* object) const
|
||||||
|
|||||||
@@ -559,6 +559,16 @@ void Scripting::Release()
|
|||||||
}
|
}
|
||||||
_objectsLocker.Unlock();
|
_objectsLocker.Unlock();
|
||||||
|
|
||||||
|
// Release assets sourced from game assemblies
|
||||||
|
const auto flaxModule = GetBinaryModuleFlaxEngine();
|
||||||
|
for (auto asset : Content::GetAssets())
|
||||||
|
{
|
||||||
|
if (asset->GetTypeHandle().Module == flaxModule)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
asset->DeleteObjectNow();
|
||||||
|
}
|
||||||
|
|
||||||
// Unload assemblies (from back to front)
|
// Unload assemblies (from back to front)
|
||||||
{
|
{
|
||||||
LOG(Info, "Unloading binary modules");
|
LOG(Info, "Unloading binary modules");
|
||||||
@@ -640,9 +650,9 @@ void Scripting::Reload(bool canTriggerSceneReload)
|
|||||||
MCore::GC::WaitForPendingFinalizers();
|
MCore::GC::WaitForPendingFinalizers();
|
||||||
|
|
||||||
// Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload)
|
// Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload)
|
||||||
|
const auto flaxModule = GetBinaryModuleFlaxEngine();
|
||||||
_objectsLocker.Lock();
|
_objectsLocker.Lock();
|
||||||
{
|
{
|
||||||
const auto flaxModule = GetBinaryModuleFlaxEngine();
|
|
||||||
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
||||||
{
|
{
|
||||||
auto obj = i->Value;
|
auto obj = i->Value;
|
||||||
@@ -657,6 +667,15 @@ void Scripting::Reload(bool canTriggerSceneReload)
|
|||||||
}
|
}
|
||||||
_objectsLocker.Unlock();
|
_objectsLocker.Unlock();
|
||||||
|
|
||||||
|
// Release assets sourced from game assemblies
|
||||||
|
for (auto asset : Content::GetAssets())
|
||||||
|
{
|
||||||
|
if (asset->GetTypeHandle().Module == flaxModule)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
asset->DeleteObjectNow();
|
||||||
|
}
|
||||||
|
|
||||||
// Unload all game modules
|
// Unload all game modules
|
||||||
LOG(Info, "Unloading game binary modules");
|
LOG(Info, "Unloading game binary modules");
|
||||||
auto modules = BinaryModule::GetModules();
|
auto modules = BinaryModule::GetModules();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "Engine/Core/Templates.h"
|
#include "Engine/Core/Templates.h"
|
||||||
|
|
||||||
extern FLAXENGINE_API class ScriptingObject* FindObject(const Guid& id, class MClass* type);
|
extern FLAXENGINE_API class ScriptingObject* FindObject(const Guid& id, class MClass* type);
|
||||||
|
extern FLAXENGINE_API class Asset* LoadAsset(const Guid& id, const struct ScriptingTypeHandle& type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for all data read streams
|
/// Base class for all data read streams
|
||||||
@@ -159,6 +160,34 @@ public:
|
|||||||
Read(ptr);
|
Read(ptr);
|
||||||
v = ptr;
|
v = ptr;
|
||||||
}
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Read(SoftObjectReference<T>& v)
|
||||||
|
{
|
||||||
|
uint32 id[4];
|
||||||
|
ReadBytes(id, sizeof(id));
|
||||||
|
v.Set(*(Guid*)id);
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Read(AssetReference<T>& v)
|
||||||
|
{
|
||||||
|
uint32 id[4];
|
||||||
|
ReadBytes(id, sizeof(id));
|
||||||
|
v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer);
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Read(WeakAssetReference<T>& v)
|
||||||
|
{
|
||||||
|
uint32 id[4];
|
||||||
|
ReadBytes(id, sizeof(id));
|
||||||
|
v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer);
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Read(SoftAssetReference<T>& v)
|
||||||
|
{
|
||||||
|
uint32 id[4];
|
||||||
|
ReadBytes(id, sizeof(id));
|
||||||
|
v.Set(*(Guid*)id);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read data array
|
/// Read data array
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ class ISerializable;
|
|||||||
class ScriptingObject;
|
class ScriptingObject;
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class ScriptingObjectReference;
|
class ScriptingObjectReference;
|
||||||
|
template<typename T>
|
||||||
|
class SoftObjectReference;
|
||||||
|
template<typename T>
|
||||||
|
class AssetReference;
|
||||||
|
template<typename T>
|
||||||
|
class WeakAssetReference;
|
||||||
|
template<typename T>
|
||||||
|
class SoftAssetReference;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for all data streams (memory streams, file streams etc.)
|
/// Base class for all data streams (memory streams, file streams etc.)
|
||||||
|
|||||||
@@ -176,6 +176,26 @@ public:
|
|||||||
{
|
{
|
||||||
Write(v.Get());
|
Write(v.Get());
|
||||||
}
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Write(const SoftObjectReference<T>& v)
|
||||||
|
{
|
||||||
|
Write(v.Get());
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Write(const AssetReference<T>& v)
|
||||||
|
{
|
||||||
|
Write(v.Get());
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Write(const WeakAssetReference<T>& v)
|
||||||
|
{
|
||||||
|
Write(v.Get());
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void Write(const SoftAssetReference<T>& v)
|
||||||
|
{
|
||||||
|
Write(v.Get());
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, typename AllocationType = HeapAllocation>
|
template<typename T, typename AllocationType = HeapAllocation>
|
||||||
void Write(const Array<T, AllocationType>& data)
|
void Write(const Array<T, AllocationType>& data)
|
||||||
|
|||||||
@@ -73,14 +73,14 @@ void TextRender::SetColor(const Color& value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 TextRender::GetFontSize() const
|
float TextRender::GetFontSize() const
|
||||||
{
|
{
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextRender::SetFontSize(int32 value)
|
void TextRender::SetFontSize(float value)
|
||||||
{
|
{
|
||||||
value = Math::Clamp(value, 1, 1024);
|
value = Math::Clamp(value, 1.0f, 1024.0f);
|
||||||
if (_size != value)
|
if (_size != value)
|
||||||
{
|
{
|
||||||
_size = value;
|
_size = value;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ private:
|
|||||||
LocalizedString _text;
|
LocalizedString _text;
|
||||||
Color _color;
|
Color _color;
|
||||||
TextLayoutOptions _layoutOptions;
|
TextLayoutOptions _layoutOptions;
|
||||||
int32 _size;
|
float _size;
|
||||||
int32 _sceneRenderingKey = -1;
|
int32 _sceneRenderingKey = -1;
|
||||||
|
|
||||||
BoundingBox _localBox;
|
BoundingBox _localBox;
|
||||||
@@ -91,12 +91,12 @@ public:
|
|||||||
/// Gets the font characters size.
|
/// Gets the font characters size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(32), Limit(1, 1000), EditorDisplay(\"Text\")")
|
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(32), Limit(1, 1000), EditorDisplay(\"Text\")")
|
||||||
int32 GetFontSize() const;
|
float GetFontSize() const;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the font characters size.
|
/// Sets the font characters size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY() void SetFontSize(int32 value);
|
API_PROPERTY() void SetFontSize(float value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The draw passes to use for rendering this object.
|
/// The draw passes to use for rendering this object.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StringAsANSI(const Char* text, const int32 length)
|
StringAsANSI(const Char* text, int32 length)
|
||||||
{
|
{
|
||||||
if (length + 1 < InlinedSize)
|
if (length + 1 < InlinedSize)
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StringAsUTF8(const Char* text, const int32 length)
|
StringAsUTF8(const Char* text, int32 length)
|
||||||
{
|
{
|
||||||
int32 lengthUtf8;
|
int32 lengthUtf8;
|
||||||
if (length + 1 < InlinedSize)
|
if (length + 1 < InlinedSize)
|
||||||
@@ -112,17 +112,17 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StringAsUTF16(const char* text, const int32 length)
|
StringAsUTF16(const char* text, int32 length)
|
||||||
{
|
{
|
||||||
if (length + 1 < InlinedSize)
|
if (length + 1 < InlinedSize)
|
||||||
{
|
{
|
||||||
StringUtils::ConvertANSI2UTF16(text, this->_inlined, length);
|
StringUtils::ConvertANSI2UTF16(text, this->_inlined, length, length);
|
||||||
this->_inlined[length] = 0;
|
this->_inlined[length] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->_dynamic = (CharType*)Allocator::Allocate((length + 1) * sizeof(CharType));
|
this->_dynamic = (CharType*)Allocator::Allocate((length + 1) * sizeof(CharType));
|
||||||
StringUtils::ConvertANSI2UTF16(text, this->_dynamic, length);
|
StringUtils::ConvertANSI2UTF16(text, this->_dynamic, length, length);
|
||||||
this->_dynamic[length] = 0;
|
this->_dynamic[length] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ namespace FlaxEngine
|
|||||||
#else
|
#else
|
||||||
private class ExtractArrayFromListContext<T>
|
private class ExtractArrayFromListContext<T>
|
||||||
{
|
{
|
||||||
public static FieldInfo? itemsField;
|
public static FieldInfo itemsField;
|
||||||
}
|
}
|
||||||
internal static T[] ExtractArrayFromList<T>(List<T> list)
|
internal static T[] ExtractArrayFromList<T>(List<T> list)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1204,7 +1204,7 @@ namespace Flax.Build.Bindings
|
|||||||
if (apiType != null)
|
if (apiType != null)
|
||||||
{
|
{
|
||||||
if (parameterInfo.IsOut)
|
if (parameterInfo.IsOut)
|
||||||
contents.Append(indent).AppendFormat("{1} {0}Temp;", parameterInfo.Name, new TypeInfo(parameterInfo.Type) { IsRef = false }.GetFullNameNative(buildData, caller)).AppendLine();
|
contents.Append(indent).AppendFormat("{1} {0}Temp;", parameterInfo.Name, parameterInfo.Type.GetFullNameNative(buildData, caller, false)).AppendLine();
|
||||||
else
|
else
|
||||||
contents.Append(indent).AppendFormat("auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine();
|
contents.Append(indent).AppendFormat("auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine();
|
||||||
if (parameterInfo.Type.IsPtr && !parameterInfo.Type.IsRef)
|
if (parameterInfo.Type.IsPtr && !parameterInfo.Type.IsRef)
|
||||||
|
|||||||
@@ -1417,6 +1417,9 @@ namespace Flax.Build.Bindings
|
|||||||
context.Tokenizer.SkipUntil(TokenType.Comma, out desc.Lang);
|
context.Tokenizer.SkipUntil(TokenType.Comma, out desc.Lang);
|
||||||
desc.Code = context.Tokenizer.ExpectToken(TokenType.String).Value.Replace("\\\"", "\"");
|
desc.Code = context.Tokenizer.ExpectToken(TokenType.String).Value.Replace("\\\"", "\"");
|
||||||
desc.Code = desc.Code.Substring(1, desc.Code.Length - 2);
|
desc.Code = desc.Code.Substring(1, desc.Code.Length - 2);
|
||||||
|
desc.Code = desc.Code.Replace("\\\n", "\n");
|
||||||
|
desc.Code = desc.Code.Replace("\\\r\n", "\n");
|
||||||
|
desc.Code = desc.Code.Replace("\t", " ");
|
||||||
context.Tokenizer.ExpectToken(TokenType.RightParent);
|
context.Tokenizer.ExpectToken(TokenType.RightParent);
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ namespace Flax.Build.Bindings
|
|||||||
GenericArgs = BindingsGenerator.Read(reader, GenericArgs);
|
GenericArgs = BindingsGenerator.Read(reader, GenericArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetFullNameNative(Builder.BuildData buildData, ApiTypeInfo caller)
|
public string GetFullNameNative(Builder.BuildData buildData, ApiTypeInfo caller, bool canRef = true, bool canConst = true)
|
||||||
{
|
{
|
||||||
var type = BindingsGenerator.FindApiTypeInfo(buildData, this, caller);
|
var type = BindingsGenerator.FindApiTypeInfo(buildData, this, caller);
|
||||||
if (type == null)
|
if (type == null)
|
||||||
@@ -155,7 +155,7 @@ namespace Flax.Build.Bindings
|
|||||||
return type.FullNameNative;
|
return type.FullNameNative;
|
||||||
|
|
||||||
var sb = new StringBuilder(64);
|
var sb = new StringBuilder(64);
|
||||||
if (IsConst)
|
if (IsConst && canConst)
|
||||||
sb.Append("const ");
|
sb.Append("const ");
|
||||||
sb.Append(type.FullNameNative);
|
sb.Append(type.FullNameNative);
|
||||||
if (GenericArgs != null)
|
if (GenericArgs != null)
|
||||||
@@ -171,7 +171,7 @@ namespace Flax.Build.Bindings
|
|||||||
}
|
}
|
||||||
if (IsPtr)
|
if (IsPtr)
|
||||||
sb.Append('*');
|
sb.Append('*');
|
||||||
if (IsRef)
|
if (IsRef && canRef)
|
||||||
sb.Append('&');
|
sb.Append('&');
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ namespace Flax.Build
|
|||||||
var referenceBuildOptions = GetBuildOptions(referenceTarget, configurationData.TargetBuildOptions.Platform, configurationData.TargetBuildOptions.Toolchain, configurationData.Architecture, configurationData.Configuration, reference.Project.ProjectFolderPath);
|
var referenceBuildOptions = GetBuildOptions(referenceTarget, configurationData.TargetBuildOptions.Platform, configurationData.TargetBuildOptions.Toolchain, configurationData.Architecture, configurationData.Configuration, reference.Project.ProjectFolderPath);
|
||||||
referenceBuildOptions.Flags |= BuildFlags.GenerateProject;
|
referenceBuildOptions.Flags |= BuildFlags.GenerateProject;
|
||||||
var referenceModules = CollectModules(rules, referenceBuildOptions.Platform, referenceTarget, referenceBuildOptions, referenceBuildOptions.Toolchain, referenceBuildOptions.Architecture, referenceBuildOptions.Configuration);
|
var referenceModules = CollectModules(rules, referenceBuildOptions.Platform, referenceTarget, referenceBuildOptions, referenceBuildOptions.Toolchain, referenceBuildOptions.Architecture, referenceBuildOptions.Configuration);
|
||||||
var referenceBinaryModules = GetBinaryModules(projectInfo, referenceTarget, referenceModules);
|
var referenceBinaryModules = referenceModules.Keys.GroupBy(x => x.BinaryModuleName).ToArray();
|
||||||
foreach (var binaryModule in referenceBinaryModules)
|
foreach (var binaryModule in referenceBinaryModules)
|
||||||
{
|
{
|
||||||
project.Defines.Add(binaryModule.Key.ToUpperInvariant() + "_API=");
|
project.Defines.Add(binaryModule.Key.ToUpperInvariant() + "_API=");
|
||||||
|
|||||||
@@ -143,6 +143,16 @@ namespace Flax.Build
|
|||||||
{
|
{
|
||||||
Log.Info("Removing: " + targetBuildOptions.IntermediateFolder);
|
Log.Info("Removing: " + targetBuildOptions.IntermediateFolder);
|
||||||
CleanDirectory(intermediateFolder);
|
CleanDirectory(intermediateFolder);
|
||||||
|
intermediateFolder.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all output files
|
||||||
|
var outputFolder = new DirectoryInfo(targetBuildOptions.OutputFolder);
|
||||||
|
if (outputFolder.Exists)
|
||||||
|
{
|
||||||
|
Log.Info("Removing: " + targetBuildOptions.OutputFolder);
|
||||||
|
CleanDirectory(outputFolder);
|
||||||
|
outputFolder.Create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using Flax.Build.Graph;
|
using Flax.Build.Graph;
|
||||||
|
using Flax.Build.NativeCpp;
|
||||||
using Flax.Deploy;
|
using Flax.Deploy;
|
||||||
|
|
||||||
namespace Flax.Build
|
namespace Flax.Build
|
||||||
{
|
{
|
||||||
static partial class Builder
|
static partial class Builder
|
||||||
{
|
{
|
||||||
public static event Action<TaskGraph, BuildData, NativeCpp.BuildOptions, Task, IGrouping<string, Module>> BuildDotNetAssembly;
|
public static event Action<TaskGraph, BuildData, BuildOptions, Task, IGrouping<string, Module>> BuildDotNetAssembly;
|
||||||
|
|
||||||
private static void BuildTargetDotNet(RulesAssembly rules, TaskGraph graph, Target target, Platform platform, TargetConfiguration configuration)
|
private static void BuildTargetDotNet(RulesAssembly rules, TaskGraph graph, Target target, Platform platform, TargetConfiguration configuration)
|
||||||
{
|
{
|
||||||
@@ -150,7 +152,7 @@ namespace Flax.Build
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BuildDotNet(TaskGraph graph, BuildData buildData, NativeCpp.BuildOptions buildOptions, string name, List<string> sourceFiles, HashSet<string> fileReferences = null, IGrouping<string, Module> binaryModule = null)
|
private static void BuildDotNet(TaskGraph graph, BuildData buildData, BuildOptions buildOptions, string name, List<string> sourceFiles, HashSet<string> fileReferences = null, IGrouping<string, Module> binaryModule = null)
|
||||||
{
|
{
|
||||||
// Setup build options
|
// Setup build options
|
||||||
var buildPlatform = Platform.BuildTargetPlatform;
|
var buildPlatform = Platform.BuildTargetPlatform;
|
||||||
@@ -163,6 +165,8 @@ namespace Flax.Build
|
|||||||
if (!dotnetSdk.IsValid)
|
if (!dotnetSdk.IsValid)
|
||||||
throw new Exception("Cannot compile C# without .NET SDK");
|
throw new Exception("Cannot compile C# without .NET SDK");
|
||||||
string dotnetPath = "dotnet", referenceAnalyzers;
|
string dotnetPath = "dotnet", referenceAnalyzers;
|
||||||
|
string[] runtimeVersionNameParts = dotnetSdk.RuntimeVersionName.Split('.');
|
||||||
|
string runtimeVersionShort = runtimeVersionNameParts[0] + '.' + runtimeVersionNameParts[1];
|
||||||
#else
|
#else
|
||||||
string monoRoot, monoPath;
|
string monoRoot, monoPath;
|
||||||
#endif
|
#endif
|
||||||
@@ -173,7 +177,7 @@ namespace Flax.Build
|
|||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
dotnetPath = Path.Combine(dotnetSdk.RootPath, "dotnet.exe");
|
dotnetPath = Path.Combine(dotnetSdk.RootPath, "dotnet.exe");
|
||||||
cscPath = Path.Combine(dotnetSdk.RootPath, @$"sdk\{dotnetSdk.VersionName}\Roslyn\bincore\csc.dll");
|
cscPath = Path.Combine(dotnetSdk.RootPath, @$"sdk\{dotnetSdk.VersionName}\Roslyn\bincore\csc.dll");
|
||||||
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, @$"shared\Microsoft.NETCore.App\{dotnetSdk.RuntimeVersionName}\");
|
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, @$"packs\Microsoft.NETCore.App.Ref\{dotnetSdk.RuntimeVersionName}\ref\net{runtimeVersionShort}\");
|
||||||
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, @$"packs\Microsoft.NETCore.App.Ref\{dotnetSdk.RuntimeVersionName}\analyzers\dotnet\cs\");
|
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, @$"packs\Microsoft.NETCore.App.Ref\{dotnetSdk.RuntimeVersionName}\analyzers\dotnet\cs\");
|
||||||
#else
|
#else
|
||||||
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Windows", "Mono");
|
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Windows", "Mono");
|
||||||
@@ -189,7 +193,7 @@ namespace Flax.Build
|
|||||||
{
|
{
|
||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
cscPath = Path.Combine(dotnetSdk.RootPath, $"sdk/{dotnetSdk.VersionName}/Roslyn/bincore/csc.dll");
|
cscPath = Path.Combine(dotnetSdk.RootPath, $"sdk/{dotnetSdk.VersionName}/Roslyn/bincore/csc.dll");
|
||||||
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, $"shared/Microsoft.NETCore.App/{dotnetSdk.RuntimeVersionName}/");
|
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/ref/net{runtimeVersionShort}/");
|
||||||
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/");
|
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/");
|
||||||
#else
|
#else
|
||||||
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Linux", "Mono");
|
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Linux", "Mono");
|
||||||
@@ -203,7 +207,7 @@ namespace Flax.Build
|
|||||||
{
|
{
|
||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
cscPath = Path.Combine(dotnetSdk.RootPath, $"sdk/{dotnetSdk.VersionName}/Roslyn/bincore/csc.dll");
|
cscPath = Path.Combine(dotnetSdk.RootPath, $"sdk/{dotnetSdk.VersionName}/Roslyn/bincore/csc.dll");
|
||||||
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, $"shared/Microsoft.NETCore.App/{dotnetSdk.RuntimeVersionName}/");
|
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/ref/net{runtimeVersionShort}/");
|
||||||
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/");
|
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/");
|
||||||
#else
|
#else
|
||||||
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Mac", "Mono");
|
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Mac", "Mono");
|
||||||
@@ -242,7 +246,9 @@ namespace Flax.Build
|
|||||||
args.Add("/filealign:512");
|
args.Add("/filealign:512");
|
||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
args.Add("/langversion:11.0");
|
args.Add("/langversion:11.0");
|
||||||
args.Add("-nowarn:8632"); // Nullable
|
args.Add(string.Format("/nullable:{0}", buildOptions.ScriptingAPI.CSharpNullableReferences.ToString().ToLowerInvariant()));
|
||||||
|
if (buildOptions.ScriptingAPI.CSharpNullableReferences == CSharpNullableReferences.Disable)
|
||||||
|
args.Add("-nowarn:8632"); // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
|
||||||
#else
|
#else
|
||||||
args.Add("/langversion:7.3");
|
args.Add("/langversion:7.3");
|
||||||
#endif
|
#endif
|
||||||
@@ -253,6 +259,7 @@ namespace Flax.Build
|
|||||||
args.Add(buildData.Configuration == TargetConfiguration.Release ? "/optimize+" : "/optimize-");
|
args.Add(buildData.Configuration == TargetConfiguration.Release ? "/optimize+" : "/optimize-");
|
||||||
#else
|
#else
|
||||||
args.Add(buildData.Configuration == TargetConfiguration.Debug ? "/optimize-" : "/optimize+");
|
args.Add(buildData.Configuration == TargetConfiguration.Debug ? "/optimize-" : "/optimize+");
|
||||||
|
args.Add(string.Format("/reference:\"{0}mscorlib.dll\"", referenceAssemblies));
|
||||||
#endif
|
#endif
|
||||||
args.Add(string.Format("/out:\"{0}\"", outputFile));
|
args.Add(string.Format("/out:\"{0}\"", outputFile));
|
||||||
args.Add(string.Format("/doc:\"{0}\"", outputDocFile));
|
args.Add(string.Format("/doc:\"{0}\"", outputDocFile));
|
||||||
@@ -260,7 +267,6 @@ namespace Flax.Build
|
|||||||
args.Add("/define:" + string.Join(";", buildOptions.ScriptingAPI.Defines));
|
args.Add("/define:" + string.Join(";", buildOptions.ScriptingAPI.Defines));
|
||||||
if (buildData.Configuration == TargetConfiguration.Debug)
|
if (buildData.Configuration == TargetConfiguration.Debug)
|
||||||
args.Add("/define:DEBUG");
|
args.Add("/define:DEBUG");
|
||||||
args.Add(string.Format("/reference:\"{0}mscorlib.dll\"", referenceAssemblies));
|
|
||||||
foreach (var reference in buildOptions.ScriptingAPI.SystemReferences)
|
foreach (var reference in buildOptions.ScriptingAPI.SystemReferences)
|
||||||
args.Add(string.Format("/reference:\"{0}{1}.dll\"", referenceAssemblies, reference));
|
args.Add(string.Format("/reference:\"{0}{1}.dll\"", referenceAssemblies, reference));
|
||||||
foreach (var reference in fileReferences)
|
foreach (var reference in fileReferences)
|
||||||
@@ -272,6 +278,13 @@ namespace Flax.Build
|
|||||||
foreach (var sourceFile in sourceFiles)
|
foreach (var sourceFile in sourceFiles)
|
||||||
args.Add("\"" + sourceFile + "\"");
|
args.Add("\"" + sourceFile + "\"");
|
||||||
|
|
||||||
|
#if USE_NETCORE
|
||||||
|
// Inject some assembly metadata (similar to msbuild in Visual Studio)
|
||||||
|
var assemblyAttributesPath = Path.Combine(buildOptions.IntermediateFolder, name + ".AssemblyAttributes.cs");
|
||||||
|
File.WriteAllText(assemblyAttributesPath, $"[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(\".NETCoreApp,Version=v{runtimeVersionShort}\", FrameworkDisplayName = \".NET {runtimeVersionShort}\")]\n", Encoding.UTF8);
|
||||||
|
args.Add("\"" + assemblyAttributesPath + "\"");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Generate response file with source files paths and compilation arguments
|
// Generate response file with source files paths and compilation arguments
|
||||||
string responseFile = Path.Combine(buildOptions.IntermediateFolder, name + ".response");
|
string responseFile = Path.Combine(buildOptions.IntermediateFolder, name + ".response");
|
||||||
Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args));
|
Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args));
|
||||||
|
|||||||
@@ -23,6 +23,32 @@ namespace Flax.Build.NativeCpp
|
|||||||
GenerateProject = 1,
|
GenerateProject = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The nullable context type used with reference types (C#).
|
||||||
|
/// </summary>
|
||||||
|
public enum CSharpNullableReferences
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The code is nullable oblivious, nullable warnings and language analysis features are disabled.
|
||||||
|
/// </summary>
|
||||||
|
Disable,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The compiler enables all null reference analysis and all language features.
|
||||||
|
/// </summary>
|
||||||
|
Enable,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The compiler performs all null analysis and emits warnings when code might dereference null.
|
||||||
|
/// </summary>
|
||||||
|
Warnings,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The compiler doesn't perform null analysis or emit warnings when code might dereference null.
|
||||||
|
/// </summary>
|
||||||
|
Annotations,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The native C++ module build settings container.
|
/// The native C++ module build settings container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -188,6 +214,15 @@ namespace Flax.Build.NativeCpp
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IgnoreMissingDocumentationWarnings;
|
public bool IgnoreMissingDocumentationWarnings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The nullable context used in C# project.
|
||||||
|
/// </summary>
|
||||||
|
public CSharpNullableReferences CSharpNullableReferences = CSharpNullableReferences.Disable;
|
||||||
|
|
||||||
|
public ScriptingAPIOptions()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the other options into this.
|
/// Adds the other options into this.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -209,6 +244,8 @@ namespace Flax.Build.NativeCpp
|
|||||||
Defines = new HashSet<string>(),
|
Defines = new HashSet<string>(),
|
||||||
SystemReferences = new HashSet<string>
|
SystemReferences = new HashSet<string>
|
||||||
{
|
{
|
||||||
|
"mscorlib",
|
||||||
|
"netstandard",
|
||||||
"Microsoft.CSharp",
|
"Microsoft.CSharp",
|
||||||
"System",
|
"System",
|
||||||
|
|
||||||
@@ -223,25 +260,32 @@ namespace Flax.Build.NativeCpp
|
|||||||
//"System.ComponentModel.TypeConverter",
|
//"System.ComponentModel.TypeConverter",
|
||||||
"System.Console",
|
"System.Console",
|
||||||
"System.Core",
|
"System.Core",
|
||||||
|
"System.Diagnostics.StackTrace",
|
||||||
"System.Globalization",
|
"System.Globalization",
|
||||||
"System.IO",
|
"System.IO",
|
||||||
"System.IO.Compression",
|
"System.IO.Compression",
|
||||||
"System.IO.FileSystem.Watcher",
|
"System.IO.FileSystem.Watcher",
|
||||||
"System.Linq",
|
"System.Linq",
|
||||||
"System.Linq.Expressions",
|
"System.Linq.Expressions",
|
||||||
|
"System.Memory",
|
||||||
|
"System.Net",
|
||||||
"System.Net.Http",
|
"System.Net.Http",
|
||||||
"System.Net.Primitives",
|
"System.Net.Primitives",
|
||||||
"System.ObjectModel",
|
"System.ObjectModel",
|
||||||
"System.Private.CoreLib",
|
"System.ValueTuple",
|
||||||
"System.Private.Uri",
|
|
||||||
//"System.Private.Xml",
|
|
||||||
|
|
||||||
"System.Reflection",
|
|
||||||
"System.Runtime",
|
"System.Runtime",
|
||||||
|
"System.Runtime.Extensions",
|
||||||
|
"System.Runtime.Handles",
|
||||||
|
"System.Runtime.Intrinsics",
|
||||||
|
"System.Runtime.Numerics",
|
||||||
|
"System.Runtime.Loader",
|
||||||
"System.Runtime.CompilerServices.Unsafe",
|
"System.Runtime.CompilerServices.Unsafe",
|
||||||
"System.Runtime.InteropServices",
|
"System.Runtime.InteropServices",
|
||||||
"System.Runtime.InteropServices.RuntimeInformation",
|
"System.Runtime.InteropServices.RuntimeInformation",
|
||||||
"System.Runtime.Serialization.Formatters", // BinaryFormatter
|
"System.Runtime.Serialization",
|
||||||
|
"System.Runtime.Serialization.Formatters",
|
||||||
|
|
||||||
"System.Security.Cryptography",
|
"System.Security.Cryptography",
|
||||||
"System.Security.Cryptography.Algorithms",
|
"System.Security.Cryptography.Algorithms",
|
||||||
"System.Security.Cryptography.Primitives",
|
"System.Security.Cryptography.Primitives",
|
||||||
@@ -249,8 +293,11 @@ namespace Flax.Build.NativeCpp
|
|||||||
"System.Threading.Tasks.Parallel",
|
"System.Threading.Tasks.Parallel",
|
||||||
//"System.Xml",
|
//"System.Xml",
|
||||||
|
|
||||||
|
"System.Threading",
|
||||||
|
"System.Threading.Thread",
|
||||||
|
|
||||||
|
"System.Reflection",
|
||||||
//"System.Reflection.Metadata",
|
//"System.Reflection.Metadata",
|
||||||
"netstandard",
|
|
||||||
},
|
},
|
||||||
SystemAnalyzers = new HashSet<string>
|
SystemAnalyzers = new HashSet<string>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ namespace Flax.Build.Plugins
|
|||||||
|
|
||||||
// Deserialize arguments
|
// Deserialize arguments
|
||||||
argNames += arg.Name;
|
argNames += arg.Name;
|
||||||
contents.AppendLine($" {arg.Type.Type} {arg.Name};");
|
contents.AppendLine($" {arg.Type.GetFullNameNative(buildData, typeInfo, false, false)} {arg.Name};");
|
||||||
contents.AppendLine($" stream->Read({arg.Name});");
|
contents.AppendLine($" stream->Read({arg.Name});");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ namespace Flax.Build.Plugins
|
|||||||
|
|
||||||
// Generated method thunk to invoke RPC to network
|
// Generated method thunk to invoke RPC to network
|
||||||
{
|
{
|
||||||
contents.Append(" static void ").Append(functionInfo.Name).AppendLine("_Invoke(ScriptingObject* obj, void** args)");
|
contents.Append(" static bool ").Append(functionInfo.Name).AppendLine("_Invoke(ScriptingObject* obj, void** args)");
|
||||||
contents.AppendLine(" {");
|
contents.AppendLine(" {");
|
||||||
contents.AppendLine(" NetworkStream* stream = NetworkReplicator::BeginInvokeRPC();");
|
contents.AppendLine(" NetworkStream* stream = NetworkReplicator::BeginInvokeRPC();");
|
||||||
contents.AppendLine(" Span<uint32> targetIds;");
|
contents.AppendLine(" Span<uint32> targetIds;");
|
||||||
@@ -270,11 +270,11 @@ namespace Flax.Build.Plugins
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialize arguments
|
// Serialize arguments
|
||||||
contents.AppendLine($" stream->Write(*({arg.Type.Type}*)args[{i}]);");
|
contents.AppendLine($" stream->Write(*(const {arg.Type.GetFullNameNative(buildData, typeInfo, false, false)}*)args[{i}]);");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke RPC
|
// Invoke RPC
|
||||||
contents.AppendLine($" NetworkReplicator::EndInvokeRPC(obj, {typeInfo.NativeName}::TypeInitializer, StringAnsiView(\"{functionInfo.Name}\", {functionInfo.Name.Length}), stream, targetIds);");
|
contents.AppendLine($" return NetworkReplicator::EndInvokeRPC(obj, {typeInfo.NativeName}::TypeInitializer, StringAnsiView(\"{functionInfo.Name}\", {functionInfo.Name.Length}), stream, targetIds);");
|
||||||
contents.AppendLine(" }");
|
contents.AppendLine(" }");
|
||||||
}
|
}
|
||||||
contents.AppendLine();
|
contents.AppendLine();
|
||||||
@@ -780,7 +780,7 @@ namespace Flax.Build.Plugins
|
|||||||
private static void GenerateCallINetworkSerializable(ref DotnetContext context, TypeDefinition type, string name, MethodDefinition method)
|
private static void GenerateCallINetworkSerializable(ref DotnetContext context, TypeDefinition type, string name, MethodDefinition method)
|
||||||
{
|
{
|
||||||
var m = new MethodDefinition(name, MethodAttributes.Public | MethodAttributes.HideBySig, context.VoidType);
|
var m = new MethodDefinition(name, MethodAttributes.Public | MethodAttributes.HideBySig, context.VoidType);
|
||||||
m.Parameters.Add(new ParameterDefinition("stream", ParameterAttributes.None, context.NetworkStreamType));
|
m.Parameters.Add(new ParameterDefinition("stream", ParameterAttributes.None, type.Module.ImportReference(context.NetworkStreamType)));
|
||||||
ILProcessor il = m.Body.GetILProcessor();
|
ILProcessor il = m.Body.GetILProcessor();
|
||||||
il.Emit(OpCodes.Nop);
|
il.Emit(OpCodes.Nop);
|
||||||
il.Emit(OpCodes.Ldarg_0);
|
il.Emit(OpCodes.Ldarg_0);
|
||||||
@@ -1304,6 +1304,9 @@ namespace Flax.Build.Plugins
|
|||||||
il.Emit(jmp4);
|
il.Emit(jmp4);
|
||||||
valueContext.Load(ref il);
|
valueContext.Load(ref il);
|
||||||
il.Emit(OpCodes.Ldloc, varStart + 1); // idx
|
il.Emit(OpCodes.Ldloc, varStart + 1); // idx
|
||||||
|
if (elementType.IsValueType)
|
||||||
|
il.Emit(OpCodes.Ldelem_Any, elementType);
|
||||||
|
else
|
||||||
il.Emit(OpCodes.Ldelem_Ref);
|
il.Emit(OpCodes.Ldelem_Ref);
|
||||||
il.Emit(OpCodes.Stloc, varStart + 2); // <elementType>
|
il.Emit(OpCodes.Stloc, varStart + 2); // <elementType>
|
||||||
|
|
||||||
@@ -1753,10 +1756,10 @@ namespace Flax.Build.Plugins
|
|||||||
if (jumpBodyEnd == null)
|
if (jumpBodyEnd == null)
|
||||||
throw new Exception("Missing IL Return op code in method " + method.Name);
|
throw new Exception("Missing IL Return op code in method " + method.Name);
|
||||||
il.Emit(OpCodes.Ldloc, varsStart + 0);
|
il.Emit(OpCodes.Ldloc, varsStart + 0);
|
||||||
il.Emit(OpCodes.Brfalse_S, jumpIf2Start);
|
il.Emit(OpCodes.Brfalse, jumpIf2Start);
|
||||||
il.Emit(OpCodes.Ldloc, varsStart + 2);
|
il.Emit(OpCodes.Ldloc, varsStart + 2);
|
||||||
il.Emit(OpCodes.Ldc_I4_2);
|
il.Emit(OpCodes.Ldc_I4_2);
|
||||||
il.Emit(OpCodes.Beq_S, jumpIfBodyStart);
|
il.Emit(OpCodes.Beq, jumpIfBodyStart);
|
||||||
// ||
|
// ||
|
||||||
il.Emit(jumpIf2Start);
|
il.Emit(jumpIf2Start);
|
||||||
il.Emit(OpCodes.Ldloc, varsStart + 1);
|
il.Emit(OpCodes.Ldloc, varsStart + 1);
|
||||||
@@ -1812,35 +1815,12 @@ namespace Flax.Build.Plugins
|
|||||||
else
|
else
|
||||||
il.Emit(OpCodes.Ldnull);
|
il.Emit(OpCodes.Ldnull);
|
||||||
var endInvokeRPC = networkReplicatorType.Resolve().GetMethod("EndInvokeRPC", 5);
|
var endInvokeRPC = networkReplicatorType.Resolve().GetMethod("EndInvokeRPC", 5);
|
||||||
|
if (endInvokeRPC.ReturnType.FullName != boolType.FullName)
|
||||||
|
throw new Exception("Invalid EndInvokeRPC return type. Remove any 'Binaries' folders to force project recompile.");
|
||||||
il.Emit(OpCodes.Call, module.ImportReference(endInvokeRPC));
|
il.Emit(OpCodes.Call, module.ImportReference(endInvokeRPC));
|
||||||
|
|
||||||
// if (server && networkMode == NetworkManagerMode.Client) return;
|
// if (EndInvokeRPC) return
|
||||||
if (methodRPC.IsServer)
|
il.Emit(OpCodes.Brtrue, jumpBodyEnd);
|
||||||
{
|
|
||||||
il.Emit(OpCodes.Nop);
|
|
||||||
il.Emit(OpCodes.Ldloc, varsStart + 2);
|
|
||||||
il.Emit(OpCodes.Ldc_I4_2);
|
|
||||||
var tmp = ilp.Create(OpCodes.Nop);
|
|
||||||
il.Emit(OpCodes.Beq_S, tmp);
|
|
||||||
il.Emit(OpCodes.Br, jumpBodyStart);
|
|
||||||
il.Emit(tmp);
|
|
||||||
//il.Emit(OpCodes.Ret);
|
|
||||||
il.Emit(OpCodes.Br, jumpBodyEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (client && networkMode == NetworkManagerMode.Server) return;
|
|
||||||
if (methodRPC.IsClient)
|
|
||||||
{
|
|
||||||
il.Emit(OpCodes.Nop);
|
|
||||||
il.Emit(OpCodes.Ldloc, varsStart + 2);
|
|
||||||
il.Emit(OpCodes.Ldc_I4_1);
|
|
||||||
var tmp = ilp.Create(OpCodes.Nop);
|
|
||||||
il.Emit(OpCodes.Beq_S, tmp);
|
|
||||||
il.Emit(OpCodes.Br, jumpBodyStart);
|
|
||||||
il.Emit(tmp);
|
|
||||||
//il.Emit(OpCodes.Ret);
|
|
||||||
il.Emit(OpCodes.Br, jumpBodyEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue to original method body
|
// Continue to original method body
|
||||||
il.Emit(jumpBodyStart);
|
il.Emit(jumpBodyStart);
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ namespace Flax.Build.Projects.VisualStudio
|
|||||||
|
|
||||||
csProjectFileContent.AppendLine(" <TargetFramework>net7.0</TargetFramework>");
|
csProjectFileContent.AppendLine(" <TargetFramework>net7.0</TargetFramework>");
|
||||||
csProjectFileContent.AppendLine(" <ImplicitUsings>disable</ImplicitUsings>");
|
csProjectFileContent.AppendLine(" <ImplicitUsings>disable</ImplicitUsings>");
|
||||||
csProjectFileContent.AppendLine(" <Nullable>annotations</Nullable>");
|
csProjectFileContent.AppendLine(string.Format(" <Nullable>{0}</Nullable>", baseConfiguration.TargetBuildOptions.ScriptingAPI.CSharpNullableReferences.ToString().ToLowerInvariant()));
|
||||||
csProjectFileContent.AppendLine(" <IsPackable>false</IsPackable>");
|
csProjectFileContent.AppendLine(" <IsPackable>false</IsPackable>");
|
||||||
csProjectFileContent.AppendLine(" <EnableDefaultItems>false</EnableDefaultItems>");
|
csProjectFileContent.AppendLine(" <EnableDefaultItems>false</EnableDefaultItems>");
|
||||||
csProjectFileContent.AppendLine(" <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>");
|
csProjectFileContent.AppendLine(" <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>");
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ namespace Flax.Build.Projects.VisualStudio
|
|||||||
if (project.Type == TargetType.DotNetCore)
|
if (project.Type == TargetType.DotNetCore)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(Path.GetDirectoryName(project.Path), "Properties/launchSettings.json");
|
var path = Path.Combine(Path.GetDirectoryName(project.Path), "Properties/launchSettings.json");
|
||||||
|
path = Utilities.NormalizePath(path);
|
||||||
if (profiles.ContainsKey(path))
|
if (profiles.ContainsKey(path))
|
||||||
profile.AppendLine(",");
|
profile.AppendLine(",");
|
||||||
profile.AppendLine($" \"{project.BaseName}\": {{");
|
profile.AppendLine($" \"{project.BaseName}\": {{");
|
||||||
@@ -568,6 +569,7 @@ namespace Flax.Build.Projects.VisualStudio
|
|||||||
var folder = Path.GetDirectoryName(e.Key);
|
var folder = Path.GetDirectoryName(e.Key);
|
||||||
if (!Directory.Exists(folder))
|
if (!Directory.Exists(folder))
|
||||||
Directory.CreateDirectory(folder);
|
Directory.CreateDirectory(folder);
|
||||||
|
profile.Clear();
|
||||||
profile.AppendLine("{");
|
profile.AppendLine("{");
|
||||||
profile.AppendLine(" \"profiles\": {");
|
profile.AppendLine(" \"profiles\": {");
|
||||||
profile.AppendLine(e.Value);
|
profile.AppendLine(e.Value);
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ namespace Flax.Build
|
|||||||
|
|
||||||
public static void GetType(this ModuleDefinition module, string fullName, out TypeReference type)
|
public static void GetType(this ModuleDefinition module, string fullName, out TypeReference type)
|
||||||
{
|
{
|
||||||
if (!module.TryGetTypeReference(fullName, out type))
|
//if (!module.TryGetTypeReference(fullName, out type)) // TODO: this seams to return 'FlaxEngine.Networking.NetworkManagerMode' as a Class instead of Enum
|
||||||
{
|
{
|
||||||
// Do manual search
|
// Do manual search
|
||||||
foreach (var a in module.AssemblyReferences)
|
foreach (var a in module.AssemblyReferences)
|
||||||
|
|||||||
Reference in New Issue
Block a user