diff --git a/Source/Editor/Content/Proxy/MaterialProxy.cs b/Source/Editor/Content/Proxy/MaterialProxy.cs
index 9cbd54975..cf7c8b77e 100644
--- a/Source/Editor/Content/Proxy/MaterialProxy.cs
+++ b/Source/Editor/Content/Proxy/MaterialProxy.cs
@@ -72,11 +72,9 @@ namespace FlaxEditor.Content
/// The material item to use as a base material.
public static void CreateMaterialInstance(BinaryAssetItem materialItem)
{
- if (materialItem == null)
- throw new ArgumentNullException();
-
+ var materialInstanceName = materialItem.ShortName + " Instance";
var materialInstanceProxy = Editor.Instance.ContentDatabase.GetProxy();
- Editor.Instance.Windows.ContentWin.NewItem(materialInstanceProxy, null, item => OnMaterialInstanceCreated(item, materialItem));
+ Editor.Instance.Windows.ContentWin.NewItem(materialInstanceProxy, null, item => OnMaterialInstanceCreated(item, materialItem), materialInstanceName);
}
private static void OnMaterialInstanceCreated(ContentItem item, BinaryAssetItem materialItem)
diff --git a/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
index e784048af..071810ffa 100644
--- a/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
@@ -145,7 +145,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
else
{
// No localization so initialize with empty table
- var path = Path.Combine(Path.Combine(Path.GetDirectoryName(GameSettings.Load().Localization.Path), "Localization", culture.Name + ".json"));
+ var folder = Path.Combine(Path.GetDirectoryName(GameSettings.Load().Localization.Path), "Localization");
+ if (!Directory.Exists(folder))
+ Directory.CreateDirectory(folder);
+ var path = Path.Combine(Path.Combine(folder, culture.Name + ".json"));
var table = FlaxEngine.Content.CreateVirtualAsset();
table.Locale = culture.Name;
if (!table.Save(path))
diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
index 318dafcc6..8fe5fd5f2 100644
--- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
@@ -145,7 +145,7 @@ namespace FlaxEditor.CustomEditors.Editors
_size.IntValue.MinValue = 0;
_size.IntValue.MaxValue = ushort.MaxValue;
_size.IntValue.Value = size;
- _size.IntValue.ValueChanged += OnSizeChanged;
+ _size.IntValue.EditEnd += OnSizeChanged;
}
// Elements
diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
index 7dd590576..1b9a7cca1 100644
--- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
@@ -190,7 +190,7 @@ namespace FlaxEditor.CustomEditors.Editors
_size.IntValue.MinValue = 0;
_size.IntValue.MaxValue = _notNullItems ? size : ushort.MaxValue;
_size.IntValue.Value = size;
- _size.IntValue.ValueChanged += OnSizeChanged;
+ _size.IntValue.EditEnd += OnSizeChanged;
}
// Elements
diff --git a/Source/Editor/CustomEditors/Elements/ComboBoxElement.cs b/Source/Editor/CustomEditors/Elements/ComboBoxElement.cs
index b9bda5985..3e3f34e07 100644
--- a/Source/Editor/CustomEditors/Elements/ComboBoxElement.cs
+++ b/Source/Editor/CustomEditors/Elements/ComboBoxElement.cs
@@ -6,7 +6,7 @@ using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Elements
{
///
- /// The combobx element.
+ /// The combobox element.
///
///
public class ComboBoxElement : LayoutElement
diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs
index 61c223835..53af4387f 100644
--- a/Source/Editor/CustomEditors/Values/ValueContainer.cs
+++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs
@@ -187,7 +187,7 @@ namespace FlaxEditor.CustomEditors
{
for (int i = 0; i < Count; i++)
{
- if (this[i] == referenceSceneObject)
+ if ((SceneObject)this[i] == referenceSceneObject)
continue;
if (this[i] == null || (this[i] is SceneObject valueSceneObject && valueSceneObject && valueSceneObject.PrefabObjectID != referenceSceneObject.PrefabObjectID))
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 4d2cf4346..67d89786d 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -9,7 +9,6 @@ using FlaxEditor.Content;
using FlaxEditor.Content.Import;
using FlaxEditor.Content.Settings;
using FlaxEditor.Content.Thumbnails;
-using FlaxEditor.GUI;
using FlaxEditor.Modules;
using FlaxEditor.Modules.SourceCodeEditing;
using FlaxEditor.Options;
@@ -307,6 +306,7 @@ namespace FlaxEditor
_areModulesInited = true;
// Preload initial scene asset
+ try
{
var startupSceneMode = Options.Options.General.StartupSceneMode;
if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene))
@@ -323,12 +323,23 @@ namespace FlaxEditor
}
case GeneralOptions.StartupSceneModes.LastOpened:
{
- if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName) && Guid.TryParse(lastSceneIdName, out var lastSceneId))
- Internal_LoadAsset(ref lastSceneId);
+ if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName))
+ {
+ var lastScenes = JsonSerializer.Deserialize(lastSceneIdName);
+ foreach (var scene in lastScenes)
+ {
+ var lastScene = scene;
+ Internal_LoadAsset(ref lastScene);
+ }
+ }
break;
}
}
}
+ catch (Exception)
+ {
+ // Ignore errors
+ }
InitializationStart?.Invoke();
@@ -401,58 +412,69 @@ namespace FlaxEditor
}
// Load scene
-
- // scene cmd line argument
- var scene = ContentDatabase.Find(_startupSceneCmdLine);
- if (scene is SceneItem)
+ try
{
- Editor.Log("Loading scene specified in command line");
- Scene.OpenScene(_startupSceneCmdLine);
- return;
- }
-
- // if no scene cmd line argument is provided
- var startupSceneMode = Options.Options.General.StartupSceneMode;
- if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene))
- {
- // Fallback to default project scene if nothing saved in the cache
- startupSceneMode = GeneralOptions.StartupSceneModes.ProjectDefault;
- }
- switch (startupSceneMode)
- {
- case GeneralOptions.StartupSceneModes.ProjectDefault:
- {
- if (string.IsNullOrEmpty(GameProject.DefaultScene))
- break;
- JsonSerializer.ParseID(GameProject.DefaultScene, out var defaultSceneId);
- var defaultScene = ContentDatabase.Find(defaultSceneId);
- if (defaultScene is SceneItem)
+ // Scene cmd line argument
+ var scene = ContentDatabase.Find(_startupSceneCmdLine);
+ if (scene is SceneItem)
{
- Editor.Log("Loading default project scene");
- Scene.OpenScene(defaultSceneId);
-
- // Use spawn point
- Windows.EditWin.Viewport.ViewRay = GameProject.DefaultSceneSpawn;
+ Editor.Log("Loading scene specified in command line");
+ Scene.OpenScene(_startupSceneCmdLine);
+ return;
}
- break;
- }
- case GeneralOptions.StartupSceneModes.LastOpened:
- {
- if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName) && Guid.TryParse(lastSceneIdName, out var lastSceneId))
+ var startupSceneMode = Options.Options.General.StartupSceneMode;
+ if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene))
{
- var lastScene = ContentDatabase.Find(lastSceneId);
- if (lastScene is SceneItem)
+ // Fallback to default project scene if nothing saved in the cache
+ startupSceneMode = GeneralOptions.StartupSceneModes.ProjectDefault;
+ }
+ switch (startupSceneMode)
+ {
+ case GeneralOptions.StartupSceneModes.ProjectDefault:
+ {
+ if (string.IsNullOrEmpty(GameProject.DefaultScene))
+ break;
+ JsonSerializer.ParseID(GameProject.DefaultScene, out var defaultSceneId);
+ var defaultScene = ContentDatabase.Find(defaultSceneId);
+ if (defaultScene is SceneItem)
{
- Editor.Log("Loading last opened scene");
- Scene.OpenScene(lastSceneId);
+ Editor.Log("Loading default project scene");
+ Scene.OpenScene(defaultSceneId);
+
+ // Use spawn point
+ Windows.EditWin.Viewport.ViewRay = GameProject.DefaultSceneSpawn;
+ }
+ break;
+ }
+ case GeneralOptions.StartupSceneModes.LastOpened:
+ {
+ if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName))
+ {
+ var lastScenes = JsonSerializer.Deserialize(lastSceneIdName);
+ foreach (var sceneId in lastScenes)
+ {
+ var lastScene = ContentDatabase.Find(sceneId);
+ if (!(lastScene is SceneItem))
+ continue;
+
+ Editor.Log($"Loading last opened scene: {lastScene.ShortName}");
+ if (sceneId == lastScenes[0])
+ Scene.OpenScene(sceneId);
+ else
+ Level.LoadSceneAsync(sceneId);
+ }
// Restore view
if (ProjectCache.TryGetCustomData(ProjectDataLastSceneSpawn, out var lastSceneSpawnName))
Windows.EditWin.Viewport.ViewRay = JsonSerializer.Deserialize(lastSceneSpawnName);
}
+ break;
+ }
}
- break;
}
+ catch (Exception)
+ {
+ // Ignore errors
}
}
@@ -581,8 +603,8 @@ namespace FlaxEditor
UI.UpdateStatusBar();
}
- if (UI?.StatusBar?.Text != null && !UI.StatusBar.Text.Contains("Auto") &&
- _saveNowButton != null && _cancelSaveButton != null &&
+ if (UI?.StatusBar?.Text != null && !UI.StatusBar.Text.Contains("Auto") &&
+ _saveNowButton != null && _cancelSaveButton != null &&
(_saveNowButton.Visible || _cancelSaveButton.Visible))
{
_saveNowButton.Visible = false;
@@ -662,11 +684,14 @@ namespace FlaxEditor
// Start exit
StateMachine.GoToState();
- // Cache last opened scene
+ // Cache last opened scenes
{
- var lastSceneId = Level.ScenesCount > 0 ? Level.Scenes[0].ID : Guid.Empty;
+ var lastScenes = Level.Scenes;
+ var lastSceneIds = new Guid[lastScenes.Length];
+ for (int i = 0; i < lastScenes.Length; i++)
+ lastSceneIds[i] = lastScenes[i].ID;
var lastSceneSpawn = Windows.EditWin.Viewport.ViewRay;
- ProjectCache.SetCustomData(ProjectDataLastScene, lastSceneId.ToString());
+ ProjectCache.SetCustomData(ProjectDataLastScene, JsonSerializer.Serialize(lastSceneIds));
ProjectCache.SetCustomData(ProjectDataLastSceneSpawn, JsonSerializer.Serialize(lastSceneSpawn));
}
diff --git a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs
index 9e51cc21a..eb3fa39b6 100644
--- a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs
+++ b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs
@@ -174,7 +174,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
continue;
// Prevent from adding the same track twice
- if (SubTracks.Any(x => x is IObjectTrack y && y.Object == script))
+ if (SubTracks.Any(x => x is IObjectTrack y && y.Object as SceneObject == script))
continue;
var name = Utilities.Utils.GetPropertyNameUI(script.GetType().Name);
diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs
index 8691cd14d..ced70a281 100644
--- a/Source/Editor/GUI/Tree/TreeNode.cs
+++ b/Source/Editor/GUI/Tree/TreeNode.cs
@@ -661,17 +661,20 @@ namespace FlaxEditor.GUI.Tree
// Draw drag and drop effect
if (IsDragOver && _tree.DraggedOverNode == this)
{
- Color dragOverColor = style.BackgroundSelected * 0.6f;
+ Color dragOverColor = style.BackgroundSelected;
Rectangle rect;
switch (_dragOverMode)
{
case DragItemPositioning.At:
+ dragOverColor *= 0.6f;
rect = textRect;
break;
case DragItemPositioning.Above:
+ dragOverColor *= 1.2f;
rect = new Rectangle(textRect.X, textRect.Top - DefaultDragInsertPositionMargin - DefaultNodeOffsetY - _margin.Top, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
break;
case DragItemPositioning.Below:
+ dragOverColor *= 1.2f;
rect = new Rectangle(textRect.X, textRect.Bottom + _margin.Bottom - DefaultDragInsertPositionMargin, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
break;
default:
diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs
index e5b2efee3..5e3b722b9 100644
--- a/Source/Editor/Modules/ContentDatabaseModule.cs
+++ b/Source/Editor/Modules/ContentDatabaseModule.cs
@@ -655,7 +655,7 @@ namespace FlaxEditor.Modules
var children = folder.Children.ToArray();
for (int i = 0; i < children.Length; i++)
{
- Delete(children[0]);
+ Delete(children[i]);
}
}
diff --git a/Source/Editor/Modules/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs
index 4d1aee93e..0a51038ac 100644
--- a/Source/Editor/Modules/SceneModule.cs
+++ b/Source/Editor/Modules/SceneModule.cs
@@ -315,6 +315,42 @@ namespace FlaxEditor.Modules
Editor.StateMachine.ChangingScenesState.UnloadScene(Level.Scenes);
}
+ ///
+ /// Closes all of the scenes except for the specified scene (async).
+ ///
+ /// The scene to not close.
+ public void CloseAllScenesExcept(Scene scene)
+ {
+ // Check if cannot change scene now
+ if (!Editor.StateMachine.CurrentState.CanChangeScene)
+ return;
+
+ var scenes = new List();
+ foreach (var s in Level.Scenes)
+ {
+ if (s == scene)
+ continue;
+ scenes.Add(s);
+ }
+
+ // In play-mode Editor mocks the level streaming script
+ if (Editor.IsPlayMode)
+ {
+ foreach (var s in scenes)
+ {
+ Level.UnloadSceneAsync(s);
+ }
+ return;
+ }
+
+ // Ensure to save all pending changes
+ if (CheckSaveBeforeClose())
+ return;
+
+ // Unload scenes
+ Editor.StateMachine.ChangingScenesState.UnloadScene(scenes);
+ }
+
///
/// Show save before scene load/unload action.
///
diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs
index df7a11b1a..0405e4fdf 100644
--- a/Source/Editor/SceneGraph/Actors/SceneNode.cs
+++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs
@@ -77,6 +77,8 @@ namespace FlaxEditor.SceneGraph.Actors
}
contextMenu.AddButton("Save scene", OnSave).LinkTooltip("Saves this scene.").Enabled = IsEdited && !Editor.IsPlayMode;
contextMenu.AddButton("Unload scene", OnUnload).LinkTooltip("Unloads this scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
+ if (Level.ScenesCount > 1)
+ contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
base.OnContextMenu(contextMenu);
}
@@ -95,5 +97,10 @@ namespace FlaxEditor.SceneGraph.Actors
{
Editor.Instance.Scene.CloseScene(Scene);
}
+
+ private void OnUnloadAllButSelectedScene()
+ {
+ Editor.Instance.Scene.CloseAllScenesExcept(Scene);
+ }
}
}
diff --git a/Source/Editor/Surface/Archetypes/Math.cs b/Source/Editor/Surface/Archetypes/Math.cs
index d2aef9ae5..1b8f62e62 100644
--- a/Source/Editor/Surface/Archetypes/Math.cs
+++ b/Source/Editor/Surface/Archetypes/Math.cs
@@ -47,7 +47,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = desc,
Flags = NodeFlags.AllGraphs,
AlternativeTitles = altTitles,
- Size = new Float2(140, 40),
+ Size = new Float2(150, 40),
DefaultType = new ScriptType(inputType),
ConnectionsHints = hints,
IndependentBoxes = new[] { 0, 1 },
diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs
index 97be9a20d..dd861d4af 100644
--- a/Source/Editor/Surface/Archetypes/Particles.cs
+++ b/Source/Editor/Surface/Archetypes/Particles.cs
@@ -730,12 +730,8 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
-
- NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Float(30, 0, 0),
-
- NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Float(30, Surface.Constants.LayoutOffsetY, 1),
+ NodeElementArchetype.Factory.Input(0, "Min", true, typeof(float), 1, 0),
+ NodeElementArchetype.Factory.Input(1, "Max", true, typeof(float), 2, 1),
}
},
new NodeArchetype
@@ -753,14 +749,8 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float2), 0),
-
- NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Vector_X(30, 0, 0),
- NodeElementArchetype.Factory.Vector_Y(83, 0, 0),
-
- NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Vector_X(30, Surface.Constants.LayoutOffsetY, 1),
- NodeElementArchetype.Factory.Vector_Y(83, Surface.Constants.LayoutOffsetY, 1),
+ NodeElementArchetype.Factory.Input(0, "Min", true, typeof(Float2), 1, 0),
+ NodeElementArchetype.Factory.Input(1, "Max", true, typeof(Float2), 2, 1),
}
},
new NodeArchetype
@@ -778,16 +768,8 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
-
- NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Vector_X(30, 0, 0),
- NodeElementArchetype.Factory.Vector_Y(83, 0, 0),
- NodeElementArchetype.Factory.Vector_Z(136, 0, 0),
-
- NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Vector_X(30, Surface.Constants.LayoutOffsetY, 1),
- NodeElementArchetype.Factory.Vector_Y(83, Surface.Constants.LayoutOffsetY, 1),
- NodeElementArchetype.Factory.Vector_Z(136, Surface.Constants.LayoutOffsetY, 1),
+ NodeElementArchetype.Factory.Input(0, "Min", true, typeof(Float3), 1, 0),
+ NodeElementArchetype.Factory.Input(1, "Max", true, typeof(Float3), 2, 1),
}
},
new NodeArchetype
@@ -805,18 +787,8 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float4), 0),
-
- NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Vector_X(30, 0, 0),
- NodeElementArchetype.Factory.Vector_Y(83, 0, 0),
- NodeElementArchetype.Factory.Vector_Z(136, 0, 0),
- NodeElementArchetype.Factory.Vector_W(189, 0, 0),
-
- NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
- NodeElementArchetype.Factory.Vector_X(30, Surface.Constants.LayoutOffsetY, 1),
- NodeElementArchetype.Factory.Vector_Y(83, Surface.Constants.LayoutOffsetY, 1),
- NodeElementArchetype.Factory.Vector_Z(136, Surface.Constants.LayoutOffsetY, 1),
- NodeElementArchetype.Factory.Vector_W(189, Surface.Constants.LayoutOffsetY, 1),
+ NodeElementArchetype.Factory.Input(0, "Min", true, typeof(Float4), 1, 0),
+ NodeElementArchetype.Factory.Input(1, "Max", true, typeof(Float4), 2, 1),
}
},
diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp
index 9b7f4979b..a5ed0356f 100644
--- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp
+++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp
@@ -47,6 +47,8 @@ AssetReference CustomTextureMaterial;
ModelInstanceEntries InstanceBuffers[static_cast(IconTypes::MAX)];
Dictionary ActorTypeToIconType;
Dictionary> ActorTypeToTexture;
+Dictionary, AssetReference> ActorToTexture;
+Dictionary, AssetReference> TextureToMaterial;
class ViewportIconsRendererService : public EngineService
{
@@ -103,10 +105,18 @@ void ViewportIconsRenderer::AddActor(Actor* actor)
actor->GetSceneRendering()->AddViewportIcon(actor);
}
+void ViewportIconsRenderer::AddActorWithTexture(Actor* actor, Texture* iconTexture)
+{
+ CHECK(actor && actor->GetScene() && iconTexture);
+ ActorToTexture[actor] = iconTexture;
+ actor->GetSceneRendering()->AddViewportIcon(actor);
+}
+
void ViewportIconsRenderer::RemoveActor(Actor* actor)
{
CHECK(actor && actor->GetScene());
actor->GetSceneRendering()->RemoveViewportIcon(actor);
+ ActorToTexture.Remove(actor);
}
void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw)
@@ -128,7 +138,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
ScriptingTypeHandle typeHandle = icon->GetTypeHandle();
draw.Buffer = nullptr;
- if (ActorTypeToTexture.TryGet(typeHandle, texture))
+ if (ActorToTexture.TryGet(icon, texture) || ActorTypeToTexture.TryGet(typeHandle, texture))
{
// Use custom texture
draw.Buffer = &InstanceBuffers[static_cast(IconTypes::CustomTexture)];
@@ -137,9 +147,20 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
// Lazy-init (use in-built icon material with custom texture)
draw.Buffer->Setup(1);
draw.Buffer->At(0).ReceiveDecals = false;
- draw.Buffer->At(0).Material = InstanceBuffers[0][0].Material->CreateVirtualInstance();
+ draw.Buffer->At(0).ShadowsMode = ShadowsCastingMode::None;
}
- draw.Buffer->At(0).Material->SetParameterValue(TEXT("Image"), Variant(texture));
+
+ AssetReference material;
+
+ if (!TextureToMaterial.TryGet(texture, material))
+ {
+ // Create custom material per custom texture
+ TextureToMaterial[texture] = InstanceBuffers[0][0].Material->CreateVirtualInstance();
+ TextureToMaterial[texture]->SetParameterValue(TEXT("Image"), Variant(texture));
+ material = TextureToMaterial[texture];
+ }
+
+ draw.Buffer->At(0).Material = material;
}
else if (ActorTypeToIconType.TryGet(typeHandle, iconType))
{
@@ -170,6 +191,8 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
Matrix m1, m2, world;
BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
IconTypes iconType;
+ AssetReference texture;
+
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
{
// Create world matrix
@@ -182,7 +205,39 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
// Draw icon
GeometryDrawStateData drawState;
draw.DrawState = &drawState;
- draw.Buffer = &InstanceBuffers[static_cast(iconType)];
+
+ // Support custom icons through types, but not onces that were added through actors,
+ // since they cant register while in prefab view anyway
+ if (ActorTypeToTexture.TryGet(actor->GetTypeHandle(), texture))
+ {
+ // Use custom texture
+ draw.Buffer = &InstanceBuffers[static_cast(IconTypes::CustomTexture)];
+ if (draw.Buffer->Count() == 0)
+ {
+ // Lazy-init (use in-built icon material with custom texture)
+ draw.Buffer->Setup(1);
+ draw.Buffer->At(0).ReceiveDecals = false;
+ draw.Buffer->At(0).ShadowsMode = ShadowsCastingMode::None;
+ }
+
+ AssetReference material;
+
+ if (!TextureToMaterial.TryGet(texture, material))
+ {
+ // Create custom material per custom texture
+ TextureToMaterial[texture] = InstanceBuffers[0][0].Material->CreateVirtualInstance();
+ TextureToMaterial[texture]->SetParameterValue(TEXT("Image"), Variant(texture));
+ material = TextureToMaterial[texture];
+ }
+
+ draw.Buffer->At(0).Material = material;
+ }
+ else
+ {
+ // Use predefined material
+ draw.Buffer = &InstanceBuffers[static_cast(iconType)];
+ }
+
draw.World = &world;
draw.Bounds = sphere;
QuadModel->Draw(renderContext, draw);
@@ -196,9 +251,10 @@ bool ViewportIconsRendererService::Init()
{
QuadModel = Content::LoadAsyncInternal(TEXT("Engine/Models/Quad"));
#define INIT(type, path) \
- InstanceBuffers[static_cast(IconTypes::type)].Setup(1); \
- InstanceBuffers[static_cast(IconTypes::type)][0].ReceiveDecals = false; \
- InstanceBuffers[static_cast(IconTypes::type)][0].Material = Content::LoadAsyncInternal(TEXT(path))
+ InstanceBuffers[static_cast(IconTypes::type)].Setup(1); \
+ InstanceBuffers[static_cast(IconTypes::type)][0].ReceiveDecals = false; \
+ InstanceBuffers[static_cast(IconTypes::type)][0].ShadowsMode = ShadowsCastingMode::None; \
+ InstanceBuffers[static_cast(IconTypes::type)][0].Material = Content::LoadAsyncInternal(TEXT(path))
INIT(PointLight, "Editor/Icons/PointLight");
INIT(DirectionalLight, "Editor/Icons/DirectionalLight");
INIT(EnvironmentProbe, "Editor/Icons/EnvironmentProbe");
@@ -236,4 +292,6 @@ void ViewportIconsRendererService::Dispose()
for (int32 i = 0; i < ARRAY_COUNT(InstanceBuffers); i++)
InstanceBuffers[i].Release();
ActorTypeToIconType.Clear();
+ ActorToTexture.Clear();
+ TextureToMaterial.Clear();
}
diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.h b/Source/Editor/Utilities/ViewportIconsRenderer.h
index 509252795..4062c2642 100644
--- a/Source/Editor/Utilities/ViewportIconsRenderer.h
+++ b/Source/Editor/Utilities/ViewportIconsRenderer.h
@@ -37,6 +37,13 @@ public:
/// The actor to register for icon drawing.
API_FUNCTION() static void AddActor(Actor* actor);
+ ///
+ /// Adds actor to the viewport icon rendering.
+ ///
+ /// The actor to register for icon drawing.
+ /// The icon texture to draw.
+ API_FUNCTION() static void AddActorWithTexture(Actor* actor, Texture* iconTexture);
+
///
/// Removes actor from the viewport icon rendering.
///
diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs
index ecc68581c..e6606f35f 100644
--- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs
+++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs
@@ -126,6 +126,10 @@ namespace FlaxEditor.Windows.Assets
_presenter.NoSelectionText = "Failed to load asset. See log for more. " + ex.Message.Replace('\n', ' ');
}
}
+ else if (string.IsNullOrEmpty(dataTypeName))
+ {
+ _presenter.NoSelectionText = "Empty data type.";
+ }
else
{
_presenter.NoSelectionText = string.Format("Missing type '{0}'.", dataTypeName);
diff --git a/Source/Editor/Windows/Assets/ParticleSystemWindow.cs b/Source/Editor/Windows/Assets/ParticleSystemWindow.cs
index 814978858..0b59dc86c 100644
--- a/Source/Editor/Windows/Assets/ParticleSystemWindow.cs
+++ b/Source/Editor/Windows/Assets/ParticleSystemWindow.cs
@@ -156,14 +156,14 @@ namespace FlaxEditor.Windows.Assets
private bool HasEmitter => _track.Asset != null;
- [EditorDisplay("Particle Emitter"), VisibleIf("HasEmitter"), EditorOrder(200), Tooltip("The start frame of the media event.")]
+ [EditorDisplay("Particle Emitter"), VisibleIf(nameof(HasEmitter)), EditorOrder(200), Tooltip("The start frame of the media event.")]
public int StartFrame
{
get => _track.Media.Count > 0 ? _track.TrackMedia.StartFrame : 0;
set => _track.TrackMedia.StartFrame = value;
}
- [EditorDisplay("Particle Emitter"), Limit(1), VisibleIf("HasEmitter"), EditorOrder(300), Tooltip("The total duration of the media event in the timeline sequence frames amount.")]
+ [EditorDisplay("Particle Emitter"), Limit(1), VisibleIf(nameof(HasEmitter)), EditorOrder(300), Tooltip("The total duration of the media event in the timeline sequence frames amount.")]
public int DurationFrames
{
get => _track.Media.Count > 0 ? _track.TrackMedia.DurationFrames : 0;
diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs
index a2e40d133..42e4c47ef 100644
--- a/Source/Editor/Windows/ContentWindow.cs
+++ b/Source/Editor/Windows/ContentWindow.cs
@@ -656,6 +656,11 @@ namespace FlaxEditor.Windows
throw new ArgumentNullException(nameof(proxy));
string name = initialName ?? proxy.NewItemName;
+
+ // If the proxy can not be created in the current folder, then navigate to the content folder
+ if (!proxy.CanCreate(CurrentViewFolder))
+ Navigate(Editor.Instance.ContentDatabase.Game.Content);
+
ContentFolder parentFolder = CurrentViewFolder;
string parentFolderPath = parentFolder.Path;
diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp
index a30fa7b78..e58aa458d 100644
--- a/Source/Engine/Content/Content.cpp
+++ b/Source/Engine/Content/Content.cpp
@@ -1002,17 +1002,12 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo&
}
#if ASSETS_LOADING_EXTRA_VERIFICATION
-
- // Ensure we have valid asset info
- ASSERT(assetInfo.TypeName.HasChars() && assetInfo.Path.HasChars());
-
// Check if file exists
if (!FileSystem::FileExists(assetInfo.Path))
{
LOG(Error, "Cannot find file '{0}'", assetInfo.Path);
return nullptr;
}
-
#endif
// Find asset factory based in its type
diff --git a/Source/Engine/Content/JsonAsset.cs b/Source/Engine/Content/JsonAsset.cs
index 3278a3377..afd83bafd 100644
--- a/Source/Engine/Content/JsonAsset.cs
+++ b/Source/Engine/Content/JsonAsset.cs
@@ -35,6 +35,11 @@ namespace FlaxEngine
return null;
var dataTypeName = DataTypeName;
+ if (string.IsNullOrEmpty(dataTypeName))
+ {
+ Debug.LogError(string.Format("Missing typename of data in Json asset '{0}'.", Path), this);
+ return null;
+ }
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h
index c1d038783..53427ab52 100644
--- a/Source/Engine/Level/Actor.h
+++ b/Source/Engine/Level/Actor.h
@@ -379,7 +379,7 @@ public:
}
///
- /// Gets the actor static fags.
+ /// Gets the actor static flags.
///
API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-80), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorStaticFlagsEditor\")")
FORCE_INLINE StaticFlags GetStaticFlags() const
diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp
index 4d81886a8..4fa37c80d 100644
--- a/Source/Engine/Level/Level.cpp
+++ b/Source/Engine/Level/Level.cpp
@@ -903,12 +903,11 @@ bool LevelImpl::unloadScene(Scene* scene)
bool LevelImpl::unloadScenes()
{
auto scenes = Level::Scenes;
- for (int32 i = 0; i < scenes.Count(); i++)
+ for (int32 i = scenes.Count() - 1; i >= 0; i--)
{
if (unloadScene(scenes[i]))
return true;
}
-
return false;
}
@@ -1210,6 +1209,15 @@ bool LevelImpl::saveScene(Scene* scene, const String& path)
LOG(Info, "Scene saved! Time {0} ms", Math::CeilToInt((float)(DateTime::NowUTC() - startTime).GetTotalMilliseconds()));
+#if USE_EDITOR
+ // Reload asset at the target location if is loaded
+ Asset* asset = Content::GetAsset(sceneId);
+ if (!asset)
+ asset = Content::GetAsset(path);
+ if (asset)
+ asset->Reload();
+#endif
+
// Fire event
CallSceneEvent(SceneEventType::OnSceneSaved, scene, sceneId);
diff --git a/Source/Engine/Level/Tags.cpp b/Source/Engine/Level/Tags.cpp
index 63cfd8442..602aea445 100644
--- a/Source/Engine/Level/Tags.cpp
+++ b/Source/Engine/Level/Tags.cpp
@@ -73,7 +73,7 @@ Array Tags::GetSubTags(Tag parentTag)
return subTags;
}
-bool Tags::HasTag(const Array& list, const Tag& tag)
+bool Tags::HasTag(const Array& list, const Tag tag)
{
if (tag.Index == 0)
return false;
@@ -87,7 +87,7 @@ bool Tags::HasTag(const Array& list, const Tag& tag)
return false;
}
-bool Tags::HasTagExact(const Array& list, const Tag& tag)
+bool Tags::HasTagExact(const Array& list, const Tag tag)
{
if (tag.Index == 0)
return false;
diff --git a/Source/Engine/Level/Tags.h b/Source/Engine/Level/Tags.h
index 04ba76a23..864adcda7 100644
--- a/Source/Engine/Level/Tags.h
+++ b/Source/Engine/Level/Tags.h
@@ -106,7 +106,7 @@ public:
/// The tags list to use.
/// The tag to check.
/// True if given tag is contained by the list of tags. Returns false for empty list.
- static bool HasTag(const Array& list, const Tag& tag);
+ static bool HasTag(const Array& list, const Tag tag);
///
/// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag.
@@ -114,7 +114,7 @@ public:
/// The tags list to use.
/// The tag to check.
/// True if given tag is contained by the list of tags. Returns false for empty list.
- static bool HasTagExact(const Array& list, const Tag& tag);
+ static bool HasTagExact(const Array& list, const Tag tag);
///
/// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact.
diff --git a/Source/Engine/Level/Types.h b/Source/Engine/Level/Types.h
index 9a29fde6e..a9938658b 100644
--- a/Source/Engine/Level/Types.h
+++ b/Source/Engine/Level/Types.h
@@ -100,7 +100,7 @@ API_ENUM(Attributes="Flags") enum class StaticFlags
Navigation = 1 << 3,
///
- /// Objects is fully static on the scene.
+ /// Object is fully static in the scene.
///
FullyStatic = Transform | ReflectionProbe | Lightmap | Navigation,
diff --git a/Source/Engine/Localization/LocalizedStringTable.cpp b/Source/Engine/Localization/LocalizedStringTable.cpp
index 74c9b6774..e14328f89 100644
--- a/Source/Engine/Localization/LocalizedStringTable.cpp
+++ b/Source/Engine/Localization/LocalizedStringTable.cpp
@@ -15,6 +15,7 @@ REGISTER_JSON_ASSET(LocalizedStringTable, "FlaxEngine.LocalizedStringTable", tru
LocalizedStringTable::LocalizedStringTable(const SpawnParams& params, const AssetInfo* info)
: JsonAssetBase(params, info)
{
+ DataTypeName = TypeName;
}
void LocalizedStringTable::AddString(const StringView& id, const StringView& value)
diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp
index 597c7695c..905d1a11c 100644
--- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp
+++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp
@@ -388,33 +388,33 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va
// Random Float Range
case 213:
{
- auto& a = node->Values[0].AsFloat;
- auto& b = node->Values[1].AsFloat;
- value = writeLocal(VariantType::Float, String::Format(TEXT("lerp({0}, {1}, RAND)"), a, b), node);
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat();
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat();
+ value = writeLocal(VariantType::Float, String::Format(TEXT("lerp({0}, {1}, RAND)"), a.Value, b.Value), node);
break;
}
// Random Vector2 Range
case 214:
{
- auto& a = node->Values[0].AsFloat2();
- auto& b = node->Values[1].AsFloat2();
- value = writeLocal(VariantType::Float2, String::Format(TEXT("float2(lerp({0}, {1}, RAND), lerp({2}, {3}, RAND))"), a.X, b.X, a.Y, b.Y), node);
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat2();
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat2();
+ value = writeLocal(VariantType::Float2, String::Format(TEXT("float2(lerp({0}.x, {1}.x, RAND), lerp({0}.y, {1}.y, RAND))"), a.Value, b.Value), node);
break;
}
// Random Vector3 Range
case 215:
{
- auto& a = node->Values[0].AsFloat3();
- auto& b = node->Values[1].AsFloat3();
- value = writeLocal(VariantType::Float3, String::Format(TEXT("float3(lerp({0}, {1}, RAND), lerp({2}, {3}, RAND), lerp({4}, {5}, RAND))"), a.X, b.X, a.Y, b.Y, a.Z, b.Z), node);
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat3();
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat3();
+ value = writeLocal(VariantType::Float3, String::Format(TEXT("float3(lerp({0}.x, {1}.x, RAND), lerp({0}.y, {1}.y, RAND), lerp({0}.z, {1}.z, RAND))"), a.Value, b.Value), node);
break;
}
// Random Vector4 Range
case 216:
{
- auto& a = node->Values[0].AsFloat4();
- auto& b = node->Values[1].AsFloat4();
- value = writeLocal(VariantType::Float4, String::Format(TEXT("float4(lerp({0}, {1}, RAND), lerp({2}, {3}, RAND), lerp({4}, {5}, RAND), lerp({6}, {7}, RAND))"), a.X, b.X, a.Y, b.Y, a.Z, b.Z, a.W, b.W), node);
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat4();
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat4();
+ value = writeLocal(VariantType::Float4, String::Format(TEXT("float4(lerp({0}.x, {1}.x, RAND), lerp({0}.y, {1}.y, RAND), lerp({0}.z, {1}.z, RAND), lerp({0}.w, {1}.w, RAND))"), a.Value, b.Value), node);
break;
}
// Particle Emitter Function
diff --git a/Source/Engine/Particles/ParticleSystem.cpp b/Source/Engine/Particles/ParticleSystem.cpp
index b04dfe0c0..6f63101b2 100644
--- a/Source/Engine/Particles/ParticleSystem.cpp
+++ b/Source/Engine/Particles/ParticleSystem.cpp
@@ -212,6 +212,12 @@ Asset::LoadResult ParticleSystem::load()
int32 version;
stream.ReadInt32(&version);
+#if USE_EDITOR
+ // Skip unused parameters
+#define SKIP_UNUSED_PARAM_OVERRIDE() if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First] == nullptr || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr) continue
+#else
+#define SKIP_UNUSED_PARAM_OVERRIDE()
+#endif
switch (version)
{
case 1:
@@ -281,13 +287,7 @@ Asset::LoadResult ParticleSystem::load()
stream.ReadInt32(&key.First);
stream.Read(key.Second);
stream.ReadCommonValue(&value);
-
-#if USE_EDITOR
- // Skip unused parameters
- if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr)
- continue;
-#endif
-
+ SKIP_UNUSED_PARAM_OVERRIDE();
EmittersParametersOverrides.Add(key, Variant(value));
}
}
@@ -361,13 +361,7 @@ Asset::LoadResult ParticleSystem::load()
stream.ReadInt32(&key.First);
stream.Read(key.Second);
stream.ReadCommonValue(&value);
-
-#if USE_EDITOR
- // Skip unused parameters
- if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr)
- continue;
-#endif
-
+ SKIP_UNUSED_PARAM_OVERRIDE();
EmittersParametersOverrides[key] = Variant(value);
}
}
@@ -440,13 +434,7 @@ Asset::LoadResult ParticleSystem::load()
stream.ReadInt32(&key.First);
stream.Read(key.Second);
stream.ReadVariant(&value);
-
-#if USE_EDITOR
- // Skip unused parameters
- if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr)
- continue;
-#endif
-
+ SKIP_UNUSED_PARAM_OVERRIDE();
EmittersParametersOverrides[key] = value;
}
}
@@ -457,6 +445,7 @@ Asset::LoadResult ParticleSystem::load()
LOG(Warning, "Unknown timeline version {0}.", version);
return LoadResult::InvalidData;
}
+#undef SKIP_UNUSED_PARAM_OVERRIDE
#if !BUILD_RELEASE
_debugName = StringUtils::GetFileNameWithoutExtension(GetPath());
diff --git a/Source/Engine/Scripting/Object.cs b/Source/Engine/Scripting/Object.cs
index 227fe99e6..bc1a4b1b5 100644
--- a/Source/Engine/Scripting/Object.cs
+++ b/Source/Engine/Scripting/Object.cs
@@ -205,6 +205,42 @@ namespace FlaxEngine
return obj != null && obj.__unmanagedPtr != IntPtr.Zero;
}
+ ///
+ /// Checks whether the two objects are equal.
+ ///
+ ///
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Object left, Object right)
+ {
+ IntPtr leftPtr = (object)left != null ? left.__unmanagedPtr : IntPtr.Zero;
+ IntPtr rightPtr = (object)right != null ? right.__unmanagedPtr : IntPtr.Zero;
+ return leftPtr == rightPtr;
+ }
+
+ ///
+ /// Checks whether the two objects are not equal.
+ ///
+ ///
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Object left, Object right)
+ {
+ IntPtr leftPtr = (object)left != null ? left.__unmanagedPtr : IntPtr.Zero;
+ IntPtr rightPtr = (object)right != null ? right.__unmanagedPtr : IntPtr.Zero;
+ return leftPtr != rightPtr;
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is FlaxEngine.Object o)
+ return o.__unmanagedPtr == __unmanagedPtr;
+ return false;
+ }
+
///
/// Gets the pointer to the native object. Handles null object reference (returns zero).
///
diff --git a/Source/Engine/Scripting/ScriptingObject.h b/Source/Engine/Scripting/ScriptingObject.h
index 5682f2409..183e23297 100644
--- a/Source/Engine/Scripting/ScriptingObject.h
+++ b/Source/Engine/Scripting/ScriptingObject.h
@@ -267,3 +267,5 @@ API_CLASS(InBuild) class FLAXENGINE_API PersistentScriptingObject : public Scrip
public:
PersistentScriptingObject(const SpawnParams& params);
};
+
+extern FLAXENGINE_API class ScriptingObject* FindObject(const Guid& id, class MClass* type);
diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs
index 3bb0f8a89..07388a09b 100644
--- a/Source/Engine/Serialization/JsonConverters.cs
+++ b/Source/Engine/Serialization/JsonConverters.cs
@@ -37,7 +37,7 @@ namespace FlaxEngine.Json
{
// Skip serialization as reference id for the root object serialization (eg. Script)
var cache = JsonSerializer.Current.Value;
- if (cache != null && cache.IsDuringSerialization && cache.SerializerWriter.SerializeStackSize == 0)
+ if (cache != null && cache.IsWriting && cache.SerializerWriter.SerializeStackSize == 0)
{
return false;
}
diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs
index 8dad82d64..d988b307c 100644
--- a/Source/Engine/Serialization/JsonSerializer.cs
+++ b/Source/Engine/Serialization/JsonSerializer.cs
@@ -26,7 +26,8 @@ namespace FlaxEngine.Json
public JsonSerializerInternalWriter SerializerWriter;
public UnmanagedMemoryStream MemoryStream;
public StreamReader Reader;
- public bool IsDuringSerialization;
+ public bool IsWriting;
+ public bool IsReading;
public unsafe SerializerCache(JsonSerializerSettings settings)
{
@@ -50,6 +51,49 @@ namespace FlaxEngine.Json
DateFormatString = JsonSerializer.DateFormatString,
};
}
+
+ public void ReadBegin()
+ {
+ if (IsReading)
+ {
+ // TODO: Reset reading state (eg if previous deserialization got exception)
+ }
+ IsWriting = false;
+ IsReading = true;
+ }
+
+ public void ReadEnd()
+ {
+ IsReading = false;
+ }
+
+ public void WriteBegin()
+ {
+ if (IsWriting)
+ {
+ // Reset writing state (eg if previous serialization got exception)
+ JsonWriter = new JsonTextWriter(StringWriter)
+ {
+ IndentChar = '\t',
+ Indentation = 1,
+ Formatting = JsonSerializer.Formatting,
+ DateFormatHandling = JsonSerializer.DateFormatHandling,
+ DateTimeZoneHandling = JsonSerializer.DateTimeZoneHandling,
+ FloatFormatHandling = JsonSerializer.FloatFormatHandling,
+ StringEscapeHandling = JsonSerializer.StringEscapeHandling,
+ Culture = JsonSerializer.Culture,
+ DateFormatString = JsonSerializer.DateFormatString,
+ };
+ }
+ StringBuilder.Clear();
+ IsWriting = true;
+ IsReading = false;
+ }
+
+ public void WriteEnd()
+ {
+ IsWriting = false;
+ }
}
internal static JsonSerializerSettings Settings = CreateDefaultSettings(false);
@@ -122,9 +166,9 @@ namespace FlaxEngine.Json
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
Current.Value = cache;
- cache.StringBuilder.Clear();
- cache.IsDuringSerialization = true;
+ cache.WriteBegin();
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
+ cache.WriteEnd();
return cache.StringBuilder.ToString();
}
@@ -141,9 +185,9 @@ namespace FlaxEngine.Json
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
Current.Value = cache;
- cache.StringBuilder.Clear();
- cache.IsDuringSerialization = true;
+ cache.WriteBegin();
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
+ cache.WriteEnd();
return cache.StringBuilder.ToString();
}
@@ -161,9 +205,9 @@ namespace FlaxEngine.Json
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
Current.Value = cache;
- cache.StringBuilder.Clear();
- cache.IsDuringSerialization = true;
+ cache.WriteBegin();
cache.SerializerWriter.SerializeDiff(cache.JsonWriter, obj, type, other);
+ cache.WriteEnd();
return cache.StringBuilder.ToString();
}
@@ -176,21 +220,20 @@ namespace FlaxEngine.Json
public static void Deserialize(object input, string json)
{
var cache = Cache.Value;
- cache.IsDuringSerialization = false;
- Current.Value = cache;
-
+ cache.ReadBegin();
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
{
cache.JsonSerializer.Populate(reader, input);
-
- if (!cache.JsonSerializer.CheckAdditionalContent)
- return;
- while (reader.Read())
+ if (cache.JsonSerializer.CheckAdditionalContent)
{
- if (reader.TokenType != JsonToken.Comment)
- throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ while (reader.Read())
+ {
+ if (reader.TokenType != JsonToken.Comment)
+ throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ }
}
}
+ cache.ReadEnd();
}
///
@@ -214,22 +257,20 @@ namespace FlaxEngine.Json
{
object result;
var cache = Cache.Value;
- cache.IsDuringSerialization = false;
- Current.Value = cache;
-
+ cache.ReadBegin();
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
{
result = cache.JsonSerializer.Deserialize(reader, objectType);
-
- if (!cache.JsonSerializer.CheckAdditionalContent)
- return result;
- while (reader.Read())
+ if (cache.JsonSerializer.CheckAdditionalContent)
{
- if (reader.TokenType != JsonToken.Comment)
- throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ while (reader.Read())
+ {
+ if (reader.TokenType != JsonToken.Comment)
+ throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ }
}
}
-
+ cache.ReadEnd();
return result;
}
@@ -242,22 +283,21 @@ namespace FlaxEngine.Json
{
object result;
var cache = Cache.Value;
- cache.IsDuringSerialization = false;
- Current.Value = cache;
-
+ cache.ReadBegin();
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
{
result = cache.JsonSerializer.Deserialize(reader);
- if (!cache.JsonSerializer.CheckAdditionalContent)
- return result;
- while (reader.Read())
+ if (cache.JsonSerializer.CheckAdditionalContent)
{
- if (reader.TokenType != JsonToken.Comment)
- throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ while (reader.Read())
+ {
+ if (reader.TokenType != JsonToken.Comment)
+ throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ }
}
}
-
+ cache.ReadEnd();
return result;
}
@@ -270,8 +310,7 @@ namespace FlaxEngine.Json
public static unsafe void Deserialize(object input, byte* jsonBuffer, int jsonLength)
{
var cache = Cache.Value;
- cache.IsDuringSerialization = false;
- Current.Value = cache;
+ cache.ReadBegin();
/*// Debug json string reading
cache.MemoryStream.Initialize(jsonBuffer, jsonLength);
@@ -291,14 +330,16 @@ namespace FlaxEngine.Json
{
cache.JsonSerializer.Populate(jsonReader, input);
}
-
- if (!cache.JsonSerializer.CheckAdditionalContent)
- return;
- while (jsonReader.Read())
+ if (cache.JsonSerializer.CheckAdditionalContent)
{
- if (jsonReader.TokenType != JsonToken.Comment)
- throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ while (jsonReader.Read())
+ {
+ if (jsonReader.TokenType != JsonToken.Comment)
+ throw new Exception("Additional text found in JSON string after finishing deserializing object.");
+ }
}
+
+ cache.ReadEnd();
}
///
diff --git a/Source/Engine/Serialization/ReadStream.h b/Source/Engine/Serialization/ReadStream.h
index 69e86fa4b..37f8b8433 100644
--- a/Source/Engine/Serialization/ReadStream.h
+++ b/Source/Engine/Serialization/ReadStream.h
@@ -5,8 +5,6 @@
#include "Stream.h"
#include "Engine/Core/Templates.h"
-extern FLAXENGINE_API class ScriptingObject* FindObject(const Guid& id, class MClass* type);
-
///
/// Base class for all data read streams
///
diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h
index 7543f8241..182ac71cf 100644
--- a/Source/Engine/Serialization/Serialization.h
+++ b/Source/Engine/Serialization/Serialization.h
@@ -456,7 +456,7 @@ namespace Serialization
Guid id;
Deserialize(stream, id, modifier);
modifier->IdsMapping.TryGet(id, id);
- v = (T*)FindObject(id, T::GetStaticClass());
+ v = (T*)::FindObject(id, T::GetStaticClass());
}
// Scripting Object Reference
diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp
index 4e067e61c..4aac619af 100644
--- a/Source/Engine/Visject/VisjectGraph.cpp
+++ b/Source/Engine/Visject/VisjectGraph.cpp
@@ -1273,16 +1273,16 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
// Random Float Range
case 213:
{
- auto& a = node->Values[0].AsFloat;
- auto& b = node->Values[1].AsFloat;
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat;
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat;
value = Math::Lerp(a, b, RAND);
break;
}
// Random Vector2 Range
case 214:
{
- auto a = (Float2)node->Values[0];
- auto b = (Float2)node->Values[1];
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat2();
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat2();
value = Float2(
Math::Lerp(a.X, b.X, RAND),
Math::Lerp(a.Y, b.Y, RAND)
@@ -1292,8 +1292,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
// Random Vector3 Range
case 215:
{
- auto a = (Float3)node->Values[0];
- auto b = (Float3)node->Values[1];
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat3();
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat3();
value = Float3(
Math::Lerp(a.X, b.X, RAND),
Math::Lerp(a.Y, b.Y, RAND),
@@ -1304,8 +1304,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
// Random Vector4 Range
case 216:
{
- auto a = (Float4)node->Values[0];
- auto b = (Float4)node->Values[1];
+ auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat4();
+ auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat4();
value = Float4(
Math::Lerp(a.X, b.X, RAND),
Math::Lerp(a.Y, b.Y, RAND),