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),