// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using FlaxEditor.SceneGraph.Actors; using FlaxEditor.SceneGraph.GUI; using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph { /// /// A tree node used to visualize scene actors structure in . It's a ViewModel object for . /// It's part of the Scene Graph. /// /// /// [HideInEditor] public class ActorNode : SceneGraphNode { /// /// The linked actor object. /// protected readonly Actor _actor; /// /// The tree node used to present hierarchy structure in GUI. /// protected readonly ActorTreeNode _treeNode; /// /// Gets the actor. /// public Actor Actor => _actor; /// /// Gets the tree node (part of the GUI). /// public ActorTreeNode TreeNode => _treeNode; /// /// The actor child nodes used to represent special parts of the actor (meshes, links, surfaces). /// public readonly List ActorChildNodes = new List(); /// /// Initializes a new instance of the class. /// /// The actor. public ActorNode(Actor actor) : base(actor.ID) { _actor = actor; _treeNode = new ActorTreeNode(); _treeNode.LinkNode(this); } /// /// Initializes a new instance of the class. /// /// The actor. /// The custom tree node. protected ActorNode(Actor actor, ActorTreeNode treeNode) : base(actor.ID) { _actor = actor; _treeNode = treeNode; _treeNode.LinkNode(this); } internal ActorNode(Actor actor, Guid id) : base(id) { _actor = actor; _treeNode = new ActorTreeNode(); _treeNode.LinkNode(this); } /// /// Tries to find the tree node for the specified actor. /// /// The actor. /// Tree node or null if cannot find it. public ActorNode Find(Actor actor) { // Check itself if (_actor == actor) return this; // Check deeper for (int i = 0; i < ChildNodes.Count; i++) { if (ChildNodes[i] is ActorNode node) { var result = node.Find(actor); if (result != null) return result; } } return null; } /// /// Adds the child node. /// /// The node. /// The node public ActorChildNode AddChildNode(ActorChildNode node) { ActorChildNodes.Add(node); node.ParentNode = this; return node; } /// /// Disposes the child nodes. /// public void DisposeChildNodes() { // Send event to root so if any of this child nodes is selected we can handle it var root = Root; if (root != null) { root.OnActorChildNodesDispose(this); } for (int i = 0; i < ActorChildNodes.Count; i++) ActorChildNodes[i].Dispose(); ActorChildNodes.Clear(); } /// /// Tries to find the tree node for the specified actor in child nodes collection. /// /// The actor. /// Tree node or null if cannot find it. public ActorNode FindChildActor(Actor actor) { for (int i = 0; i < ChildNodes.Count; i++) { if (ChildNodes[i] is ActorNode node && node.Actor == actor) { return node; } } return null; } /// /// Gets a value indicating whether this actor can be used to create prefab from it (as a root). /// public virtual bool CanCreatePrefab => (Actor.HideFlags & HideFlags.DontSave) != HideFlags.DontSave; /// /// Gets a value indicating whether this actor has a valid linkage to the prefab asset. /// public virtual bool HasPrefabLink => Actor.HasPrefabLink; /// public override string Name => _actor.Name; /// public override SceneNode ParentScene { get { var scene = _actor.Scene; return scene != null ? SceneGraphFactory.FindNode(scene.ID) as SceneNode : null; } } /// public override bool CanTransform => (_actor.StaticFlags & StaticFlags.Transform) == 0; /// public override bool CanCopyPaste => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0; /// public override bool IsActive => _actor.IsActive; /// public override bool IsActiveInHierarchy => _actor.IsActiveInHierarchy; /// public override int OrderInParent { get => _actor.OrderInParent; set => _actor.OrderInParent = value; } /// public override Transform Transform { get => _actor.Transform; set => _actor.Transform = value; } /// public override SceneGraphNode ParentNode { set { if (!(value is ActorNode)) throw new InvalidOperationException("ActorNode can have only ActorNode as a parent node."); base.ParentNode = value; } } /// public override object EditableObject => _actor; /// public override SceneGraphNode RayCast(ref RayCastData ray, out float distance, out Vector3 normal) { var hit = base.RayCast(ref ray, out distance, out normal); // Skip actors that should not be selected if (hit != null && _actor != null && (_actor.HideFlags & HideFlags.DontSelect) == HideFlags.DontSelect) { hit = ParentNode; } return hit; } /// public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) { return _actor.IntersectsItself(ray.Ray, out distance, out normal); } /// public override void OnDebugDraw(ViewportDebugDrawData data) { data.Add(_actor); } /// public override void Delete() { FlaxEngine.Object.Destroy(_actor); } /// /// Action called after spawning actor in editor (via drag to viewport, with toolbox, etc.). /// Can be used to tweak default values of the actor. /// public virtual void PostSpawn() { } /// protected override void OnParentChanged() { // Update UI node connections _treeNode.Parent = (ParentNode as ActorNode)?.TreeNode; // Check if it's a new node and parent has been already ready // (eg. we build new node for spawned actor and link it to the game) if (_treeNode.Parent != null && !_treeNode.Parent.IsLayoutLocked) { _treeNode.Parent.SortChildren(); // Update UI _treeNode.IsLayoutLocked = false; _treeNode.PerformLayout(); } base.OnParentChanged(); } /// public override void Dispose() { // Cleanup UI _treeNode.Dispose(); base.Dispose(); } } }