diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax
index 28860d3cc..828d620d2 100644
--- a/Content/Editor/CubeTexturePreviewMaterial.flax
+++ b/Content/Editor/CubeTexturePreviewMaterial.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c04e226f6f8aa0de1049694ed813b0bd75864da76be2df43fb9db08162daadaa
-size 31647
+oid sha256:28e5aaeb274e7590bc9ec13212e041d4d14baef562487507ea3621ec040c393b
+size 31807
diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax
index d44e7590a..80d319a03 100644
--- a/Content/Editor/Gizmo/Material.flax
+++ b/Content/Editor/Gizmo/Material.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8b8d0157ba0eb18a8ab9613b4429343b4f52fdeb14d1adc73c0682a75b6a7466
-size 32411
+oid sha256:fc18e2ff1f55cfc41a5b083843b11e1573ac7066bc58153262b2b61ea6105fce
+size 32486
diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax
index d4ff0c0cd..b21daaa5a 100644
--- a/Content/Editor/TexturePreviewMaterial.flax
+++ b/Content/Editor/TexturePreviewMaterial.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2970eb5c0000661663a3cadffb1b8e67215f4425da24d5313e1d07fc44f0594d
-size 10413
+oid sha256:52921ebe6efaf3a74950e817a4e2224ef2ef54e0698efc042ba9cd2c17693e54
+size 10568
diff --git a/Flax.flaxproj b/Flax.flaxproj
index d8e86b1ed..50fcaeda3 100644
--- a/Flax.flaxproj
+++ b/Flax.flaxproj
@@ -3,7 +3,7 @@
"Version": {
"Major": 1,
"Minor": 6,
- "Build": 6342
+ "Build": 6344
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
diff --git a/Source/Editor/Content/Proxy/ShaderProxy.cs b/Source/Editor/Content/Proxy/ShaderProxy.cs
index 2f5c6fe42..98d4eaa2d 100644
--- a/Source/Editor/Content/Proxy/ShaderProxy.cs
+++ b/Source/Editor/Content/Proxy/ShaderProxy.cs
@@ -29,7 +29,7 @@ namespace FlaxEditor.Content
if (asset)
{
var source = Editor.GetShaderSourceCode(asset);
- Utilities.Utils.ShowSourceCodeWindow(source, "Shader Source", item.RootWindow.Window);
+ Utilities.Utils.ShowSourceCodeWindow(source, "Shader Source", item.RootWindow?.Window);
}
return null;
}
diff --git a/Source/Editor/CustomEditorWindow.cs b/Source/Editor/CustomEditorWindow.cs
index 2d5293f30..242b4d28d 100644
--- a/Source/Editor/CustomEditorWindow.cs
+++ b/Source/Editor/CustomEditorWindow.cs
@@ -3,7 +3,6 @@
using FlaxEditor.CustomEditors;
using FlaxEditor.Windows;
using FlaxEngine.GUI;
-using FlaxEngine;
using DockState = FlaxEditor.GUI.Docking.DockState;
namespace FlaxEditor
@@ -86,8 +85,12 @@ namespace FlaxEditor
if (!FlaxEngine.Scripting.IsTypeFromGameScripts(type))
return;
- Editor.Instance.Windows.AddToRestore(this);
+ if (!Window.IsHidden)
+ {
+ Editor.Instance.Windows.AddToRestore(this);
+ }
Window.Close();
+ Window.Dispose();
}
///
diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
index 28c039dc8..5a10b9a52 100644
--- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
@@ -167,7 +167,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
Presenter.Undo?.AddAction(new MultiUndoAction(actions));
// Build ragdoll
- SceneGraph.Actors.AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
+ AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
}
private void OnRebuildBone(Button button)
@@ -191,7 +191,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
}
// 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)
diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
index 17999d772..e89c80372 100644
--- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
@@ -33,7 +33,12 @@ namespace FlaxEditor.CustomEditors.Editors
[CustomEditor(typeof(Asset)), DefaultEditor]
public class AssetRefEditor : CustomEditor
{
- private AssetPicker _picker;
+ ///
+ /// The asset picker used to get a reference to an asset.
+ ///
+ public AssetPicker Picker;
+
+ private bool _isRefreshing;
private ScriptType _valueType;
///
@@ -44,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (HasDifferentTypes)
return;
- _picker = layout.Custom().CustomControl;
+ Picker = layout.Custom().CustomControl;
_valueType = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
var assetType = _valueType;
@@ -66,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
// Generic file picker
assetType = ScriptType.Null;
- _picker.FileExtension = assetReference.TypeName;
+ Picker.FileExtension = assetReference.TypeName;
}
else
{
@@ -78,23 +83,25 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
- _picker.AssetType = assetType;
- _picker.Height = height;
- _picker.SelectedItemChanged += OnSelectedItemChanged;
+ Picker.AssetType = assetType;
+ Picker.Height = height;
+ Picker.SelectedItemChanged += OnSelectedItemChanged;
}
private void OnSelectedItemChanged()
{
+ if (_isRefreshing)
+ return;
if (typeof(AssetItem).IsAssignableFrom(_valueType.Type))
- SetValue(_picker.SelectedItem);
+ SetValue(Picker.SelectedItem);
else if (_valueType.Type == typeof(Guid))
- SetValue(_picker.SelectedID);
+ SetValue(Picker.SelectedID);
else if (_valueType.Type == typeof(SceneReference))
- SetValue(new SceneReference(_picker.SelectedID));
+ SetValue(new SceneReference(Picker.SelectedID));
else if (_valueType.Type == typeof(string))
- SetValue(_picker.SelectedPath);
+ SetValue(Picker.SelectedPath);
else
- SetValue(_picker.SelectedAsset);
+ SetValue(Picker.SelectedAsset);
}
///
@@ -104,16 +111,18 @@ namespace FlaxEditor.CustomEditors.Editors
if (!HasDifferentValues)
{
+ _isRefreshing = true;
if (Values[0] is AssetItem assetItem)
- _picker.SelectedItem = assetItem;
+ Picker.SelectedItem = assetItem;
else if (Values[0] is Guid guid)
- _picker.SelectedID = guid;
+ Picker.SelectedID = guid;
else if (Values[0] is SceneReference sceneAsset)
- _picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
+ Picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
else if (Values[0] is string path)
- _picker.SelectedPath = path;
+ Picker.SelectedPath = path;
else
- _picker.SelectedAsset = Values[0] as Asset;
+ Picker.SelectedAsset = Values[0] as Asset;
+ _isRefreshing = false;
}
}
}
diff --git a/Source/Editor/CustomEditors/Editors/ColorEditor.cs b/Source/Editor/CustomEditors/Editors/ColorEditor.cs
index 2877fe29e..4edbde432 100644
--- a/Source/Editor/CustomEditors/Editors/ColorEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ColorEditor.cs
@@ -41,7 +41,17 @@ namespace FlaxEditor.CustomEditors.Editors
}
else
{
- element.CustomControl.Value = (Color)Values[0];
+ var value = Values[0];
+ if (value is Color asColor)
+ element.CustomControl.Value = asColor;
+ else if (value is Color32 asColor32)
+ element.CustomControl.Value = asColor32;
+ else if (value is Float4 asFloat4)
+ element.CustomControl.Value = asFloat4;
+ else if (value is Double4 asDouble4)
+ element.CustomControl.Value = (Float4)asDouble4;
+ else if (value is Vector4 asVector4)
+ element.CustomControl.Value = asVector4;
}
}
}
diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs
index a08a254d4..277f9ecb0 100644
--- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs
@@ -1,6 +1,8 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using FlaxEditor.CustomEditors.Elements;
+using FlaxEditor.CustomEditors.GUI;
+using FlaxEditor.Scripting;
using FlaxEngine;
namespace FlaxEditor.CustomEditors.Editors
@@ -13,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Editors
{
private GroupElement _group;
private bool _updateName;
+ private int _entryIndex;
+ private bool _isRefreshing;
+ private MaterialBase _material;
+ private ModelInstanceActor _modelInstance;
+ private AssetRefEditor _materialEditor;
///
public override void Initialize(LayoutElementsContainer layout)
@@ -21,47 +28,122 @@ namespace FlaxEditor.CustomEditors.Editors
var group = layout.Group("Entry");
_group = group;
+ if (ParentEditor == null)
+ return;
+ var entry = (ModelInstanceEntry)Values[0];
+ var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
+ var materialLabel = new PropertyNameLabel("Material");
+ materialLabel.TooltipText = "The mesh surface material used for the rendering.";
+ if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance)
+ {
+ _entryIndex = entryIndex;
+ _modelInstance = modelInstance;
+ var slots = modelInstance.MaterialSlots;
+ if (entry.Material == slots[entryIndex].Material)
+ {
+ // Ensure that entry with default material set is set back to null
+ modelInstance.SetMaterial(entryIndex, null);
+ }
+ _material = modelInstance.GetMaterial(entryIndex);
+ var defaultValue = GPUDevice.Instance.DefaultMaterial;
+ if (slots[entryIndex].Material)
+ {
+ // Use default value set on asset (eg. Model Asset)
+ defaultValue = slots[entryIndex].Material;
+ }
+
+ // Create material picker
+ var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase);
+ var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue);
+ materialEditor.Values.SetDefaultValue(defaultValue);
+ materialEditor.RefreshDefaultValue();
+ materialEditor.Picker.SelectedItemChanged += OnSelectedMaterialChanged;
+ _materialEditor = materialEditor;
+ }
+
base.Initialize(group);
}
+ private void OnSelectedMaterialChanged()
+ {
+ if (_isRefreshing)
+ return;
+ _isRefreshing = true;
+ var slots = _modelInstance.MaterialSlots;
+ var material = _materialEditor.Picker.SelectedAsset as MaterialBase;
+ var defaultMaterial = GPUDevice.Instance.DefaultMaterial;
+ var value = (ModelInstanceEntry)Values[0];
+ var prevMaterial = value.Material;
+ if (!material)
+ {
+ // Fallback to default material
+ _materialEditor.Picker.SelectedAsset = defaultMaterial;
+ value.Material = defaultMaterial;
+ }
+ else if (material == slots[_entryIndex].Material)
+ {
+ // Asset default material
+ value.Material = null;
+ }
+ else if (material == defaultMaterial && !slots[_entryIndex].Material)
+ {
+ // Default material while asset has no set as well
+ value.Material = null;
+ }
+ else
+ {
+ // Custom material
+ value.Material = material;
+ }
+ if (prevMaterial != value.Material)
+ SetValue(value);
+ _isRefreshing = false;
+ }
+
+ ///
+ protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
+ {
+ // Skip material member as it is overridden
+ if (item.Info.Name == "Material" && _materialEditor != null)
+ return;
+ base.SpawnProperty(itemLayout, itemValues, item);
+ }
+
///
public override void Refresh()
{
+ // Update panel title to match material slot name
if (_updateName &&
_group != null &&
ParentEditor?.ParentEditor != null &&
ParentEditor.ParentEditor.Values.Count > 0)
{
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
- if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel)
+ if (ParentEditor.ParentEditor.Values[0] is ModelInstanceActor modelInstance)
{
- var model = staticModel.Model;
- if (model && model.IsLoaded)
+ var slots = modelInstance.MaterialSlots;
+ if (slots != null && slots.Length > entryIndex)
{
- var slots = model.MaterialSlots;
- if (slots != null && slots.Length > entryIndex)
- {
- _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
- _updateName = false;
- }
- }
- }
- else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel)
- {
- var model = animatedModel.SkinnedModel;
- if (model && model.IsLoaded)
- {
- var slots = model.MaterialSlots;
- if (slots != null && slots.Length > entryIndex)
- {
- _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
- _updateName = false;
- }
+ _updateName = false;
+ _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
}
}
}
+ // Refresh currently selected material
+ _material = _modelInstance.GetMaterial(_entryIndex);
+
base.Refresh();
}
+
+ ///
+ protected override void Deinitialize()
+ {
+ _material = null;
+ _modelInstance = null;
+ _materialEditor = null;
+
+ base.Deinitialize();
+ }
}
}
diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs
index 948663892..ab5fd5d1a 100644
--- a/Source/Editor/CustomEditors/Editors/TagEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs
@@ -390,7 +390,6 @@ namespace FlaxEditor.CustomEditors.Editors
if (addTagDropPanel.IsClosed)
{
- Debug.Log("Hit");
nameTextBox.BorderColor = Color.Transparent;
nameTextBox.BorderSelectedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
return;
diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs
index 64578570c..cc7c48d23 100644
--- a/Source/Editor/Editor.Build.cs
+++ b/Source/Editor/Editor.Build.cs
@@ -37,7 +37,8 @@ public class Editor : EditorModule
{
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.ComponentModel.TypeConverter");
@@ -101,5 +102,7 @@ public class Editor : EditorModule
files.Add(Path.Combine(FolderPath, "Cooker/GameCooker.h"));
files.Add(Path.Combine(FolderPath, "Cooker/PlatformTools.h"));
files.Add(Path.Combine(FolderPath, "Cooker/Steps/CookAssetsStep.h"));
+ files.Add(Path.Combine(FolderPath, "Utilities/ScreenUtilities.h"));
+ files.Add(Path.Combine(FolderPath, "Utilities/ViewportIconsRenderer.h"));
}
}
diff --git a/Source/Editor/GUI/Popups/TypeSearchPopup.cs b/Source/Editor/GUI/Popups/TypeSearchPopup.cs
index 9d9ee2040..73c5baab7 100644
--- a/Source/Editor/GUI/Popups/TypeSearchPopup.cs
+++ b/Source/Editor/GUI/Popups/TypeSearchPopup.cs
@@ -27,6 +27,17 @@ namespace FlaxEditor.GUI
///
public ScriptType Type => _type;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TypeItemView()
+ {
+ _type = ScriptType.Null;
+ Name = "";
+ TooltipText = "Unset value.";
+ Tag = _type;
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -83,6 +94,7 @@ namespace FlaxEditor.GUI
// TODO: use async thread to search types without UI stall
var allTypes = Editor.Instance.CodeEditing.All.Get();
+ AddItem(new TypeItemView());
for (int i = 0; i < allTypes.Count; i++)
{
var type = allTypes[i];
diff --git a/Source/Editor/Gizmo/SelectionOutline.cs b/Source/Editor/Gizmo/SelectionOutline.cs
index ccc4bdd27..1374c7247 100644
--- a/Source/Editor/Gizmo/SelectionOutline.cs
+++ b/Source/Editor/Gizmo/SelectionOutline.cs
@@ -178,6 +178,8 @@ namespace FlaxEditor.Gizmo
if (selection[i] is ActorNode actorNode && actorNode.Actor != null)
CollectActors(actorNode.Actor);
}
+ if (_actors.Count == 0)
+ return;
// Render selected objects depth
Renderer.DrawSceneDepth(context, task, customDepth, _actors);
diff --git a/Source/Editor/ProjectInfo.cs b/Source/Editor/ProjectInfo.cs
index ed9470c35..b00c4e042 100644
--- a/Source/Editor/ProjectInfo.cs
+++ b/Source/Editor/ProjectInfo.cs
@@ -20,7 +20,7 @@ namespace FlaxEditor
/// The to write to.
/// The value.
/// The calling serializer.
- public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
@@ -44,7 +44,7 @@ namespace FlaxEditor
/// The existing property value of the JSON that is being converted.
/// The calling serializer.
/// The object value.
- 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)
{
diff --git a/Source/Editor/ProjectInfo.h b/Source/Editor/ProjectInfo.h
index 256269cc9..9f52e414f 100644
--- a/Source/Editor/ProjectInfo.h
+++ b/Source/Editor/ProjectInfo.h
@@ -112,6 +112,7 @@ public:
{
Version = ::Version(1, 0);
DefaultSceneSpawn = Ray(Vector3::Zero, Vector3::Forward);
+ DefaultScene = Guid::Empty;
}
///
diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp
index eaffe5691..3082a531e 100644
--- a/Source/Editor/Scripting/ScriptsBuilder.cpp
+++ b/Source/Editor/Scripting/ScriptsBuilder.cpp
@@ -613,6 +613,28 @@ bool ScriptsBuilderService::Init()
const String targetOutput = Globals::ProjectFolder / TEXT("Binaries") / target / platform / architecture / configuration;
Array files;
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())
LOG(Info, "Removing {0} files from previous Editor run hot-reloads", files.Count());
for (auto& file : files)
diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs
index 3f74e4c07..bae62556e 100644
--- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs
+++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs
@@ -363,7 +363,6 @@ namespace FlaxEditor.Surface.ContextMenu
Profiler.BeginEvent("VisjectCM.RemoveGroup");
if (group.Archetypes.Count == 0)
{
- Debug.Log("Remove");
_groups.RemoveAt(i);
group.Dispose();
}
diff --git a/Source/Editor/Tools/Foliage/FoliageTools.cpp b/Source/Editor/Tools/Foliage/FoliageTools.cpp
index b24bcdf00..7d0de7d41 100644
--- a/Source/Editor/Tools/Foliage/FoliageTools.cpp
+++ b/Source/Editor/Tools/Foliage/FoliageTools.cpp
@@ -292,7 +292,6 @@ void FoliageTools::Paint(Foliage* foliage, Span foliageTypesIndices, cons
{
PROFILE_CPU_NAMED("Place Instances");
- Matrix matrix;
FoliageInstance instance;
Quaternion tmp;
Matrix world;
diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs
index b4fb8f8f1..3281d130a 100644
--- a/Source/Editor/Viewport/EditorViewport.cs
+++ b/Source/Editor/Viewport/EditorViewport.cs
@@ -1538,6 +1538,7 @@ namespace FlaxEditor.Viewport
new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur"),
new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows"),
new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug"),
+ new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug"),
new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw"),
};
diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
index 2310668b6..12371f947 100644
--- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
+++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
@@ -3,8 +3,6 @@
using System;
using FlaxEditor.GUI.ContextMenu;
using FlaxEngine;
-using FlaxEditor.GUI.Input;
-using FlaxEngine.GUI;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Viewport.Previews
@@ -15,11 +13,10 @@ namespace FlaxEditor.Viewport.Previews
///
public class AnimatedModelPreview : AssetPreview
{
- private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
- private bool _showNodes, _showBounds, _showFloor, _showCurrentLOD, _showNodesNames;
private AnimatedModel _previewModel;
+ private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
+ private bool _showNodes, _showBounds, _showFloor, _showNodesNames;
private StaticModel _floorModel;
- private ContextMenuButton _showCurrentLODButton;
private bool _playAnimation, _playAnimationOnce;
private float _playSpeed = 1.0f;
@@ -188,8 +185,8 @@ namespace FlaxEditor.Viewport.Previews
{
UseTimeScale = false,
UpdateWhenOffscreen = true,
- //_previewModel.BoundsScale = 1000.0f;
- UpdateMode = AnimatedModel.AnimationUpdateMode.Manual
+ BoundsScale = 100.0f,
+ UpdateMode = AnimatedModel.AnimationUpdateMode.Manual,
};
Task.AddCustomActor(_previewModel);
@@ -207,26 +204,6 @@ namespace FlaxEditor.Viewport.Previews
// Show Floor
_showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor);
_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
@@ -339,44 +316,6 @@ namespace FlaxEditor.Viewport.Previews
_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;
- }
-
///
protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext)
{
@@ -440,45 +379,6 @@ namespace FlaxEditor.Viewport.Previews
}
}
- ///
- 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);
- }
- }
-
///
public override void Update(float deltaTime)
{
@@ -498,14 +398,21 @@ namespace FlaxEditor.Viewport.Previews
}
}
+ ///
+ /// Resets the camera to focus on a object.
+ ///
+ public void ResetCamera()
+ {
+ ViewportCamera.SetArcBallView(_previewModel.Box);
+ }
+
///
public override bool OnKeyDown(KeyboardKeys key)
{
switch (key)
{
case KeyboardKeys.F:
- // Pay respect..
- ViewportCamera.SetArcBallView(_previewModel.Box);
+ ResetCamera();
return true;
case KeyboardKeys.Spacebar:
PlayAnimation = !PlayAnimation;
@@ -525,7 +432,6 @@ namespace FlaxEditor.Viewport.Previews
_showNodesButton = null;
_showBoundsButton = null;
_showFloorButton = null;
- _showCurrentLODButton = null;
_showNodesNamesButton = null;
base.OnDestroy();
diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs
index 632ec199e..5520d6c4c 100644
--- a/Source/Editor/Viewport/Previews/MaterialPreview.cs
+++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs
@@ -4,6 +4,8 @@ using System;
using FlaxEditor.Surface;
using FlaxEngine;
using FlaxEngine.GUI;
+using FlaxEditor.Viewport.Widgets;
+using FlaxEditor.GUI.ContextMenu;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Viewport.Previews
@@ -46,6 +48,7 @@ namespace FlaxEditor.Viewport.Previews
private int _selectedModelIndex;
private Image _guiMaterialControl;
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
+ private ContextMenu _modelWidgetButtonMenu;
///
/// Gets or sets the material asset to preview. It can be or .
@@ -95,20 +98,33 @@ namespace FlaxEditor.Viewport.Previews
Task.AddCustomActor(_previewModel);
// Create context menu for primitive switching
- if (useWidgets && ViewWidgetButtonMenu != null)
+ if (useWidgets)
{
- ViewWidgetButtonMenu.AddSeparator();
- var modelSelect = ViewWidgetButtonMenu.AddChildMenu("Model").ContextMenu;
-
- // Fill out all models
- for (int i = 0; i < Models.Length; i++)
+ // Model mode widget
+ var modelMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
+ _modelWidgetButtonMenu = new ContextMenu();
+ _modelWidgetButtonMenu.VisibleChanged += control =>
{
- var button = modelSelect.AddButton(Models[i]);
- button.Tag = i;
- }
+ if (!control.Visible)
+ return;
+ _modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
- // Link the action
- modelSelect.ButtonClicked += (button) => SelectedModelIndex = (int)button.Tag;
+ // Fill out all models
+ for (int i = 0; i < Models.Length; i++)
+ {
+ var index = i;
+ var button = _modelWidgetButtonMenu.AddButton(Models[index]);
+ button.ButtonClicked += _ => SelectedModelIndex = index;
+ button.Checked = SelectedModelIndex == index;
+ button.Tag = index;
+ }
+ };
+ new ViewportWidgetButton("Model", SpriteHandle.Invalid, _modelWidgetButtonMenu)
+ {
+ TooltipText = "Change material model",
+ Parent = modelMode,
+ };
+ modelMode.Parent = this;
}
}
diff --git a/Source/Editor/Viewport/Previews/ModelBasePreview.cs b/Source/Editor/Viewport/Previews/ModelBasePreview.cs
index ffba23094..e4f1109d4 100644
--- a/Source/Editor/Viewport/Previews/ModelBasePreview.cs
+++ b/Source/Editor/Viewport/Previews/ModelBasePreview.cs
@@ -1,6 +1,5 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
-using FlaxEditor.GUI.Input;
using FlaxEngine;
using Object = FlaxEngine.Object;
@@ -56,25 +55,14 @@ namespace FlaxEditor.Viewport.Previews
// Link actors for rendering
Task.AddCustomActor(StaticModel);
Task.AddCustomActor(AnimatedModel);
+ }
- if (useWidgets)
- {
- // 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 += () =>
- {
- StaticModel.ForcedLOD = previewLODValue.Value;
- AnimatedModel.ForcedLOD = previewLODValue.Value;
- };
- ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = StaticModel.ForcedLOD;
- }
- }
+ ///
+ /// Resets the camera to focus on a object.
+ ///
+ public void ResetCamera()
+ {
+ ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
}
private void OnBegin(RenderTask task, GPUContext context)
@@ -103,8 +91,7 @@ namespace FlaxEditor.Viewport.Previews
switch (key)
{
case KeyboardKeys.F:
- // Pay respect..
- ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
+ ResetCamera();
break;
}
return base.OnKeyDown(key);
diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs
index 9193b6d40..a0b6d0ca0 100644
--- a/Source/Editor/Viewport/Previews/ModelPreview.cs
+++ b/Source/Editor/Viewport/Previews/ModelPreview.cs
@@ -1,10 +1,10 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using FlaxEditor.GUI.ContextMenu;
-using FlaxEditor.GUI.Input;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
+using FlaxEditor.Viewport.Widgets;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Viewport.Previews
@@ -16,10 +16,27 @@ namespace FlaxEditor.Viewport.Previews
public class ModelPreview : AssetPreview
{
private ContextMenuButton _showBoundsButton, _showCurrentLODButton, _showNormalsButton, _showTangentsButton, _showBitangentsButton, _showFloorButton;
+ private ContextMenu _previewLODsWidgetButtonMenu;
private StaticModel _previewModel, _floorModel;
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor;
private MeshDataCache _meshDatas;
+ ///
+ /// Gets or sets a value that shows LOD statistics
+ ///
+ public bool ShowCurrentLOD
+ {
+ get => _showCurrentLOD;
+ set
+ {
+ if (_showCurrentLOD == value)
+ return;
+ _showCurrentLOD = value;
+ if (_showCurrentLODButton != null)
+ _showCurrentLODButton.Checked = value;
+ }
+ }
+
///
/// Gets or sets the model asset to preview.
///
@@ -198,17 +215,36 @@ namespace FlaxEditor.Viewport.Previews
});
_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");
- previewLOD.CloseMenuOnClick = false;
- var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
+ if (!control.Visible)
+ return;
+ var model = _previewModel.Model;
+ if (model && !model.WaitForLoaded())
{
- Parent = previewLOD
- };
- previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
- ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
- }
+ _previewLODsWidgetButtonMenu.ItemsContainer.DisposeChildren();
+ var lods = model.LODs.Length;
+ for (int i = -1; i < lods; i++)
+ {
+ 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;
}
}
@@ -312,7 +348,7 @@ namespace FlaxEditor.Viewport.Previews
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;
@@ -346,8 +382,11 @@ namespace FlaxEditor.Viewport.Previews
if (_showCurrentLOD)
{
var asset = Model;
- var lodIndex = ComputeLODIndex(asset, out var screenSize);
- string text = string.Format("Current LOD: {0}\nScreen Size: {1:F2}", lodIndex, screenSize);
+ var lodIndex = ComputeLODIndex(asset, out var 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)
{
var lods = asset.LODs;
@@ -369,14 +408,21 @@ namespace FlaxEditor.Viewport.Previews
}
}
+ ///
+ /// Resets the camera to focus on a object.
+ ///
+ public void ResetCamera()
+ {
+ ViewportCamera.SetArcBallView(_previewModel.Box);
+ }
+
///
public override bool OnKeyDown(KeyboardKeys key)
{
switch (key)
{
case KeyboardKeys.F:
- // Pay respect..
- ViewportCamera.SetArcBallView(_previewModel.Box);
+ ResetCamera();
break;
}
return base.OnKeyDown(key);
@@ -389,6 +435,7 @@ namespace FlaxEditor.Viewport.Previews
Object.Destroy(ref _previewModel);
_showBoundsButton = null;
_showCurrentLODButton = null;
+ _previewLODsWidgetButtonMenu = null;
_showNormalsButton = null;
_showTangentsButton = null;
_showBitangentsButton = null;
diff --git a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs
new file mode 100644
index 000000000..08f8d8509
--- /dev/null
+++ b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs
@@ -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
+{
+ ///
+ /// Animation asset preview editor viewport.
+ ///
+ ///
+ public class SkinnedModelPreview : AnimatedModelPreview
+ {
+ private bool _showCurrentLOD;
+ private ContextMenuButton _showCurrentLODButton;
+ private ContextMenu _previewLODsWidgetButtonMenu;
+
+ ///
+ /// Gets or sets a value that shows LOD statistics
+ ///
+ public bool ShowCurrentLOD
+ {
+ get => _showCurrentLOD;
+ set
+ {
+ if (_showCurrentLOD == value)
+ return;
+ _showCurrentLOD = value;
+ if (_showCurrentLODButton != null)
+ _showCurrentLODButton.Checked = value;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true use widgets.
+ 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;
+ }
+
+ ///
+ 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);
+ }
+ }
+
+ ///
+ public override void OnDestroy()
+ {
+ _showCurrentLODButton = null;
+ _previewLODsWidgetButtonMenu = null;
+
+ base.OnDestroy();
+ }
+ }
+}
diff --git a/Source/Editor/Windows/Assets/CollisionDataWindow.cs b/Source/Editor/Windows/Assets/CollisionDataWindow.cs
index 9ce4c8765..cc1daa45b 100644
--- a/Source/Editor/Windows/Assets/CollisionDataWindow.cs
+++ b/Source/Editor/Windows/Assets/CollisionDataWindow.cs
@@ -182,6 +182,7 @@ namespace FlaxEditor.Windows.Assets
{
// Toolstrip
_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");
// Split Panel
diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs
index e44882b73..f70f6a3b5 100644
--- a/Source/Editor/Windows/Assets/ModelWindow.cs
+++ b/Source/Editor/Windows/Assets/ModelWindow.cs
@@ -779,6 +779,7 @@ namespace FlaxEditor.Windows.Assets
private MeshDataCache _meshData;
private ModelImportSettings _importSettings = new ModelImportSettings();
private float _backfacesThreshold = 0.6f;
+ private ToolStripButton _showCurrentLODButton;
///
public ModelWindow(Editor editor, AssetItem item)
@@ -786,6 +787,9 @@ namespace FlaxEditor.Windows.Assets
{
// Toolstrip
_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");
// Model preview
@@ -869,6 +873,8 @@ namespace FlaxEditor.Windows.Assets
}
}
+ _showCurrentLODButton.Checked = _preview.ShowCurrentLOD;
+
base.Update(deltaTime);
}
@@ -946,6 +952,7 @@ namespace FlaxEditor.Windows.Assets
base.OnDestroy();
Object.Destroy(ref _highlightActor);
+ _showCurrentLODButton = null;
}
}
}
diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs
index d18593549..a7ee6e767 100644
--- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs
+++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs
@@ -31,7 +31,7 @@ namespace FlaxEditor.Windows.Assets
///
public sealed class SkinnedModelWindow : ModelBaseWindow
{
- private sealed class Preview : AnimatedModelPreview
+ private sealed class Preview : SkinnedModelPreview
{
private readonly SkinnedModelWindow _window;
@@ -1105,6 +1105,7 @@ namespace FlaxEditor.Windows.Assets
private Preview _preview;
private AnimatedModel _highlightActor;
private ToolStripButton _showNodesButton;
+ private ToolStripButton _showCurrentLODButton;
private MeshData[][] _meshDatas;
private bool _meshDatasInProgress;
@@ -1116,7 +1117,9 @@ namespace FlaxEditor.Windows.Assets
{
// Toolstrip
_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");
+ _toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole model");
_toolstrip.AddSeparator();
_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;
base.Update(deltaTime);
@@ -1349,6 +1353,7 @@ namespace FlaxEditor.Windows.Assets
Object.Destroy(ref _highlightActor);
_preview = null;
_showNodesButton = null;
+ _showCurrentLODButton = null;
}
}
}
diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs
index b02f63f87..49b03ede1 100644
--- a/Source/Editor/Windows/DebugLogWindow.cs
+++ b/Source/Editor/Windows/DebugLogWindow.cs
@@ -80,6 +80,7 @@ namespace FlaxEditor.Windows
public LogGroup Group;
public LogEntryDescription Desc;
public SpriteHandle Icon;
+ public int LogCount = 1;
public LogEntry(DebugLogWindow window, ref LogEntryDescription desc)
: base(0, 0, 120, DefaultHeight)
@@ -137,7 +138,14 @@ namespace FlaxEditor.Windows
// Title
var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10);
Render2D.PushClip(ref clientRect);
- Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
+ if (LogCount == 1)
+ {
+ Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
+ }
+ else if (LogCount > 1)
+ {
+ Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground);
+ }
Render2D.PopClip();
}
@@ -289,6 +297,7 @@ namespace FlaxEditor.Windows
private readonly List _pendingEntries = new List(32);
private readonly ToolStripButton _clearOnPlayButton;
+ private readonly ToolStripButton _collapseLogsButton;
private readonly ToolStripButton _pauseOnErrorButton;
private readonly ToolStripButton[] _groupButtons = new ToolStripButton[3];
@@ -316,6 +325,7 @@ namespace FlaxEditor.Windows
};
toolstrip.AddButton("Clear", Clear).LinkTooltip("Clears all log entries");
_clearOnPlayButton = (ToolStripButton)toolstrip.AddButton("Clear on Play").SetAutoCheck(true).SetChecked(true).LinkTooltip("Clears all log entries on enter playmode");
+ _collapseLogsButton = (ToolStripButton)toolstrip.AddButton("Collapse").SetAutoCheck(true).SetChecked(true).LinkTooltip("Collapses similar logs.");
_pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error").SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
toolstrip.AddSeparator();
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides error messages");
@@ -612,6 +622,30 @@ namespace FlaxEditor.Windows
var top = _entriesPanel.Children.Count != 0 ? _entriesPanel.Children[_entriesPanel.Children.Count - 1].Bottom + spacing : margin.Top;
for (int i = 0; i < _pendingEntries.Count; i++)
{
+ if (_collapseLogsButton.Checked)
+ {
+ bool logExists = false;
+ foreach (var child in _entriesPanel.Children)
+ {
+ if (child is LogEntry entry)
+ {
+ var pendingEntry = _pendingEntries[i];
+ if (string.Equals(entry.Desc.Title, pendingEntry.Desc.Title, StringComparison.Ordinal) &&
+ string.Equals(entry.Desc.LocationFile, pendingEntry.Desc.LocationFile, StringComparison.Ordinal) &&
+ entry.Desc.Level == pendingEntry.Desc.Level &&
+ string.Equals(entry.Desc.Description, pendingEntry.Desc.Description, StringComparison.Ordinal) &&
+ entry.Desc.LocationLine == pendingEntry.Desc.LocationLine)
+ {
+ entry.LogCount += 1;
+ newEntry = entry;
+ logExists = true;
+ break;
+ }
+ }
+ }
+ if (logExists)
+ continue;
+ }
newEntry = _pendingEntries[i];
newEntry.Visible = _groupButtons[(int)newEntry.Group].Checked;
anyVisible |= newEntry.Visible;
diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
index c508ab3c8..31b97fb52 100644
--- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
+++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
@@ -217,6 +217,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed);
// Evaluate nested animations
+ bool hasNested = false;
if (anim->NestedAnims.Count() != 0)
{
for (auto& e : anim->NestedAnims)
@@ -239,6 +240,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);
+ hasNested = true;
}
}
}
@@ -291,7 +293,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
dstNode.Scale = srcNode.Scale * weight;
dstNode.Orientation = srcNode.Orientation * weight;
}
- else
+ else if (!hasNested)
{
dstNode = srcNode;
}
diff --git a/Source/Engine/Content/Assets/MaterialBase.h b/Source/Engine/Content/Assets/MaterialBase.h
index bca70b6da..070347348 100644
--- a/Source/Engine/Content/Assets/MaterialBase.h
+++ b/Source/Engine/Content/Assets/MaterialBase.h
@@ -27,7 +27,6 @@ public:
///
/// Returns true if material is an material instance.
///
- /// True if it's a material instance, otherwise false.
virtual bool IsMaterialInstance() const = 0;
public:
diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp
index b11a2af96..e292a0133 100644
--- a/Source/Engine/Content/Assets/VisualScript.cpp
+++ b/Source/Engine/Content/Assets/VisualScript.cpp
@@ -618,7 +618,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
// Return
case 5:
{
- auto& scope = ThreadStacks.Get().Stack->Scope;
+ auto scope = ThreadStacks.Get().Stack->Scope;
scope->FunctionReturn = tryGetValue(node->GetBox(1), Value::Zero);
break;
}
@@ -634,7 +634,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
else
{
// Evaluate method parameter value from the current scope
- auto& scope = ThreadStacks.Get().Stack->Scope;
+ auto scope = ThreadStacks.Get().Stack->Scope;
int32 index = boxBase->ID - 1;
if (index < scope->Parameters.Length())
value = scope->Parameters.Get()[index];
@@ -1138,7 +1138,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
break;
}
int32 arrayIndex = 0;
- for (; iteratorIndex < scope->ReturnedValues.Count(); arrayIndex++)
+ for (; arrayIndex < scope->ReturnedValues.Count(); arrayIndex++)
{
const auto& e = scope->ReturnedValues[arrayIndex];
if (e.NodeId == node->ID && e.BoxId == 1)
diff --git a/Source/Engine/Content/Content.Build.cs b/Source/Engine/Content/Content.Build.cs
index f89ad6277..6bfc3ee22 100644
--- a/Source/Engine/Content/Content.Build.cs
+++ b/Source/Engine/Content/Content.Build.cs
@@ -37,6 +37,9 @@ public class Content : EngineModule
files.AddRange(Directory.GetFiles(FolderPath, "*.h", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Assets"), "*.h", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Cache"), "*.h", SearchOption.TopDirectoryOnly));
+ files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Factories"), "*.h", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Storage"), "*.h", SearchOption.TopDirectoryOnly));
+ files.Add(Path.Combine(FolderPath, "Upgraders/BinaryAssetUpgrader.h"));
+ files.Add(Path.Combine(FolderPath, "Upgraders/IAssetUpgrader.h"));
}
}
diff --git a/Source/Engine/ContentExporters/ContentExporters.Build.cs b/Source/Engine/ContentExporters/ContentExporters.Build.cs
index 6721d43a9..a118022b2 100644
--- a/Source/Engine/ContentExporters/ContentExporters.Build.cs
+++ b/Source/Engine/ContentExporters/ContentExporters.Build.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
+using System.IO;
using Flax.Build;
using Flax.Build.NativeCpp;
@@ -23,5 +24,7 @@ public class ContentExporters : EngineModule
///
public override void GetFilesToDeploy(List files)
{
+ files.Add(Path.Combine(FolderPath, "AssetsExportingManager.h"));
+ files.Add(Path.Combine(FolderPath, "Types.h"));
}
}
diff --git a/Source/Engine/ContentImporters/ContentImporters.Build.cs b/Source/Engine/ContentImporters/ContentImporters.Build.cs
index 8c4614cd5..f404037a6 100644
--- a/Source/Engine/ContentImporters/ContentImporters.Build.cs
+++ b/Source/Engine/ContentImporters/ContentImporters.Build.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
+using System.IO;
using Flax.Build;
using Flax.Build.NativeCpp;
@@ -31,5 +32,7 @@ public class ContentImporters : EngineModule
///
public override void GetFilesToDeploy(List files)
{
+ files.Add(Path.Combine(FolderPath, "AssetsImportingManager.h"));
+ files.Add(Path.Combine(FolderPath, "Types.h"));
}
}
diff --git a/Source/Engine/Core/Collections/HashSet.h b/Source/Engine/Core/Collections/HashSet.h
index 698788869..ad6f8ffc6 100644
--- a/Source/Engine/Core/Collections/HashSet.h
+++ b/Source/Engine/Core/Collections/HashSet.h
@@ -357,8 +357,8 @@ public:
{
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
{
- if (i->Value)
- ::Delete(i->Value);
+ if (i->Item)
+ ::Delete(i->Item);
}
Clear();
}
diff --git a/Source/Engine/Core/Math/BoundingBox.h b/Source/Engine/Core/Math/BoundingBox.h
index 0ee4fc1a9..d06ef0b3f 100644
--- a/Source/Engine/Core/Math/BoundingBox.h
+++ b/Source/Engine/Core/Math/BoundingBox.h
@@ -38,9 +38,7 @@ public:
///
/// Empty constructor.
///
- BoundingBox()
- {
- }
+ BoundingBox() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/BoundingFrustum.h b/Source/Engine/Core/Math/BoundingFrustum.h
index 61c5d122f..1502b6302 100644
--- a/Source/Engine/Core/Math/BoundingFrustum.h
+++ b/Source/Engine/Core/Math/BoundingFrustum.h
@@ -12,6 +12,7 @@
///
API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum
{
+ friend CollisionsHelper;
private:
Matrix _matrix;
@@ -33,9 +34,7 @@ public:
///
/// Empty constructor.
///
- BoundingFrustum()
- {
- }
+ BoundingFrustum() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/BoundingSphere.h b/Source/Engine/Core/Math/BoundingSphere.h
index 08f5df7f1..da303e8d5 100644
--- a/Source/Engine/Core/Math/BoundingSphere.h
+++ b/Source/Engine/Core/Math/BoundingSphere.h
@@ -34,9 +34,7 @@ public:
///
/// Empty constructor.
///
- BoundingSphere()
- {
- }
+ BoundingSphere() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp
index 60ba61f28..8ad5b7673 100644
--- a/Source/Engine/Core/Math/CollisionsHelper.cpp
+++ b/Source/Engine/Core/Math/CollisionsHelper.cpp
@@ -939,7 +939,6 @@ bool CollisionsHelper::RayIntersectsSphere(const Ray& ray, const BoundingSphere&
normal = Vector3::Up;
return false;
}
-
const Vector3 point = ray.Position + ray.Direction * distance;
normal = Vector3::Normalize(point - sphere.Center);
return true;
@@ -953,22 +952,17 @@ bool CollisionsHelper::RayIntersectsSphere(const Ray& ray, const BoundingSphere&
point = Vector3::Zero;
return false;
}
-
point = ray.Position + ray.Direction * distance;
return true;
}
PlaneIntersectionType CollisionsHelper::PlaneIntersectsPoint(const Plane& plane, const Vector3& point)
{
- Real distance = Vector3::Dot(plane.Normal, point);
- distance += plane.D;
-
+ const Real distance = Vector3::Dot(plane.Normal, point) + plane.D;
if (distance > Plane::DistanceEpsilon)
return PlaneIntersectionType::Front;
-
if (distance < Plane::DistanceEpsilon)
return PlaneIntersectionType::Back;
-
return PlaneIntersectionType::Intersecting;
}
@@ -1169,7 +1163,6 @@ ContainmentType CollisionsHelper::SphereContainsPoint(const BoundingSphere& sphe
{
if (Vector3::DistanceSquared(point, sphere.Center) <= sphere.Radius * sphere.Radius)
return ContainmentType::Contains;
-
return ContainmentType::Disjoint;
}
@@ -1254,13 +1247,10 @@ ContainmentType CollisionsHelper::SphereContainsBox(const BoundingSphere& sphere
ContainmentType CollisionsHelper::SphereContainsSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
{
const Real distance = Vector3::Distance(sphere1.Center, sphere2.Center);
-
if (sphere1.Radius + sphere2.Radius < distance)
return ContainmentType::Disjoint;
-
if (sphere1.Radius - sphere2.Radius < distance)
return ContainmentType::Intersects;
-
return ContainmentType::Contains;
}
@@ -1274,7 +1264,8 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
auto result = ContainmentType::Contains;
for (int32 i = 0; i < 6; i++)
{
- Plane plane = frustum.GetPlane(i);
+ Plane plane = frustum._planes[i];
+
Vector3 p = box.Minimum;
if (plane.Normal.X >= 0)
p.X = box.Maximum.X;
@@ -1282,7 +1273,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
p.Y = box.Maximum.Y;
if (plane.Normal.Z >= 0)
p.Z = box.Maximum.Z;
- if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
+ if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
return ContainmentType::Disjoint;
p = box.Maximum;
@@ -1292,7 +1283,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
p.Y = box.Minimum.Y;
if (plane.Normal.Z >= 0)
p.Z = box.Minimum.Z;
- if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
+ if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
result = ContainmentType::Intersects;
}
return result;
diff --git a/Source/Engine/Core/Math/Color.h b/Source/Engine/Core/Math/Color.h
index 997ed9c17..af93fc0f2 100644
--- a/Source/Engine/Core/Math/Color.h
+++ b/Source/Engine/Core/Math/Color.h
@@ -50,9 +50,7 @@ public:
///
/// Empty constructor.
///
- Color()
- {
- }
+ Color() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/Color32.h b/Source/Engine/Core/Math/Color32.h
index 36da3d41d..6c2b9648d 100644
--- a/Source/Engine/Core/Math/Color32.h
+++ b/Source/Engine/Core/Math/Color32.h
@@ -53,9 +53,7 @@ public:
///
/// Empty constructor.
///
- Color32()
- {
- }
+ Color32() = default;
///
/// Constructs a new Color32 with given r, g, b, a components.
diff --git a/Source/Engine/Core/Math/Half.h b/Source/Engine/Core/Math/Half.h
index 06b03895d..346b5d6b3 100644
--- a/Source/Engine/Core/Math/Half.h
+++ b/Source/Engine/Core/Math/Half.h
@@ -121,9 +121,7 @@ public:
///
/// Default constructor
///
- Half2()
- {
- }
+ Half2() = default;
///
/// Init
@@ -185,9 +183,7 @@ public:
Half Z;
public:
- Half3()
- {
- }
+ Half3() = default;
Half3(Half x, Half y, Half z)
: X(x)
@@ -242,9 +238,7 @@ public:
Half W;
public:
- Half4()
- {
- }
+ Half4() = default;
Half4(Half x, Half y, Half z, Half w)
: X(x)
diff --git a/Source/Engine/Core/Math/Matrix.h b/Source/Engine/Core/Math/Matrix.h
index 7d6d57bac..aed2ead8d 100644
--- a/Source/Engine/Core/Math/Matrix.h
+++ b/Source/Engine/Core/Math/Matrix.h
@@ -83,9 +83,7 @@ public:
///
/// Empty constructor.
///
- Matrix()
- {
- }
+ Matrix() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/Matrix3x3.h b/Source/Engine/Core/Math/Matrix3x3.h
index 1e29be85a..344f68455 100644
--- a/Source/Engine/Core/Math/Matrix3x3.h
+++ b/Source/Engine/Core/Math/Matrix3x3.h
@@ -63,9 +63,7 @@ public:
///
/// Empty constructor.
///
- Matrix3x3()
- {
- }
+ Matrix3x3() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/Plane.h b/Source/Engine/Core/Math/Plane.h
index 57a2be344..8c3d4129b 100644
--- a/Source/Engine/Core/Math/Plane.h
+++ b/Source/Engine/Core/Math/Plane.h
@@ -30,9 +30,7 @@ public:
///
/// Empty constructor.
///
- Plane()
- {
- }
+ Plane() = default;
///
/// Init
diff --git a/Source/Engine/Core/Math/Quaternion.h b/Source/Engine/Core/Math/Quaternion.h
index 5b164f979..e6425f7ab 100644
--- a/Source/Engine/Core/Math/Quaternion.h
+++ b/Source/Engine/Core/Math/Quaternion.h
@@ -67,9 +67,7 @@ public:
///
/// Empty constructor.
///
- Quaternion()
- {
- }
+ Quaternion() = default;
///
/// Init
diff --git a/Source/Engine/Core/Math/Ray.h b/Source/Engine/Core/Math/Ray.h
index c8486f39e..f2f9081e5 100644
--- a/Source/Engine/Core/Math/Ray.h
+++ b/Source/Engine/Core/Math/Ray.h
@@ -35,9 +35,7 @@ public:
///
/// Empty constructor.
///
- Ray()
- {
- }
+ Ray() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/Rectangle.h b/Source/Engine/Core/Math/Rectangle.h
index c59905fd6..bf282b6e3 100644
--- a/Source/Engine/Core/Math/Rectangle.h
+++ b/Source/Engine/Core/Math/Rectangle.h
@@ -31,9 +31,7 @@ public:
///
/// Empty constructor.
///
- Rectangle()
- {
- }
+ Rectangle() = default;
// Init
// @param x Rectangle location X coordinate
diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h
index ef644f08a..1079b4c23 100644
--- a/Source/Engine/Core/Math/Transform.h
+++ b/Source/Engine/Core/Math/Transform.h
@@ -40,9 +40,7 @@ public:
///
/// Empty constructor.
///
- Transform()
- {
- }
+ Transform() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/Triangle.h b/Source/Engine/Core/Math/Triangle.h
index a92919cfc..63f0e34ec 100644
--- a/Source/Engine/Core/Math/Triangle.h
+++ b/Source/Engine/Core/Math/Triangle.h
@@ -30,9 +30,7 @@ public:
///
/// Empty constructor.
///
- Triangle()
- {
- }
+ Triangle() = default;
///
/// Initializes a new instance of the struct.
diff --git a/Source/Engine/Core/Math/Vector2.h b/Source/Engine/Core/Math/Vector2.h
index 87ced1de6..83deb009a 100644
--- a/Source/Engine/Core/Math/Vector2.h
+++ b/Source/Engine/Core/Math/Vector2.h
@@ -60,9 +60,7 @@ public:
///
/// Empty constructor.
///
- Vector2Base()
- {
- }
+ Vector2Base() = default;
FORCE_INLINE Vector2Base(T xy)
: X(xy)
diff --git a/Source/Engine/Core/Math/Vector3.h b/Source/Engine/Core/Math/Vector3.h
index 33be7f2d6..cad5250b7 100644
--- a/Source/Engine/Core/Math/Vector3.h
+++ b/Source/Engine/Core/Math/Vector3.h
@@ -89,9 +89,7 @@ public:
///
/// Empty constructor.
///
- Vector3Base()
- {
- }
+ Vector3Base() = default;
FORCE_INLINE Vector3Base(T xyz)
: X(xyz)
diff --git a/Source/Engine/Core/Math/Vector4.h b/Source/Engine/Core/Math/Vector4.h
index 213f05815..5c7b24c4a 100644
--- a/Source/Engine/Core/Math/Vector4.h
+++ b/Source/Engine/Core/Math/Vector4.h
@@ -76,9 +76,7 @@ public:
///
/// Empty constructor.
///
- Vector4Base()
- {
- }
+ Vector4Base() = default;
FORCE_INLINE Vector4Base(T xyzw)
: X(xyzw)
diff --git a/Source/Engine/Core/Math/Viewport.h b/Source/Engine/Core/Math/Viewport.h
index bb4ec8f8b..d478eb7cc 100644
--- a/Source/Engine/Core/Math/Viewport.h
+++ b/Source/Engine/Core/Math/Viewport.h
@@ -52,9 +52,7 @@ public:
///
/// Empty constructor.
///
- Viewport()
- {
- }
+ Viewport() = default;
// Init
// @param x The x coordinate of the upper-left corner of the viewport in pixels
diff --git a/Source/Engine/Core/Memory/Memory.h b/Source/Engine/Core/Memory/Memory.h
index 770fd2c22..96acc0746 100644
--- a/Source/Engine/Core/Memory/Memory.h
+++ b/Source/Engine/Core/Memory/Memory.h
@@ -36,6 +36,32 @@ namespace AllocatorExt
return result;
}
+ ///
+ /// Reallocates block of the memory.
+ ///
+ ///
+ /// A pointer to the memory block to reallocate.
+ /// The size of the new allocation (in bytes).
+ /// The memory alignment (in bytes). Must be an integer power of 2.
+ /// The pointer to the allocated chunk of the memory. The pointer is a multiple of alignment.
+ 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;
+ }
+
///
/// Reallocates block of the memory.
///
diff --git a/Source/Engine/Core/Types/Span.h b/Source/Engine/Core/Types/Span.h
index 678550e85..e0518ec77 100644
--- a/Source/Engine/Core/Types/Span.h
+++ b/Source/Engine/Core/Types/Span.h
@@ -114,3 +114,20 @@ inline Span ToSpan(const T* ptr, int32 length)
{
return Span(ptr, length);
}
+
+template
+inline Span ToSpan(const Array& data)
+{
+ return Span((U*)data.Get(), data.Count());
+}
+
+template
+inline bool SpanContains(const Span span, const T& value)
+{
+ for (int32 i = 0; i < span.Length(); i++)
+ {
+ if (span.Get()[i] == value)
+ return true;
+ }
+ return false;
+}
diff --git a/Source/Engine/Core/Types/String.cpp b/Source/Engine/Core/Types/String.cpp
index cee60c07c..e53f04af7 100644
--- a/Source/Engine/Core/Types/String.cpp
+++ b/Source/Engine/Core/Types/String.cpp
@@ -73,7 +73,7 @@ void String::Set(const char* chars, int32 length)
_length = length;
}
if (chars)
- StringUtils::ConvertANSI2UTF16(chars, _data, length);
+ StringUtils::ConvertANSI2UTF16(chars, _data, length, _length);
}
void String::SetUTF8(const char* chars, int32 length)
@@ -112,7 +112,8 @@ void String::Append(const char* chars, int32 count)
_data = (Char*)Platform::Allocate((_length + 1) * sizeof(Char), 16);
Platform::MemoryCopy(_data, oldData, oldLength * sizeof(Char));
- StringUtils::ConvertANSI2UTF16(chars, _data + oldLength, count * sizeof(Char));
+ StringUtils::ConvertANSI2UTF16(chars, _data + oldLength, count, _length);
+ _length += oldLength;
_data[_length] = 0;
Platform::Free(oldData);
diff --git a/Source/Engine/Core/Types/StringBuilder.h b/Source/Engine/Core/Types/StringBuilder.h
index 39c1b3a84..051554a23 100644
--- a/Source/Engine/Core/Types/StringBuilder.h
+++ b/Source/Engine/Core/Types/StringBuilder.h
@@ -125,7 +125,8 @@ public:
const int32 length = str && *str ? StringUtils::Length(str) : 0;
const int32 prevCnt = _data.Count();
_data.AddDefault(length);
- StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length);
+ int32 tmp;
+ StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length, tmp);
return *this;
}
diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp
index 33ffde349..973e494f1 100644
--- a/Source/Engine/Core/Types/Variant.cpp
+++ b/Source/Engine/Core/Types/Variant.cpp
@@ -660,7 +660,8 @@ Variant::Variant(const StringAnsiView& v)
const int32 length = v.Length() * sizeof(Char) + 2;
AsBlob.Data = Allocator::Allocate(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;
}
else
@@ -2578,7 +2579,8 @@ void Variant::SetString(const StringAnsiView& str)
AsBlob.Data = Allocator::Allocate(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;
}
@@ -2794,6 +2796,122 @@ String Variant::ToString() const
}
}
+void Variant::Inline()
+{
+ VariantType::Types type = VariantType::Null;
+ byte data[sizeof(Matrix)];
+ if (Type.Type == VariantType::Structure && AsBlob.Data && AsBlob.Length <= sizeof(Matrix))
+ {
+ for (int32 i = 2; i < VariantType::MAX; i++)
+ {
+ if (StringUtils::Compare(Type.TypeName, InBuiltTypesTypeNames[i]) == 0)
+ {
+ type = (VariantType::Types)i;
+ break;
+ }
+ }
+ if (type == VariantType::Null)
+ {
+ // Aliases
+ if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector2") == 0)
+ type = VariantType::Types::Vector2;
+ else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector3") == 0)
+ type = VariantType::Types::Vector3;
+ else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector4") == 0)
+ type = VariantType::Types::Vector4;
+ }
+ if (type != VariantType::Null)
+ Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length);
+ }
+ if (type != VariantType::Null)
+ {
+ switch (type)
+ {
+ case VariantType::Bool:
+ *this = *(bool*)data;
+ break;
+ case VariantType::Int:
+ *this = *(int32*)data;
+ break;
+ case VariantType::Uint:
+ *this = *(uint32*)data;
+ break;
+ case VariantType::Int64:
+ *this = *(int64*)data;
+ break;
+ case VariantType::Uint64:
+ *this = *(uint64*)data;
+ break;
+ case VariantType::Float:
+ *this = *(float*)data;
+ break;
+ case VariantType::Double:
+ *this = *(double*)data;
+ break;
+ case VariantType::Float2:
+ *this = *(Float2*)data;
+ break;
+ case VariantType::Float3:
+ *this = *(Float3*)data;
+ break;
+ case VariantType::Float4:
+ *this = *(Float4*)data;
+ break;
+ case VariantType::Color:
+ *this = *(Color*)data;
+ break;
+ case VariantType::Guid:
+ *this = *(Guid*)data;
+ break;
+ case VariantType::BoundingBox:
+ *this = Variant(*(BoundingBox*)data);
+ break;
+ case VariantType::BoundingSphere:
+ *this = *(BoundingSphere*)data;
+ break;
+ case VariantType::Quaternion:
+ *this = *(Quaternion*)data;
+ break;
+ case VariantType::Transform:
+ *this = Variant(*(Transform*)data);
+ break;
+ case VariantType::Rectangle:
+ *this = *(Rectangle*)data;
+ break;
+ case VariantType::Ray:
+ *this = Variant(*(Ray*)data);
+ break;
+ case VariantType::Matrix:
+ *this = Variant(*(Matrix*)data);
+ break;
+ case VariantType::Int2:
+ *this = *(Int2*)data;
+ break;
+ case VariantType::Int3:
+ *this = *(Int3*)data;
+ break;
+ case VariantType::Int4:
+ *this = *(Int4*)data;
+ break;
+ case VariantType::Int16:
+ *this = *(int16*)data;
+ break;
+ case VariantType::Uint16:
+ *this = *(uint16*)data;
+ break;
+ case VariantType::Double2:
+ *this = *(Double2*)data;
+ break;
+ case VariantType::Double3:
+ *this = *(Double3*)data;
+ break;
+ case VariantType::Double4:
+ *this = *(Double4*)data;
+ break;
+ }
+ }
+}
+
bool Variant::CanCast(const Variant& v, const VariantType& to)
{
if (v.Type == to)
@@ -3680,6 +3798,7 @@ void Variant::AllocStructure()
const ScriptingType& type = typeHandle.GetType();
AsBlob.Length = type.Size;
AsBlob.Data = Allocator::Allocate(AsBlob.Length);
+ Platform::MemoryClear(AsBlob.Data, AsBlob.Length);
type.Struct.Ctor(AsBlob.Data);
}
else if (typeName == "System.Int16" || typeName == "System.UInt16")
diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h
index 7745307c3..9776c8e0b 100644
--- a/Source/Engine/Core/Types/Variant.h
+++ b/Source/Engine/Core/Types/Variant.h
@@ -359,6 +359,9 @@ public:
void SetAsset(Asset* asset);
String ToString() const;
+ // Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject).
+ void Inline();
+
FORCE_INLINE Variant Cast(const VariantType& to) const
{
return Cast(*this, to);
diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs
index 7eda1cdec..797b02d5b 100644
--- a/Source/Engine/Engine/NativeInterop.Managed.cs
+++ b/Source/Engine/Engine/NativeInterop.Managed.cs
@@ -50,13 +50,13 @@ namespace FlaxEngine.Interop
/// The resources must be released by calling FreePooled() instead of Free()-method.
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);
return managedArray;
}
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)
=> new ManagedArray(ptr, length, arrayType, elementType);
@@ -86,7 +86,7 @@ namespace FlaxEngine.Interop
_length = arr.Length;
_arrayType = arrayType;
_elementType = arr.GetType().GetElementType();
- _elementSize = Marshal.SizeOf(_elementType);
+ _elementSize = NativeInterop.GetTypeSize(_elementType);
}
internal void Allocate(int length) where T : unmanaged
@@ -117,7 +117,7 @@ namespace FlaxEngine.Interop
_length = length;
_arrayType = arrayType;
_elementType = elementType;
- _elementSize = Marshal.SizeOf(elementType);
+ _elementSize = NativeInterop.GetTypeSize(_elementType);
}
~ManagedArray()
diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs
index a3531d11d..2e3bc9183 100644
--- a/Source/Engine/Engine/NativeInterop.Marshallers.cs
+++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs
@@ -350,14 +350,14 @@ namespace FlaxEngine.Interop
#endif
public static class NativeToManaged
{
- public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
+ public static T[] AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
{
if (unmanaged is null)
return null;
return new T[numElements];
}
- public static Span GetManagedValuesDestination(T[]? managed) => managed;
+ public static Span GetManagedValuesDestination(T[] managed) => managed;
public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
{
@@ -390,7 +390,7 @@ namespace FlaxEngine.Interop
#endif
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)
{
@@ -399,10 +399,10 @@ namespace FlaxEngine.Interop
}
numElements = managed.Length;
ManagedArray managedArray = ManagedArray.AllocatePooledArray(managed.Length);
- return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
+ return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Normal);
}
- public static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed;
+ public static ReadOnlySpan GetManagedValuesSource(T[] managed) => managed;
public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
{
@@ -431,7 +431,7 @@ namespace FlaxEngine.Interop
ManagedArray unmanagedArray;
ManagedHandle handle;
- public void FromManaged(T[]? managed)
+ public void FromManaged(T[] managed)
{
if (managed == null)
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)
{
@@ -489,7 +489,7 @@ namespace FlaxEngine.Interop
return (TUnmanagedElement*)handle;
}
- public static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed;
+ public static ReadOnlySpan GetManagedValuesSource(T[] managed) => managed;
public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
{
@@ -499,9 +499,9 @@ namespace FlaxEngine.Interop
return unmanagedArray.ToSpan();
}
- 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 GetManagedValuesDestination(T[]? managed) => managed;
+ public static Span GetManagedValuesDestination(T[] managed) => managed;
public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
{
diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs
index c079a14e1..57454ad1e 100644
--- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs
+++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs
@@ -244,7 +244,25 @@ namespace FlaxEngine.Interop
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
typeAttributes = (uint)type.Attributes,
};
- *assemblyHandle = GetAssemblyHandle(type.Assembly);
+
+ Assembly assembly = null;
+ if (type.IsGenericType && !type.Assembly.IsCollectible)
+ {
+ // The owning assembly of a generic type with type arguments referencing
+ // collectible assemblies must be one of the collectible assemblies.
+ foreach (var genericType in type.GetGenericArguments())
+ {
+ if (genericType.Assembly.IsCollectible)
+ {
+ assembly = genericType.Assembly;
+ break;
+ }
+ }
+ }
+ if (assembly == null)
+ assembly = type.Assembly;
+
+ *assemblyHandle = GetAssemblyHandle(assembly);
}
[UnmanagedCallersOnly]
@@ -770,6 +788,13 @@ namespace FlaxEngine.Interop
field.field.SetValue(fieldOwner, value);
}
+ [UnmanagedCallersOnly]
+ internal static int FieldGetOffset(ManagedHandle fieldHandle)
+ {
+ FieldHolder field = Unsafe.As(fieldHandle.Target);
+ return (int)Marshal.OffsetOf(field.field.DeclaringType, field.field.Name);
+ }
+
[UnmanagedCallersOnly]
internal static void FieldGetValue(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr)
{
@@ -778,6 +803,15 @@ namespace FlaxEngine.Interop
field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset);
}
+ [UnmanagedCallersOnly]
+ internal static IntPtr FieldGetValueBoxed(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle)
+ {
+ object fieldOwner = fieldOwnerHandle.Target;
+ FieldHolder field = Unsafe.As(fieldHandle.Target);
+ object fieldValue = field.field.GetValue(fieldOwner);
+ return Invoker.MarshalReturnValueGeneric(field.field.FieldType, fieldValue);
+ }
+
[UnmanagedCallersOnly]
internal static void WriteArrayReference(ManagedHandle arrayHandle, IntPtr valueHandle, int index)
{
@@ -885,6 +919,7 @@ namespace FlaxEngine.Interop
handle.Free();
fieldHandleCacheCollectible.Clear();
#endif
+ _typeSizeCache.Clear();
foreach (var pair in classAttributesCacheCollectible)
pair.Value.Free();
@@ -923,7 +958,7 @@ namespace FlaxEngine.Interop
if (nativeType.IsClass)
size = sizeof(IntPtr);
else
- size = Marshal.SizeOf(nativeType);
+ size = GetTypeSize(nativeType);
return size;
}
diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
index faa19d060..acfd7181b 100644
--- a/Source/Engine/Engine/NativeInterop.cs
+++ b/Source/Engine/Engine/NativeInterop.cs
@@ -46,6 +46,7 @@ namespace FlaxEngine.Interop
#endif
private static Dictionary
Sky = 1 << 26,
+ ///
+ /// Shows/hides light debug shapes.
+ ///
+ LightsDebug = 1 << 27,
+
///
/// Default flags for Game.
///
diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h
index 4dbcf6d06..422e554a0 100644
--- a/Source/Engine/Graphics/GPUDevice.h
+++ b/Source/Engine/Graphics/GPUDevice.h
@@ -238,7 +238,7 @@ public:
///
/// Gets the default material.
///
- MaterialBase* GetDefaultMaterial() const;
+ API_PROPERTY() MaterialBase* GetDefaultMaterial() const;
///
/// Gets the default material (Deformable domain).
diff --git a/Source/Engine/Graphics/Shaders/GPUShader.cpp b/Source/Engine/Graphics/Shaders/GPUShader.cpp
index 2666bf36b..3639e01b1 100644
--- a/Source/Engine/Graphics/Shaders/GPUShader.cpp
+++ b/Source/Engine/Graphics/Shaders/GPUShader.cpp
@@ -145,7 +145,7 @@ bool GPUShader::Create(MemoryReadStream& stream)
// Create CB
#if GPU_ENABLE_RESOURCE_NAMING
- String name = ToString() + TEXT(".CB") + i;
+ String name = String::Format(TEXT("{}.CB{}"), ToString(), i);
#else
String name;
#endif
diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp
index 67d84b81d..08fb77223 100644
--- a/Source/Engine/Level/Actor.cpp
+++ b/Source/Engine/Level/Actor.cpp
@@ -1337,6 +1337,20 @@ Actor* Actor::FindActor(const MClass* type, const StringView& name) const
return nullptr;
}
+Actor* Actor::FindActor(const MClass* type, const Tag& tag) const
+{
+ CHECK_RETURN(type, nullptr);
+ if (GetClass()->IsSubClassOf(type) && HasTag(tag))
+ return const_cast(this);
+ for (auto child : Children)
+ {
+ const auto actor = child->FindActor(type, tag);
+ if (actor)
+ return actor;
+ }
+ return nullptr;
+}
+
Script* Actor::FindScript(const MClass* type) const
{
CHECK_RETURN(type, nullptr);
diff --git a/Source/Engine/Level/Actor.cs b/Source/Engine/Level/Actor.cs
index 957d09c7a..661b350cb 100644
--- a/Source/Engine/Level/Actor.cs
+++ b/Source/Engine/Level/Actor.cs
@@ -269,6 +269,17 @@ namespace FlaxEngine
{
return FindActor(typeof(T), name) as T;
}
+
+ ///
+ /// Tries to find actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy).
+ ///
+ /// A tag on the object.
+ /// Type of the object.
+ /// Actor instance if found, null otherwise.
+ public T FindActor(Tag tag) where T : Actor
+ {
+ return FindActor(typeof(T), tag) as T;
+ }
///
/// Searches for all actors of a specific type in this actor children list.
diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h
index fb9fb0848..510bc9eb8 100644
--- a/Source/Engine/Level/Actor.h
+++ b/Source/Engine/Level/Actor.h
@@ -739,6 +739,14 @@ public:
/// Actor instance if found, null otherwise.
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name) const;
+ ///
+ /// Tries to find the actor of the given type and tag in this actor hierarchy.
+ ///
+ /// Type of the actor to search for. Includes any actors derived from the type.
+ /// The tag of the actor to search for.
+ /// Actor instance if found, null otherwise.
+ API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag) const;
+
///
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
///
@@ -759,6 +767,17 @@ public:
{
return (T*)FindActor(T::GetStaticClass(), name);
}
+
+ ///
+ /// Tries to find the actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy).
+ ///
+ /// The tag of the actor to search for.
+ /// Actor instance if found, null otherwise.
+ template
+ FORCE_INLINE T* FindActor(const Tag& tag) const
+ {
+ return (T*)FindActor(T::GetStaticClass(), tag);
+ }
///
/// Tries to find the script of the given type in this actor hierarchy (checks this actor and all children hierarchy).
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index 882026db7..dff527924 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -144,7 +144,7 @@ void AnimatedModel::SetCurrentPose(const Array& nodesTransformation, boo
Matrix invWorld;
Matrix::Invert(world, invWorld);
for (auto& m : GraphInstance.NodesPose)
- m = invWorld * m;
+ m = m * invWorld;
}
OnAnimationUpdated();
}
@@ -603,7 +603,13 @@ void AnimatedModel::OnAnimationUpdated_Sync()
// Update synchronous stuff
UpdateSockets();
ApplyRootMotion(GraphInstance.RootMotion);
- AnimationUpdated();
+ if (!_isDuringUpdateEvent)
+ {
+ // Prevent stack-overflow when gameplay modifies the pose within the event
+ _isDuringUpdateEvent = true;
+ AnimationUpdated();
+ _isDuringUpdateEvent = false;
+ }
}
void AnimatedModel::OnAnimationUpdated()
@@ -892,6 +898,31 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
DrawModes |= DrawPass::GlobalSurfaceAtlas;
}
+const Span AnimatedModel::GetMaterialSlots() const
+{
+ const auto model = SkinnedModel.Get();
+ if (model && !model->WaitForLoaded())
+ return ToSpan(model->MaterialSlots);
+ return Span();
+}
+
+MaterialBase* AnimatedModel::GetMaterial(int32 entryIndex)
+{
+ if (SkinnedModel)
+ SkinnedModel->WaitForLoaded();
+ else
+ return nullptr;
+ CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
+ MaterialBase* material = Entries[entryIndex].Material.Get();
+ if (!material)
+ {
+ material = SkinnedModel->MaterialSlots[entryIndex].Material.Get();
+ if (!material)
+ material = GPUDevice::Instance->GetDefaultMaterial();
+ }
+ return material;
+}
+
bool AnimatedModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
{
auto model = SkinnedModel.Get();
diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h
index ccd0ee3b5..763629e93 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.h
+++ b/Source/Engine/Level/Actors/AnimatedModel.h
@@ -60,6 +60,7 @@ private:
AnimationUpdateMode _actualMode;
uint32 _counter;
Real _lastMinDstSqr;
+ bool _isDuringUpdateEvent = false;
uint64 _lastUpdateFrame;
BlendShapesInstance _blendShapes;
ScriptingObjectReference _masterPose;
@@ -372,6 +373,8 @@ public:
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
+ const Span GetMaterialSlots() const override;
+ MaterialBase* GetMaterial(int32 entryIndex) override;
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
void OnDeleteObject() override;
diff --git a/Source/Engine/Level/Actors/Light.cpp b/Source/Engine/Level/Actors/Light.cpp
index 594ebdc2b..5e86f6e7e 100644
--- a/Source/Engine/Level/Actors/Light.cpp
+++ b/Source/Engine/Level/Actors/Light.cpp
@@ -26,6 +26,7 @@ void Light::OnEnable()
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
#if USE_EDITOR
GetSceneRendering()->AddViewportIcon(this);
+ GetSceneRendering()->AddLightsDebug(this);
#endif
// Base
@@ -36,6 +37,7 @@ void Light::OnDisable()
{
#if USE_EDITOR
GetSceneRendering()->RemoveViewportIcon(this);
+ GetSceneRendering()->RemoveLightsDebug(this);
#endif
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
@@ -43,6 +45,12 @@ void Light::OnDisable()
Actor::OnDisable();
}
+#if USE_EDITOR
+void Light::DrawLightsDebug(RenderView& view)
+{
+}
+#endif
+
void Light::Serialize(SerializeStream& stream, const void* otherObj)
{
// Base
diff --git a/Source/Engine/Level/Actors/Light.h b/Source/Engine/Level/Actors/Light.h
index dab613c71..86ca59ef5 100644
--- a/Source/Engine/Level/Actors/Light.h
+++ b/Source/Engine/Level/Actors/Light.h
@@ -66,6 +66,8 @@ public:
const Vector3 size(50);
return BoundingBox(_transform.Translation - size, _transform.Translation + size);
}
+
+ virtual void DrawLightsDebug(RenderView& view);
#endif
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.cpp b/Source/Engine/Level/Actors/ModelInstanceActor.cpp
index 0c1879d83..03b873864 100644
--- a/Source/Engine/Level/Actors/ModelInstanceActor.cpp
+++ b/Source/Engine/Level/Actors/ModelInstanceActor.cpp
@@ -39,10 +39,9 @@ void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material)
MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 entryIndex)
{
WaitForModelLoad();
- CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
- auto material = Entries[entryIndex].Material.Get();
+ MaterialBase* material = GetMaterial(entryIndex);
CHECK_RETURN(material && !material->WaitForLoaded(), nullptr);
- const auto result = material->CreateVirtualInstance();
+ MaterialInstance* result = material->CreateVirtualInstance();
Entries[entryIndex].Material = result;
if (_sceneRenderingKey != -1)
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h
index 825b64c23..3faad35b3 100644
--- a/Source/Engine/Level/Actors/ModelInstanceActor.h
+++ b/Source/Engine/Level/Actors/ModelInstanceActor.h
@@ -35,6 +35,17 @@ public:
///
API_PROPERTY() void SetEntries(const Array& value);
+ ///
+ /// Gets the material slots array set on the asset (eg. model or skinned model asset).
+ ///
+ API_PROPERTY(Sealed) virtual const Span GetMaterialSlots() const = 0;
+
+ ///
+ /// Gets the material used to draw the meshes which are assigned to that slot (set in Entries or model's default).
+ ///
+ /// The material slot entry index.
+ API_FUNCTION(Sealed) virtual MaterialBase* GetMaterial(int32 entryIndex) = 0;
+
///
/// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot.
///
diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp
index 49c0f4c88..912d21a6a 100644
--- a/Source/Engine/Level/Actors/PointLight.cpp
+++ b/Source/Engine/Level/Actors/PointLight.cpp
@@ -143,6 +143,16 @@ void PointLight::OnDebugDrawSelected()
LightWithShadow::OnDebugDrawSelected();
}
+void PointLight::DrawLightsDebug(RenderView& view)
+{
+ const BoundingSphere sphere(_sphere.Center - view.Origin, _sphere.Radius);
+ if (!view.CullingFrustum.Intersects(sphere) || !EnumHasAnyFlags(view.Flags, ViewFlags::PointLights))
+ return;
+
+ // Draw influence range
+ DEBUG_DRAW_WIRE_SPHERE(_sphere, Color::Yellow, 0, true);
+}
+
#endif
void PointLight::OnLayerChanged()
diff --git a/Source/Engine/Level/Actors/PointLight.h b/Source/Engine/Level/Actors/PointLight.h
index d3e170a03..50df19232 100644
--- a/Source/Engine/Level/Actors/PointLight.h
+++ b/Source/Engine/Level/Actors/PointLight.h
@@ -95,6 +95,7 @@ public:
#if USE_EDITOR
void OnDebugDraw() override;
void OnDebugDrawSelected() override;
+ void DrawLightsDebug(RenderView& view) override;
#endif
void OnLayerChanged() override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp
index 08dc5ddea..6ca841b41 100644
--- a/Source/Engine/Level/Actors/SplineModel.cpp
+++ b/Source/Engine/Level/Actors/SplineModel.cpp
@@ -341,6 +341,31 @@ void SplineModel::OnParentChanged()
OnSplineUpdated();
}
+const Span SplineModel::GetMaterialSlots() const
+{
+ const auto model = Model.Get();
+ if (model && !model->WaitForLoaded())
+ return ToSpan(model->MaterialSlots);
+ return Span();
+}
+
+MaterialBase* SplineModel::GetMaterial(int32 entryIndex)
+{
+ if (Model)
+ Model->WaitForLoaded();
+ else
+ return nullptr;
+ CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
+ MaterialBase* material = Entries[entryIndex].Material.Get();
+ if (!material)
+ {
+ material = Model->MaterialSlots[entryIndex].Material.Get();
+ if (!material)
+ material = GPUDevice::Instance->GetDefaultDeformableMaterial();
+ }
+ return material;
+}
+
bool SplineModel::HasContentLoaded() const
{
return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded();
diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h
index 50864d7b7..2a3d45a27 100644
--- a/Source/Engine/Level/Actors/SplineModel.h
+++ b/Source/Engine/Level/Actors/SplineModel.h
@@ -115,6 +115,8 @@ public:
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
void OnParentChanged() override;
+ const Span GetMaterialSlots() const override;
+ MaterialBase* GetMaterial(int32 entryIndex) override;
protected:
// [ModelInstanceActor]
diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp
index 26f1700d5..90878fc00 100644
--- a/Source/Engine/Level/Actors/SpotLight.cpp
+++ b/Source/Engine/Level/Actors/SpotLight.cpp
@@ -203,6 +203,11 @@ void SpotLight::OnDebugDrawSelected()
DEBUG_DRAW_LINE(position, position + forward * radius + right * discRadius, color, 0, true);
DEBUG_DRAW_LINE(position, position + forward * radius - right * discRadius, color, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius + up * falloffDiscRadius, color * 0.6f, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius - up * falloffDiscRadius, color * 0.6f, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius + right * falloffDiscRadius, color * 0.6f, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius - right * falloffDiscRadius, color * 0.6f, 0, true);
+
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, discRadius, color, 0, true);
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, falloffDiscRadius, color * 0.6f, 0, true);
@@ -210,6 +215,34 @@ void SpotLight::OnDebugDrawSelected()
LightWithShadow::OnDebugDrawSelected();
}
+void SpotLight::DrawLightsDebug(RenderView& view)
+{
+ const BoundingSphere sphere(_sphere.Center - view.Origin, _sphere.Radius);
+ if (!view.CullingFrustum.Intersects(sphere) || !EnumHasAnyFlags(view.Flags, ViewFlags::SpotLights))
+ return;
+
+ const auto color = Color::Yellow;
+ Vector3 right = _transform.GetRight();
+ Vector3 up = _transform.GetUp();
+ Vector3 forward = GetDirection();
+ float radius = GetScaledRadius();
+ float discRadius = radius * Math::Tan(_outerConeAngle * DegreesToRadians);
+ float falloffDiscRadius = radius * Math::Tan(_innerConeAngle * DegreesToRadians);
+ Vector3 position = GetPosition();
+
+ DEBUG_DRAW_LINE(position, position + forward * radius + up * discRadius, color, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius - up * discRadius, color, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius + right * discRadius, color, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius - right * discRadius, color, 0, true);
+
+ DEBUG_DRAW_LINE(position, position + forward * radius + up * falloffDiscRadius, color * 0.6f, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius - up * falloffDiscRadius, color * 0.6f, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius + right * falloffDiscRadius, color * 0.6f, 0, true);
+ DEBUG_DRAW_LINE(position, position + forward * radius - right * falloffDiscRadius, color * 0.6f, 0, true);
+
+ DEBUG_DRAW_CIRCLE(position + forward * radius, forward, discRadius, color, 0, true);
+ DEBUG_DRAW_CIRCLE(position + forward * radius, forward, falloffDiscRadius, color * 0.6f, 0, true);
+}
#endif
void SpotLight::Serialize(SerializeStream& stream, const void* otherObj)
diff --git a/Source/Engine/Level/Actors/SpotLight.h b/Source/Engine/Level/Actors/SpotLight.h
index 6bb0daf8d..e6179a522 100644
--- a/Source/Engine/Level/Actors/SpotLight.h
+++ b/Source/Engine/Level/Actors/SpotLight.h
@@ -127,6 +127,7 @@ public:
#if USE_EDITOR
void OnDebugDraw() override;
void OnDebugDrawSelected() override;
+ void DrawLightsDebug(RenderView& view) override;
#endif
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp
index 3eb3b6012..93eef3056 100644
--- a/Source/Engine/Level/Actors/StaticModel.cpp
+++ b/Source/Engine/Level/Actors/StaticModel.cpp
@@ -535,6 +535,31 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
}
}
+const Span StaticModel::GetMaterialSlots() const
+{
+ const auto model = Model.Get();
+ if (model && !model->WaitForLoaded())
+ return ToSpan(model->MaterialSlots);
+ return Span();
+}
+
+MaterialBase* StaticModel::GetMaterial(int32 entryIndex)
+{
+ if (Model)
+ Model->WaitForLoaded();
+ else
+ return nullptr;
+ CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
+ MaterialBase* material = Entries[entryIndex].Material.Get();
+ if (!material)
+ {
+ material = Model->MaterialSlots[entryIndex].Material.Get();
+ if (!material)
+ material = GPUDevice::Instance->GetDefaultMaterial();
+ }
+ return material;
+}
+
bool StaticModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
{
auto model = Model.Get();
diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h
index af3aa53c0..600f174c9 100644
--- a/Source/Engine/Level/Actors/StaticModel.h
+++ b/Source/Engine/Level/Actors/StaticModel.h
@@ -121,7 +121,7 @@ public:
/// The zero-based mesh index.
/// The LOD index.
/// Material or null if not assigned.
- API_FUNCTION() MaterialBase* GetMaterial(int32 meshIndex, int32 lodIndex = 0) const;
+ API_FUNCTION() MaterialBase* GetMaterial(int32 meshIndex, int32 lodIndex) const;
///
/// Gets the color of the painter vertex (this model instance).
@@ -166,6 +166,8 @@ public:
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
+ const Span GetMaterialSlots() const override;
+ MaterialBase* GetMaterial(int32 entryIndex) override;
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp
index ab8a9bbfe..587d20331 100644
--- a/Source/Engine/Level/Level.cpp
+++ b/Source/Engine/Level/Level.cpp
@@ -726,116 +726,6 @@ int32 Level::GetLayerIndex(const StringView& layer)
return result;
}
-Actor* FindActorRecursive(Actor* node, const Tag& tag)
-{
- if (node->HasTag(tag))
- return node;
- Actor* result = nullptr;
- for (Actor* child : node->Children)
- {
- result = FindActorRecursive(child, tag);
- if (result)
- break;
- }
- return result;
-}
-
-void FindActorsRecursive(Actor* node, const Tag& tag, const bool activeOnly, Array& result)
-{
- if (activeOnly && !node->GetIsActive())
- return;
- if (node->HasTag(tag))
- result.Add(node);
- for (Actor* child : node->Children)
- FindActorsRecursive(child, tag, activeOnly, result);
-}
-
-void FindActorsRecursiveByParentTags(Actor* node, const Array& tags, const bool activeOnly, Array& result)
-{
- if (activeOnly && !node->GetIsActive())
- return;
- for (Tag tag : tags)
- {
- if (node->HasTag(tag))
- {
- result.Add(node);
- break;
- }
- }
- for (Actor* child : node->Children)
- FindActorsRecursiveByParentTags(child, tags, activeOnly, result);
-}
-
-Actor* Level::FindActor(const Tag& tag, Actor* root)
-{
- PROFILE_CPU();
- if (root)
- return FindActorRecursive(root, tag);
- Actor* result = nullptr;
- for (Scene* scene : Scenes)
- {
- result = FindActorRecursive(scene, tag);
- if (result)
- break;
- }
- return result;
-}
-
-void FindActorRecursive(Actor* node, const Tag& tag, Array& result)
-{
- if (node->HasTag(tag))
- result.Add(node);
- for (Actor* child : node->Children)
- FindActorRecursive(child, tag, result);
-}
-
-Array Level::FindActors(const Tag& tag, const bool activeOnly, Actor* root)
-{
- PROFILE_CPU();
- Array result;
- if (root)
- {
- FindActorsRecursive(root, tag, activeOnly, result);
- }
- else
- {
- ScopeLock lock(ScenesLock);
- for (Scene* scene : Scenes)
- FindActorsRecursive(scene, tag, activeOnly, result);
- }
- return result;
-}
-
-Array Level::FindActorsByParentTag(const Tag& parentTag, const bool activeOnly, Actor* root)
-{
- PROFILE_CPU();
- Array result;
- const Array subTags = Tags::GetSubTags(parentTag);
-
- if (subTags.Count() == 0)
- {
- return result;
- }
- if (subTags.Count() == 1)
- {
- result = FindActors(subTags[0], activeOnly, root);
- return result;
- }
-
- if (root)
- {
- FindActorsRecursiveByParentTags(root, subTags, activeOnly, result);
- }
- else
- {
- ScopeLock lock(ScenesLock);
- for (Scene* scene : Scenes)
- FindActorsRecursiveByParentTags(scene, subTags, activeOnly, result);
- }
-
- return result;
-}
-
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
{
PROFILE_CPU();
@@ -1505,6 +1395,143 @@ Actor* Level::FindActor(const MClass* type, const StringView& name)
return result;
}
+Actor* FindActorRecursive(Actor* node, const Tag& tag)
+{
+ if (node->HasTag(tag))
+ return node;
+ Actor* result = nullptr;
+ for (Actor* child : node->Children)
+ {
+ result = FindActorRecursive(child, tag);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+Actor* FindActorRecursiveByType(Actor* node, const MClass* type, const Tag& tag)
+{
+ CHECK_RETURN(type, nullptr);
+ if (node->HasTag(tag) && node->GetClass()->IsSubClassOf(type))
+ return node;
+ Actor* result = nullptr;
+ for (Actor* child : node->Children)
+ {
+ result = FindActorRecursiveByType(child, type, tag);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+void FindActorsRecursive(Actor* node, const Tag& tag, const bool activeOnly, Array& result)
+{
+ if (activeOnly && !node->GetIsActive())
+ return;
+ if (node->HasTag(tag))
+ result.Add(node);
+ for (Actor* child : node->Children)
+ FindActorsRecursive(child, tag, activeOnly, result);
+}
+
+void FindActorsRecursiveByParentTags(Actor* node, const Array& tags, const bool activeOnly, Array& result)
+{
+ if (activeOnly && !node->GetIsActive())
+ return;
+ for (Tag tag : tags)
+ {
+ if (node->HasTag(tag))
+ {
+ result.Add(node);
+ break;
+ }
+ }
+ for (Actor* child : node->Children)
+ FindActorsRecursiveByParentTags(child, tags, activeOnly, result);
+}
+
+Actor* Level::FindActor(const Tag& tag, Actor* root)
+{
+ PROFILE_CPU();
+ if (root)
+ return FindActorRecursive(root, tag);
+ Actor* result = nullptr;
+ for (Scene* scene : Scenes)
+ {
+ result = FindActorRecursive(scene, tag);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+Actor* Level::FindActor(const MClass* type, const Tag& tag, Actor* root)
+{
+ CHECK_RETURN(type, nullptr);
+ if (root)
+ return FindActorRecursiveByType(root, type, tag);
+ Actor* result = nullptr;
+ ScopeLock lock(ScenesLock);
+ for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
+ result = Scenes[i]->FindActor(type, tag);
+ return result;
+}
+
+void FindActorRecursive(Actor* node, const Tag& tag, Array& result)
+{
+ if (node->HasTag(tag))
+ result.Add(node);
+ for (Actor* child : node->Children)
+ FindActorRecursive(child, tag, result);
+}
+
+Array Level::FindActors(const Tag& tag, const bool activeOnly, Actor* root)
+{
+ PROFILE_CPU();
+ Array result;
+ if (root)
+ {
+ FindActorsRecursive(root, tag, activeOnly, result);
+ }
+ else
+ {
+ ScopeLock lock(ScenesLock);
+ for (Scene* scene : Scenes)
+ FindActorsRecursive(scene, tag, activeOnly, result);
+ }
+ return result;
+}
+
+Array Level::FindActorsByParentTag(const Tag& parentTag, const bool activeOnly, Actor* root)
+{
+ PROFILE_CPU();
+ Array result;
+ const Array subTags = Tags::GetSubTags(parentTag);
+
+ if (subTags.Count() == 0)
+ {
+ return result;
+ }
+ if (subTags.Count() == 1)
+ {
+ result = FindActors(subTags[0], activeOnly, root);
+ return result;
+ }
+
+ if (root)
+ {
+ FindActorsRecursiveByParentTags(root, subTags, activeOnly, result);
+ }
+ else
+ {
+ ScopeLock lock(ScenesLock);
+ for (Scene* scene : Scenes)
+ FindActorsRecursiveByParentTags(scene, subTags, activeOnly, result);
+ }
+
+ return result;
+}
+
Script* Level::FindScript(const MClass* type)
{
CHECK_RETURN(type, nullptr);
diff --git a/Source/Engine/Level/Level.cs b/Source/Engine/Level/Level.cs
index c38b287ca..1e8524f2c 100644
--- a/Source/Engine/Level/Level.cs
+++ b/Source/Engine/Level/Level.cs
@@ -77,6 +77,18 @@ namespace FlaxEngine
{
return FindActor(typeof(T), name) as T;
}
+
+ ///
+ /// Tries to find actor of the given type and tag in a root actor or all loaded scenes.
+ ///
+ /// A tag on the object.
+ /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
+ /// Type of the object.
+ /// Found actor or null.
+ public static T FindActor(Tag tag, Actor root = null) where T : Actor
+ {
+ return FindActor(typeof(T), tag, root) as T;
+ }
///
/// Tries to find actor with the given ID in all loaded scenes. It's very fast O(1) lookup.
diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h
index 9a041bda9..1f0acda2d 100644
--- a/Source/Engine/Level/Level.h
+++ b/Source/Engine/Level/Level.h
@@ -371,6 +371,41 @@ public:
/// Actor instance if found, null otherwise.
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name);
+ ///
+ /// Tries to find the actor with the given tag (returns the first one found).
+ ///
+ /// The tag of the actor to search for.
+ /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
+ /// Found actor or null.
+ API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr);
+
+ ///
+ /// Tries to find the actor of the given type and tag in all the loaded scenes.
+ ///
+ /// Type of the actor to search for. Includes any actors derived from the type.
+ /// The tag of the actor to search for.
+ /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
+ /// Actor instance if found, null otherwise.
+ API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, Actor* root = nullptr);
+
+ ///
+ /// Tries to find the actors with the given tag (returns all found).
+ ///
+ /// The tag of the actor to search for.
+ /// Find only active actors.
+ /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
+ /// Found actors or empty if none.
+ API_FUNCTION() static Array FindActors(const Tag& tag, const bool activeOnly = false, Actor* root = nullptr);
+
+ ///
+ /// Search actors using a parent parentTag.
+ ///
+ /// The tag to search actors with subtags belonging to this tag
+ /// Find only active actors.
+ /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
+ /// Returns all actors that have subtags belonging to the given parent parentTag
+ API_FUNCTION() static Array FindActorsByParentTag(const Tag& parentTag, const bool activeOnly = false, Actor* root = nullptr);
+
///
/// Tries to find the actor of the given type in all the loaded scenes.
///
@@ -392,6 +427,18 @@ public:
return (T*)FindActor(T::GetStaticClass(), name);
}
+ ///
+ /// Tries to find the actor of the given type and tag in a root actor or all the loaded scenes.
+ /// The tag of the actor to search for.
+ /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
+ ///
+ /// Actor instance if found, null otherwise.
+ template
+ FORCE_INLINE static T* FindActor(const Tag& tag, Actor* root = nullptr)
+ {
+ return (T*)FindActor(T::GetStaticClass(), tag, root);
+ }
+
///
/// Tries to find the script of the given type in all the loaded scenes.
///
@@ -481,33 +528,6 @@ public:
///
API_FUNCTION() static int32 GetLayerIndex(const StringView& layer);
-public:
- ///
- /// Tries to find the actor with the given tag (returns the first one found).
- ///
- /// The tag of the actor to search for.
- /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
- /// Found actor or null.
- API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr);
-
- ///
- /// Tries to find the actors with the given tag (returns all found).
- ///
- /// The tag of the actor to search for.
- /// Find only active actors.
- /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
- /// Found actors or empty if none.
- API_FUNCTION() static Array FindActors(const Tag& tag, const bool activeOnly = false, Actor* root = nullptr);
-
- ///
- /// Search actors using a parent parentTag.
- ///
- /// The tag to search actors with subtags belonging to this tag
- /// Find only active actors.
- /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.
- /// Returns all actors that have subtags belonging to the given parent parentTag
- API_FUNCTION() static Array FindActorsByParentTag(const Tag& parentTag, const bool activeOnly = false, Actor* root = nullptr);
-
private:
// Actor API
enum class ActorEventType
diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp
index b75cd6851..65773b6c0 100644
--- a/Source/Engine/Level/Scene/SceneRendering.cpp
+++ b/Source/Engine/Level/Scene/SceneRendering.cpp
@@ -96,6 +96,16 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
physicsDebugData[i](view);
}
}
+
+ // Draw light shapes
+ if (EnumHasAnyFlags(view.Flags, ViewFlags::LightsDebug))
+ {
+ const LightsDebugCallback* lightsDebugData = LightsDebug.Get();
+ for (int32 i = 0; i < LightsDebug.Count(); i++)
+ {
+ lightsDebugData[i](view);
+ }
+ }
}
#endif
}
@@ -157,14 +167,16 @@ void SceneRendering::UpdateActor(Actor* a, int32& key)
const int32 category = a->_drawCategory;
ScopeLock lock(Locker);
auto& list = Actors[category];
- if (list.IsEmpty())
+ if (list.Count() <= key) // Ignore invalid key softly
return;
auto& e = list[key];
- ASSERT_LOW_LAYER(a == e.Actor);
- for (auto* listener : _listeners)
- listener->OnSceneRenderingUpdateActor(a, e.Bounds);
- e.LayerMask = a->GetLayerMask();
- e.Bounds = a->GetSphere();
+ if (e.Actor == a)
+ {
+ for (auto* listener : _listeners)
+ listener->OnSceneRenderingUpdateActor(a, e.Bounds);
+ e.LayerMask = a->GetLayerMask();
+ e.Bounds = a->GetSphere();
+ }
}
void SceneRendering::RemoveActor(Actor* a, int32& key)
@@ -172,14 +184,16 @@ void SceneRendering::RemoveActor(Actor* a, int32& key)
const int32 category = a->_drawCategory;
ScopeLock lock(Locker);
auto& list = Actors[category];
- if (list.HasItems())
+ if (list.Count() > key) // Ignore invalid key softly (eg. list after batch clear during scene unload)
{
- auto& e = list[key];
- ASSERT_LOW_LAYER(a == e.Actor);
- for (auto* listener : _listeners)
- listener->OnSceneRenderingRemoveActor(a);
- e.Actor = nullptr;
- e.LayerMask = 0;
+ auto& e = list.Get()[key];
+ if (e.Actor == a)
+ {
+ for (auto* listener : _listeners)
+ listener->OnSceneRenderingRemoveActor(a);
+ e.Actor = nullptr;
+ e.LayerMask = 0;
+ }
}
key = -1;
}
diff --git a/Source/Engine/Level/Scene/SceneRendering.h b/Source/Engine/Level/Scene/SceneRendering.h
index ef9fc6c3c..0afe4ea3c 100644
--- a/Source/Engine/Level/Scene/SceneRendering.h
+++ b/Source/Engine/Level/Scene/SceneRendering.h
@@ -65,6 +65,7 @@ class FLAXENGINE_API SceneRendering
{
#if USE_EDITOR
typedef Function PhysicsDebugCallback;
+ typedef Function LightsDebugCallback;
friend class ViewportIconsRendererService;
#endif
public:
@@ -95,6 +96,7 @@ public:
private:
#if USE_EDITOR
Array PhysicsDebug;
+ Array LightsDebug;
Array ViewportIcons;
#endif
@@ -153,6 +155,22 @@ public:
PhysicsDebug.Remove(f);
}
+ template
+ FORCE_INLINE void AddLightsDebug(T* obj)
+ {
+ LightsDebugCallback f;
+ f.Bind(obj);
+ LightsDebug.Add(f);
+ }
+
+ template
+ void RemoveLightsDebug(T* obj)
+ {
+ LightsDebugCallback f;
+ f.Bind(obj);
+ LightsDebug.Remove(f);
+ }
+
FORCE_INLINE void AddViewportIcon(Actor* obj)
{
ViewportIcons.Add(obj);
diff --git a/Source/Engine/Level/Spline.cs b/Source/Engine/Level/Spline.cs
index 28a6aff8c..a2c9aecde 100644
--- a/Source/Engine/Level/Spline.cs
+++ b/Source/Engine/Level/Spline.cs
@@ -23,8 +23,8 @@ namespace FlaxEngine
if (_keyframes == null || _keyframes.Length != count)
_keyframes = new BezierCurve.Keyframe[count];
#if !BUILD_RELEASE
- if (Marshal.SizeOf(typeof(BezierCurve.Keyframe)) != Transform.SizeInBytes * 3 + sizeof(float))
- throw new Exception("Invalid size of BezierCurve keyframe " + Marshal.SizeOf(typeof(BezierCurve.Keyframe)) + " bytes.");
+ if (System.Runtime.CompilerServices.Unsafe.SizeOf.Keyframe>() != Transform.SizeInBytes * 3 + sizeof(float))
+ throw new Exception("Invalid size of BezierCurve keyframe " + System.Runtime.CompilerServices.Unsafe.SizeOf.Keyframe>() + " bytes.");
#endif
Internal_GetKeyframes(__unmanagedPtr, _keyframes);
return _keyframes;
diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp
index 32b7978d0..6ec5e52c2 100644
--- a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp
+++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp
@@ -23,7 +23,7 @@ void NetworkReplicationHierarchyUpdateResult::Init()
{
_clientsHaveLocation = false;
_clients.Resize(NetworkManager::Clients.Count());
- _clientsMask = NetworkClientsMask();
+ _clientsMask = NetworkManager::Mode == NetworkManagerMode::Client ? NetworkClientsMask::All : NetworkClientsMask();
for (int32 i = 0; i < _clients.Count(); i++)
_clientsMask.SetBit(i);
_entries.Clear();
@@ -63,6 +63,17 @@ bool NetworkReplicationNode::RemoveObject(ScriptingObject* 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)
{
const int32 index = Objects.Find(obj);
@@ -156,6 +167,7 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
cell->MinCullDistance = obj.CullDistance;
}
cell->Node->AddObject(obj);
+ _objectToCell[obj.Object] = coord;
// Cache minimum culling distance for a whole cell to skip it at once
cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance);
@@ -163,14 +175,35 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj)
{
- for (const auto& e : _children)
+ Int3 coord;
+
+ if (!_objectToCell.TryGet(obj, coord))
{
- if (e.Value.Node->RemoveObject(obj))
- {
- // TODO: remove empty cells?
- // TODO: update MinCullDistance for cell?
- return true;
- }
+ return false;
+ }
+
+ if (_children[coord].Node->RemoveObject(obj))
+ {
+ _objectToCell.Remove(obj);
+ // TODO: remove empty cells?
+ // TODO: update MinCullDistance for cell?
+ 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;
}
diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h
index 36701c15d..b054de453 100644
--- a/Source/Engine/Networking/NetworkReplicationHierarchy.h
+++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h
@@ -200,6 +200,14 @@ API_CLASS(Abstract, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API Ne
/// True on successful removal, otherwise false.
API_FUNCTION() virtual bool RemoveObject(ScriptingObject* obj);
+ ///
+ /// Gets object from the hierarchy.
+ ///
+ /// The object to get.
+ /// The hierarchy object to retrieve.
+ /// True on successful retrieval, otherwise false.
+ API_FUNCTION() virtual bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result);
+
///
/// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization.
///
@@ -238,6 +246,7 @@ private:
};
Dictionary _children;
+ Dictionary _objectToCell;
public:
///
@@ -247,6 +256,7 @@ public:
void AddObject(NetworkReplicationHierarchyObject obj) override;
bool RemoveObject(ScriptingObject* obj) override;
+ bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result) override;
void Update(NetworkReplicationHierarchyUpdateResult* result) override;
};
diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp
index c41e104ef..279391e31 100644
--- a/Source/Engine/Networking/NetworkReplicator.cpp
+++ b/Source/Engine/Networking/NetworkReplicator.cpp
@@ -700,9 +700,9 @@ void NetworkReplicator::AddRPC(const ScriptingTypeHandle& typeHandle, const Stri
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(targetIds));
+ return EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan(targetIds));
}
StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name)
@@ -1113,12 +1113,12 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
return CachedWriteStream;
}
-void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds)
+bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds)
{
Scripting::ObjectsLookupIdMapping.Set(nullptr);
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
if (!info || !obj || NetworkManager::IsOffline())
- return;
+ return false;
ObjectsLock.Lock();
auto& rpc = RpcQueue.AddOne();
rpc.Object = obj;
@@ -1135,12 +1135,23 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
}
#endif
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)
{
ScopeLock lock(ObjectsLock);
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)
@@ -1193,6 +1204,7 @@ void NetworkInternal::NetworkReplicatorClear()
Objects.Remove(it);
}
}
+ Objects.Clear();
RpcQueue.Clear();
SpawnQueue.Clear();
DespawnQueue.Clear();
diff --git a/Source/Engine/Networking/NetworkReplicator.cs b/Source/Engine/Networking/NetworkReplicator.cs
index 1ca251f75..bc437b82e 100644
--- a/Source/Engine/Networking/NetworkReplicator.cs
+++ b/Source/Engine/Networking/NetworkReplicator.cs
@@ -120,10 +120,11 @@ namespace FlaxEngine.Networking
/// The RPC name.
/// The RPC serialized arguments stream returned from BeginInvokeRPC.
/// Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.
+ /// True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).
[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);
}
///
diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h
index 156e7eeea..ecccc52cd 100644
--- a/Source/Engine/Networking/NetworkReplicator.h
+++ b/Source/Engine/Networking/NetworkReplicator.h
@@ -195,13 +195,14 @@ public:
/// The RPC name.
/// The RPC serialized arguments stream returned from BeginInvokeRPC.
/// Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.
- static void EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds = Span());
+ /// True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).
+ static bool EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds = Span());
private:
#if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function& serialize, const Function& deserialize);
API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function& 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);
#endif
};
diff --git a/Source/Engine/Networking/NetworkRpc.h b/Source/Engine/Networking/NetworkRpc.h
index c0c8a558e..6e8d26752 100644
--- a/Source/Engine/Networking/NetworkRpc.h
+++ b/Source/Engine/Networking/NetworkRpc.h
@@ -41,7 +41,7 @@ struct FLAXENGINE_API NetworkRpcInfo
uint8 Client : 1;
uint8 Channel : 4;
void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag);
- void (*Invoke)(ScriptingObject* obj, void** args);
+ bool (*Invoke)(ScriptingObject* obj, void** args);
void* Tag;
///
@@ -83,10 +83,7 @@ FORCE_INLINE void NetworkRpcInitArg(Array>& args, con
{ \
Array> args; \
NetworkRpcInitArg(args, __VA_ARGS__); \
- rpcInfo.Invoke(this, args.Get()); \
- if (rpcInfo.Server && networkMode == NetworkManagerMode::Client) \
- return; \
- if (rpcInfo.Client && networkMode == NetworkManagerMode::Server) \
+ if (rpcInfo.Invoke(this, args.Get())) \
return; \
} \
}
diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp
index 649d01d85..9ef996869 100644
--- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp
+++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp
@@ -189,7 +189,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va
const Matrix viewProjection = context.ViewTask ? context.ViewTask->View.PrevViewProjection : Matrix::Identity;
const Float3 position = (Float3)TryGetValue(node->GetBox(0), Value::Zero);
Float4 projPos;
- Float3::Transform(position, viewProjection);
+ Float3::Transform(position, viewProjection, projPos);
projPos /= projPos.W;
value = Float2(projPos.X * 0.5f + 0.5f, projPos.Y * 0.5f + 0.5f);
break;
diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp
index 2ed59c8f6..f5f729d39 100644
--- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp
+++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp
@@ -230,10 +230,11 @@ void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
{
const Vector3 currentPos = wheel.Collider->GetPosition();
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(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 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)
{
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 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;
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.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(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_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())
{
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.SuspensionTraceStart, 5.0f), Color::AliceBlue, 0, false);
diff --git a/Source/Engine/Platform/Base/WindowBase.cpp b/Source/Engine/Platform/Base/WindowBase.cpp
index e2460e373..5cff76361 100644
--- a/Source/Engine/Platform/Base/WindowBase.cpp
+++ b/Source/Engine/Platform/Base/WindowBase.cpp
@@ -125,6 +125,7 @@ WindowBase::~WindowBase()
{
ASSERT(!RenderTask);
ASSERT(!_swapChain);
+ WindowsManager::Unregister((Window*)this);
}
bool WindowBase::IsMain() const
diff --git a/Source/Engine/Platform/Base/WindowsManager.cpp b/Source/Engine/Platform/Base/WindowsManager.cpp
index ed2106cf5..0d58008d8 100644
--- a/Source/Engine/Platform/Base/WindowsManager.cpp
+++ b/Source/Engine/Platform/Base/WindowsManager.cpp
@@ -25,7 +25,6 @@ WindowsManagerService WindowsManagerServiceInstance;
Window* WindowsManager::GetByNativePtr(void* handle)
{
Window* result = nullptr;
-
WindowsLocker.Lock();
for (int32 i = 0; i < Windows.Count(); i++)
{
@@ -36,7 +35,6 @@ Window* WindowsManager::GetByNativePtr(void* handle)
}
}
WindowsLocker.Unlock();
-
return result;
}
@@ -61,7 +59,7 @@ void WindowsManagerService::Update()
// Update windows
const float deltaTime = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
WindowsManager::WindowsLocker.Lock();
- for (auto& win : WindowsManager::Windows)
+ for (Window* win : WindowsManager::Windows)
{
if (win && win->IsVisible())
win->OnUpdate(deltaTime);
@@ -74,7 +72,7 @@ void WindowsManagerService::Dispose()
// Close remaining windows
WindowsManager::WindowsLocker.Lock();
auto windows = WindowsManager::Windows;
- for (auto& win : windows)
+ for (Window* win : windows)
{
win->Close(ClosingReason::EngineExit);
}
diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp
index 04f928bba..46b978e1d 100644
--- a/Source/Engine/Platform/Mac/MacPlatform.cpp
+++ b/Source/Engine/Platform/Mac/MacPlatform.cpp
@@ -56,36 +56,94 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
{
if (CommandLine::Options.Headless)
return DialogResult::None;
- CFStringRef textRef = AppleUtils::ToString(text);
- CFStringRef captionRef = AppleUtils::ToString(caption);
- CFOptionFlags flags = 0;
+ NSAlert* alert = [[NSAlert alloc] init];
+ ASSERT(alert);
switch (buttons)
{
case MessageBoxButtons::AbortRetryIgnore:
+ [alert addButtonWithTitle:@"Abort"];
+ [alert addButtonWithTitle:@"Retry"];
+ [alert addButtonWithTitle:@"Ignore"];
+ break;
+ case MessageBoxButtons::OK:
+ [alert addButtonWithTitle:@"OK"];
+ break;
case MessageBoxButtons::OKCancel:
+ [alert addButtonWithTitle:@"OK"];
+ [alert addButtonWithTitle:@"Cancel"];
+ break;
case MessageBoxButtons::RetryCancel:
+ [alert addButtonWithTitle:@"Retry"];
+ [alert addButtonWithTitle:@"Cancel"];
+ break;
case MessageBoxButtons::YesNo:
+ [alert addButtonWithTitle:@"Yes"];
+ [alert addButtonWithTitle:@"No"];
+ break;
case MessageBoxButtons::YesNoCancel:
- flags |= kCFUserNotificationCancelResponse;
+ [alert addButtonWithTitle:@"Yes"];
+ [alert addButtonWithTitle:@"No"];
+ [alert addButtonWithTitle:@"Cancel"];
break;
}
switch (icon)
{
case MessageBoxIcon::Information:
- flags |= kCFUserNotificationNoteAlertLevel;
+ [alert setAlertStyle:NSAlertStyleCritical];
break;
case MessageBoxIcon::Error:
case MessageBoxIcon::Stop:
- flags |= kCFUserNotificationStopAlertLevel;
+ [alert setAlertStyle:NSAlertStyleInformational];
break;
case MessageBoxIcon::Warning:
- flags |= kCFUserNotificationCautionAlertLevel;
+ [alert setAlertStyle:NSAlertStyleWarning];
break;
}
- SInt32 result = CFUserNotificationDisplayNotice(0, flags, nullptr, nullptr, nullptr, captionRef, textRef, nullptr);
- CFRelease(captionRef);
- CFRelease(textRef);
- return DialogResult::OK;
+ [alert setMessageText:(NSString*)AppleUtils::ToString(caption)];
+ [alert setInformativeText:(NSString*)AppleUtils::ToString(text)];
+ NSInteger button = [alert runModal];
+ DialogResult result = DialogResult::OK;
+ switch (buttons)
+ {
+ case MessageBoxButtons::AbortRetryIgnore:
+ if (button == NSAlertFirstButtonReturn)
+ result = DialogResult::Abort;
+ else if (button == NSAlertSecondButtonReturn)
+ result = DialogResult::Retry;
+ else
+ result = DialogResult::Ignore;
+ break;
+ case MessageBoxButtons::OK:
+ result = DialogResult::OK;
+ break;
+ case MessageBoxButtons::OKCancel:
+ if (button == NSAlertFirstButtonReturn)
+ result = DialogResult::OK;
+ else
+ result = DialogResult::Cancel;
+ break;
+ case MessageBoxButtons::RetryCancel:
+ if (button == NSAlertFirstButtonReturn)
+ result = DialogResult::Retry;
+ else
+ result = DialogResult::Cancel;
+ break;
+ case MessageBoxButtons::YesNo:
+ if (button == NSAlertFirstButtonReturn)
+ result = DialogResult::Yes;
+ else
+ result = DialogResult::No;
+ break;
+ case MessageBoxButtons::YesNoCancel:
+ if (button == NSAlertFirstButtonReturn)
+ result = DialogResult::Yes;
+ else if (button == NSAlertSecondButtonReturn)
+ result = DialogResult::No;
+ else
+ result = DialogResult::Cancel;
+ break;
+ }
+ return result;
}
Float2 AppleUtils::PosToCoca(const Float2& pos)
@@ -301,15 +359,16 @@ Float2 MacPlatform::GetMousePosition()
CGEventRef event = CGEventCreate(nullptr);
CGPoint cursor = CGEventGetLocation(event);
CFRelease(event);
- return Float2((float)cursor.x, (float)cursor.y);
+ return Float2((float)cursor.x, (float)cursor.y) * MacPlatform::ScreenScale;
}
void MacPlatform::SetMousePosition(const Float2& pos)
{
CGPoint cursor;
- cursor.x = (CGFloat)pos.X;
- cursor.y = (CGFloat)pos.Y;
+ cursor.x = (CGFloat)(pos.X / MacPlatform::ScreenScale);
+ cursor.y = (CGFloat)(pos.Y / MacPlatform::ScreenScale);
CGWarpMouseCursorPosition(cursor);
+ CGAssociateMouseAndMouseCursorPosition(true);
}
Float2 MacPlatform::GetDesktopSize()
diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp
index 90bb07279..aeee93e37 100644
--- a/Source/Engine/Platform/Mac/MacWindow.cpp
+++ b/Source/Engine/Platform/Mac/MacWindow.cpp
@@ -72,11 +72,11 @@ KeyboardKeys GetKey(NSEvent* event)
case 0x30: return KeyboardKeys::Tab;
case 0x31: return KeyboardKeys::Spacebar;
case 0x32: return KeyboardKeys::BackQuote;
- case 0x33: return KeyboardKeys::Delete;
+ case 0x33: return KeyboardKeys::Backspace;
//case 0x34:
case 0x35: return KeyboardKeys::Escape;
- //case 0x36:
- //case 0x37: Command
+ case 0x36: return KeyboardKeys::Control; // Command (right)
+ case 0x37: return KeyboardKeys::Control; // Command (left)
case 0x38: return KeyboardKeys::Shift;
case 0x39: return KeyboardKeys::Capital;
case 0x3A: return KeyboardKeys::Alt;
@@ -378,7 +378,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
{
KeyboardKeys key = GetKey(event);
if (key != KeyboardKeys::None)
- Input::Keyboard->OnKeyDown(key);
+ Input::Keyboard->OnKeyDown(key, Window);
// Send a text input event
switch (key)
@@ -400,7 +400,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
if (length >= 16)
length = 15;
[text getCharacters:buffer range:NSMakeRange(0, length)];
- Input::Keyboard->OnCharInput((Char)buffer[0]);
+ Input::Keyboard->OnCharInput((Char)buffer[0], Window);
}
}
@@ -408,7 +408,32 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
{
KeyboardKeys key = GetKey(event);
if (key != KeyboardKeys::None)
- Input::Keyboard->OnKeyUp(key);
+ Input::Keyboard->OnKeyUp(key, Window);
+}
+
+- (void)flagsChanged:(NSEvent*)event
+{
+ int32 modMask;
+ int32 keyCode = [event keyCode];
+ if (keyCode == 0x36 || keyCode == 0x37)
+ modMask = NSEventModifierFlagCommand;
+ else if (keyCode == 0x38 || keyCode == 0x3c)
+ modMask = NSEventModifierFlagShift;
+ else if (keyCode == 0x3a || keyCode == 0x3d)
+ modMask = NSEventModifierFlagOption;
+ else if (keyCode == 0x3b || keyCode == 0x3e)
+ modMask = NSEventModifierFlagControl;
+ else
+ return;
+ KeyboardKeys key = GetKey(event);
+ if (key != KeyboardKeys::None)
+ {
+ int32 modifierFlags = [event modifierFlags];
+ if ((modifierFlags & modMask) == modMask)
+ Input::Keyboard->OnKeyDown(key, Window);
+ else
+ Input::Keyboard->OnKeyUp(key, Window);
+ }
}
- (void)scrollWheel:(NSEvent*)event
@@ -643,7 +668,6 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
// TODO: impl StartPosition for MacWindow
// TODO: impl Fullscreen for MacWindow
// TODO: impl ShowInTaskbar for MacWindow
- // TODO: impl AllowInput for MacWindow
// TODO: impl IsTopmost for MacWindow
}
diff --git a/Source/Engine/Platform/StringUtils.h b/Source/Engine/Platform/StringUtils.h
index 04e644d2e..72b3b5f4b 100644
--- a/Source/Engine/Platform/StringUtils.h
+++ b/Source/Engine/Platform/StringUtils.h
@@ -179,7 +179,7 @@ public:
public:
// 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
static void ConvertUTF162ANSI(const Char* from, char* to, int32 len);
diff --git a/Source/Engine/Platform/Unix/UnixStringUtils.cpp b/Source/Engine/Platform/Unix/UnixStringUtils.cpp
index 7e5c34b24..daa705769 100644
--- a/Source/Engine/Platform/Unix/UnixStringUtils.cpp
+++ b/Source/Engine/Platform/Unix/UnixStringUtils.cpp
@@ -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;
char16_t* u16cur = to;
while (u8cur < u8end)
{
- len = Utf8CodepointLength(*u8cur);
+ int32 len = Utf8CodepointLength(*u8cur);
uint32 codepoint = Utf8ToUtf32Codepoint(u8cur, len);
// 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;
}
+ toLength = (int32)(u16cur - to);
}
static const char32_t kByteMask = 0x000000BF;
diff --git a/Source/Engine/Platform/Win32/Win32StringUtils.cpp b/Source/Engine/Platform/Win32/Win32StringUtils.cpp
index d1634d90d..ea1eb8fe9 100644
--- a/Source/Engine/Platform/Win32/Win32StringUtils.cpp
+++ b/Source/Engine/Platform/Win32/Win32StringUtils.cpp
@@ -179,10 +179,12 @@ const char* StringUtils::Find(const char* str, const char* 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)
- mbstowcs(to, from, len);
+ if (fromLength)
+ toLength = (int32)mbstowcs(to, from, fromLength);
+ else
+ toLength = 0;
}
void StringUtils::ConvertUTF162ANSI(const Char* from, char* to, int32 len)
diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp
index 8d02312af..4d43272d1 100644
--- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp
+++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp
@@ -992,7 +992,8 @@ void ReadPipe(HANDLE pipe, Array& rawData, Array& logData, LogType l
// Log contents
logData.Clear();
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';
if (settings.LogOutput)
Log::Logger::Write(logType, StringView(logData.Get(), rawData.Count()));
diff --git a/Source/Engine/Renderer/Renderer.cs b/Source/Engine/Renderer/Renderer.cs
index bfb81a84c..71dca424b 100644
--- a/Source/Engine/Renderer/Renderer.cs
+++ b/Source/Engine/Renderer/Renderer.cs
@@ -17,10 +17,13 @@ namespace FlaxEngine
[Unmanaged]
public static void DrawSceneDepth(GPUContext context, SceneRenderTask task, GPUTexture output, List customActors)
{
- if (customActors.Count == 0)
- return;
- var temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME
- var tempCount = temp.Length;
+ Actor[] temp = null;
+ int tempCount = 0;
+ if (customActors != null && customActors.Count != 0)
+ {
+ temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME
+ tempCount = temp.Length;
+ }
Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), temp, ref tempCount);
}
@@ -32,7 +35,14 @@ namespace FlaxEngine
[Unmanaged]
public static void DrawActors(ref RenderContext renderContext, List customActors)
{
- DrawActors(ref renderContext, Utils.ExtractArrayFromList(customActors));
+ Actor[] temp = null;
+ int tempCount = 0;
+ if (customActors != null && customActors.Count != 0)
+ {
+ temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME
+ tempCount = temp.Length;
+ }
+ Internal_DrawActors(ref renderContext, temp, ref tempCount);
}
}
}
diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp
index 1736cc106..8d75d7a95 100644
--- a/Source/Engine/Scripting/Runtime/DotNet.cpp
+++ b/Source/Engine/Scripting/Runtime/DotNet.cpp
@@ -846,7 +846,7 @@ bool MClass::IsSubClassOf(const MClass* klass, bool checkInterfaces) const
bool MClass::HasInterface(const MClass* klass) const
{
static void* TypeIsAssignableFrom = GetStaticMethodPointer(TEXT("TypeIsAssignableFrom"));
- return klass && CallStaticMethod(TypeIsAssignableFrom, _handle, klass->_handle);
+ return klass && CallStaticMethod(TypeIsAssignableFrom, klass->_handle, _handle);
}
bool MClass::IsInstanceOfType(MObject* object) const
@@ -1163,8 +1163,8 @@ MType* MField::GetType() const
int32 MField::GetOffset() const
{
- MISSING_CODE("TODO: MField::GetOffset"); // TODO: MField::GetOffset
- return 0;
+ static void* FieldGetOffsetPtr = GetStaticMethodPointer(TEXT("FieldGetOffset"));
+ return CallStaticMethod(FieldGetOffsetPtr, _handle);
}
void MField::GetValue(MObject* instance, void* result) const
@@ -1175,8 +1175,8 @@ void MField::GetValue(MObject* instance, void* result) const
MObject* MField::GetValueBoxed(MObject* instance) const
{
- MISSING_CODE("TODO: MField::GetValueBoxed"); // TODO: MField::GetValueBoxed
- return nullptr;
+ static void* FieldGetValueBoxedPtr = GetStaticMethodPointer(TEXT("FieldGetValueBoxed"));
+ return CallStaticMethod(FieldGetValueBoxedPtr, instance, _handle);
}
void MField::SetValue(MObject* instance, void* value) const
diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp
index bbd109cd1..bbfa802f1 100644
--- a/Source/Engine/Scripting/Scripting.cpp
+++ b/Source/Engine/Scripting/Scripting.cpp
@@ -476,12 +476,28 @@ bool Scripting::Load()
// Load FlaxEngine
const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
- if (((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->Load(flaxEnginePath))
+ auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine();
+ if (flaxEngineModule->Assembly->Load(flaxEnginePath))
{
LOG(Error, "Failed to load FlaxEngine C# assembly.");
return true;
}
+ // Insert type aliases for vector types that don't exist in C++ but are just typedef (properly redirect them to actual types)
+ // TODO: add support for automatic typedef aliases setup for scripting module to properly lookup type from the alias typename
+#if USE_LARGE_WORLDS
+ flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double2"];
+ flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double3"];
+ flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double4"];
+#else
+ flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float2"];
+ flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float3"];
+ flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float4"];
+#endif
+ flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector2")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"];
+ flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector3")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"];
+ flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector4")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"];
+
#if USE_EDITOR
// Skip loading game modules in Editor on startup - Editor loads them later during splash screen (eg. after first compilation)
static bool SkipFirstLoad = true;
@@ -559,6 +575,16 @@ void Scripting::Release()
}
_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)
{
LOG(Info, "Unloading binary modules");
diff --git a/Source/Engine/Serialization/ReadStream.h b/Source/Engine/Serialization/ReadStream.h
index 9d3215442..be80e3249 100644
--- a/Source/Engine/Serialization/ReadStream.h
+++ b/Source/Engine/Serialization/ReadStream.h
@@ -6,6 +6,7 @@
#include "Engine/Core/Templates.h"
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);
///
/// Base class for all data read streams
@@ -159,6 +160,34 @@ public:
Read(ptr);
v = ptr;
}
+ template
+ FORCE_INLINE void Read(SoftObjectReference& v)
+ {
+ uint32 id[4];
+ ReadBytes(id, sizeof(id));
+ v.Set(*(Guid*)id);
+ }
+ template
+ FORCE_INLINE void Read(AssetReference& v)
+ {
+ uint32 id[4];
+ ReadBytes(id, sizeof(id));
+ v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer);
+ }
+ template
+ FORCE_INLINE void Read(WeakAssetReference& v)
+ {
+ uint32 id[4];
+ ReadBytes(id, sizeof(id));
+ v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer);
+ }
+ template
+ FORCE_INLINE void Read(SoftAssetReference& v)
+ {
+ uint32 id[4];
+ ReadBytes(id, sizeof(id));
+ v.Set(*(Guid*)id);
+ }
///
/// Read data array
diff --git a/Source/Engine/Serialization/Stream.h b/Source/Engine/Serialization/Stream.h
index 83f0eaf21..e477eee95 100644
--- a/Source/Engine/Serialization/Stream.h
+++ b/Source/Engine/Serialization/Stream.h
@@ -17,6 +17,14 @@ class ISerializable;
class ScriptingObject;
template
class ScriptingObjectReference;
+template
+class SoftObjectReference;
+template
+class AssetReference;
+template
+class WeakAssetReference;
+template
+class SoftAssetReference;
///
/// Base class for all data streams (memory streams, file streams etc.)
diff --git a/Source/Engine/Serialization/WriteStream.h b/Source/Engine/Serialization/WriteStream.h
index 92ac211ca..95bd87df9 100644
--- a/Source/Engine/Serialization/WriteStream.h
+++ b/Source/Engine/Serialization/WriteStream.h
@@ -176,6 +176,26 @@ public:
{
Write(v.Get());
}
+ template
+ FORCE_INLINE void Write(const SoftObjectReference& v)
+ {
+ Write(v.Get());
+ }
+ template
+ FORCE_INLINE void Write(const AssetReference& v)
+ {
+ Write(v.Get());
+ }
+ template
+ FORCE_INLINE void Write(const WeakAssetReference& v)
+ {
+ Write(v.Get());
+ }
+ template
+ FORCE_INLINE void Write(const SoftAssetReference& v)
+ {
+ Write(v.Get());
+ }
template
void Write(const Array& data)
diff --git a/Source/Engine/Utilities/StringConverter.h b/Source/Engine/Utilities/StringConverter.h
index 340278c00..e494a8b94 100644
--- a/Source/Engine/Utilities/StringConverter.h
+++ b/Source/Engine/Utilities/StringConverter.h
@@ -49,7 +49,7 @@ public:
{
}
- StringAsANSI(const Char* text, const int32 length)
+ StringAsANSI(const Char* text, int32 length)
{
if (length + 1 < InlinedSize)
{
@@ -83,7 +83,7 @@ public:
{
}
- StringAsUTF8(const Char* text, const int32 length)
+ StringAsUTF8(const Char* text, int32 length)
{
int32 lengthUtf8;
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)
{
- StringUtils::ConvertANSI2UTF16(text, this->_inlined, length);
+ StringUtils::ConvertANSI2UTF16(text, this->_inlined, length, length);
this->_inlined[length] = 0;
}
else
{
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;
}
}
diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs
index 5ac43ecd9..cb385efcf 100644
--- a/Source/Engine/Utilities/Utils.cs
+++ b/Source/Engine/Utilities/Utils.cs
@@ -255,7 +255,7 @@ namespace FlaxEngine
#else
private class ExtractArrayFromListContext
{
- public static FieldInfo? itemsField;
+ public static FieldInfo itemsField;
}
internal static T[] ExtractArrayFromList(List list)
{
diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp
index 37154e09f..ca4495f67 100644
--- a/Source/Engine/Visject/VisjectGraph.cpp
+++ b/Source/Engine/Visject/VisjectGraph.cpp
@@ -675,6 +675,10 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
}
}
}
+
+ // For in-built structures try to convert it into internal format for better comparability with the scripting
+ value.Inline();
+
break;
}
// Unpack Structure
diff --git a/Source/Engine/Visject/VisjectGraph.h b/Source/Engine/Visject/VisjectGraph.h
index 238823afd..0d841f3d4 100644
--- a/Source/Engine/Visject/VisjectGraph.h
+++ b/Source/Engine/Visject/VisjectGraph.h
@@ -246,6 +246,7 @@ public:
void ProcessGroupCollections(Box* box, Node* node, Value& value);
protected:
+ void InlineVariantStruct(Variant& v);
virtual Value eatBox(Node* caller, Box* box) = 0;
virtual Graph* GetCurrentGraph() const = 0;
diff --git a/Source/Shaders/Common.hlsl b/Source/Shaders/Common.hlsl
index 13d2ad28f..19ba3f861 100644
--- a/Source/Shaders/Common.hlsl
+++ b/Source/Shaders/Common.hlsl
@@ -26,6 +26,7 @@
#define FEATURE_LEVEL_ES3_1 2
#define FEATURE_LEVEL_SM4 3
#define FEATURE_LEVEL_SM5 4
+#define FEATURE_LEVEL_SM6 5
#if !defined(FEATURE_LEVEL)
#error "Invalid platform defines"
#endif
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
index 0cb8982a2..d69b8dcbf 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
@@ -1204,7 +1204,7 @@ namespace Flax.Build.Bindings
if (apiType != null)
{
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
contents.Append(indent).AppendFormat("auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine();
if (parameterInfo.Type.IsPtr && !parameterInfo.Type.IsRef)
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
index b09b0c137..1eeefaf27 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
@@ -847,6 +847,9 @@ namespace Flax.Build.Bindings
case "hidden":
desc.IsHidden = true;
break;
+ case "sealed":
+ desc.IsVirtual = false;
+ break;
case "tag":
ParseTag(ref desc.Tags, tag);
break;
@@ -1417,6 +1420,9 @@ namespace Flax.Build.Bindings
context.Tokenizer.SkipUntil(TokenType.Comma, out desc.Lang);
desc.Code = context.Tokenizer.ExpectToken(TokenType.String).Value.Replace("\\\"", "\"");
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);
return desc;
}
diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs
index 7b2326527..df47c85e4 100644
--- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs
+++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs
@@ -144,7 +144,7 @@ namespace Flax.Build.Bindings
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);
if (type == null)
@@ -155,7 +155,7 @@ namespace Flax.Build.Bindings
return type.FullNameNative;
var sb = new StringBuilder(64);
- if (IsConst)
+ if (IsConst && canConst)
sb.Append("const ");
sb.Append(type.FullNameNative);
if (GenericArgs != null)
@@ -171,7 +171,7 @@ namespace Flax.Build.Bindings
}
if (IsPtr)
sb.Append('*');
- if (IsRef)
+ if (IsRef && canRef)
sb.Append('&');
return sb.ToString();
}
diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs
index 2860148de..4fbabe986 100644
--- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs
+++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs
@@ -330,7 +330,7 @@ namespace Flax.Build
var referenceBuildOptions = GetBuildOptions(referenceTarget, configurationData.TargetBuildOptions.Platform, configurationData.TargetBuildOptions.Toolchain, configurationData.Architecture, configurationData.Configuration, reference.Project.ProjectFolderPath);
referenceBuildOptions.Flags |= BuildFlags.GenerateProject;
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)
{
project.Defines.Add(binaryModule.Key.ToUpperInvariant() + "_API=");
diff --git a/Source/Tools/Flax.Build/Build/Builder.cs b/Source/Tools/Flax.Build/Build/Builder.cs
index 0765a94ab..aa0f7c879 100644
--- a/Source/Tools/Flax.Build/Build/Builder.cs
+++ b/Source/Tools/Flax.Build/Build/Builder.cs
@@ -143,6 +143,16 @@ namespace Flax.Build
{
Log.Info("Removing: " + targetBuildOptions.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();
}
}
}
diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs
index e6a250eb5..13433f92c 100644
--- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs
+++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text;
using Flax.Build.Graph;
using Flax.Build.NativeCpp;
using Flax.Deploy;
@@ -12,7 +13,7 @@ namespace Flax.Build
{
static partial class Builder
{
- public static event Action> BuildDotNetAssembly;
+ public static event Action> BuildDotNetAssembly;
private static void BuildTargetDotNet(RulesAssembly rules, TaskGraph graph, Target target, Platform platform, TargetConfiguration configuration)
{
@@ -151,7 +152,7 @@ namespace Flax.Build
}
}
- private static void BuildDotNet(TaskGraph graph, BuildData buildData, NativeCpp.BuildOptions buildOptions, string name, List sourceFiles, HashSet fileReferences = null, IGrouping binaryModule = null)
+ private static void BuildDotNet(TaskGraph graph, BuildData buildData, BuildOptions buildOptions, string name, List sourceFiles, HashSet fileReferences = null, IGrouping binaryModule = null)
{
// Setup build options
var buildPlatform = Platform.BuildTargetPlatform;
@@ -164,6 +165,8 @@ namespace Flax.Build
if (!dotnetSdk.IsValid)
throw new Exception("Cannot compile C# without .NET SDK");
string dotnetPath = "dotnet", referenceAnalyzers;
+ string[] runtimeVersionNameParts = dotnetSdk.RuntimeVersionName.Split('.');
+ string runtimeVersionShort = runtimeVersionNameParts[0] + '.' + runtimeVersionNameParts[1];
#else
string monoRoot, monoPath;
#endif
@@ -174,7 +177,7 @@ namespace Flax.Build
#if USE_NETCORE
dotnetPath = Path.Combine(dotnetSdk.RootPath, "dotnet.exe");
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\");
#else
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Windows", "Mono");
@@ -190,7 +193,7 @@ namespace Flax.Build
{
#if USE_NETCORE
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/");
#else
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Linux", "Mono");
@@ -204,7 +207,7 @@ namespace Flax.Build
{
#if USE_NETCORE
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/");
#else
monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Mac", "Mono");
@@ -256,6 +259,7 @@ namespace Flax.Build
args.Add(buildData.Configuration == TargetConfiguration.Release ? "/optimize+" : "/optimize-");
#else
args.Add(buildData.Configuration == TargetConfiguration.Debug ? "/optimize-" : "/optimize+");
+ args.Add(string.Format("/reference:\"{0}mscorlib.dll\"", referenceAssemblies));
#endif
args.Add(string.Format("/out:\"{0}\"", outputFile));
args.Add(string.Format("/doc:\"{0}\"", outputDocFile));
@@ -263,7 +267,6 @@ namespace Flax.Build
args.Add("/define:" + string.Join(";", buildOptions.ScriptingAPI.Defines));
if (buildData.Configuration == TargetConfiguration.Debug)
args.Add("/define:DEBUG");
- args.Add(string.Format("/reference:\"{0}mscorlib.dll\"", referenceAssemblies));
foreach (var reference in buildOptions.ScriptingAPI.SystemReferences)
args.Add(string.Format("/reference:\"{0}{1}.dll\"", referenceAssemblies, reference));
foreach (var reference in fileReferences)
@@ -275,6 +278,13 @@ namespace Flax.Build
foreach (var sourceFile in sourceFiles)
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
string responseFile = Path.Combine(buildOptions.IntermediateFolder, name + ".response");
Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args));
diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
index e8739a9fc..d3a706a43 100644
--- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
+++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
@@ -199,7 +199,11 @@ namespace Flax.Build
if (rid == ridFallback)
ridFallback = "";
if (string.IsNullOrEmpty(dotnetPath))
- dotnetPath = "/usr/share/dotnet/";
+ {
+ dotnetPath = "/usr/lib/dotnet/";
+ if (!Directory.Exists(dotnetPath))
+ dotnetPath = "/usr/share/dotnet/";
+ }
break;
}
case TargetPlatform.Mac:
diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs
index c2ea81372..ef365d3b7 100644
--- a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs
+++ b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs
@@ -244,6 +244,8 @@ namespace Flax.Build.NativeCpp
Defines = new HashSet(),
SystemReferences = new HashSet
{
+ "mscorlib",
+ "netstandard",
"Microsoft.CSharp",
"System",
@@ -258,25 +260,32 @@ namespace Flax.Build.NativeCpp
//"System.ComponentModel.TypeConverter",
"System.Console",
"System.Core",
+ "System.Diagnostics.StackTrace",
"System.Globalization",
"System.IO",
"System.IO.Compression",
"System.IO.FileSystem.Watcher",
"System.Linq",
"System.Linq.Expressions",
+ "System.Memory",
+ "System.Net",
"System.Net.Http",
"System.Net.Primitives",
"System.ObjectModel",
- "System.Private.CoreLib",
- "System.Private.Uri",
- //"System.Private.Xml",
+ "System.ValueTuple",
- "System.Reflection",
"System.Runtime",
+ "System.Runtime.Extensions",
+ "System.Runtime.Handles",
+ "System.Runtime.Intrinsics",
+ "System.Runtime.Numerics",
+ "System.Runtime.Loader",
"System.Runtime.CompilerServices.Unsafe",
"System.Runtime.InteropServices",
"System.Runtime.InteropServices.RuntimeInformation",
- "System.Runtime.Serialization.Formatters", // BinaryFormatter
+ "System.Runtime.Serialization",
+ "System.Runtime.Serialization.Formatters",
+
"System.Security.Cryptography",
"System.Security.Cryptography.Algorithms",
"System.Security.Cryptography.Primitives",
@@ -284,8 +293,11 @@ namespace Flax.Build.NativeCpp
"System.Threading.Tasks.Parallel",
//"System.Xml",
+ "System.Threading",
+ "System.Threading.Thread",
+
+ "System.Reflection",
//"System.Reflection.Metadata",
- "netstandard",
},
SystemAnalyzers = new HashSet
{
diff --git a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs
index 21464ab55..d646a81cd 100644
--- a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs
+++ b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs
@@ -241,7 +241,7 @@ namespace Flax.Build.Plugins
// Deserialize arguments
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});");
}
@@ -254,7 +254,7 @@ namespace Flax.Build.Plugins
// 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(" NetworkStream* stream = NetworkReplicator::BeginInvokeRPC();");
contents.AppendLine(" Span targetIds;");
@@ -270,11 +270,11 @@ namespace Flax.Build.Plugins
}
// 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
- 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();
@@ -780,7 +780,7 @@ namespace Flax.Build.Plugins
private static void GenerateCallINetworkSerializable(ref DotnetContext context, TypeDefinition type, string name, MethodDefinition method)
{
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();
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
@@ -1304,7 +1304,10 @@ namespace Flax.Build.Plugins
il.Emit(jmp4);
valueContext.Load(ref il);
il.Emit(OpCodes.Ldloc, varStart + 1); // idx
- il.Emit(OpCodes.Ldelem_Ref);
+ if (elementType.IsValueType)
+ il.Emit(OpCodes.Ldelem_Any, elementType);
+ else
+ il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Stloc, varStart + 2); //
// Serialize item value
@@ -1753,10 +1756,10 @@ namespace Flax.Build.Plugins
if (jumpBodyEnd == null)
throw new Exception("Missing IL Return op code in method " + method.Name);
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.Ldc_I4_2);
- il.Emit(OpCodes.Beq_S, jumpIfBodyStart);
+ il.Emit(OpCodes.Beq, jumpIfBodyStart);
// ||
il.Emit(jumpIf2Start);
il.Emit(OpCodes.Ldloc, varsStart + 1);
@@ -1812,35 +1815,12 @@ namespace Flax.Build.Plugins
else
il.Emit(OpCodes.Ldnull);
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));
- // if (server && networkMode == NetworkManagerMode.Client) return;
- if (methodRPC.IsServer)
- {
- 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);
- }
+ // if (EndInvokeRPC) return
+ il.Emit(OpCodes.Brtrue, jumpBodyEnd);
// Continue to original method body
il.Emit(jumpBodyStart);
diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs
index fb5c90872..1955a57ba 100644
--- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs
+++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs
@@ -547,6 +547,7 @@ namespace Flax.Build.Projects.VisualStudio
if (project.Type == TargetType.DotNetCore)
{
var path = Path.Combine(Path.GetDirectoryName(project.Path), "Properties/launchSettings.json");
+ path = Utilities.NormalizePath(path);
if (profiles.ContainsKey(path))
profile.AppendLine(",");
profile.AppendLine($" \"{project.BaseName}\": {{");
@@ -568,6 +569,7 @@ namespace Flax.Build.Projects.VisualStudio
var folder = Path.GetDirectoryName(e.Key);
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
+ profile.Clear();
profile.AppendLine("{");
profile.AppendLine(" \"profiles\": {");
profile.AppendLine(e.Value);