Merge remote-tracking branch 'origin/master' into 1.10
# Conflicts: # Source/Editor/Windows/Profiler/Memory.cs
This commit is contained in:
@@ -79,6 +79,7 @@ namespace FlaxEditor.CustomEditors
|
||||
_presenter = presenter;
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
Pivot = Float2.Zero;
|
||||
IsScrollable = true;
|
||||
}
|
||||
|
||||
@@ -195,6 +196,15 @@ namespace FlaxEditor.CustomEditors
|
||||
Presenter.AfterLayout?.Invoke(layout);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
Editor = null;
|
||||
_overrideEditor = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnModified()
|
||||
{
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
if (_linkedPrefabId != Guid.Empty)
|
||||
{
|
||||
_linkedPrefabId = Guid.Empty;
|
||||
Editor.Instance.Prefabs.PrefabApplied -= OnPrefabApplying;
|
||||
Editor.Instance.Prefabs.PrefabApplying -= OnPrefabApplying;
|
||||
Editor.Instance.Prefabs.PrefabApplied -= OnPrefabApplied;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
var bottomLeftCell = new VerticalPanel
|
||||
{
|
||||
Pivot = Float2.Zero,
|
||||
Spacing = 0,
|
||||
TopMargin = 0,
|
||||
BottomMargin = 0,
|
||||
|
||||
@@ -1057,6 +1057,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_scriptToggles = null;
|
||||
_scripts.Clear();
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
public void Setup(CollectionEditor editor, int index, bool canReorder = true)
|
||||
{
|
||||
Pivot = Float2.Zero;
|
||||
HeaderHeight = 18;
|
||||
_canReorder = canReorder;
|
||||
EnableDropDownIcon = true;
|
||||
@@ -884,6 +885,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
set => _pickerValidator.FileExtension = value;
|
||||
}
|
||||
|
||||
public DragAreaControl()
|
||||
{
|
||||
Pivot = Float2.Zero;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
|
||||
@@ -819,6 +819,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
OnGroupsEnd();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_visibleIfCaches = null;
|
||||
_visibleIfPropertiesListsCache = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace FlaxEditor.CustomEditors.Elements
|
||||
/// </summary>
|
||||
public readonly DropPanel Panel = new DropPanel
|
||||
{
|
||||
Pivot = Float2.Zero,
|
||||
ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight),
|
||||
ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown),
|
||||
EnableDropDownIcon = true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Elements
|
||||
@@ -13,7 +14,10 @@ namespace FlaxEditor.CustomEditors.Elements
|
||||
/// <summary>
|
||||
/// The panel.
|
||||
/// </summary>
|
||||
public readonly HorizontalPanel Panel = new HorizontalPanel();
|
||||
public readonly HorizontalPanel Panel = new HorizontalPanel
|
||||
{
|
||||
Pivot = Float2.Zero,
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContainerControl ContainerControl => Panel;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Elements
|
||||
@@ -13,7 +14,10 @@ namespace FlaxEditor.CustomEditors.Elements
|
||||
/// <summary>
|
||||
/// The panel.
|
||||
/// </summary>
|
||||
public readonly VerticalPanel Panel = new VerticalPanel();
|
||||
public readonly VerticalPanel Panel = new VerticalPanel
|
||||
{
|
||||
Pivot = Float2.Zero,
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContainerControl ContainerControl => Panel;
|
||||
|
||||
@@ -851,6 +851,11 @@ namespace FlaxEditor
|
||||
{
|
||||
LogWarning("Exception: " + ex.Message);
|
||||
LogWarning(ex.StackTrace);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
LogWarning("Inner exception:");
|
||||
LogWarning(ex.InnerException);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1509,7 +1514,8 @@ namespace FlaxEditor
|
||||
var win = Windows.GameWin?.Root;
|
||||
if (win?.RootWindow is WindowRootControl root)
|
||||
{
|
||||
pos = Float2.Round(Windows.GameWin.Viewport.PointFromScreen(pos) * root.DpiScale);
|
||||
pos = Windows.GameWin.Viewport.PointFromScreen(pos);
|
||||
pos = Float2.Round(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1522,7 +1528,8 @@ namespace FlaxEditor
|
||||
var win = Windows.GameWin?.Root;
|
||||
if (win?.RootWindow is WindowRootControl root)
|
||||
{
|
||||
pos = Float2.Round(Windows.GameWin.Viewport.PointToScreen(pos / root.DpiScale));
|
||||
pos = Windows.GameWin.Viewport.PointToScreen(pos);
|
||||
pos = Float2.Round(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1554,6 +1561,7 @@ namespace FlaxEditor
|
||||
else
|
||||
result = gameWin.Viewport.Size;
|
||||
|
||||
result *= root.DpiScale;
|
||||
result = Float2.Round(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +464,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
base.Focus();
|
||||
|
||||
SelectTab();
|
||||
SelectTab(false);
|
||||
BringToFront();
|
||||
}
|
||||
|
||||
|
||||
@@ -294,6 +294,7 @@ namespace FlaxEditor.GUI
|
||||
Parent = _scrollPanel,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
IsScrollable = true,
|
||||
Pivot = Float2.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -833,6 +833,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
{
|
||||
AutoFocus = false,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Pivot = Float2.Zero,
|
||||
Offsets = Margin.Zero,
|
||||
IsScrollable = true,
|
||||
BottomMargin = 40.0f,
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace FlaxEditor.Gizmo
|
||||
/// <inheritdoc />
|
||||
public EditorPrimitives()
|
||||
{
|
||||
Order = -100;
|
||||
Order = 100;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace FlaxEngine.Gizmo;
|
||||
/// <summary>
|
||||
/// Class for adding viewport rubber band selection.
|
||||
/// </summary>
|
||||
public class ViewportRubberBandSelector
|
||||
public sealed class ViewportRubberBandSelector
|
||||
{
|
||||
private bool _isMosueCaptured;
|
||||
private bool _isRubberBandSpanning;
|
||||
@@ -38,7 +38,7 @@ public class ViewportRubberBandSelector
|
||||
/// <returns>True if selection started, otherwise false.</returns>
|
||||
public bool TryStartingRubberBandSelection()
|
||||
{
|
||||
if (!_isRubberBandSpanning && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
|
||||
if (!_isRubberBandSpanning && _owner.Gizmos.Active != null && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
|
||||
{
|
||||
_tryStartRubberBand = true;
|
||||
return true;
|
||||
@@ -90,7 +90,7 @@ public class ViewportRubberBandSelector
|
||||
_rubberBandRect = new Rectangle(_cachedStartingMousePosition, Float2.Zero);
|
||||
_tryStartRubberBand = false;
|
||||
}
|
||||
else if (_isRubberBandSpanning && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
|
||||
else if (_isRubberBandSpanning && _owner.Gizmos.Active != null && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
|
||||
{
|
||||
_rubberBandRect.Width = mousePosition.X - _cachedStartingMousePosition.X;
|
||||
_rubberBandRect.Height = mousePosition.Y - _cachedStartingMousePosition.Y;
|
||||
@@ -162,8 +162,8 @@ public class ViewportRubberBandSelector
|
||||
projection.Init(_owner.Viewport);
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
// Check for custom can select code
|
||||
if (!node.CanSelectActorNodeWithSelector())
|
||||
// Skip actors that cannot be selected
|
||||
if (!node.CanSelectInViewport)
|
||||
continue;
|
||||
var a = node.Actor;
|
||||
|
||||
@@ -232,30 +232,15 @@ public class ViewportRubberBandSelector
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to draw the rubber band. Begins render 2D.
|
||||
/// Draws the ruber band during owner viewport UI drawing.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU Context.</param>
|
||||
/// <param name="target">The GPU texture target.</param>
|
||||
/// <param name="targetDepth">The GPU texture target depth.</param>
|
||||
public void Draw(GPUContext context, GPUTexture target, GPUTexture targetDepth)
|
||||
{
|
||||
// Draw RubberBand for rect selection
|
||||
if (!_isRubberBandSpanning)
|
||||
return;
|
||||
Render2D.Begin(context, target, targetDepth);
|
||||
Draw2D();
|
||||
Render2D.End();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to draw the rubber band. Use if already rendering 2D context.
|
||||
/// </summary>
|
||||
public void Draw2D()
|
||||
public void Draw()
|
||||
{
|
||||
if (!_isRubberBandSpanning)
|
||||
return;
|
||||
Render2D.FillRectangle(_rubberBandRect, Style.Current.Selection);
|
||||
Render2D.DrawRectangle(_rubberBandRect, Style.Current.SelectionBorder);
|
||||
var style = Style.Current;
|
||||
Render2D.FillRectangle(_rubberBandRect, style.Selection);
|
||||
Render2D.DrawRectangle(_rubberBandRect, style.SelectionBorder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace FlaxEditor.Modules
|
||||
private bool _enableEvents;
|
||||
private bool _isDuringFastSetup;
|
||||
private bool _rebuildFlag;
|
||||
private bool _rebuildInitFlag;
|
||||
private int _itemsCreated;
|
||||
private int _itemsDeleted;
|
||||
private readonly HashSet<MainContentTreeNode> _dirtyNodes = new HashSet<MainContentTreeNode>();
|
||||
@@ -61,7 +62,7 @@ namespace FlaxEditor.Modules
|
||||
public event Action WorkspaceModified;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when workspace has will be rebuilt.
|
||||
/// Occurs when workspace will be rebuilt.
|
||||
/// </summary>
|
||||
public event Action WorkspaceRebuilding;
|
||||
|
||||
@@ -88,6 +89,9 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// Register AssetItems serialization helper (serialize ref ID only)
|
||||
FlaxEngine.Json.JsonSerializer.Settings.Converters.Add(new AssetItemConverter());
|
||||
|
||||
ScriptsBuilder.ScriptsReload += OnScriptsReload;
|
||||
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||
}
|
||||
|
||||
private void OnContentAssetDisposing(Asset asset)
|
||||
@@ -817,6 +821,7 @@ namespace FlaxEditor.Modules
|
||||
Profiler.BeginEvent("ContentDatabase.Rebuild");
|
||||
var startTime = Platform.TimeSeconds;
|
||||
_rebuildFlag = false;
|
||||
_rebuildInitFlag = false;
|
||||
_enableEvents = false;
|
||||
|
||||
// Load all folders
|
||||
@@ -1230,8 +1235,6 @@ namespace FlaxEditor.Modules
|
||||
LoadProjects(Game.Project);
|
||||
}
|
||||
|
||||
RebuildInternal();
|
||||
|
||||
Editor.ContentImporting.ImportFileEnd += (obj, failed) =>
|
||||
{
|
||||
var path = obj.ResultUrl;
|
||||
@@ -1239,6 +1242,15 @@ namespace FlaxEditor.Modules
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() => OnImportFileDone(path));
|
||||
};
|
||||
_enableEvents = true;
|
||||
_rebuildInitFlag = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndInit()
|
||||
{
|
||||
// Handle init when project was loaded without scripts loading ()
|
||||
if (_rebuildInitFlag)
|
||||
RebuildInternal();
|
||||
}
|
||||
|
||||
private void OnImportFileDone(string path)
|
||||
@@ -1313,6 +1325,52 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScriptsReload()
|
||||
{
|
||||
var enabledEvents = _enableEvents;
|
||||
_enableEvents = false;
|
||||
_isDuringFastSetup = true;
|
||||
var startItems = _itemsCreated;
|
||||
foreach (var project in Projects)
|
||||
{
|
||||
if (project.Content != null)
|
||||
{
|
||||
//Dispose(project.Content.Folder);
|
||||
for (int i = 0; i < project.Content.Folder.Children.Count; i++)
|
||||
{
|
||||
Dispose(project.Content.Folder.Children[i]);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
if (project.Source != null)
|
||||
{
|
||||
//Dispose(project.Source.Folder);
|
||||
for (int i = 0; i < project.Source.Folder.Children.Count; i++)
|
||||
{
|
||||
Dispose(project.Source.Folder.Children[i]);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<ContentProxy> removeProxies = new List<ContentProxy>();
|
||||
foreach (var proxy in Editor.Instance.ContentDatabase.Proxy)
|
||||
{
|
||||
if (proxy.GetType().IsCollectible)
|
||||
removeProxies.Add(proxy);
|
||||
}
|
||||
foreach (var proxy in removeProxies)
|
||||
RemoveProxy(proxy, false);
|
||||
|
||||
_isDuringFastSetup = false;
|
||||
_enableEvents = enabledEvents;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadEnd()
|
||||
{
|
||||
RebuildInternal();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnUpdate()
|
||||
{
|
||||
@@ -1340,6 +1398,8 @@ namespace FlaxEditor.Modules
|
||||
public override void OnExit()
|
||||
{
|
||||
FlaxEngine.Content.AssetDisposing -= OnContentAssetDisposing;
|
||||
ScriptsBuilder.ScriptsReload -= OnScriptsReload;
|
||||
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||
|
||||
// Disable events
|
||||
_enableEvents = false;
|
||||
|
||||
@@ -391,6 +391,20 @@ namespace FlaxEditor.Modules
|
||||
public override void OnInit()
|
||||
{
|
||||
ImportFileEntry.RegisterDefaultTypes();
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
{
|
||||
// Remove import file types from scripting assemblies
|
||||
List<string> removeFileTypes = new List<string>();
|
||||
foreach (var pair in ImportFileEntry.FileTypes)
|
||||
{
|
||||
if (pair.Value.Method.IsCollectible || (pair.Value.Target != null && pair.Value.Target.GetType().IsCollectible))
|
||||
removeFileTypes.Add(pair.Key);
|
||||
}
|
||||
foreach (var fileType in removeFileTypes)
|
||||
ImportFileEntry.FileTypes.Remove(fileType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -451,6 +465,7 @@ namespace FlaxEditor.Modules
|
||||
/// <inheritdoc />
|
||||
public override void OnExit()
|
||||
{
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
EndWorker();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace FlaxEditor.Modules
|
||||
: base(editor)
|
||||
{
|
||||
// After editor cache but before the windows
|
||||
InitOrder = -900;
|
||||
InitOrder = -800;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace FlaxEditor.Modules
|
||||
internal UIModule(Editor editor)
|
||||
: base(editor)
|
||||
{
|
||||
InitOrder = -90;
|
||||
InitOrder = -70;
|
||||
VisjectSurfaceBackground = FlaxEngine.Content.LoadAsyncInternal<Texture>("Editor/VisjectSurface");
|
||||
ColorValueBox.ShowPickColorDialog += ShowPickColorDialog;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEditor.Windows.Profiler;
|
||||
@@ -39,6 +41,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
public DockState DockState;
|
||||
public DockPanel DockedTo;
|
||||
public int DockedTabIndex;
|
||||
public float? SplitterValue = null;
|
||||
|
||||
public bool SelectOnShow = false;
|
||||
@@ -48,6 +51,8 @@ namespace FlaxEditor.Modules
|
||||
public Float2 FloatSize;
|
||||
public Float2 FloatPosition;
|
||||
|
||||
public Guid AssetItemID;
|
||||
|
||||
// Constructor, to allow for default values
|
||||
public WindowRestoreData()
|
||||
{
|
||||
@@ -803,43 +808,64 @@ namespace FlaxEditor.Modules
|
||||
Level.SceneSaving += OnSceneSaving;
|
||||
Level.SceneUnloaded += OnSceneUnloaded;
|
||||
Level.SceneUnloading += OnSceneUnloading;
|
||||
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||
Editor.ContentDatabase.WorkspaceRebuilt += OnWorkspaceRebuilt;
|
||||
Editor.StateMachine.StateChanged += OnEditorStateChanged;
|
||||
}
|
||||
|
||||
internal void AddToRestore(AssetEditorWindow win)
|
||||
{
|
||||
AddToRestore(win, win.GetType(), new WindowRestoreData
|
||||
{
|
||||
AssetItemID = win.Item.ID,
|
||||
});
|
||||
}
|
||||
|
||||
internal void AddToRestore(CustomEditorWindow win)
|
||||
{
|
||||
var type = win.GetType();
|
||||
AddToRestore(win.Window, win.GetType(), new WindowRestoreData());
|
||||
}
|
||||
|
||||
private void AddToRestore(EditorWindow win, Type type, WindowRestoreData winData)
|
||||
{
|
||||
// Validate if can restore type
|
||||
var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
if (constructor == null || type.IsGenericType)
|
||||
return;
|
||||
|
||||
var winData = new WindowRestoreData();
|
||||
var panel = win.Window.ParentDockPanel;
|
||||
var panel = win.ParentDockPanel;
|
||||
|
||||
// Ensure that this window is only selected following recompilation
|
||||
// if it was the active tab in its dock panel. Otherwise, there is a
|
||||
// risk of interrupting the user's workflow by potentially selecting
|
||||
// background tabs.
|
||||
winData.SelectOnShow = panel.SelectedTab == win.Window;
|
||||
if (panel is FloatWindowDockPanel)
|
||||
var window = win.RootWindow.Window;
|
||||
winData.SelectOnShow = panel.SelectedTab == win;
|
||||
winData.DockedTabIndex = 0;
|
||||
if (panel is FloatWindowDockPanel && window != null && panel.TabsCount == 1)
|
||||
{
|
||||
winData.DockState = DockState.Float;
|
||||
var window = win.Window.RootWindow.Window;
|
||||
winData.FloatPosition = window.Position;
|
||||
winData.FloatSize = window.ClientSize;
|
||||
winData.Maximize = window.IsMaximized;
|
||||
winData.Minimize = window.IsMinimized;
|
||||
winData.DockedTo = panel;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < panel.Tabs.Count; i++)
|
||||
{
|
||||
if (panel.Tabs[i] == win)
|
||||
{
|
||||
winData.DockedTabIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (panel.TabsCount > 1)
|
||||
{
|
||||
winData.DockState = DockState.DockFill;
|
||||
winData.DockedTo = panel;
|
||||
}else
|
||||
}
|
||||
else
|
||||
{
|
||||
winData.DockState = panel.TryGetDockState(out var splitterValue);
|
||||
winData.DockedTo = panel.ParentDockPanel;
|
||||
@@ -851,38 +877,93 @@ namespace FlaxEditor.Modules
|
||||
_restoreWindows.Add(winData);
|
||||
}
|
||||
|
||||
private void OnScriptsReloadEnd()
|
||||
private void OnWorkspaceRebuilt()
|
||||
{
|
||||
for (int i = 0; i < _restoreWindows.Count; i++)
|
||||
// Go in reverse order to create floating Prefab windows first before docked windows
|
||||
for (int i = _restoreWindows.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var winData = _restoreWindows[i];
|
||||
|
||||
try
|
||||
{
|
||||
var assembly = Utils.GetAssemblyByName(winData.AssemblyName);
|
||||
if (assembly != null)
|
||||
if (assembly == null)
|
||||
continue;
|
||||
|
||||
var type = assembly.GetType(winData.TypeName);
|
||||
if (type == null)
|
||||
continue;
|
||||
|
||||
if (type.IsAssignableTo(typeof(AssetEditorWindow)))
|
||||
{
|
||||
var type = assembly.GetType(winData.TypeName);
|
||||
if (type != null)
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
|
||||
var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
|
||||
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
|
||||
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
|
||||
if (winData.DockState == DockState.Float)
|
||||
{
|
||||
var win = (CustomEditorWindow)Activator.CreateInstance(type);
|
||||
win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue);
|
||||
if (winData.DockState == DockState.Float)
|
||||
var window = win.RootWindow.Window;
|
||||
window.Position = winData.FloatPosition;
|
||||
if (winData.Maximize)
|
||||
{
|
||||
var window = win.Window.RootWindow.Window;
|
||||
window.Position = winData.FloatPosition;
|
||||
if (winData.Maximize)
|
||||
{
|
||||
window.Maximize();
|
||||
}
|
||||
else if (winData.Minimize)
|
||||
{
|
||||
window.Minimize();
|
||||
}
|
||||
else
|
||||
{
|
||||
window.ClientSize = winData.FloatSize;
|
||||
}
|
||||
window.Maximize();
|
||||
}
|
||||
else if (winData.Minimize)
|
||||
{
|
||||
window.Minimize();
|
||||
}
|
||||
else
|
||||
{
|
||||
window.ClientSize = winData.FloatSize;
|
||||
}
|
||||
|
||||
// Update panel reference in other windows docked to this panel
|
||||
foreach (ref var otherData in CollectionsMarshal.AsSpan(_restoreWindows))
|
||||
{
|
||||
if (otherData.DockedTo == winData.DockedTo)
|
||||
otherData.DockedTo = win.ParentDockPanel;
|
||||
}
|
||||
}
|
||||
var panel = win.ParentDockPanel;
|
||||
int currentTabIndex = 0;
|
||||
for (int pi = 0; pi < panel.TabsCount; pi++)
|
||||
{
|
||||
if (panel.Tabs[pi] == win)
|
||||
{
|
||||
currentTabIndex = pi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (currentTabIndex > winData.DockedTabIndex)
|
||||
{
|
||||
win.ParentDockPanel.MoveTabLeft(currentTabIndex);
|
||||
currentTabIndex--;
|
||||
}
|
||||
while (currentTabIndex < winData.DockedTabIndex)
|
||||
{
|
||||
win.ParentDockPanel.MoveTabRight(currentTabIndex);
|
||||
currentTabIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var win = (CustomEditorWindow)Activator.CreateInstance(type);
|
||||
win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue);
|
||||
if (winData.DockState == DockState.Float)
|
||||
{
|
||||
var window = win.Window.RootWindow.Window;
|
||||
window.Position = winData.FloatPosition;
|
||||
if (winData.Maximize)
|
||||
{
|
||||
window.Maximize();
|
||||
}
|
||||
else if (winData.Minimize)
|
||||
{
|
||||
window.Minimize();
|
||||
}
|
||||
else
|
||||
{
|
||||
window.ClientSize = winData.FloatSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -893,6 +974,11 @@ namespace FlaxEditor.Modules
|
||||
Editor.LogWarning(string.Format("Failed to restore window {0} (assembly: {1})", winData.TypeName, winData.AssemblyName));
|
||||
}
|
||||
}
|
||||
|
||||
// Restored windows stole the focus from Editor
|
||||
if (_restoreWindows.Count > 0)
|
||||
Editor.Instance.Windows.MainWindow.Focus();
|
||||
|
||||
_restoreWindows.Clear();
|
||||
}
|
||||
|
||||
@@ -1048,7 +1134,7 @@ namespace FlaxEditor.Modules
|
||||
Level.SceneSaving -= OnSceneSaving;
|
||||
Level.SceneUnloaded -= OnSceneUnloaded;
|
||||
Level.SceneUnloading -= OnSceneUnloading;
|
||||
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||
Editor.ContentDatabase.WorkspaceRebuilt -= OnWorkspaceRebuilt;
|
||||
Editor.StateMachine.StateChanged -= OnEditorStateChanged;
|
||||
|
||||
// Close main window
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace FlaxEditor.SceneGraph
|
||||
GetAllChildActorNodes(nodes);
|
||||
return nodes.ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get all nested actor nodes under this actor node.
|
||||
/// </summary>
|
||||
@@ -213,12 +213,18 @@ namespace FlaxEditor.SceneGraph
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether an actor node can be selected with a selector.
|
||||
/// Whether an actor node can be selected with a selector inside editor viewport.
|
||||
/// </summary>
|
||||
/// <returns>True if the actor node can be selected</returns>
|
||||
public virtual bool CanSelectActorNodeWithSelector()
|
||||
public virtual bool CanSelectInViewport
|
||||
{
|
||||
return Actor && Actor.HideFlags is not (HideFlags.DontSelect or HideFlags.FullyHidden) && Actor is not EmptyActor && IsActive;
|
||||
get
|
||||
{
|
||||
var actor = Actor;
|
||||
return actor &&
|
||||
actor.IsActiveInHierarchy &&
|
||||
(actor.HideFlags & HideFlags.DontSelect) == HideFlags.None &&
|
||||
actor.GetType() != typeof(EmptyActor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -33,12 +33,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSelectActorNodeWithSelector()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scene.
|
||||
/// </summary>
|
||||
@@ -53,6 +47,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSelectInViewport => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreatePrefab => false;
|
||||
|
||||
|
||||
@@ -169,7 +169,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
var actor = new BoxCollider
|
||||
{
|
||||
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||
Transform = staticModelNode.Actor.Transform,
|
||||
};
|
||||
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
|
||||
@@ -180,7 +179,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
var actor = new SphereCollider
|
||||
{
|
||||
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||
Transform = staticModelNode.Actor.Transform,
|
||||
};
|
||||
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
|
||||
@@ -191,7 +189,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
var actor = new BoxCollider
|
||||
{
|
||||
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||
Transform = staticModelNode.Actor.Transform,
|
||||
Size = new Float3(100.0f, 100.0f, 1.0f),
|
||||
};
|
||||
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||
@@ -203,7 +200,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
var actor = new CapsuleCollider
|
||||
{
|
||||
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||
Transform = staticModelNode.Actor.Transform,
|
||||
Radius = 25.0f,
|
||||
Height = 50.0f,
|
||||
};
|
||||
@@ -220,7 +216,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
var actor = new MeshCollider
|
||||
{
|
||||
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||
Transform = staticModelNode.Actor.Transform,
|
||||
CollisionData = collisionData,
|
||||
};
|
||||
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||
|
||||
@@ -80,9 +80,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSelectActorNodeWithSelector()
|
||||
{
|
||||
return Actor is UICanvas uiCanvas && uiCanvas.Is3D && base.CanSelectActorNodeWithSelector();
|
||||
}
|
||||
public override bool CanSelectInViewport => base.CanSelectInViewport && Actor is UICanvas uiCanvas && uiCanvas.Is3D;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,29 +42,31 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSelectActorNodeWithSelector()
|
||||
public override bool CanSelectInViewport
|
||||
{
|
||||
// Check if control and skip if canvas is 2D
|
||||
if (Actor is not UIControl uiControl)
|
||||
return false;
|
||||
UICanvas canvas = null;
|
||||
var controlParent = uiControl.Parent;
|
||||
while (controlParent != null && controlParent is not Scene)
|
||||
get
|
||||
{
|
||||
if (controlParent is UICanvas uiCanvas)
|
||||
{
|
||||
canvas = uiCanvas;
|
||||
break;
|
||||
}
|
||||
controlParent = controlParent.Parent;
|
||||
}
|
||||
|
||||
if (canvas != null)
|
||||
{
|
||||
if (canvas.Is2D)
|
||||
// Check if control and skip if canvas is 2D
|
||||
if (Actor is not UIControl uiControl)
|
||||
return false;
|
||||
UICanvas canvas = null;
|
||||
var controlParent = uiControl.Parent;
|
||||
while (controlParent != null && controlParent is not Scene)
|
||||
{
|
||||
if (controlParent is UICanvas uiCanvas)
|
||||
{
|
||||
canvas = uiCanvas;
|
||||
break;
|
||||
}
|
||||
controlParent = controlParent.Parent;
|
||||
}
|
||||
if (canvas != null)
|
||||
{
|
||||
if (canvas.Is2D)
|
||||
return false;
|
||||
}
|
||||
return base.CanSelectInViewport;
|
||||
}
|
||||
return base.CanSelectActorNodeWithSelector();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,6 +469,7 @@ namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
ChildNodes[i].OnDispose();
|
||||
}
|
||||
ChildNodes.Clear();
|
||||
|
||||
SceneGraphFactory.Nodes.Remove(ID);
|
||||
}
|
||||
|
||||
@@ -164,10 +164,21 @@ namespace FlaxEditor.States
|
||||
{
|
||||
Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state.");
|
||||
|
||||
// Bind events
|
||||
Level.SceneLoaded += OnSceneEvent;
|
||||
Level.SceneLoadError += OnSceneEvent;
|
||||
Level.SceneUnloaded += OnSceneEvent;
|
||||
// Bind events, only bind loading event and error if re-loading the same scene to avoid issues.
|
||||
if (_scenesToUnload.Count == 1 && _scenesToLoad.Count == 1)
|
||||
{
|
||||
if (_scenesToLoad[0] == _scenesToUnload[0].ID)
|
||||
{
|
||||
Level.SceneLoaded += OnSceneEvent;
|
||||
Level.SceneLoadError += OnSceneEvent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Level.SceneLoaded += OnSceneEvent;
|
||||
Level.SceneLoadError += OnSceneEvent;
|
||||
Level.SceneUnloaded += OnSceneEvent;
|
||||
}
|
||||
|
||||
// Push scenes changing requests
|
||||
for (int i = 0; i < _scenesToUnload.Count; i++)
|
||||
|
||||
@@ -161,6 +161,8 @@ namespace FlaxEditor.Surface
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
{
|
||||
_nodesCache.Clear();
|
||||
|
||||
// Check if any of the nodes comes from the game scripts - those can be reloaded at runtime so prevent crashes
|
||||
bool hasTypeFromGameScripts = Editor.Instance.CodeEditing.AnimGraphNodes.HasTypeFromGameScripts;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace FlaxEditor.Surface
|
||||
public BehaviorTreeSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo)
|
||||
: base(owner, onSave, undo, CreateStyle())
|
||||
{
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
}
|
||||
|
||||
private static SurfaceStyle CreateStyle()
|
||||
@@ -35,6 +36,11 @@ namespace FlaxEditor.Surface
|
||||
return style;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
{
|
||||
_nodesCache.Clear();
|
||||
}
|
||||
|
||||
private static void DrawBox(Box box)
|
||||
{
|
||||
var rect = new Rectangle(Float2.Zero, box.Size);
|
||||
@@ -186,6 +192,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
_nodesCache.Wait();
|
||||
|
||||
base.OnDestroy();
|
||||
|
||||
@@ -235,6 +235,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
Parent = panel1,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
};
|
||||
_groupsPanel = panel2;
|
||||
@@ -292,6 +293,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
X = 8,
|
||||
Width = Width * 0.5f - 16,
|
||||
AutoSize = true,
|
||||
Pivot = Float2.Zero,
|
||||
};
|
||||
|
||||
_descriptionOutputPanel = new VerticalPanel()
|
||||
@@ -300,6 +302,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
X = Width * 0.5f + 8,
|
||||
Width = Width * 0.5f - 16,
|
||||
AutoSize = true,
|
||||
Pivot = Float2.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// <param name="archetype">The group archetype.</param>
|
||||
public VisjectCMGroup(VisjectCM cm, GroupArchetype archetype)
|
||||
{
|
||||
Pivot = Float2.Zero;
|
||||
ContextMenu = cm;
|
||||
Archetypes.Add(archetype);
|
||||
Name = archetype.Name;
|
||||
|
||||
@@ -415,6 +415,15 @@ namespace FlaxEditor.Surface
|
||||
// Init drag handlers
|
||||
DragHandlers.Add(_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem));
|
||||
DragHandlers.Add(_dragParameters = new DragNames<DragDropEventArgs>(SurfaceParameter.DragPrefix, ValidateDragParameter));
|
||||
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
{
|
||||
_activeVisjectCM = null;
|
||||
_cmPrimaryMenu?.Dispose();
|
||||
_cmPrimaryMenu = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1023,6 +1032,8 @@ namespace FlaxEditor.Surface
|
||||
_activeVisjectCM = null;
|
||||
_cmPrimaryMenu?.Dispose();
|
||||
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,12 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
_supportsImplicitCastFromObjectToBoolean = true;
|
||||
DragHandlers.Add(_dragActors = new DragActors(ValidateDragActor));
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
{
|
||||
_nodesCache.Clear();
|
||||
}
|
||||
|
||||
private bool ValidateDragActor(ActorNode actor)
|
||||
@@ -631,6 +637,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
_nodesCache.Wait();
|
||||
|
||||
base.OnDestroy();
|
||||
|
||||
@@ -352,6 +352,7 @@ namespace FlaxEditor.Tools.Foliage
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(4, 4, 4, 0),
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = splitPanel.Panel1
|
||||
};
|
||||
|
||||
@@ -204,6 +204,7 @@ namespace FlaxEditor.Tools.Foliage
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(4, 4, 4, 0),
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = splitPanel.Panel1
|
||||
};
|
||||
|
||||
@@ -1211,6 +1211,7 @@ namespace FlaxEditor.Utilities
|
||||
{
|
||||
Parent = panel1,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
};
|
||||
tree = new Tree(false)
|
||||
|
||||
@@ -7,14 +7,11 @@ using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Tools;
|
||||
using FlaxEditor.Viewport.Modes;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Gizmo;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Tools;
|
||||
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport
|
||||
@@ -26,10 +23,8 @@ namespace FlaxEditor.Viewport
|
||||
public class MainEditorGizmoViewport : EditorGizmoViewport, IEditorPrimitivesOwner
|
||||
{
|
||||
private readonly Editor _editor;
|
||||
|
||||
private readonly ContextMenuButton _showGridButton;
|
||||
private readonly ContextMenuButton _showNavigationButton;
|
||||
|
||||
private SelectionOutline _customSelectionOutline;
|
||||
|
||||
/// <summary>
|
||||
@@ -218,7 +213,7 @@ namespace FlaxEditor.Viewport
|
||||
TransformGizmo.ApplyTransformation += ApplyTransform;
|
||||
TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate;
|
||||
Gizmos.Active = TransformGizmo;
|
||||
|
||||
|
||||
// Add rubber band selector
|
||||
_rubberBandSelector = new ViewportRubberBandSelector(this);
|
||||
|
||||
@@ -375,10 +370,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
Gizmos[i].Draw(ref renderContext);
|
||||
}
|
||||
|
||||
// Draw RubberBand for rect selection
|
||||
_rubberBandSelector.Draw(context, target, targetDepth);
|
||||
|
||||
|
||||
// Draw selected objects debug shapes and visuals
|
||||
if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw)
|
||||
{
|
||||
@@ -594,6 +586,15 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Draw rubber band for rectangle selection
|
||||
_rubberBandSelector.Draw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OrientViewport(ref Quaternion orientation)
|
||||
{
|
||||
@@ -609,7 +610,8 @@ namespace FlaxEditor.Viewport
|
||||
base.OnMouseMove(location);
|
||||
|
||||
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
|
||||
bool canStart = !((Gizmos.Active.IsControllingMouse || Gizmos.Active is VertexPaintingGizmo || Gizmos.Active is ClothPaintingGizmo) || IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown);
|
||||
bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) &&
|
||||
Gizmos.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse;
|
||||
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos, ViewFrustum);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
InputActions.Add(options => options.Save, Save);
|
||||
|
||||
UpdateTitle();
|
||||
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -151,6 +153,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
|
||||
if (_item != null)
|
||||
{
|
||||
// Ensure to remove linkage to the item
|
||||
@@ -160,6 +164,15 @@ namespace FlaxEditor.Windows.Assets
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected virtual void OnScriptsReloadBegin()
|
||||
{
|
||||
if (!IsHidden)
|
||||
{
|
||||
Editor.Instance.Windows.AddToRestore(this);
|
||||
}
|
||||
}
|
||||
|
||||
#region IEditable Implementation
|
||||
|
||||
private bool _isEdited;
|
||||
|
||||
@@ -268,8 +268,11 @@ namespace FlaxEditor.Windows.Assets
|
||||
UpdateKnowledge();
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
/// <inheritdoc />
|
||||
protected override void OnScriptsReloadBegin()
|
||||
{
|
||||
base.OnScriptsReloadBegin();
|
||||
|
||||
// TODO: impl hot-reload for BT to nicely refresh state (save asset, clear undo/properties, reload surface)
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -124,8 +124,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
/// <inheritdoc />
|
||||
protected override void OnScriptsReloadBegin()
|
||||
{
|
||||
base.OnScriptsReloadBegin();
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -307,6 +309,20 @@ namespace FlaxEditor.Windows.Assets
|
||||
base.OnAssetLoadFailed();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
base.OnLostFocus();
|
||||
_optionsCM?.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnExit()
|
||||
{
|
||||
base.OnExit();
|
||||
_optionsCM?.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnItemReimported(ContentItem item)
|
||||
{
|
||||
@@ -329,6 +345,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_isRegisteredForScriptsReload = false;
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
}
|
||||
_optionsCM?.Dispose();
|
||||
_typeText = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,6 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
Editor.Prefabs.PrefabApplied += OnPrefabApplied;
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||
|
||||
// Setup input actions
|
||||
InputActions.Add(options => options.Undo, () =>
|
||||
@@ -291,8 +290,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
/// <inheritdoc />
|
||||
protected override void OnScriptsReloadBegin()
|
||||
{
|
||||
base.OnScriptsReloadBegin();
|
||||
_isScriptsReloading = true;
|
||||
|
||||
if (_asset == null || !_asset.IsLoaded)
|
||||
@@ -320,19 +321,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
Graph.MainActor = null;
|
||||
_viewport.Prefab = null;
|
||||
_undo?.Clear(); // TODO: maybe don't clear undo?
|
||||
}
|
||||
|
||||
private void OnScriptsReloadEnd()
|
||||
{
|
||||
_isScriptsReloading = false;
|
||||
|
||||
if (_asset == null || !_asset.IsLoaded)
|
||||
return;
|
||||
|
||||
// Restore
|
||||
OnPrefabOpened();
|
||||
_undo.Clear();
|
||||
ClearEditedFlag();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnUndoEvent(IUndoAction action)
|
||||
@@ -555,7 +545,6 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
Editor.Prefabs.PrefabApplied -= OnPrefabApplied;
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||
|
||||
_undo.Dispose();
|
||||
Graph.Dispose();
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace FlaxEditor.Windows
|
||||
private const string ProjectDataLastViewedFolder = "LastViewedFolder";
|
||||
private bool _isWorkspaceDirty;
|
||||
private string _workspaceRebuildLocation;
|
||||
private string _lastViewedFolderBeforeReload;
|
||||
private SplitPanel _split;
|
||||
private Panel _contentViewPanel;
|
||||
private Panel _contentTreePanel;
|
||||
@@ -144,26 +145,6 @@ namespace FlaxEditor.Windows
|
||||
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
|
||||
// Content database events
|
||||
editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true;
|
||||
editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved;
|
||||
editor.ContentDatabase.WorkspaceRebuilding += () => { _workspaceRebuildLocation = SelectedNode?.Path; };
|
||||
editor.ContentDatabase.WorkspaceRebuilt += () =>
|
||||
{
|
||||
var selected = Editor.ContentDatabase.Find(_workspaceRebuildLocation);
|
||||
if (selected is ContentFolder selectedFolder)
|
||||
{
|
||||
_navigationUnlocked = false;
|
||||
RefreshView(selectedFolder.Node);
|
||||
_tree.Select(selectedFolder.Node);
|
||||
UpdateItemsSearch();
|
||||
_navigationUnlocked = true;
|
||||
UpdateUI();
|
||||
}
|
||||
else
|
||||
ShowRoot();
|
||||
};
|
||||
|
||||
var options = Editor.Options;
|
||||
options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
@@ -1036,6 +1017,61 @@ namespace FlaxEditor.Windows
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnInit()
|
||||
{
|
||||
// Content database events
|
||||
Editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true;
|
||||
Editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved;
|
||||
Editor.ContentDatabase.WorkspaceRebuilding += () => { _workspaceRebuildLocation = SelectedNode?.Path; };
|
||||
Editor.ContentDatabase.WorkspaceRebuilt += () =>
|
||||
{
|
||||
var selected = Editor.ContentDatabase.Find(_workspaceRebuildLocation);
|
||||
if (selected is ContentFolder selectedFolder)
|
||||
{
|
||||
_navigationUnlocked = false;
|
||||
RefreshView(selectedFolder.Node);
|
||||
_tree.Select(selectedFolder.Node);
|
||||
UpdateItemsSearch();
|
||||
_navigationUnlocked = true;
|
||||
UpdateUI();
|
||||
}
|
||||
else if (_root != null)
|
||||
ShowRoot();
|
||||
};
|
||||
|
||||
Refresh();
|
||||
|
||||
// Load last viewed folder
|
||||
if (Editor.ProjectCache.TryGetCustomData(ProjectDataLastViewedFolder, out string lastViewedFolder))
|
||||
{
|
||||
if (Editor.ContentDatabase.Find(lastViewedFolder) is ContentFolder folder)
|
||||
_tree.Select(folder.Node);
|
||||
}
|
||||
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
{
|
||||
var lastViewedFolder = _tree.Selection.Count == 1 ? _tree.SelectedNode as ContentTreeNode : null;
|
||||
_lastViewedFolderBeforeReload = lastViewedFolder?.Path ?? string.Empty;
|
||||
|
||||
_tree.RemoveChild(_root);
|
||||
_root = null;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadEnd()
|
||||
{
|
||||
Refresh();
|
||||
|
||||
if (!string.IsNullOrEmpty(_lastViewedFolderBeforeReload))
|
||||
{
|
||||
if (Editor.ContentDatabase.Find(_lastViewedFolderBeforeReload) is ContentFolder folder)
|
||||
_tree.Select(folder.Node);
|
||||
}
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
// Setup content root node
|
||||
_root = new RootContentTreeNode
|
||||
@@ -1072,13 +1108,6 @@ namespace FlaxEditor.Windows
|
||||
// Update UI layout
|
||||
_isLayoutLocked = false;
|
||||
PerformLayout();
|
||||
|
||||
// Load last viewed folder
|
||||
if (Editor.ProjectCache.TryGetCustomData(ProjectDataLastViewedFolder, out string lastViewedFolder))
|
||||
{
|
||||
if (Editor.ContentDatabase.Find(lastViewedFolder) is ContentFolder folder)
|
||||
_tree.Select(folder.Node);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1226,6 +1255,8 @@ namespace FlaxEditor.Windows
|
||||
_viewDropdown = null;
|
||||
|
||||
Editor.Options.OptionsChanged -= OnOptionsChanged;
|
||||
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -390,6 +390,7 @@ namespace FlaxEditor.Windows
|
||||
_entriesPanel = new VerticalPanel
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Pivot = Float2.Zero,
|
||||
Offsets = Margin.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = _split.Panel1,
|
||||
|
||||
@@ -582,7 +582,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private void OnOutputTextChanged()
|
||||
{
|
||||
if (IsLayoutLocked)
|
||||
if (IsLayoutLocked || _output == null)
|
||||
return;
|
||||
|
||||
_hScroll.Maximum = Mathf.Max(_output.TextSize.X, _hScroll.Minimum);
|
||||
|
||||
@@ -162,11 +162,13 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
Parent = this,
|
||||
};
|
||||
var panel = new VerticalPanel
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Pivot = Float2.Zero,
|
||||
Offsets = Margin.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = scroll,
|
||||
@@ -187,6 +189,7 @@ namespace FlaxEditor.Windows
|
||||
var vp = new Panel
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
Parent = this,
|
||||
};
|
||||
_addPluginProjectButton = new Button
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -88,6 +88,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#if USE_PROFILER
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Windows.Profiler
|
||||
@@ -29,6 +30,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = Margin.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
Parent = panel,
|
||||
};
|
||||
|
||||
@@ -92,6 +92,7 @@ namespace FlaxEditor.Windows
|
||||
Parent = this,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offset = Vector2.Zero,
|
||||
Pivot = Float2.Zero,
|
||||
AutoSize = false,
|
||||
Bounds = Rectangle.Empty
|
||||
};
|
||||
|
||||
@@ -150,6 +150,22 @@ namespace FlaxEditor.Windows
|
||||
_searchBox.Clear();
|
||||
_groupSearch.DisposeChildren();
|
||||
_groupSearch.PerformLayout();
|
||||
|
||||
// Remove tabs
|
||||
var tabs = new List<Tab>();
|
||||
foreach (var child in _actorGroups.Children)
|
||||
{
|
||||
if (child is Tab tab)
|
||||
{
|
||||
if (tab.Text != "Search")
|
||||
tabs.Add(tab);
|
||||
}
|
||||
}
|
||||
foreach (var tab in tabs)
|
||||
{
|
||||
var group = _actorGroups.Children.Find(T => T == tab);
|
||||
group.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScriptsReloadEnd()
|
||||
|
||||
@@ -73,7 +73,7 @@ void AnimationData::Swap(AnimationData& other)
|
||||
Channels.Swap(other.Channels);
|
||||
}
|
||||
|
||||
void AnimationData::Dispose()
|
||||
void AnimationData::Release()
|
||||
{
|
||||
Name.Clear();
|
||||
Duration = 0.0;
|
||||
|
||||
@@ -170,5 +170,5 @@ public:
|
||||
/// <summary>
|
||||
/// Releases data.
|
||||
/// </summary>
|
||||
void Dispose();
|
||||
void Release();
|
||||
};
|
||||
|
||||
@@ -738,7 +738,7 @@ void Animation::unload(bool isReloading)
|
||||
Level::ScriptsReloadStart.Unbind<Animation, &Animation::OnScriptsReloadStart>(this);
|
||||
}
|
||||
#endif
|
||||
Data.Dispose();
|
||||
Data.Release();
|
||||
for (const auto& e : Events)
|
||||
{
|
||||
for (const auto& k : e.Second.GetKeyframes())
|
||||
|
||||
@@ -203,7 +203,7 @@ void AnimationGraph::FindDependencies(AnimGraphBase* graph)
|
||||
{
|
||||
for (const auto& node : graph->Nodes)
|
||||
{
|
||||
if (node.Type == GRAPH_NODE_MAKE_TYPE(9, 24))
|
||||
if (node.Type == GRAPH_NODE_MAKE_TYPE(9, 24) && node.Assets.Count() > 0)
|
||||
{
|
||||
const auto function = node.Assets[0].As<AnimationGraphFunction>();
|
||||
if (function)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Engine/Level/Scripts/ModelPrefab.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Utilities/RectPack.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "AssetsImportingManager.h"
|
||||
|
||||
@@ -171,6 +172,33 @@ bool SortMeshGroups(IGrouping<StringView, MeshData*> const& i1, IGrouping<String
|
||||
return i1.GetKey().Compare(i2.GetKey()) < 0;
|
||||
}
|
||||
|
||||
void CloneObject(rapidjson_flax::StringBuffer& buffer, SceneObject* src, SceneObject* dst, bool stripName = false)
|
||||
{
|
||||
// Serialize source
|
||||
buffer.Clear();
|
||||
CompactJsonWriter writer(buffer);
|
||||
writer.StartObject();
|
||||
const void* defaultInstance = src->GetType().GetDefaultInstance();
|
||||
src->Serialize(writer, defaultInstance);
|
||||
writer.EndObject();
|
||||
|
||||
// Parse json
|
||||
rapidjson_flax::Document document;
|
||||
document.Parse(buffer.GetString(), buffer.GetSize());
|
||||
|
||||
// Strip unwanted data
|
||||
document.RemoveMember("ID");
|
||||
document.RemoveMember("ParentID");
|
||||
document.RemoveMember("PrefabID");
|
||||
document.RemoveMember("PrefabObjectID");
|
||||
if (stripName)
|
||||
document.RemoveMember("Name");
|
||||
|
||||
// Deserialize destination
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
dst->Deserialize(document, &*modifier);
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModel::Import(CreateAssetContext& context)
|
||||
{
|
||||
// Get import options
|
||||
@@ -659,6 +687,8 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
|
||||
|
||||
// Create prefab structure
|
||||
Dictionary<int32, Actor*> nodeToActor;
|
||||
Dictionary<Guid, SceneObject*> newPrefabObjects; // Maps prefab object id to the restored and linked object
|
||||
rapidjson_flax::StringBuffer jsonBuffer;
|
||||
Array<Actor*> nodeActors;
|
||||
Actor* rootActor = nullptr;
|
||||
for (int32 nodeIndex = 0; nodeIndex < data.Nodes.Count(); nodeIndex++)
|
||||
@@ -749,7 +779,6 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
|
||||
// Link with object from prefab (if reimporting)
|
||||
if (prefab)
|
||||
{
|
||||
rapidjson_flax::StringBuffer buffer;
|
||||
for (Actor* a : nodeActors)
|
||||
{
|
||||
for (const auto& i : prefab->ObjectsCache)
|
||||
@@ -761,33 +790,12 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
|
||||
continue;
|
||||
|
||||
// Preserve local changes made in the prefab
|
||||
{
|
||||
// Serialize
|
||||
buffer.Clear();
|
||||
CompactJsonWriter writer(buffer);
|
||||
writer.StartObject();
|
||||
const void* defaultInstance = o->GetType().GetDefaultInstance();
|
||||
o->Serialize(writer, defaultInstance);
|
||||
writer.EndObject();
|
||||
|
||||
// Parse json
|
||||
rapidjson_flax::Document document;
|
||||
document.Parse(buffer.GetString(), buffer.GetSize());
|
||||
|
||||
// Strip unwanted data
|
||||
document.RemoveMember("ID");
|
||||
document.RemoveMember("ParentID");
|
||||
document.RemoveMember("PrefabID");
|
||||
document.RemoveMember("PrefabObjectID");
|
||||
document.RemoveMember("Name");
|
||||
|
||||
// Deserialize object
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
a->Deserialize(document, &*modifier);
|
||||
}
|
||||
CloneObject(jsonBuffer, o, a, true);
|
||||
|
||||
// Mark as this object already exists in prefab so will be preserved when updating it
|
||||
a->LinkPrefab(o->GetPrefabID(), o->GetPrefabObjectID());
|
||||
const Guid prefabObjectId = o->GetPrefabObjectID();
|
||||
a->LinkPrefab(o->GetPrefabID(), prefabObjectId);
|
||||
newPrefabObjects.Add(prefabObjectId, a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -808,12 +816,43 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
|
||||
{
|
||||
if (i.Value->GetTypeHandle() == modelPrefabScript->GetTypeHandle())
|
||||
{
|
||||
modelPrefabScript->LinkPrefab(i.Value->GetPrefabID(), i.Value->GetPrefabObjectID());
|
||||
const Guid prefabObjectId = i.Value->GetPrefabObjectID();
|
||||
modelPrefabScript->LinkPrefab(i.Value->GetPrefabID(), prefabObjectId);
|
||||
newPrefabObjects.Add(prefabObjectId, modelPrefabScript);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prefab)
|
||||
{
|
||||
// Preserve existing objects added by user (eg. colliders, sfx, vfx, scripts)
|
||||
for (const auto& i : prefab->ObjectsCache)
|
||||
{
|
||||
// Skip already restored objects
|
||||
const Guid prefabObjectId = i.Key;
|
||||
if (newPrefabObjects.ContainsKey(prefabObjectId))
|
||||
continue;
|
||||
SceneObject* defaultObject = i.Value;
|
||||
// TODO: ignore objects that were imported previously but not now (eg. mesh was removed from source asset)
|
||||
|
||||
// Find parent to link
|
||||
SceneObject* parent;
|
||||
if (!newPrefabObjects.TryGet(defaultObject->GetParent()->GetPrefabObjectID(), parent))
|
||||
continue;
|
||||
|
||||
// Duplicate object
|
||||
SceneObject* restoredObject = (SceneObject*)Scripting::NewObject(defaultObject->GetTypeHandle());
|
||||
if (!restoredObject)
|
||||
continue;
|
||||
CloneObject(jsonBuffer, defaultObject, restoredObject);
|
||||
restoredObject->SetParent((Actor*)parent);
|
||||
|
||||
// Link with existing prefab instance
|
||||
restoredObject->LinkPrefab(i.Value->GetPrefabID(), prefabObjectId);
|
||||
newPrefabObjects.Add(prefabObjectId, restoredObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Create prefab instead of native asset
|
||||
bool failed;
|
||||
|
||||
@@ -176,6 +176,7 @@ namespace FlaxEngine.Interop
|
||||
_managedHandle.Free();
|
||||
_unmanagedData = IntPtr.Zero;
|
||||
}
|
||||
_arrayType = _elementType = null;
|
||||
ManagedArrayPool.Put(this);
|
||||
}
|
||||
|
||||
@@ -442,22 +443,25 @@ namespace FlaxEngine.Interop
|
||||
/// <summary>
|
||||
/// Tries to free all references to old weak handles so GC can collect them.
|
||||
/// </summary>
|
||||
internal static void TryCollectWeakHandles()
|
||||
internal static void TryCollectWeakHandles(bool force = false)
|
||||
{
|
||||
if (weakHandleAccumulator < nextWeakPoolCollection)
|
||||
return;
|
||||
if (!force)
|
||||
{
|
||||
if (weakHandleAccumulator < nextWeakPoolCollection)
|
||||
return;
|
||||
|
||||
nextWeakPoolCollection = weakHandleAccumulator + 1000;
|
||||
nextWeakPoolCollection = weakHandleAccumulator + 1000;
|
||||
|
||||
// Try to swap pools after garbage collection or whenever the pool gets too large
|
||||
var gc0CollectionCount = GC.CollectionCount(0);
|
||||
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
|
||||
return;
|
||||
nextWeakPoolGCCollection = gc0CollectionCount + 1;
|
||||
// Try to swap pools after garbage collection or whenever the pool gets too large
|
||||
var gc0CollectionCount = GC.CollectionCount(0);
|
||||
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
|
||||
return;
|
||||
nextWeakPoolGCCollection = gc0CollectionCount + 1;
|
||||
|
||||
// Prevent huge allocations from swapping the pools in the middle of the operation
|
||||
if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold)
|
||||
return;
|
||||
// Prevent huge allocations from swapping the pools in the middle of the operation
|
||||
if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold)
|
||||
return;
|
||||
}
|
||||
lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp();
|
||||
|
||||
// Swap the pools and release the oldest pool for GC
|
||||
|
||||
@@ -972,7 +972,49 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void ReloadScriptingAssemblyLoadContext()
|
||||
internal static void CreateScriptingAssemblyLoadContext()
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
if (scriptingAssemblyLoadContext != null)
|
||||
{
|
||||
// Wait for previous ALC to finish unloading, track it without holding strong references to it
|
||||
GCHandle weakRef = GCHandle.Alloc(scriptingAssemblyLoadContext, GCHandleType.WeakTrackResurrection);
|
||||
scriptingAssemblyLoadContext = null;
|
||||
#if false
|
||||
// In case the ALC doesn't unload properly: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability#debug-unloading-issues
|
||||
while (true)
|
||||
#else
|
||||
for (int attempts = 5; attempts > 0; attempts--)
|
||||
#endif
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
if (!IsHandleAlive(weakRef))
|
||||
break;
|
||||
System.Threading.Thread.Sleep(1);
|
||||
}
|
||||
if (IsHandleAlive(weakRef))
|
||||
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded.");
|
||||
weakRef.Free();
|
||||
|
||||
static bool IsHandleAlive(GCHandle weakRef)
|
||||
{
|
||||
// Checking the target in scope somehow holds a reference to it...?
|
||||
return weakRef.Target != null;
|
||||
}
|
||||
}
|
||||
|
||||
scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible: true);
|
||||
scriptingAssemblyLoadContext.Resolving += OnScriptingAssemblyLoadContextResolving;
|
||||
#else
|
||||
scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible: false);
|
||||
#endif
|
||||
DelegateHelpers.InitMethods();
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void UnloadScriptingAssemblyLoadContext()
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
// Clear all caches which might hold references to assemblies in collectible ALC
|
||||
@@ -990,24 +1032,86 @@ namespace FlaxEngine.Interop
|
||||
handle.Free();
|
||||
propertyHandleCacheCollectible.Clear();
|
||||
|
||||
foreach (var key in assemblyHandles.Keys.Where(x => x.IsCollectible))
|
||||
assemblyHandles.Remove(key);
|
||||
foreach (var key in assemblyOwnedNativeLibraries.Keys.Where(x => x.IsCollectible))
|
||||
assemblyOwnedNativeLibraries.Remove(key);
|
||||
|
||||
_typeSizeCache.Clear();
|
||||
|
||||
foreach (var pair in classAttributesCacheCollectible)
|
||||
pair.Value.Free();
|
||||
classAttributesCacheCollectible.Clear();
|
||||
|
||||
ArrayFactory.marshalledTypes.Clear();
|
||||
ArrayFactory.arrayTypes.Clear();
|
||||
ArrayFactory.createArrayDelegates.Clear();
|
||||
|
||||
FlaxEngine.Json.JsonSerializer.ResetCache();
|
||||
DelegateHelpers.Release();
|
||||
|
||||
// Ensure both pools are empty
|
||||
ManagedHandle.ManagedHandlePool.TryCollectWeakHandles(true);
|
||||
ManagedHandle.ManagedHandlePool.TryCollectWeakHandles(true);
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
{
|
||||
// HACK: Workaround for TypeDescriptor holding references to collectible types (https://github.com/dotnet/runtime/issues/30656)
|
||||
|
||||
Type typeDescriptionProviderType = typeof(System.ComponentModel.TypeDescriptionProvider);
|
||||
MethodInfo clearCacheMethod = typeDescriptionProviderType?.Assembly.GetType("System.ComponentModel.ReflectionCachesUpdateHandler")?.GetMethod("ClearCache");
|
||||
if (clearCacheMethod != null)
|
||||
clearCacheMethod.Invoke(null, new object[] { null });
|
||||
|
||||
Type TypeDescriptorType = typeof(System.ComponentModel.TypeDescriptor);
|
||||
object s_providerTable = TypeDescriptorType?.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
|
||||
|
||||
// Added in .NET runtime 8.0.10, used as the main locking object
|
||||
object s_commonSyncObject = TypeDescriptorType?.GetField("s_commonSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
|
||||
if (s_commonSyncObject == null)
|
||||
s_commonSyncObject = s_providerTable;
|
||||
|
||||
// Removed in .NET runtime 8.0.7
|
||||
object s_internalSyncObject = TypeDescriptorType?.GetField("s_internalSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
|
||||
object s_defaultProviders = TypeDescriptorType?.GetField("s_defaultProviders", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
|
||||
if (s_internalSyncObject != null && s_defaultProviders != null)
|
||||
{
|
||||
lock (s_internalSyncObject)
|
||||
InvokeClear(s_defaultProviders);
|
||||
}
|
||||
|
||||
// Replaces s_defaultProviders in 8.0.7
|
||||
object s_defaultProviderInitialized = TypeDescriptorType?.GetField("s_defaultProviderInitialized", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
|
||||
if (s_commonSyncObject != null && s_defaultProviderInitialized != null)
|
||||
{
|
||||
lock (s_commonSyncObject)
|
||||
InvokeClear(s_defaultProviderInitialized);
|
||||
}
|
||||
|
||||
object s_providerTypeTable = TypeDescriptorType?.GetField("s_providerTypeTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
|
||||
if (s_providerTable != null && s_providerTypeTable != null)
|
||||
{
|
||||
lock (s_commonSyncObject)
|
||||
InvokeClear(s_providerTypeTable);
|
||||
InvokeClear(s_providerTable);
|
||||
}
|
||||
|
||||
static void InvokeClear(object instance)
|
||||
{
|
||||
Type type = instance.GetType();
|
||||
Assertions.Assert.IsTrue(type.Name == "ConcurrentDictionary`2" || type.Name == "Hashtable" || type.Name == "WeakHashtable");
|
||||
type.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(x => x.Name == "Clear")?.Invoke(instance, Array.Empty<object>());
|
||||
}
|
||||
}
|
||||
|
||||
// Unload the ALC
|
||||
bool unloading = true;
|
||||
scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; };
|
||||
scriptingAssemblyLoadContext.Unload();
|
||||
scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;
|
||||
|
||||
while (unloading)
|
||||
System.Threading.Thread.Sleep(1);
|
||||
|
||||
InitScriptingAssemblyLoadContext();
|
||||
DelegateHelpers.InitMethods();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -73,19 +73,6 @@ namespace FlaxEngine.Interop
|
||||
return nativeLibrary;
|
||||
}
|
||||
|
||||
private static void InitScriptingAssemblyLoadContext()
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
var isCollectible = true;
|
||||
#else
|
||||
var isCollectible = false;
|
||||
#endif
|
||||
scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible);
|
||||
#if FLAX_EDITOR
|
||||
scriptingAssemblyLoadContext.Resolving += OnScriptingAssemblyLoadContextResolving;
|
||||
#endif
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void Init()
|
||||
{
|
||||
@@ -97,8 +84,6 @@ namespace FlaxEngine.Interop
|
||||
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
|
||||
|
||||
InitScriptingAssemblyLoadContext();
|
||||
DelegateHelpers.InitMethods();
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
@@ -1475,11 +1460,11 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static class ArrayFactory
|
||||
{
|
||||
private delegate Array CreateArrayDelegate(long size);
|
||||
internal delegate Array CreateArrayDelegate(long size);
|
||||
|
||||
private static ConcurrentDictionary<Type, Type> marshalledTypes = new ConcurrentDictionary<Type, Type>(1, 3);
|
||||
private static ConcurrentDictionary<Type, Type> arrayTypes = new ConcurrentDictionary<Type, Type>(1, 3);
|
||||
private static ConcurrentDictionary<Type, CreateArrayDelegate> createArrayDelegates = new ConcurrentDictionary<Type, CreateArrayDelegate>(1, 3);
|
||||
internal static ConcurrentDictionary<Type, Type> marshalledTypes = new ConcurrentDictionary<Type, Type>(1, 3);
|
||||
internal static ConcurrentDictionary<Type, Type> arrayTypes = new ConcurrentDictionary<Type, Type>(1, 3);
|
||||
internal static ConcurrentDictionary<Type, CreateArrayDelegate> createArrayDelegates = new ConcurrentDictionary<Type, CreateArrayDelegate>(1, 3);
|
||||
|
||||
internal static Type GetMarshalledType(Type elementType)
|
||||
{
|
||||
@@ -1645,17 +1630,6 @@ namespace FlaxEngine.Interop
|
||||
return RegisterType(type, true).typeHolder;
|
||||
}
|
||||
|
||||
internal static (TypeHolder typeHolder, ManagedHandle handle) GetTypeHolderAndManagedHandle(Type type)
|
||||
{
|
||||
if (managedTypes.TryGetValue(type, out (TypeHolder typeHolder, ManagedHandle handle) tuple))
|
||||
return tuple;
|
||||
#if FLAX_EDITOR
|
||||
if (managedTypesCollectible.TryGetValue(type, out tuple))
|
||||
return tuple;
|
||||
#endif
|
||||
return RegisterType(type, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a static ManagedHandle to TypeHolder for given Type, and caches it if needed.
|
||||
/// </summary>
|
||||
@@ -1781,6 +1755,14 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void Release()
|
||||
{
|
||||
MakeNewCustomDelegateFunc = null;
|
||||
#if FLAX_EDITOR
|
||||
MakeNewCustomDelegateFuncCollectible = null;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static Type MakeNewCustomDelegate(Type[] parameters)
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
|
||||
@@ -74,7 +74,7 @@ Float2 Screen::ScreenToGameViewport(const Float2& screenPos)
|
||||
return Editor::Managed->ScreenToGameViewport(screenPos);
|
||||
#else
|
||||
auto win = Engine::MainWindow;
|
||||
return win ? win->ScreenToClient(screenPos) : Float2::Minimum;
|
||||
return win ? win->ScreenToClient(screenPos) / win->GetDpiScale() : Float2::Minimum;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ Float2 Screen::GameViewportToScreen(const Float2& viewportPos)
|
||||
return Editor::Managed->GameViewportToScreen(viewportPos);
|
||||
#else
|
||||
auto win = Engine::MainWindow;
|
||||
return win ? win->ClientToScreen(viewportPos) : Float2::Minimum;
|
||||
return win ? win->ClientToScreen(viewportPos * win->GetDpiScale()) : Float2::Minimum;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -174,8 +174,10 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether depth buffer is binded to the pipeline.
|
||||
/// [Deprecated in v1.10]
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if depth buffer is binded; otherwise, <c>false</c>.</returns>
|
||||
DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in ")
|
||||
virtual bool IsDepthBufferBinded() = 0;
|
||||
|
||||
public:
|
||||
|
||||
@@ -34,8 +34,8 @@ void GUIMaterialShader::Bind(BindParameters& params)
|
||||
auto materialData = reinterpret_cast<GUIMaterialShaderData*>(cb.Get());
|
||||
cb = cb.Slice(sizeof(GUIMaterialShaderData));
|
||||
int32 srv = 0;
|
||||
const auto ps = context->IsDepthBufferBinded() ? _cache.Depth : _cache.NoDepth;
|
||||
auto customData = (Render2D::CustomData*)params.CustomData;
|
||||
const auto ps = customData->UseDepthBuffer ? _cache.Depth : _cache.NoDepth;
|
||||
|
||||
// Setup parameters
|
||||
MaterialParameter::BindMeta bindMeta;
|
||||
@@ -83,26 +83,21 @@ void GUIMaterialShader::Unload()
|
||||
|
||||
bool GUIMaterialShader::Load()
|
||||
{
|
||||
GPUPipelineState::Description psDesc0 = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||
psDesc0.Wireframe = EnumHasAnyFlags(_info.FeaturesFlags, MaterialFeaturesFlags::Wireframe);
|
||||
psDesc0.VS = _shader->GetVS("VS_GUI");
|
||||
psDesc0.PS = _shader->GetPS("PS_GUI");
|
||||
psDesc0.BlendMode = BlendingMode::AlphaBlend;
|
||||
|
||||
psDesc0.DepthEnable = psDesc0.DepthWriteEnable = true;
|
||||
auto desc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||
desc.Wireframe = EnumHasAnyFlags(_info.FeaturesFlags, MaterialFeaturesFlags::Wireframe);
|
||||
desc.VS = _shader->GetVS("VS_GUI");
|
||||
desc.PS = _shader->GetPS("PS_GUI");
|
||||
desc.BlendMode = BlendingMode::AlphaBlend;
|
||||
desc.DepthEnable = true;
|
||||
_cache.Depth = GPUDevice::Instance->CreatePipelineState();
|
||||
_cache.NoDepth = GPUDevice::Instance->CreatePipelineState();
|
||||
|
||||
bool failed = _cache.Depth->Init(psDesc0);
|
||||
|
||||
psDesc0.DepthEnable = psDesc0.DepthWriteEnable = false;
|
||||
failed |= _cache.NoDepth->Init(psDesc0);
|
||||
|
||||
bool failed = _cache.Depth->Init(desc);
|
||||
desc.DepthEnable = false;
|
||||
failed |= _cache.NoDepth->Init(desc);
|
||||
if (failed)
|
||||
{
|
||||
LOG(Warning, "Failed to create GUI material pipeline state.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -768,10 +768,7 @@ void Render2D::End()
|
||||
IsScissorsRectEmpty = false;
|
||||
for (int32 i = 0; i < DrawCalls.Count(); i++)
|
||||
{
|
||||
// Peek draw call
|
||||
const auto& drawCall = DrawCalls[i];
|
||||
|
||||
// Check if cannot add element to the batching
|
||||
if (batchSize != 0 && !CanBatchDrawCalls(DrawCalls[batchStart], drawCall))
|
||||
{
|
||||
// Flush batched elements
|
||||
@@ -999,6 +996,7 @@ void DrawBatch(int32 startIndex, int32 count)
|
||||
Render2D::CustomData customData;
|
||||
customData.ViewProjection = ViewProjection;
|
||||
customData.ViewSize = Float2::One;
|
||||
customData.UseDepthBuffer = DepthBuffer != nullptr;
|
||||
bindParams.CustomData = &customData;
|
||||
material->Bind(bindParams);
|
||||
|
||||
@@ -1035,6 +1033,7 @@ void DrawBatch(int32 startIndex, int32 count)
|
||||
Render2D::CustomData customData;
|
||||
customData.ViewProjection = ViewProjection;
|
||||
customData.ViewSize = Float2(d.AsMaterial.Width, d.AsMaterial.Height);
|
||||
customData.UseDepthBuffer = DepthBuffer != nullptr;
|
||||
bindParams.CustomData = &customData;
|
||||
material->Bind(bindParams);
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ API_CLASS(Static) class FLAXENGINE_API Render2D
|
||||
{
|
||||
Matrix ViewProjection;
|
||||
Float2 ViewSize;
|
||||
bool UseDepthBuffer;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@@ -796,12 +796,8 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp
|
||||
// Mark as managed type
|
||||
object->Flags |= ObjectFlags::IsManagedType;
|
||||
|
||||
// Initialize managed instance
|
||||
if (params.Managed)
|
||||
{
|
||||
object->SetManagedInstance((MObject*)params.Managed);
|
||||
}
|
||||
else
|
||||
// Initialize managed instance (ScriptingObject ctor copies managed object handle)
|
||||
if (!params.Managed)
|
||||
{
|
||||
// Invoke managed ctor (to match C++ logic)
|
||||
object->CreateManaged();
|
||||
|
||||
@@ -45,9 +45,16 @@ public:
|
||||
/// </summary>
|
||||
static void UnloadEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Creates the assembly load context for assemblies used by Scripting.
|
||||
/// </summary>
|
||||
static void CreateScriptingAssemblyLoadContext();
|
||||
|
||||
#if USE_EDITOR
|
||||
// Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again).
|
||||
static void ReloadScriptingAssemblyLoadContext();
|
||||
/// <summary>
|
||||
/// Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again).
|
||||
/// </summary>
|
||||
static void UnloadScriptingAssemblyLoadContext();
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -330,9 +330,15 @@ void MCore::UnloadEngine()
|
||||
ShutdownHostfxr();
|
||||
}
|
||||
|
||||
void MCore::CreateScriptingAssemblyLoadContext()
|
||||
{
|
||||
static void* CreateScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("CreateScriptingAssemblyLoadContext"));
|
||||
CallStaticMethod<void>(CreateScriptingAssemblyLoadContextPtr);
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void MCore::ReloadScriptingAssemblyLoadContext()
|
||||
void MCore::UnloadScriptingAssemblyLoadContext()
|
||||
{
|
||||
// Clear any cached class attributes (see https://github.com/FlaxEngine/FlaxEngine/issues/1108)
|
||||
for (const auto& e : CachedClassHandles)
|
||||
@@ -377,8 +383,8 @@ void MCore::ReloadScriptingAssemblyLoadContext()
|
||||
}
|
||||
}
|
||||
|
||||
static void* ReloadScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("ReloadScriptingAssemblyLoadContext"));
|
||||
CallStaticMethod<void>(ReloadScriptingAssemblyLoadContextPtr);
|
||||
static void* UnloadScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("UnloadScriptingAssemblyLoadContext"));
|
||||
CallStaticMethod<void>(UnloadScriptingAssemblyLoadContextPtr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -715,9 +715,13 @@ void MCore::UnloadEngine()
|
||||
#endif
|
||||
}
|
||||
|
||||
void MCore::CreateScriptingAssemblyLoadContext()
|
||||
{
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void MCore::ReloadScriptingAssemblyLoadContext()
|
||||
void MCore::UnloadScriptingAssemblyLoadContext()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -58,9 +58,13 @@ void MCore::UnloadEngine()
|
||||
MRootDomain = nullptr;
|
||||
}
|
||||
|
||||
void MCore::CreateScriptingAssemblyLoadContext()
|
||||
{
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void MCore::ReloadScriptingAssemblyLoadContext()
|
||||
void MCore::UnloadScriptingAssemblyLoadContext()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,8 @@ bool ScriptingService::Init()
|
||||
return true;
|
||||
}
|
||||
|
||||
MCore::CreateScriptingAssemblyLoadContext();
|
||||
|
||||
// Cache root domain
|
||||
_rootDomain = MCore::GetRootDomain();
|
||||
|
||||
@@ -710,7 +712,8 @@ void Scripting::Reload(bool canTriggerSceneReload)
|
||||
_hasGameModulesLoaded = false;
|
||||
|
||||
// Release and create a new assembly load context for user assemblies
|
||||
MCore::ReloadScriptingAssemblyLoadContext();
|
||||
MCore::UnloadScriptingAssemblyLoadContext();
|
||||
MCore::CreateScriptingAssemblyLoadContext();
|
||||
|
||||
// Give GC a try to cleanup old user objects and the other mess
|
||||
MCore::GC::Collect();
|
||||
|
||||
@@ -170,6 +170,10 @@ namespace FlaxEngine
|
||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
||||
Localization.LocalizationChanged += OnLocalizationChanged;
|
||||
#if FLAX_EDITOR
|
||||
FlaxEditor.ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
FlaxEditor.ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||
#endif
|
||||
|
||||
OnLocalizationChanged();
|
||||
if (!Engine.IsEditor)
|
||||
@@ -178,6 +182,19 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
private static void OnScriptsReloadBegin()
|
||||
{
|
||||
// Tooltip might hold references to scripting assemblies
|
||||
Style.Current.SharedTooltip = null;
|
||||
}
|
||||
|
||||
private static void OnScriptsReloadEnd()
|
||||
{
|
||||
Style.Current.SharedTooltip = new Tooltip();
|
||||
}
|
||||
#endif
|
||||
|
||||
private static void OnLocalizationChanged()
|
||||
{
|
||||
// Invariant-globalization only (see InitHostfxr with Mono)
|
||||
@@ -368,6 +385,10 @@ namespace FlaxEngine
|
||||
|
||||
MainThreadTaskScheduler.Dispose();
|
||||
Json.JsonSerializer.Dispose();
|
||||
#if FLAX_EDITOR
|
||||
FlaxEditor.ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
|
||||
FlaxEditor.ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -89,7 +89,11 @@ void SerializableScriptingObject::Deserialize(DeserializeStream& stream, ISerial
|
||||
}
|
||||
|
||||
ScriptingObject::ScriptingObject(const SpawnParams& params)
|
||||
: _gcHandle(0)
|
||||
#if USE_NETCORE
|
||||
: _gcHandle((MGCHandle)params.Managed)
|
||||
#elif !COMPILE_WITHOUT_CSHARP
|
||||
: _gcHandle(params.Managed ? MCore::GCHandle::New(params.Managed) : 0)
|
||||
#endif
|
||||
, _type(params.Type)
|
||||
, _id(params.ID)
|
||||
{
|
||||
|
||||
@@ -473,6 +473,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
BackgroundColor = Color.Transparent,
|
||||
Pivot = Float2.Zero,
|
||||
IsScrollable = true,
|
||||
AutoSize = true,
|
||||
Parent = popup.MainPanel,
|
||||
|
||||
@@ -337,9 +337,7 @@ namespace FlaxEngine.GUI
|
||||
size.X = _textSize.X + Margin.Width;
|
||||
if (_autoHeight)
|
||||
size.Y = _textSize.Y + Margin.Height;
|
||||
var pivotRelative = PivotRelative;
|
||||
Size = size;
|
||||
PivotRelative = pivotRelative;
|
||||
Resize(ref size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,13 +233,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
ref TextBlock textBlock = ref textBlocks[i];
|
||||
var textBlockSize = textBlock.Bounds.BottomRight - lineOrigin;
|
||||
var ascender = textBlock.Ascender;
|
||||
//if (ascender <= 0)
|
||||
{
|
||||
var textBlockFont = textBlock.Style.Font.GetFont();
|
||||
if (textBlockFont)
|
||||
ascender = textBlockFont.Ascender;
|
||||
}
|
||||
var ascender = textBlock.GetAscender();
|
||||
lineAscender = Mathf.Max(lineAscender, ascender);
|
||||
lineSize = Float2.Max(lineSize, textBlockSize);
|
||||
}
|
||||
@@ -256,13 +250,7 @@ namespace FlaxEngine.GUI
|
||||
case TextBlockStyle.Alignments.Baseline:
|
||||
{
|
||||
// Match the baseline of the line (use ascender)
|
||||
var ascender = textBlock.Ascender;
|
||||
if (ascender <= 0)
|
||||
{
|
||||
var textBlockFont = textBlock.Style.Font.GetFont();
|
||||
if (textBlockFont)
|
||||
ascender = textBlockFont.Ascender;
|
||||
}
|
||||
var ascender = textBlock.GetAscender();
|
||||
vOffset = lineAscender - ascender;
|
||||
textBlock.Bounds.Location.Y += vOffset;
|
||||
break;
|
||||
|
||||
@@ -176,7 +176,8 @@ namespace FlaxEngine.GUI
|
||||
var font = imageBlock.Style.Font.GetFont();
|
||||
if (font)
|
||||
imageBlock.Bounds.Size = new Float2(font.Height);
|
||||
imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep original aspect ratio
|
||||
var imageSize = image.Size;
|
||||
imageBlock.Bounds.Size.X *= imageSize.X / imageSize.Y; // Keep original aspect ratio
|
||||
bool hasWidth = TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width);
|
||||
imageBlock.Bounds.Width = width;
|
||||
bool hasHeight = TryParseNumberTag(ref tag, "height", imageBlock.Bounds.Height, out var height);
|
||||
@@ -185,9 +186,9 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
// Maintain aspect ratio after scaling by just width or height
|
||||
if (hasHeight)
|
||||
imageBlock.Bounds.Size.X = imageBlock.Bounds.Size.Y * image.Size.X / image.Size.Y;
|
||||
imageBlock.Bounds.Size.X = imageBlock.Bounds.Size.Y * imageSize.X / imageSize.Y;
|
||||
else
|
||||
imageBlock.Bounds.Size.Y = imageBlock.Bounds.Size.X * image.Size.Y / image.Size.X;
|
||||
imageBlock.Bounds.Size.Y = imageBlock.Bounds.Size.X * imageSize.Y / imageSize.X;
|
||||
}
|
||||
TryParseNumberTag(ref tag, "scale", 1.0f, out var scale);
|
||||
imageBlock.Bounds.Size *= scale;
|
||||
|
||||
@@ -419,6 +419,19 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the control based on where the pivot is rather than just the top-left.
|
||||
/// </summary>
|
||||
[NoAnimate]
|
||||
public void Resize(ref Float2 value)
|
||||
{
|
||||
if (_bounds.Size.Equals(ref value))
|
||||
return;
|
||||
var bounds = new Rectangle(_bounds.Location, value);
|
||||
bounds.Location += (_bounds.Size - value) * Pivot; // Pivot-relative resizing
|
||||
SetBounds(ref bounds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the control cached bounds (based on anchors and offsets).
|
||||
/// </summary>
|
||||
|
||||
@@ -122,8 +122,6 @@ namespace FlaxEngine.GUI
|
||||
if (_parent == value)
|
||||
return;
|
||||
|
||||
Defocus();
|
||||
|
||||
Float2 oldParentSize;
|
||||
if (_parent != null)
|
||||
{
|
||||
|
||||
@@ -585,7 +585,8 @@ namespace FlaxEngine.GUI
|
||||
_cachedHeight = height;
|
||||
if (_animationProgress >= 1.0f && _isClosed)
|
||||
y = minHeight;
|
||||
Height = Mathf.Max(minHeight, y);
|
||||
var size = new Float2(Width, Mathf.Max(minHeight, y));
|
||||
Resize(ref size);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace FlaxEngine.GUI
|
||||
size.X = left + right;
|
||||
if (!ControlChildSize)
|
||||
size.Y = maxHeight;
|
||||
Size = size;
|
||||
Resize(ref size);
|
||||
}
|
||||
else if (_alignment != TextAlignment.Near && hasAnyLeft)
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace FlaxEngine.GUI
|
||||
size.Y = top + bottom;
|
||||
if (!ControlChildSize)
|
||||
size.X = maxWidth;
|
||||
Size = size;
|
||||
Resize(ref size);
|
||||
}
|
||||
else if (_alignment != TextAlignment.Near && hasAnyTop)
|
||||
{
|
||||
|
||||
@@ -31,5 +31,18 @@ namespace FlaxEngine.GUI
|
||||
/// The custom tag.
|
||||
/// </summary>
|
||||
public object Tag;
|
||||
|
||||
internal float GetAscender()
|
||||
{
|
||||
float ascender = Ascender;
|
||||
if (Mathf.IsZero(ascender))
|
||||
{
|
||||
// Use ascender from the font
|
||||
var textBlockFont = Style.Font.GetFont();
|
||||
if (textBlockFont)
|
||||
ascender = textBlockFont.Ascender;
|
||||
}
|
||||
return ascender;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace FlaxEngine.GUI
|
||||
public Color Color;
|
||||
|
||||
/// <summary>
|
||||
/// The text shadow color (tint and opacity). Set to transparent to disable shadow drawing.
|
||||
/// The text shadow color (tint and opacity). Transparent color disables shadow drawing.
|
||||
/// </summary>
|
||||
[EditorOrder(30)]
|
||||
public Color ShadowColor;
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace FlaxEngine.GUI
|
||||
// Unlink
|
||||
IsLayoutLocked = true;
|
||||
Parent = null;
|
||||
_showTarget = null;
|
||||
|
||||
// Close window
|
||||
if (_window)
|
||||
|
||||
@@ -23,11 +23,11 @@ MMethod* UICanvas_EndPlay = nullptr;
|
||||
MMethod* UICanvas_ParentChanged = nullptr;
|
||||
|
||||
#define UICANVAS_INVOKE(event) \
|
||||
auto instance = GetManagedInstance(); \
|
||||
if (instance) \
|
||||
auto* managed = GetManagedInstance(); \
|
||||
if (managed) \
|
||||
{ \
|
||||
MObject* exception = nullptr; \
|
||||
UICanvas_##event->Invoke(instance, nullptr, &exception); \
|
||||
UICanvas_##event->Invoke(managed, nullptr, &exception); \
|
||||
if (exception) \
|
||||
{ \
|
||||
MException ex(exception); \
|
||||
|
||||
@@ -314,7 +314,8 @@ namespace FlaxEngine
|
||||
{
|
||||
_guiRoot = new CanvasRootControl(this)
|
||||
{
|
||||
IsLayoutLocked = false
|
||||
IsLayoutLocked = false,
|
||||
Pivot = Float2.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,11 @@ MMethod* UIControl_BeginPlay = nullptr;
|
||||
MMethod* UIControl_EndPlay = nullptr;
|
||||
|
||||
#define UICONTROL_INVOKE(event) \
|
||||
if (HasManagedInstance()) \
|
||||
auto* managed = GetManagedInstance(); \
|
||||
if (managed) \
|
||||
{ \
|
||||
MObject* exception = nullptr; \
|
||||
UIControl_##event->Invoke(GetManagedInstance(), nullptr, &exception); \
|
||||
UIControl_##event->Invoke(managed, nullptr, &exception); \
|
||||
if (exception) \
|
||||
{ \
|
||||
MException ex(exception); \
|
||||
|
||||
@@ -35,38 +35,58 @@ namespace FlaxEngine
|
||||
|
||||
// Set value
|
||||
_control = value;
|
||||
if (_control == null)
|
||||
return;
|
||||
|
||||
// Link the new one (events and parent)
|
||||
if (_control != null)
|
||||
// Setup control
|
||||
var isDuringPlay = IsDuringPlay;
|
||||
_blockEvents = true;
|
||||
var container = _control as ContainerControl;
|
||||
if (container != null)
|
||||
{
|
||||
// Setup control
|
||||
_blockEvents = true;
|
||||
var containerControl = _control as ContainerControl;
|
||||
if (containerControl != null)
|
||||
containerControl.UnlockChildrenRecursive();
|
||||
_control.Visible = IsActive;
|
||||
_control.Parent = GetParent();
|
||||
_control.IndexInParent = OrderInParent;
|
||||
_control.Location = new Float2(LocalPosition);
|
||||
_control.LocationChanged += OnControlLocationChanged;
|
||||
|
||||
// Link children UI controls
|
||||
if (containerControl != null)
|
||||
if (isDuringPlay)
|
||||
container.UnlockChildrenRecursive(); // Enable layout changes to any dynamically added UI
|
||||
else
|
||||
container.LockChildrenRecursive(); // Block layout changes during deserialization
|
||||
}
|
||||
_control.Visible = IsActive;
|
||||
{
|
||||
var parent = GetParent();
|
||||
if (parent != null && !parent.IsLayoutLocked && !isDuringPlay)
|
||||
{
|
||||
var children = ChildrenCount;
|
||||
var parent = Parent;
|
||||
for (int i = 0; i < children; i++)
|
||||
// Reparent but prevent layout if we're during pre-game setup (eg. deserialization) to avoid UI breaking during auto-layout resizing
|
||||
parent.IsLayoutLocked = true;
|
||||
_control.Parent = parent;
|
||||
parent.IsLayoutLocked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_control.Parent = parent;
|
||||
}
|
||||
}
|
||||
_control.IndexInParent = OrderInParent;
|
||||
_control.Location = new Float2(LocalPosition);
|
||||
_control.LocationChanged += OnControlLocationChanged;
|
||||
|
||||
// Link children UI controls
|
||||
if (container != null)
|
||||
{
|
||||
var children = ChildrenCount;
|
||||
var parent = Parent;
|
||||
for (int i = 0; i < children; i++)
|
||||
{
|
||||
var child = GetChild(i) as UIControl;
|
||||
if (child != null && child.HasControl && child != parent)
|
||||
{
|
||||
var child = GetChild(i) as UIControl;
|
||||
if (child != null && child.HasControl && child != parent)
|
||||
{
|
||||
child.Control.Parent = containerControl;
|
||||
}
|
||||
child.Control.Parent = container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh
|
||||
_blockEvents = false;
|
||||
// Refresh layout
|
||||
_blockEvents = false;
|
||||
if (isDuringPlay)
|
||||
{
|
||||
if (prevControl == null && _control.Parent != null)
|
||||
_control.Parent.PerformLayout();
|
||||
else
|
||||
@@ -326,11 +346,13 @@ namespace FlaxEngine
|
||||
{
|
||||
if ((_control == null || _control.GetType() != controlType) && controlType != null)
|
||||
{
|
||||
// Create a new control
|
||||
Control = (Control)Activator.CreateInstance(controlType);
|
||||
}
|
||||
|
||||
if (_control != null)
|
||||
{
|
||||
// Populate control object with properties
|
||||
Json.JsonSerializer.Deserialize(_control, json);
|
||||
|
||||
// Synchronize actor with control location
|
||||
@@ -374,16 +396,32 @@ namespace FlaxEngine
|
||||
|
||||
internal void BeginPlay()
|
||||
{
|
||||
if (_control != null)
|
||||
var control = _control;
|
||||
if (control == null)
|
||||
return;
|
||||
|
||||
// Setup control
|
||||
control.Visible = IsActive && control.Visible;
|
||||
control.Parent = GetParent();
|
||||
control.IndexInParent = OrderInParent;
|
||||
|
||||
// Setup navigation (all referenced controls are now loaded)
|
||||
Internal_GetNavTargets(__unmanagedPtr, out UIControl up, out UIControl down, out UIControl left, out UIControl right);
|
||||
control.NavTargetUp = up?.Control;
|
||||
control.NavTargetDown = down?.Control;
|
||||
control.NavTargetLeft = left?.Control;
|
||||
control.NavTargetRight = right?.Control;
|
||||
|
||||
// Refresh layout (BeginPlay is called for parents first, then skip if already called by outer control)
|
||||
var container = control as ContainerControl;
|
||||
if (control.Parent == null || (container != null && container.IsLayoutLocked))
|
||||
{
|
||||
_control.Visible = IsActive && _control.Visible;
|
||||
_control.Parent = GetParent();
|
||||
_control.IndexInParent = OrderInParent;
|
||||
Internal_GetNavTargets(__unmanagedPtr, out UIControl up, out UIControl down, out UIControl left, out UIControl right);
|
||||
_control.NavTargetUp = up?.Control;
|
||||
_control.NavTargetDown = down?.Control;
|
||||
_control.NavTargetLeft = left?.Control;
|
||||
_control.NavTargetRight = right?.Control;
|
||||
if (container != null)
|
||||
{
|
||||
//container.UnlockChildrenRecursive();
|
||||
container.IsLayoutLocked = false; // Forces whole children tree lock/unlock sequence in PerformLayout
|
||||
}
|
||||
control.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -364,8 +364,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
|
||||
// Fmod
|
||||
case 40:
|
||||
{
|
||||
Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
|
||||
Value v2 = tryGetValue(node->GetBox(1), Value::Zero);
|
||||
Value v1 = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
Value v2 = tryGetValue(node->GetBox(1), 1, Value::Zero);
|
||||
value = writeFunction2(node, v1, v2, TEXT("fmod"));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -260,7 +260,11 @@ namespace Flax.Build
|
||||
return true;
|
||||
var rules = Builder.GenerateRulesAssembly();
|
||||
var target = rules.GetTarget(name);
|
||||
return target == null || target.Modules.TrueForAll(x => !rules.GetModule(x).BuildNativeCode);
|
||||
return target == null || target.Modules.TrueForAll(moduleName =>
|
||||
{
|
||||
var module = rules.GetModule(moduleName);
|
||||
return module != null && !module.BuildNativeCode;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user