From d2888d00078948cd8d8cb15e100eefca4af908fe Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 9 Jan 2021 23:21:31 +0100 Subject: [PATCH 1/5] Adding Convert feature to Actor context menu. --- .../Windows/SceneTreeWindow.ContextMenu.cs | 22 ++++++++++++++++ Source/Editor/Windows/SceneTreeWindow.cs | 25 ++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index b6756b438..87f13b13f 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -55,6 +55,28 @@ namespace FlaxEditor.Windows b = contextMenu.AddButton("Duplicate", Editor.SceneEditing.Duplicate); b.Enabled = hasSthSelected; + var convertMenu = contextMenu.AddChildMenu("Convert"); + var convertActorCm = convertMenu.ContextMenu; + for (int i = 0; i < SpawnActorsGroups.Length; i++) + { + var group = SpawnActorsGroups[i]; + + if (group.Types.Length == 1) + { + var type = group.Types[0].Value; + convertActorCm.AddButton(group.Types[0].Key, () => Convert(type)); + } + else + { + var groupCm = convertActorCm.AddChildMenu(group.Name).ContextMenu; + for (int j = 0; j < group.Types.Length; j++) + { + var type = group.Types[j].Value; + groupCm.AddButton(group.Types[j].Key, () => Convert(type)); + } + } + } + b = contextMenu.AddButton("Delete", Editor.SceneEditing.Delete); b.Enabled = hasSthSelected; diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 3db4df799..997f764d2 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -122,7 +122,30 @@ namespace FlaxEditor.Windows // Spawn it Editor.SceneEditing.Spawn(actor, parentActor); } - + + private void Convert(Type to) + { + if (!Editor.SceneEditing.HasSthSelected || !(Editor.SceneEditing.Selection[0] is ActorNode)) + return; + Actor old = ((ActorNode)Editor.SceneEditing.Selection[0]).Actor; + Actor actor = (Actor)FlaxEngine.Object.New(to); + actor.Transform = old.Transform; + if (old.Parent != null) + actor.Parent = old.Parent; + actor.StaticFlags = old.StaticFlags; + actor.HideFlags = old.HideFlags; + actor.Layer = old.Layer; + actor.Tag = old.Tag; + actor.Name = old.Name; + actor.IsActive = old.IsActive; + for (var i = old.Children.Length - 1; i >= 0 ; i--) + { + old.Children[i].Parent = actor; + } + Editor.SceneEditing.Delete(); + Editor.SceneEditing.Spawn(actor, actor.Parent); + } + /// /// Focuses search box. /// From f4a587faf92102f310f2ffa64e8028ba51b60dfd Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 16 Feb 2021 17:36:23 +0100 Subject: [PATCH 2/5] Support undo. --- Source/Editor/Modules/SceneEditingModule.cs | 78 +++++++++++++++++++ .../Windows/SceneTreeWindow.ContextMenu.cs | 34 ++++---- Source/Editor/Windows/SceneTreeWindow.cs | 25 +----- 3 files changed, 97 insertions(+), 40 deletions(-) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 78ffd26b0..7827e5003 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -250,6 +250,84 @@ namespace FlaxEditor.Modules } } + public void Convert(Type to) + { + if (!Editor.SceneEditing.HasSthSelected || !(Editor.SceneEditing.Selection[0] is ActorNode)) + return; + + if (Level.IsAnySceneLoaded == false) + throw new InvalidOperationException("Cannot spawn actor when no scene is loaded."); + + var actionList = new List(); + + Actor old = ((ActorNode)Editor.SceneEditing.Selection[0]).Actor; + Actor actor = (Actor)FlaxEngine.Object.New(to); + + actionList.Add(new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo)); + actionList.Add(new DeleteActorsAction(new List + { + Editor.SceneEditing.Selection[0] + })); + SelectionDeleteBegin?.Invoke(); + actionList[1].Do(); + SelectionDeleteEnd?.Invoke(); + + bool isPlayMode = Editor.StateMachine.IsPlayMode; + + SpawnBegin?.Invoke(); + + actor.Transform = old.Transform; + if (old.Parent != null) + actor.Parent = old.Parent; + actor.StaticFlags = old.StaticFlags; + actor.HideFlags = old.HideFlags; + actor.Layer = old.Layer; + actor.Tag = old.Tag; + actor.Name = old.Name; + actor.IsActive = old.IsActive; + for (var i = old.Children.Length - 1; i >= 0 ; i--) + { + old.Children[i].Parent = actor; + } + + Level.SpawnActor(actor, actor.Parent); + + if (isPlayMode) + actor.StaticFlags = StaticFlags.None; + + var actorNode = Editor.Instance.Scene.GetActorNode(actor); + if (actorNode == null) + throw new InvalidOperationException("Failed to create scene node for the spawned actor."); + + actorNode.PostSpawn(); + Editor.Scene.MarkSceneEdited(actor.Scene); + + actionList.Add(new DeleteActorsAction(new List + { + actorNode + }, true)); + actionList.Add(new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[]{actorNode}, OnSelectionUndo)); + var actions = new MultiUndoAction(actionList); + Undo.AddAction(actions); + SpawnEnd?.Invoke(); + + var options = Editor.Options.Options; + + // Auto CSG mesh rebuild + if (!isPlayMode && options.General.AutoRebuildCSG) + { + if (actor is BoxBrush && actor.Scene) + actor.Scene.BuildCSG(options.General.AutoRebuildCSGTimeoutMs); + } + + // Auto NavMesh rebuild + if (!isPlayMode && options.General.AutoRebuildNavMesh && actor.Scene && (actor.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation) + { + var bounds = actor.BoxWithChildren; + Navigation.BuildNavMesh(actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs); + } + } + /// /// Deletes the selected objects. Supports undo/redo. /// diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 87f13b13f..aee456d81 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -55,28 +55,30 @@ namespace FlaxEditor.Windows b = contextMenu.AddButton("Duplicate", Editor.SceneEditing.Duplicate); b.Enabled = hasSthSelected; - var convertMenu = contextMenu.AddChildMenu("Convert"); - var convertActorCm = convertMenu.ContextMenu; - for (int i = 0; i < SpawnActorsGroups.Length; i++) + if (Editor.SceneEditing.SelectionCount == 1) { - var group = SpawnActorsGroups[i]; + var convertMenu = contextMenu.AddChildMenu("Convert"); + var convertActorCm = convertMenu.ContextMenu; + for (int i = 0; i < SpawnActorsGroups.Length; i++) + { + var group = SpawnActorsGroups[i]; - if (group.Types.Length == 1) - { - var type = group.Types[0].Value; - convertActorCm.AddButton(group.Types[0].Key, () => Convert(type)); - } - else - { - var groupCm = convertActorCm.AddChildMenu(group.Name).ContextMenu; - for (int j = 0; j < group.Types.Length; j++) + if (group.Types.Length == 1) { - var type = group.Types[j].Value; - groupCm.AddButton(group.Types[j].Key, () => Convert(type)); + var type = group.Types[0].Value; + convertActorCm.AddButton(group.Types[0].Key, () => Editor.SceneEditing.Convert(type)); + } + else + { + var groupCm = convertActorCm.AddChildMenu(group.Name).ContextMenu; + for (int j = 0; j < group.Types.Length; j++) + { + var type = group.Types[j].Value; + groupCm.AddButton(group.Types[j].Key, () => Editor.SceneEditing.Convert(type)); + } } } } - b = contextMenu.AddButton("Delete", Editor.SceneEditing.Delete); b.Enabled = hasSthSelected; diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 997f764d2..3db4df799 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -122,30 +122,7 @@ namespace FlaxEditor.Windows // Spawn it Editor.SceneEditing.Spawn(actor, parentActor); } - - private void Convert(Type to) - { - if (!Editor.SceneEditing.HasSthSelected || !(Editor.SceneEditing.Selection[0] is ActorNode)) - return; - Actor old = ((ActorNode)Editor.SceneEditing.Selection[0]).Actor; - Actor actor = (Actor)FlaxEngine.Object.New(to); - actor.Transform = old.Transform; - if (old.Parent != null) - actor.Parent = old.Parent; - actor.StaticFlags = old.StaticFlags; - actor.HideFlags = old.HideFlags; - actor.Layer = old.Layer; - actor.Tag = old.Tag; - actor.Name = old.Name; - actor.IsActive = old.IsActive; - for (var i = old.Children.Length - 1; i >= 0 ; i--) - { - old.Children[i].Parent = actor; - } - Editor.SceneEditing.Delete(); - Editor.SceneEditing.Spawn(actor, actor.Parent); - } - + /// /// Focuses search box. /// From bc229d0bb659512202d78e90830f758abbb0b4f7 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 16 Feb 2021 18:25:44 +0100 Subject: [PATCH 3/5] Support converting actor with scripts. --- Source/Editor/Modules/SceneEditingModule.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 7827e5003..d4fb8fe70 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -285,6 +285,10 @@ namespace FlaxEditor.Modules actor.Tag = old.Tag; actor.Name = old.Name; actor.IsActive = old.IsActive; + for (var i = old.ScriptsCount - 1; i >=0; i--) + { + old.Scripts[i].Parent = actor; + } for (var i = old.Children.Length - 1; i >= 0 ; i--) { old.Children[i].Parent = actor; @@ -306,7 +310,7 @@ namespace FlaxEditor.Modules { actorNode }, true)); - actionList.Add(new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[]{actorNode}, OnSelectionUndo)); + actionList.Add(new SelectionChangeAction(new SceneGraphNode[0], new SceneGraphNode[]{actorNode}, OnSelectionUndo)); var actions = new MultiUndoAction(actionList); Undo.AddAction(actions); SpawnEnd?.Invoke(); From 1c5b779ae92e4b68eb8491cf7938e3dd56b5205c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 16 Feb 2021 18:27:02 +0100 Subject: [PATCH 4/5] Add Convert docs. --- Source/Editor/Modules/SceneEditingModule.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index d4fb8fe70..1cec21305 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -250,6 +250,10 @@ namespace FlaxEditor.Modules } } + /// + /// Converts the selected actor to another type. + /// + /// The type to convert in. public void Convert(Type to) { if (!Editor.SceneEditing.HasSthSelected || !(Editor.SceneEditing.Selection[0] is ActorNode)) From d896f4f699778719b4dc663ef42c4a90f7b254d2 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Wed, 17 Feb 2021 11:38:42 +0100 Subject: [PATCH 5/5] Fix script error on undo. --- Source/Editor/Modules/SceneEditingModule.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 1cec21305..19edaf7f7 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -268,9 +268,10 @@ namespace FlaxEditor.Modules Actor actor = (Actor)FlaxEngine.Object.New(to); actionList.Add(new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo)); + actionList[0].Do(); actionList.Add(new DeleteActorsAction(new List { - Editor.SceneEditing.Selection[0] + Editor.Instance.Scene.GetActorNode(old) })); SelectionDeleteBegin?.Invoke(); actionList[1].Do(); @@ -291,9 +292,12 @@ namespace FlaxEditor.Modules actor.IsActive = old.IsActive; for (var i = old.ScriptsCount - 1; i >=0; i--) { - old.Scripts[i].Parent = actor; + var script = old.Scripts[i]; + script.Actor = actor; + Guid newid = Guid.NewGuid(); + FlaxEngine.Object.Internal_ChangeID(FlaxEngine.Object.GetUnmanagedPtr(script), ref newid); } - for (var i = old.Children.Length - 1; i >= 0 ; i--) + for (var i = old.Children.Length - 1; i >= 0; i--) { old.Children[i].Parent = actor; } @@ -315,6 +319,7 @@ namespace FlaxEditor.Modules actorNode }, true)); actionList.Add(new SelectionChangeAction(new SceneGraphNode[0], new SceneGraphNode[]{actorNode}, OnSelectionUndo)); + actionList[3].Do(); var actions = new MultiUndoAction(actionList); Undo.AddAction(actions); SpawnEnd?.Invoke();