/// <summary> /// Deletes selected objects. /// </summary> public void Delete() { // Peek things that can be removed var objects = Selection.Where(x => x.CanDelete && x != Graph.Main).ToList().BuildAllNodes().Where(x => x.CanDelete).ToList(); if (objects.Count == 0) { return; } // Change selection var action1 = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo); // Delete objects var action2 = new CustomDeleteActorsAction(objects); // Merge actions and perform them var action = new MultiUndoAction(new IUndoAction[] { action1, action2 }, action2.ActionString); action.Do(); Undo.AddAction(action); }
/// <summary> /// Deletes the selected objects. Supports undo/redo. /// </summary> public void Delete() { // Peek things that can be removed var objects = Selection.Where(x => x.CanDelete).ToList().BuildAllNodes().Where(x => x.CanDelete).ToList(); if (objects.Count == 0) { return; } // Change selection var action1 = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0]); // Delete objects var action2 = new DeleteActorsAction(objects); // Merge two actions and perform them var action = new MultiUndoAction(new IUndoAction[] { action1, action2 }, action2.ActionString); action.Do(); Undo.AddAction(action); // Auto CSG mesh rebuild foreach (var obj in objects) { if (obj is ActorNode node && node.Actor is BoxBrush) { node.Actor.Scene.BuildCSG(); } } }
/// <summary> /// Pastes the copied objects. Supports undo/redo. /// </summary> /// <param name="pasteTargetActor">The target actor to paste copied data.</param> public void Paste(Actor pasteTargetActor) { // Get clipboard data var data = Clipboard.RawData; // Set paste target if only one actor is selected and no target provided if (pasteTargetActor == null && SelectionCount == 1 && Selection[0] is ActorNode actorNode) { pasteTargetActor = actorNode.Actor; } // Create paste action var pasteAction = PasteActorsAction.Paste(data, pasteTargetActor?.ID ?? Guid.Empty); if (pasteAction != null) { pasteAction.Do(out _, out var nodeParents); // Select spawned objects (parents only) var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast<SceneGraphNode>().ToArray(), OnSelectionUndo); selectAction.Do(); // Build single compound undo action that pastes the actors and selects the created objects (parents only) Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); OnSelectionChanged(); } }
/// <summary> /// Deletes the selected objects. Supports undo/redo. /// </summary> public void Delete() { // Peek things that can be removed var objects = Selection.Where(x => x.CanDelete).ToList().BuildAllNodes().Where(x => x.CanDelete).ToList(); if (objects.Count == 0) { return; } bool isPlayMode = Editor.StateMachine.IsPlayMode; SelectionDeleteBegin?.Invoke(); // Change selection var action1 = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo); // Delete objects var action2 = new DeleteActorsAction(objects); // Merge two actions and perform them var action = new MultiUndoAction(new IUndoAction[] { action1, action2 }, action2.ActionString); action.Do(); Undo.AddAction(action); SelectionDeleteEnd?.Invoke(); var options = Editor.Options.Options; // Auto CSG mesh rebuild if (!isPlayMode && options.General.AutoRebuildCSG) { foreach (var obj in objects) { if (obj is ActorNode node && node.Actor is BoxBrush) { node.Actor.Scene.BuildCSG(options.General.AutoRebuildCSGTimeoutMs); } } } // Auto NavMesh rebuild if (!isPlayMode && options.General.AutoRebuildNavMesh) { foreach (var obj in objects) { if (obj is ActorNode node && node.Actor.Scene && (node.Actor.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation) { var bounds = node.Actor.BoxWithChildren; node.Actor.Scene.BuildNavMesh(bounds, options.General.AutoRebuildNavMeshTimeoutMs); } } } }
public void FinishSelectionChange() { Selection.SymmetryMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one); UpdateSelectionRing(); if(AllowSymmetry) GenerateSymmetry(); EditMap.MarkerSelectionOptions.UpdateOptions(); SelectionChangeAction?.Invoke(); }
private void OnPasteAcction(PasteActorsAction pasteAction) { pasteAction.Do(out _, out var nodeParents); // Select spawned objects var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast <SceneGraphNode>().ToArray(), OnSelectionUndo); selectAction.Do(); Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); OnSelectionChanges(); }
/// <summary> /// Deletes the selected objects. Supports undo/redo. /// </summary> public void Delete() { // Peek things that can be removed var objects = Selection.Where(x => x.CanDelete).ToList().BuildAllNodes().Where(x => x.CanDelete).ToList(); if (objects.Count == 0) { return; } var isSceneTreeFocus = Editor.Windows.SceneWin.ContainsFocus; SelectionDeleteBegin?.Invoke(); // Change selection var action1 = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo); // Delete objects var action2 = new DeleteActorsAction(objects); // Merge two actions and perform them var action = new MultiUndoAction(new IUndoAction[] { action1, action2 }, action2.ActionString); action.Do(); Undo.AddAction(action); SelectionDeleteEnd?.Invoke(); OnDirty(objects); if (isSceneTreeFocus) { Editor.Windows.SceneWin.Focus(); } }
/// <summary> /// Duplicates the selected objects. Supports undo/redo. /// </summary> public void Duplicate() { // Peek things that can be copied (copy all actors) var nodes = Selection.Where(x => x.CanDuplicate).ToList().BuildAllNodes(); if (nodes.Count == 0) return; var actors = new List<Actor>(); var newSelection = new List<SceneGraphNode>(); List<IUndoAction> customUndoActions = null; foreach (var node in nodes) { if (node.CanDuplicate) { if (node is ActorNode actorNode) { actors.Add(actorNode.Actor); } else { var customDuplicatedObject = node.Duplicate(out var customUndoAction); if (customDuplicatedObject != null) newSelection.Add(customDuplicatedObject); if (customUndoAction != null) { if (customUndoActions == null) customUndoActions = new List<IUndoAction>(); customUndoActions.Add(customUndoAction); } } } } if (actors.Count == 0) { // Duplicate custom scene graph nodes only without actors if (newSelection.Count != 0) { // Select spawned objects (parents only) var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo); selectAction.Do(); // Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only) var customUndoActionsCount = customUndoActions?.Count ?? 0; var undoActions = new IUndoAction[1 + customUndoActionsCount]; for (int i = 0; i < customUndoActionsCount; i++) undoActions[i] = customUndoActions[i]; undoActions[undoActions.Length - 1] = selectAction; Undo.AddAction(new MultiUndoAction(undoActions)); OnSelectionChanged(); } return; } // Serialize actors var data = Actor.ToBytes(actors.ToArray()); if (data == null) { Editor.LogError("Failed to copy actors data."); return; } // Create paste action (with selecting spawned objects) var pasteAction = PasteActorsAction.Duplicate(data, Guid.Empty); if (pasteAction != null) { pasteAction.Do(out _, out var nodeParents); // Select spawned objects (parents only) newSelection.AddRange(nodeParents); var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo); selectAction.Do(); // Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only) var customUndoActionsCount = customUndoActions?.Count ?? 0; var undoActions = new IUndoAction[2 + customUndoActionsCount]; undoActions[0] = pasteAction; for (int i = 0; i < customUndoActionsCount; i++) undoActions[i + 1] = customUndoActions[i]; undoActions[undoActions.Length - 1] = selectAction; Undo.AddAction(new MultiUndoAction(undoActions)); OnSelectionChanged(); } }
/// <summary> /// Converts the selected actor to another type. /// </summary> /// <param name="to">The type to convert in.</param> 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 IUndoAction[4]; Actor old = ((ActorNode)Editor.SceneEditing.Selection[0]).Actor; Actor actor = (Actor)FlaxEngine.Object.New(to); var parent = old.Parent; var orderInParent = old.OrderInParent; SelectionDeleteBegin?.Invoke(); actionList[0] = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo); actionList[0].Do(); actionList[1] = new DeleteActorsAction(new List<SceneGraphNode> { Editor.Instance.Scene.GetActorNode(old) }); actionList[1].Do(); SelectionDeleteEnd?.Invoke(); SpawnBegin?.Invoke(); // Copy properties actor.Transform = old.Transform; actor.StaticFlags = old.StaticFlags; actor.HideFlags = old.HideFlags; actor.Layer = old.Layer; actor.Tag = old.Tag; actor.Name = old.Name; actor.IsActive = old.IsActive; // Spawn actor Level.SpawnActor(actor, parent); if (parent != null) actor.OrderInParent = orderInParent; if (Editor.StateMachine.IsPlayMode) actor.StaticFlags = StaticFlags.None; // Move children for (var i = old.ScriptsCount - 1; i >= 0; i--) { 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--) { old.Children[i].Parent = actor; } 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[2] = new DeleteActorsAction(new List<SceneGraphNode> { actorNode }, true); actionList[3] = new SelectionChangeAction(new SceneGraphNode[0], new SceneGraphNode[] { actorNode }, OnSelectionUndo); actionList[3].Do(); var actions = new MultiUndoAction(actionList); Undo.AddAction(actions); SpawnEnd?.Invoke(); OnDirty(actorNode); }
/// <summary> /// Converts the selected actor to another type. /// </summary> /// <param name="to">The type to convert in.</param> 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 IUndoAction[4]; var oldNode = (ActorNode)Editor.SceneEditing.Selection[0]; var old = oldNode.Actor; var actor = (Actor)FlaxEngine.Object.New(to); var parent = old.Parent; var orderInParent = old.OrderInParent; // Steps: // - deselect old actor // - destroy old actor // - spawn new actor // - select new actor SelectionDeleteBegin?.Invoke(); actionList[0] = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo); actionList[0].Do(); actionList[1] = new DeleteActorsAction(oldNode.BuildAllNodes().Where(x => x.CanDelete).ToList()); SelectionDeleteEnd?.Invoke(); SpawnBegin?.Invoke(); // Copy properties actor.Transform = old.Transform; actor.StaticFlags = old.StaticFlags; actor.HideFlags = old.HideFlags; actor.Layer = old.Layer; actor.Tag = old.Tag; actor.Name = old.Name; actor.IsActive = old.IsActive; // Spawn actor Level.SpawnActor(actor, parent); if (parent != null) { actor.OrderInParent = orderInParent; } if (Editor.StateMachine.IsPlayMode) { actor.StaticFlags = StaticFlags.None; } // Move children var scripts = old.Scripts; for (var i = scripts.Length - 1; i >= 0; i--) { scripts[i].Actor = actor; } var children = old.Children; for (var i = children.Length - 1; i >= 0; i--) { children[i].Parent = actor; } var actorNode = Editor.Instance.Scene.GetActorNode(actor); if (actorNode == null) { throw new InvalidOperationException("Failed to create scene node for the spawned actor."); } actorNode.PostConvert(oldNode); actorNode.PostSpawn(); Editor.Scene.MarkSceneEdited(actor.Scene); actionList[1].Do(); actionList[2] = new DeleteActorsAction(actorNode.BuildAllNodes().Where(x => x.CanDelete).ToList(), true); actionList[3] = new SelectionChangeAction(new SceneGraphNode[0], new SceneGraphNode[] { actorNode }, OnSelectionUndo); actionList[3].Do(); Undo.AddAction(new MultiUndoAction(actionList, "Convert actor")); SpawnEnd?.Invoke(); OnDirty(actorNode); }