You're breathtaking!
This commit is contained in:
96
Source/Editor/SceneGraph/ActorChildNode.cs
Normal file
96
Source/Editor/SceneGraph/ActorChildNode.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper base class for actor sub nodes (eg. link points, child parts).
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.SceneGraphNode" />
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.ActorNode" />
|
||||
[HideInEditor]
|
||||
public abstract class ActorChildNode : SceneGraphNode
|
||||
{
|
||||
/// <summary>
|
||||
/// The node index.
|
||||
/// </summary>
|
||||
public readonly int Index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorChildNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The child id.</param>
|
||||
/// <param name="index">The child index.</param>
|
||||
protected ActorChildNode(Guid id, int index)
|
||||
: base(id)
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => ParentNode.Name + "." + Index;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SceneNode ParentScene => ParentNode.ParentScene;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanTransform => ParentNode.CanTransform;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActive => ParentNode.IsActive;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActiveInHierarchy => ParentNode.IsActiveInHierarchy;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int OrderInParent
|
||||
{
|
||||
get => Index;
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDelete => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCopyPaste => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrag => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object EditableObject => ParentNode.EditableObject;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object UndoRecordObject => ParentNode.UndoRecordObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper base class for actor sub nodes (eg. link points, child parts).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The parent actor type.</typeparam>
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.SceneGraphNode" />
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.ActorNode" />
|
||||
public abstract class ActorChildNode<T> : ActorChildNode where T : ActorNode
|
||||
{
|
||||
/// <summary>
|
||||
/// The actor.
|
||||
/// </summary>
|
||||
protected readonly T _actor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorChildNode{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="actor">The parent actor.</param>
|
||||
/// <param name="id">The child id.</param>
|
||||
/// <param name="index">The child index.</param>
|
||||
protected ActorChildNode(T actor, Guid id, int index)
|
||||
: base(id, index)
|
||||
{
|
||||
_actor = actor;
|
||||
}
|
||||
}
|
||||
}
|
||||
284
Source/Editor/SceneGraph/ActorNode.cs
Normal file
284
Source/Editor/SceneGraph/ActorNode.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// A tree node used to visualize scene actors structure in <see cref="SceneTreeWindow"/>. It's a ViewModel object for <see cref="Actor"/>.
|
||||
/// It's part of the Scene Graph.
|
||||
/// </summary>
|
||||
/// <seealso cref="SceneGraphNode" />
|
||||
/// <seealso cref="Actor" />
|
||||
[HideInEditor]
|
||||
public class ActorNode : SceneGraphNode
|
||||
{
|
||||
/// <summary>
|
||||
/// The linked actor object.
|
||||
/// </summary>
|
||||
protected readonly Actor _actor;
|
||||
|
||||
/// <summary>
|
||||
/// The tree node used to present hierarchy structure in GUI.
|
||||
/// </summary>
|
||||
protected readonly ActorTreeNode _treeNode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor.
|
||||
/// </summary>
|
||||
public Actor Actor => _actor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tree node (part of the GUI).
|
||||
/// </summary>
|
||||
public ActorTreeNode TreeNode => _treeNode;
|
||||
|
||||
/// <summary>
|
||||
/// The actor child nodes used to represent special parts of the actor (meshes, links, surfaces).
|
||||
/// </summary>
|
||||
public readonly List<ActorChildNode> ActorChildNodes = new List<ActorChildNode>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
public ActorNode(Actor actor)
|
||||
: base(actor.ID)
|
||||
{
|
||||
_actor = actor;
|
||||
_treeNode = new ActorTreeNode();
|
||||
_treeNode.LinkNode(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <param name="treeNode">The custom tree node.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the tree node for the specified actor.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <returns>Tree node or null if cannot find it.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the child node.
|
||||
/// </summary>
|
||||
/// <param name="node">The node.</param>
|
||||
/// <returns>The node</returns>
|
||||
public ActorChildNode AddChildNode(ActorChildNode node)
|
||||
{
|
||||
ActorChildNodes.Add(node);
|
||||
node.ParentNode = this;
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the child nodes.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the tree node for the specified actor in child nodes collection.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <returns>Tree node or null if cannot find it.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this actor can be used to create prefab from it (as a root).
|
||||
/// </summary>
|
||||
public virtual bool CanCreatePrefab => (Actor.HideFlags & HideFlags.DontSave) != HideFlags.DontSave;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this actor has a valid linkage to the prefab asset.
|
||||
/// </summary>
|
||||
public virtual bool HasPrefabLink => Actor.HasPrefabLink;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => _actor.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SceneNode ParentScene
|
||||
{
|
||||
get
|
||||
{
|
||||
var scene = _actor.Scene;
|
||||
return scene != null ? SceneGraphFactory.FindNode(scene.ID) as SceneNode : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanTransform => (_actor.StaticFlags & StaticFlags.Transform) == 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCopyPaste => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActive => _actor.IsActive;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActiveInHierarchy => _actor.IsActiveInHierarchy;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int OrderInParent
|
||||
{
|
||||
get => _actor.OrderInParent;
|
||||
set => _actor.OrderInParent = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Transform Transform
|
||||
{
|
||||
get => _actor.Transform;
|
||||
set => _actor.Transform = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SceneGraphNode ParentNode
|
||||
{
|
||||
set
|
||||
{
|
||||
if (!(value is ActorNode))
|
||||
throw new InvalidOperationException("ActorNode can have only ActorNode as a parent node.");
|
||||
|
||||
base.ParentNode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object EditableObject => _actor;
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
return _actor.IntersectsItself(ray.Ray, out distance, out normal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
data.Add(_actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Delete()
|
||||
{
|
||||
FlaxEngine.Object.Destroy(_actor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action called after spawning actor in editor (via drag to viewport, with toolbox, etc.).
|
||||
/// Can be used to tweak default values of the actor.
|
||||
/// </summary>
|
||||
public virtual void PostSpawn()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
// Cleanup UI
|
||||
_treeNode.Dispose();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Source/Editor/SceneGraph/ActorNodeWithIcon.cs
Normal file
36
Source/Editor/SceneGraph/ActorNodeWithIcon.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for actors with icon drawn in editor (eg. lights, probes, etc.).
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public abstract class ActorNodeWithIcon : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected ActorNodeWithIcon(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
|
||||
// Check if skip raycasts
|
||||
if ((ray.Flags & RayCastData.FlagTypes.SkipEditorPrimitives) == RayCastData.FlagTypes.SkipEditorPrimitives)
|
||||
{
|
||||
distance = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
BoundingSphere sphere = new BoundingSphere(Transform.Translation, 7.0f);
|
||||
return CollisionsHelper.RayIntersectsSphere(ref ray.Ray, ref sphere, out distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="AnimatedModel"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class AnimatedModelNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public AnimatedModelNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/AudioListenerNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/AudioListenerNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="AudioListener"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class AudioListenerNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public AudioListenerNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/AudioSourceNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/AudioSourceNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="AudioSource"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class AudioSourceNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public AudioSourceNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/BoneSocketNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/BoneSocketNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="BoneSocket"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class BoneSocketNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public BoneSocketNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
222
Source/Editor/SceneGraph/Actors/BoxBrushNode.cs
Normal file
222
Source/Editor/SceneGraph/Actors/BoxBrushNode.cs
Normal file
@@ -0,0 +1,222 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Actor node for <see cref="BoxBrush"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class BoxBrushNode : ActorNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Sub actor node used to edit volume.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.ActorChildNode{T}" />
|
||||
public sealed class SideLinkNode : ActorChildNode<BoxBrushNode>
|
||||
{
|
||||
private sealed class BrushSurfaceProxy
|
||||
{
|
||||
[HideInEditor]
|
||||
public BoxBrush Brush;
|
||||
|
||||
[HideInEditor]
|
||||
public int Index;
|
||||
|
||||
[EditorOrder(10), EditorDisplay("Brush")]
|
||||
[Tooltip("The material used to render the brush surface.")]
|
||||
public MaterialBase Material
|
||||
{
|
||||
get => Brush.Surfaces[Index].Material;
|
||||
set
|
||||
{
|
||||
var surfaces = Brush.Surfaces;
|
||||
surfaces[Index].Material = value;
|
||||
Brush.Surfaces = surfaces;
|
||||
}
|
||||
}
|
||||
|
||||
[EditorOrder(30), EditorDisplay("Brush", "UV Scale"), Limit(-1000, 1000, 0.01f)]
|
||||
[Tooltip("The surface texture coordinates scale.")]
|
||||
public Vector2 TexCoordScale
|
||||
{
|
||||
get => Brush.Surfaces[Index].TexCoordScale;
|
||||
set
|
||||
{
|
||||
var surfaces = Brush.Surfaces;
|
||||
surfaces[Index].TexCoordScale = value;
|
||||
Brush.Surfaces = surfaces;
|
||||
}
|
||||
}
|
||||
|
||||
[EditorOrder(40), EditorDisplay("Brush", "UV Offset"), Limit(-1000, 1000, 0.01f)]
|
||||
[Tooltip("The surface texture coordinates offset.")]
|
||||
public Vector2 TexCoordOffset
|
||||
{
|
||||
get => Brush.Surfaces[Index].TexCoordOffset;
|
||||
set
|
||||
{
|
||||
var surfaces = Brush.Surfaces;
|
||||
surfaces[Index].TexCoordOffset = value;
|
||||
Brush.Surfaces = surfaces;
|
||||
}
|
||||
}
|
||||
|
||||
[EditorOrder(50), EditorDisplay("Brush", "UV Rotation")]
|
||||
[Tooltip("The surface texture coordinates rotation angle (in degrees).")]
|
||||
public float TexCoordRotation
|
||||
{
|
||||
get => Brush.Surfaces[Index].TexCoordRotation;
|
||||
set
|
||||
{
|
||||
var surfaces = Brush.Surfaces;
|
||||
surfaces[Index].TexCoordRotation = value;
|
||||
Brush.Surfaces = surfaces;
|
||||
}
|
||||
}
|
||||
|
||||
[EditorOrder(20), EditorDisplay("Brush", "Scale In Lightmap"), Limit(0, 10000, 0.1f)]
|
||||
[Tooltip("The scale in lightmap (per surface).")]
|
||||
public float ScaleInLightmap
|
||||
{
|
||||
get => Brush.Surfaces[Index].ScaleInLightmap;
|
||||
set
|
||||
{
|
||||
var surfaces = Brush.Surfaces;
|
||||
surfaces[Index].ScaleInLightmap = value;
|
||||
Brush.Surfaces = surfaces;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 _offset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush actor.
|
||||
/// </summary>
|
||||
public BoxBrush Brush => (BoxBrush)((BoxBrushNode)ParentNode).Actor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush surface.
|
||||
/// </summary>
|
||||
public BrushSurface Surface
|
||||
{
|
||||
get => Brush.Surfaces[Index];
|
||||
set
|
||||
{
|
||||
var surfaces = Brush.Surfaces;
|
||||
surfaces[Index] = value;
|
||||
Brush.Surfaces = surfaces;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SideLinkNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="actor">The parent node.</param>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="index">The index.</param>
|
||||
public SideLinkNode(BoxBrushNode actor, Guid id, int index)
|
||||
: base(actor, id, index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
_offset = new Vector3(0.5f, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
_offset = new Vector3(-0.5f, 0, 0);
|
||||
break;
|
||||
case 2:
|
||||
_offset = new Vector3(0, 0.5f, 0);
|
||||
break;
|
||||
case 3:
|
||||
_offset = new Vector3(0, -0.5f, 0);
|
||||
break;
|
||||
case 4:
|
||||
_offset = new Vector3(0, 0, 0.5f);
|
||||
break;
|
||||
case 5:
|
||||
_offset = new Vector3(0, 0, -0.5f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Transform Transform
|
||||
{
|
||||
get
|
||||
{
|
||||
var actor = Brush;
|
||||
var localOffset = _offset * actor.Size + actor.Center;
|
||||
Transform localTrans = new Transform(localOffset);
|
||||
return actor.Transform.LocalToWorld(localTrans);
|
||||
}
|
||||
set
|
||||
{
|
||||
var actor = Brush;
|
||||
Transform localTrans = actor.Transform.WorldToLocal(value);
|
||||
var prevLocalOffset = _offset * actor.Size + actor.Center;
|
||||
var localOffset = Vector3.Abs(_offset) * 2.0f * localTrans.Translation;
|
||||
var localOffsetDelta = localOffset - prevLocalOffset;
|
||||
float centerScale = Index % 2 == 0 ? 0.5f : -0.5f;
|
||||
actor.Size += localOffsetDelta;
|
||||
actor.Center += localOffsetDelta * centerScale;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object EditableObject => new BrushSurfaceProxy
|
||||
{
|
||||
Brush = Brush,
|
||||
Index = Index,
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
return Brush.Intersects(Index, ref ray.Ray, out distance, out normal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
ParentNode.OnDebugDraw(data);
|
||||
data.HighlightBrushSurface(Brush.Surfaces[Index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public BoxBrushNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
var id = ID;
|
||||
var bytes = id.ToByteArray();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
bytes[0] += 1;
|
||||
AddChildNode(new SideLinkNode(this, new Guid(bytes), i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
if (((BoxBrush)_actor).OrientedBox.Intersects(ref ray.Ray))
|
||||
{
|
||||
for (int i = 0; i < ChildNodes.Count; i++)
|
||||
{
|
||||
if (ChildNodes[i] is SideLinkNode node && node.RayCastSelf(ref ray, out distance, out normal))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
distance = 0;
|
||||
normal = Vector3.Up;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
Source/Editor/SceneGraph/Actors/BoxColliderNode.cs
Normal file
35
Source/Editor/SceneGraph/Actors/BoxColliderNode.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="BoxCollider"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ColliderNode" />
|
||||
[HideInEditor]
|
||||
public sealed class BoxColliderNode : ColliderNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public BoxColliderNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
// Pick wires
|
||||
var actor = (BoxCollider)_actor;
|
||||
var box = actor.OrientedBox;
|
||||
if (Utilities.Utils.RayCastWire(ref box, ref ray.Ray, out distance, ref ray.View.Position))
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.RayCastSelf(ref ray, out distance, out normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
196
Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs
Normal file
196
Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Actor node for <see cref="BoxVolume"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public class BoxVolumeNode : ActorNode
|
||||
{
|
||||
private static readonly Int3[] BoxTrianglesIndicesCache =
|
||||
{
|
||||
new Int3(3, 1, 2),
|
||||
new Int3(3, 0, 1),
|
||||
new Int3(7, 0, 3),
|
||||
new Int3(7, 4, 0),
|
||||
new Int3(7, 6, 5),
|
||||
new Int3(7, 5, 4),
|
||||
new Int3(6, 2, 1),
|
||||
new Int3(6, 1, 5),
|
||||
new Int3(1, 0, 4),
|
||||
new Int3(1, 4, 5),
|
||||
new Int3(7, 2, 6),
|
||||
new Int3(7, 3, 2),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Sub actor node used to edit volume.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.ActorChildNode{T}" />
|
||||
public sealed class SideLinkNode : ActorChildNode<BoxVolumeNode>
|
||||
{
|
||||
private readonly Vector3 _offset;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SideLinkNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="actor">The parent node.</param>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="index">The index.</param>
|
||||
public SideLinkNode(BoxVolumeNode actor, Guid id, int index)
|
||||
: base(actor, id, index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
// Front
|
||||
_offset = new Vector3(0.5f, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
// Back
|
||||
_offset = new Vector3(-0.5f, 0, 0);
|
||||
break;
|
||||
case 2:
|
||||
// Up
|
||||
_offset = new Vector3(0, 0.5f, 0);
|
||||
break;
|
||||
case 3:
|
||||
_offset = new Vector3(0, -0.5f, 0);
|
||||
break;
|
||||
case 4:
|
||||
// Left
|
||||
_offset = new Vector3(0, 0, 0.5f);
|
||||
break;
|
||||
case 5:
|
||||
// Right
|
||||
_offset = new Vector3(0, 0, -0.5f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Transform Transform
|
||||
{
|
||||
get
|
||||
{
|
||||
var actor = (BoxVolume)((BoxVolumeNode)ParentNode).Actor;
|
||||
var localOffset = _offset * actor.Size;
|
||||
Transform localTrans = new Transform(localOffset);
|
||||
return actor.Transform.LocalToWorld(localTrans);
|
||||
}
|
||||
set
|
||||
{
|
||||
var actor = (BoxVolume)((BoxVolumeNode)ParentNode).Actor;
|
||||
Transform localTrans = actor.Transform.WorldToLocal(value);
|
||||
var prevLocalOffset = _offset * actor.Size;
|
||||
var localOffset = Vector3.Abs(_offset) * 2.0f * localTrans.Translation;
|
||||
var localOffsetDelta = localOffset - prevLocalOffset;
|
||||
float centerScale = Index % 2 == 0 ? 1.0f : -1.0f;
|
||||
actor.Size += localOffsetDelta * centerScale;
|
||||
actor.Position += localOffsetDelta * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
var sphere = new BoundingSphere(Transform.Translation, 10.0f);
|
||||
return sphere.Intersects(ref ray.Ray, out distance);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
// Draw box volume debug shapes
|
||||
ParentNode.OnDebugDraw(data);
|
||||
|
||||
// Draw plane of the selected side
|
||||
int trangle0, trangle1;
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
// Front
|
||||
trangle0 = 8;
|
||||
trangle1 = 9;
|
||||
break;
|
||||
case 1:
|
||||
// Back
|
||||
trangle0 = 10;
|
||||
trangle1 = 11;
|
||||
break;
|
||||
case 2:
|
||||
// Up
|
||||
trangle0 = 0;
|
||||
trangle1 = 1;
|
||||
break;
|
||||
case 3:
|
||||
// Down
|
||||
trangle0 = 4;
|
||||
trangle1 = 5;
|
||||
break;
|
||||
case 4:
|
||||
// Left
|
||||
trangle0 = 2;
|
||||
trangle1 = 3;
|
||||
break;
|
||||
case 5:
|
||||
// Right
|
||||
trangle0 = 6;
|
||||
trangle1 = 7;
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
var actor = (BoxVolume)((BoxVolumeNode)ParentNode).Actor;
|
||||
var box = actor.OrientedBox;
|
||||
var corners = stackalloc Vector3[8];
|
||||
box.GetCorners(corners);
|
||||
var t0 = BoxTrianglesIndicesCache[trangle0];
|
||||
var t1 = BoxTrianglesIndicesCache[trangle1];
|
||||
var color = Color.CornflowerBlue.AlphaMultiplied(0.3f);
|
||||
DebugDraw.DrawTriangle(corners[t0.X], corners[t0.Y], corners[t0.Z], color);
|
||||
DebugDraw.DrawTriangle(corners[t1.X], corners[t1.Y], corners[t1.Z], color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public BoxVolumeNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
var id = ID;
|
||||
var bytes = id.ToByteArray();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
bytes[0] += 1;
|
||||
AddChildNode(new SideLinkNode(this, new Guid(bytes), i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
|
||||
// Check if skip raycasts
|
||||
if ((ray.Flags & RayCastData.FlagTypes.SkipEditorPrimitives) == RayCastData.FlagTypes.SkipEditorPrimitives)
|
||||
{
|
||||
distance = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip itself if any link gets hit
|
||||
if (RayCastChildren(ref ray, out distance, out normal) != null)
|
||||
return false;
|
||||
|
||||
// Check itself
|
||||
var actor = (BoxVolume)_actor;
|
||||
var box = actor.OrientedBox;
|
||||
return Utilities.Utils.RayCastWire(ref box, ref ray.Ray, out distance, ref ray.View.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
Source/Editor/SceneGraph/Actors/CameraNode.cs
Normal file
35
Source/Editor/SceneGraph/Actors/CameraNode.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Camera"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class CameraNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public CameraNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
|
||||
// Check if skip raycasts
|
||||
if ((ray.Flags & RayCastData.FlagTypes.SkipEditorPrimitives) == RayCastData.FlagTypes.SkipEditorPrimitives)
|
||||
{
|
||||
distance = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return Camera.Internal_IntersectsItselfEditor(Object.GetUnmanagedPtr(_actor), ref ray.Ray, out distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
Source/Editor/SceneGraph/Actors/ColliderNode.cs
Normal file
35
Source/Editor/SceneGraph/Actors/ColliderNode.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene Graph node type used for the collider shapes.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
/// <seealso cref="Collider" />
|
||||
[HideInEditor]
|
||||
public class ColliderNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ColliderNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
// Check if skip raycasts
|
||||
if ((ray.Flags & RayCastData.FlagTypes.SkipColliders) == RayCastData.FlagTypes.SkipColliders)
|
||||
{
|
||||
distance = 0;
|
||||
normal = Vector3.Up;
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.RayCastSelf(ref ray, out distance, out normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/DecalNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/DecalNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Decal"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class DecalNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DecalNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs
Normal file
29
Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="DirectionalLight"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class DirectionalLightNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DirectionalLightNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
base.OnDebugDraw(data);
|
||||
|
||||
var transform = Actor.Transform;
|
||||
DebugDraw.DrawWireArrow(transform.Translation, transform.Orientation, 1.0f, Color.Red, 0.0f, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/EnvironmentProbeNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/EnvironmentProbeNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="EnvironmentProbe"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class EnvironmentProbeNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public EnvironmentProbeNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/ExponentialHeightFogNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/ExponentialHeightFogNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="ExponentialHeightFog"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class ExponentialHeightFogNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ExponentialHeightFogNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Source/Editor/SceneGraph/Actors/FoliageNode.cs
Normal file
19
Source/Editor/SceneGraph/Actors/FoliageNode.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Foliage"/> actor type.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public sealed class FoliageNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public FoliageNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Source/Editor/SceneGraph/Actors/NavLinkNode.cs
Normal file
89
Source/Editor/SceneGraph/Actors/NavLinkNode.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Actor node for <see cref="NavLink"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class NavLinkNode : ActorNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Sub actor node used to edit link start and end points.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.ActorChildNode{T}" />
|
||||
public sealed class LinkNode : ActorChildNode<NavLinkNode>
|
||||
{
|
||||
private readonly bool _isStart;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinkNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="actor">The parent node.</param>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="isStart">The start node or end node.</param>
|
||||
public LinkNode(NavLinkNode actor, Guid id, bool isStart)
|
||||
: base(actor, id, isStart ? 0 : 1)
|
||||
{
|
||||
_isStart = isStart;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Transform Transform
|
||||
{
|
||||
get
|
||||
{
|
||||
var actor = (NavLink)((NavLinkNode)ParentNode).Actor;
|
||||
Transform localTrans = new Transform(_isStart ? actor.Start : actor.End);
|
||||
return actor.Transform.LocalToWorld(localTrans);
|
||||
}
|
||||
set
|
||||
{
|
||||
var actor = (NavLink)((NavLinkNode)ParentNode).Actor;
|
||||
Transform localTrans = actor.Transform.WorldToLocal(value);
|
||||
if (_isStart)
|
||||
actor.Start = localTrans.Translation;
|
||||
else
|
||||
actor.End = localTrans.Translation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
var sphere = new BoundingSphere(Transform.Translation, 10.0f);
|
||||
return sphere.Intersects(ref ray.Ray, out distance);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
ParentNode.OnDebugDraw(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NavLinkNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
var bytes = ID.ToByteArray();
|
||||
bytes[0] += 1;
|
||||
AddChildNode(new LinkNode(this, new Guid(bytes), true));
|
||||
bytes[0] += 1;
|
||||
AddChildNode(new LinkNode(this, new Guid(bytes), false));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
distance = float.MaxValue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/NavMeshBoundsVolumeNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/NavMeshBoundsVolumeNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Actor node for <see cref="NavMeshBoundsVolume"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="BoxVolumeNode" />
|
||||
[HideInEditor]
|
||||
public sealed class NavMeshBoundsVolumeNode : BoxVolumeNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public NavMeshBoundsVolumeNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/ParticleEffectNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/ParticleEffectNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="ParticleEffect"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class ParticleEffectNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ParticleEffectNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/PointLightNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/PointLightNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="PointLight"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class PointLightNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public PointLightNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/PostFxVolumeNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/PostFxVolumeNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Actor node for <see cref="PostFxVolume"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class PostFxVolumeNode : BoxVolumeNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public PostFxVolumeNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/SceneAnimationPlayerNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/SceneAnimationPlayerNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="SceneAnimationPlayer"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class SceneAnimationPlayerNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SceneAnimationPlayerNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
69
Source/Editor/SceneGraph/Actors/SceneNode.cs
Normal file
69
Source/Editor/SceneGraph/Actors/SceneNode.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.SceneGraph.GUI;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Actor tree node for <see cref="FlaxEngine.Scene"/> objects.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class SceneNode : ActorNode
|
||||
{
|
||||
private bool _isEdited;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this scene is edited.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this scene is edited; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsEdited
|
||||
{
|
||||
get => _isEdited;
|
||||
set
|
||||
{
|
||||
if (_isEdited != value)
|
||||
{
|
||||
_isEdited = value;
|
||||
|
||||
_treeNode.UpdateText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scene.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The scene.
|
||||
/// </value>
|
||||
public Scene Scene => _actor as Scene;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SceneNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="scene">The scene.</param>
|
||||
public SceneNode(Scene scene)
|
||||
: base(scene, new SceneTreeNode())
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreatePrefab => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCopyPaste => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDelete => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrag => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SceneNode ParentScene => this;
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/SkyLightNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/SkyLightNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="SkyLight"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class SkyLightNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SkyLightNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/SkyNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/SkyNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Sky"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class SkyNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SkyNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/SkyboxNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/SkyboxNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Skybox"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class SkyboxNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SkyboxNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Source/Editor/SceneGraph/Actors/SpotLightNode.cs
Normal file
29
Source/Editor/SceneGraph/Actors/SpotLightNode.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="SpotLight"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNodeWithIcon" />
|
||||
[HideInEditor]
|
||||
public sealed class SpotLightNode : ActorNodeWithIcon
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SpotLightNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
base.OnDebugDraw(data);
|
||||
|
||||
var transform = Actor.Transform;
|
||||
DebugDraw.DrawWireArrow(transform.Translation, transform.Orientation, 0.3f, Color.Red, 0.0f, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Source/Editor/SceneGraph/Actors/StaticModelNode.cs
Normal file
20
Source/Editor/SceneGraph/Actors/StaticModelNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="StaticModel"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class StaticModelNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public StaticModelNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Source/Editor/SceneGraph/Actors/TerrainNode.cs
Normal file
19
Source/Editor/SceneGraph/Actors/TerrainNode.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Terrain"/> actor type.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public sealed class TerrainNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public TerrainNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Source/Editor/SceneGraph/Actors/TextRenderNode.cs
Normal file
32
Source/Editor/SceneGraph/Actors/TextRenderNode.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="TextRender"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class TextRenderNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public TextRenderNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostSpawn()
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
// Setup for default values
|
||||
var text = (TextRender)Actor;
|
||||
text.Text = "My Text";
|
||||
text.Font = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||
text.Material = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.DefaultFontMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Source/Editor/SceneGraph/Actors/UICanvasNode.cs
Normal file
50
Source/Editor/SceneGraph/Actors/UICanvasNode.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="UICanvas"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class UICanvasNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public UICanvasNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostSpawn()
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
// Rotate to match the space (GUI uses upper left corner as a root)
|
||||
Actor.LocalOrientation = Quaternion.Euler(0, -180, -180);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
normal = Vector3.Up;
|
||||
|
||||
if (Actor is UICanvas uiCanvas && uiCanvas.Is3D)
|
||||
return uiCanvas.Bounds.Intersects(ref ray.Ray, out distance);
|
||||
|
||||
distance = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
base.OnDebugDraw(data);
|
||||
|
||||
if (Actor is UICanvas uiCanvas && uiCanvas.Is3D)
|
||||
DebugDraw.DrawWireBox(uiCanvas.Bounds, Color.BlueViolet);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Source/Editor/SceneGraph/Actors/UIControlNode.cs
Normal file
29
Source/Editor/SceneGraph/Actors/UIControlNode.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="UIControl"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class UIControlNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public UIControlNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
base.OnDebugDraw(data);
|
||||
|
||||
if (Actor is UIControl uiControl)
|
||||
DebugDraw.DrawWireBox(uiControl.Bounds, Color.BlueViolet);
|
||||
}
|
||||
}
|
||||
}
|
||||
799
Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
Normal file
799
Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
Normal file
@@ -0,0 +1,799 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Tree node GUI control used as a proxy object for actors hierarchy.
|
||||
/// </summary>
|
||||
/// <seealso cref="TreeNode" />
|
||||
public class ActorTreeNode : TreeNode
|
||||
{
|
||||
private int _orderInParent;
|
||||
private DragActors _dragActors;
|
||||
private DragAssets _dragAssets;
|
||||
private DragActorType _dragActorType;
|
||||
private DragHandlers _dragHandlers;
|
||||
private List<Rectangle> _highlights;
|
||||
private bool _hasSearchFilter;
|
||||
|
||||
/// <summary>
|
||||
/// The actor node that owns this node.
|
||||
/// </summary>
|
||||
protected ActorNode _actorNode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor.
|
||||
/// </summary>
|
||||
public Actor Actor => _actorNode.Actor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor node.
|
||||
/// </summary>
|
||||
public ActorNode ActorNode => _actorNode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorTreeNode"/> class.
|
||||
/// </summary>
|
||||
public ActorTreeNode()
|
||||
: base(true)
|
||||
{
|
||||
}
|
||||
|
||||
internal virtual void LinkNode(ActorNode node)
|
||||
{
|
||||
_actorNode = node;
|
||||
var actor = node.Actor;
|
||||
if (actor != null)
|
||||
{
|
||||
_orderInParent = actor.OrderInParent;
|
||||
Visible = (actor.HideFlags & HideFlags.HideInHierarchy) == 0;
|
||||
|
||||
var id = actor.ID;
|
||||
if (Editor.Instance.ProjectCache.IsExpandedActor(ref id))
|
||||
{
|
||||
Expand(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_orderInParent = 0;
|
||||
}
|
||||
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
internal void OnOrderInParentChanged()
|
||||
{
|
||||
if (Parent is ActorTreeNode parent)
|
||||
{
|
||||
for (int i = 0; i < parent.ChildrenCount; i++)
|
||||
{
|
||||
if (parent.Children[i] is ActorTreeNode child)
|
||||
child._orderInParent = child.Actor.OrderInParent;
|
||||
}
|
||||
parent.SortChildren();
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnNameChanged()
|
||||
{
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the tree node text.
|
||||
/// </summary>
|
||||
public virtual void UpdateText()
|
||||
{
|
||||
Text = _actorNode.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the query search filter.
|
||||
/// </summary>
|
||||
/// <param name="filterText">The filter text.</param>
|
||||
public void UpdateFilter(string filterText)
|
||||
{
|
||||
// SKip hidden actors
|
||||
var actor = Actor;
|
||||
if (actor != null && (actor.HideFlags & HideFlags.HideInHierarchy) != 0)
|
||||
return;
|
||||
|
||||
bool noFilter = string.IsNullOrWhiteSpace(filterText);
|
||||
_hasSearchFilter = !noFilter;
|
||||
|
||||
// Update itself
|
||||
bool isThisVisible;
|
||||
if (noFilter)
|
||||
{
|
||||
// Clear filter
|
||||
_highlights?.Clear();
|
||||
isThisVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QueryFilterHelper.Range[] ranges;
|
||||
var text = Text;
|
||||
if (QueryFilterHelper.Match(filterText, text, out ranges))
|
||||
{
|
||||
// Update highlights
|
||||
if (_highlights == null)
|
||||
_highlights = new List<Rectangle>(ranges.Length);
|
||||
else
|
||||
_highlights.Clear();
|
||||
var font = Style.Current.FontSmall;
|
||||
var textRect = TextRect;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
var start = font.GetCharPosition(text, ranges[i].StartIndex);
|
||||
var end = font.GetCharPosition(text, ranges[i].EndIndex);
|
||||
_highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height));
|
||||
}
|
||||
isThisVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide
|
||||
_highlights?.Clear();
|
||||
isThisVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update children
|
||||
bool isAnyChildVisible = false;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i] is ActorTreeNode child)
|
||||
{
|
||||
child.UpdateFilter(filterText);
|
||||
isAnyChildVisible |= child.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
bool isExpanded = isAnyChildVisible;
|
||||
|
||||
// Restore cached state on query filter clear
|
||||
if (noFilter && actor != null)
|
||||
{
|
||||
var id = actor.ID;
|
||||
isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id);
|
||||
}
|
||||
|
||||
if (isExpanded)
|
||||
{
|
||||
Expand(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collapse(true);
|
||||
}
|
||||
|
||||
Visible = isThisVisible | isAnyChildVisible;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
// Update hidden state
|
||||
var actor = Actor;
|
||||
if (actor != null && !_hasSearchFilter)
|
||||
{
|
||||
Visible = (actor.HideFlags & HideFlags.HideInHierarchy) == 0;
|
||||
}
|
||||
|
||||
base.Update(deltaTime);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color CacheTextColor()
|
||||
{
|
||||
// Update node text color (based on ActorNode.IsActiveInHierarchy but with optimized logic a little)
|
||||
if (Parent is ActorTreeNode)
|
||||
{
|
||||
Color color = Style.Current.Foreground;
|
||||
var actor = Actor;
|
||||
if (actor != null && actor.HasPrefabLink)
|
||||
{
|
||||
// Prefab
|
||||
color = Style.Current.ProgressNormal;
|
||||
}
|
||||
|
||||
if (actor != null && !actor.IsActiveInHierarchy)
|
||||
{
|
||||
// Inactive
|
||||
return Style.Current.ForegroundGrey;
|
||||
}
|
||||
|
||||
if (actor?.Scene != null && Editor.Instance.StateMachine.IsPlayMode && actor.IsStatic)
|
||||
{
|
||||
// Static
|
||||
return color * 0.85f;
|
||||
}
|
||||
|
||||
// Default
|
||||
return color;
|
||||
}
|
||||
|
||||
return base.CacheTextColor();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Compare(Control other)
|
||||
{
|
||||
if (other is ActorTreeNode node)
|
||||
{
|
||||
return _orderInParent - node._orderInParent;
|
||||
}
|
||||
return base.Compare(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the actor renaming action.
|
||||
/// </summary>
|
||||
public void StartRenaming()
|
||||
{
|
||||
// Block renaming during scripts reload
|
||||
if (Editor.Instance.ProgressReporting.CompileScripts.IsActive)
|
||||
return;
|
||||
|
||||
Select();
|
||||
|
||||
// Start renaming the actor
|
||||
var dialog = RenamePopup.Show(this, HeaderRect, _actorNode.Name, false);
|
||||
dialog.Renamed += OnRenamed;
|
||||
}
|
||||
|
||||
private void OnRenamed(RenamePopup renamePopup)
|
||||
{
|
||||
using (new UndoBlock(ActorNode.Root.Undo, Actor, "Rename"))
|
||||
Actor.Name = renamePopup.Text;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnExpandedChanged()
|
||||
{
|
||||
base.OnExpandedChanged();
|
||||
|
||||
if (!IsLayoutLocked && Actor)
|
||||
{
|
||||
var id = Actor.ID;
|
||||
Editor.Instance.ProjectCache.SetExpandedActor(ref id, IsExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Draw all highlights
|
||||
if (_highlights != null)
|
||||
{
|
||||
var style = Style.Current;
|
||||
var color = style.ProgressNormal * 0.6f;
|
||||
for (int i = 0; i < _highlights.Count; i++)
|
||||
Render2D.FillRectangle(_highlights[i], color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (IsFocused)
|
||||
{
|
||||
if (key == KeyboardKeys.F2)
|
||||
{
|
||||
StartRenaming();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DragDropEffect OnDragEnterHeader(DragData data)
|
||||
{
|
||||
// Check if cannot edit scene or there is no scene loaded (handle case for actors in prefab editor)
|
||||
if (_actorNode?.ParentScene != null)
|
||||
{
|
||||
if (!Editor.Instance.StateMachine.CurrentState.CanEditScene || !Level.IsAnySceneLoaded)
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Editor.Instance.StateMachine.CurrentState.CanEditContent)
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
|
||||
if (_dragHandlers == null)
|
||||
_dragHandlers = new DragHandlers();
|
||||
|
||||
// Check if drop actors
|
||||
if (_dragActors == null)
|
||||
{
|
||||
_dragActors = new DragActors(ValidateDragActor);
|
||||
_dragHandlers.Add(_dragActors);
|
||||
}
|
||||
if (_dragActors.OnDragEnter(data))
|
||||
return _dragActors.Effect;
|
||||
|
||||
// Check if drag assets
|
||||
if (_dragAssets == null)
|
||||
{
|
||||
_dragAssets = new DragAssets(ValidateDragAsset);
|
||||
_dragHandlers.Add(_dragAssets);
|
||||
}
|
||||
if (_dragAssets.OnDragEnter(data))
|
||||
return _dragAssets.Effect;
|
||||
|
||||
// Check if drag actor type
|
||||
if (_dragActorType == null)
|
||||
{
|
||||
_dragActorType = new DragActorType(ValidateDragActorType);
|
||||
_dragHandlers.Add(_dragActorType);
|
||||
}
|
||||
if (_dragActorType.OnDragEnter(data))
|
||||
return _dragActorType.Effect;
|
||||
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DragDropEffect OnDragMoveHeader(DragData data)
|
||||
{
|
||||
return _dragHandlers.Effect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDragLeaveHeader()
|
||||
{
|
||||
_dragHandlers.OnDragLeave();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class ReparentAction : IUndoAction
|
||||
{
|
||||
[Serialize]
|
||||
private Guid[] _ids;
|
||||
|
||||
[Serialize]
|
||||
private int _actorsCount;
|
||||
|
||||
[Serialize]
|
||||
private Guid[] _prefabIds;
|
||||
|
||||
[Serialize]
|
||||
private Guid[] _prefabObjectIds;
|
||||
|
||||
public ReparentAction(Actor actor)
|
||||
: this(new List<Actor> { actor })
|
||||
{
|
||||
}
|
||||
|
||||
public ReparentAction(List<Actor> actors)
|
||||
{
|
||||
var allActors = new List<Actor>();
|
||||
allActors.Capacity = Mathf.NextPowerOfTwo(actors.Count);
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
{
|
||||
GetAllActors(allActors, actors[i]);
|
||||
}
|
||||
|
||||
var allScripts = new List<Script>();
|
||||
allScripts.Capacity = allActors.Capacity;
|
||||
GetAllScripts(allActors, allScripts);
|
||||
|
||||
int allCount = allActors.Count + allScripts.Count;
|
||||
_actorsCount = allActors.Count;
|
||||
_ids = new Guid[allCount];
|
||||
_prefabIds = new Guid[allCount];
|
||||
_prefabObjectIds = new Guid[allCount];
|
||||
|
||||
for (int i = 0; i < allActors.Count; i++)
|
||||
{
|
||||
_ids[i] = allActors[i].ID;
|
||||
_prefabIds[i] = allActors[i].PrefabID;
|
||||
_prefabObjectIds[i] = allActors[i].PrefabObjectID;
|
||||
}
|
||||
for (int i = 0; i < allScripts.Count; i++)
|
||||
{
|
||||
int j = _actorsCount + i;
|
||||
_ids[j] = allScripts[i].ID;
|
||||
_prefabIds[j] = allScripts[i].PrefabID;
|
||||
_prefabObjectIds[j] = allScripts[i].PrefabObjectID;
|
||||
}
|
||||
}
|
||||
|
||||
private void GetAllActors(List<Actor> allActors, Actor actor)
|
||||
{
|
||||
allActors.Add(actor);
|
||||
|
||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||
{
|
||||
var child = actor.GetChild(i);
|
||||
if (!allActors.Contains(child))
|
||||
{
|
||||
GetAllActors(allActors, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetAllScripts(List<Actor> allActors, List<Script> allScripts)
|
||||
{
|
||||
for (int i = 0; i < allActors.Count; i++)
|
||||
{
|
||||
var actor = allActors[i];
|
||||
for (int j = 0; j < actor.ScriptsCount; j++)
|
||||
{
|
||||
allScripts.Add(actor.GetScript(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ActionString => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Do()
|
||||
{
|
||||
// Note: prefab links are broken by the C++ backend on actor reparenting
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Undo()
|
||||
{
|
||||
// Restore links
|
||||
for (int i = 0; i < _actorsCount; i++)
|
||||
{
|
||||
var actor = Object.Find<Actor>(ref _ids[i]);
|
||||
if (actor != null && _prefabIds[i] != Guid.Empty)
|
||||
{
|
||||
Actor.Internal_LinkPrefab(Object.GetUnmanagedPtr(actor), ref _prefabIds[i], ref _prefabObjectIds[i]);
|
||||
}
|
||||
}
|
||||
for (int i = _actorsCount; i < _ids.Length; i++)
|
||||
{
|
||||
var script = Object.Find<Script>(ref _ids[i]);
|
||||
if (script != null && _prefabIds[i] != Guid.Empty)
|
||||
{
|
||||
Script.Internal_LinkPrefab(Object.GetUnmanagedPtr(script), ref _prefabIds[i], ref _prefabObjectIds[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_ids = null;
|
||||
_prefabIds = null;
|
||||
_prefabObjectIds = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DragDropEffect OnDragDropHeader(DragData data)
|
||||
{
|
||||
var result = DragDropEffect.None;
|
||||
|
||||
Actor myActor = Actor;
|
||||
Actor newParent;
|
||||
int newOrder = -1;
|
||||
|
||||
// Check if has no actor (only for Root Actor)
|
||||
if (myActor == null)
|
||||
{
|
||||
// Append to the last scene
|
||||
var scenes = Level.Scenes;
|
||||
if (scenes == null || scenes.Length == 0)
|
||||
throw new InvalidOperationException("No scene loaded.");
|
||||
newParent = scenes[scenes.Length - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
newParent = myActor;
|
||||
|
||||
// Use drag positioning to change target parent and index
|
||||
if (DragOverMode == DragItemPositioning.Above)
|
||||
{
|
||||
if (myActor.HasParent)
|
||||
{
|
||||
newParent = myActor.Parent;
|
||||
newOrder = myActor.OrderInParent;
|
||||
}
|
||||
}
|
||||
else if (DragOverMode == DragItemPositioning.Below)
|
||||
{
|
||||
if (myActor.HasParent)
|
||||
{
|
||||
newParent = myActor.Parent;
|
||||
newOrder = myActor.OrderInParent + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newParent == null)
|
||||
throw new InvalidOperationException("Missing parent actor.");
|
||||
|
||||
// Drag actors
|
||||
if (_dragActors != null && _dragActors.HasValidDrag)
|
||||
{
|
||||
bool worldPositionLock = Root.GetKey(KeyboardKeys.Control) == false;
|
||||
var singleObject = _dragActors.Objects.Count == 1;
|
||||
if (singleObject)
|
||||
{
|
||||
var targetActor = _dragActors.Objects[0].Actor;
|
||||
var customAction = targetActor.HasPrefabLink ? new ReparentAction(targetActor) : null;
|
||||
using (new UndoBlock(ActorNode.Root.Undo, targetActor, "Change actor parent", customAction))
|
||||
{
|
||||
targetActor.SetParent(newParent, worldPositionLock);
|
||||
targetActor.OrderInParent = newOrder;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var targetActors = _dragActors.Objects.ConvertAll(x => x.Actor);
|
||||
var customAction = targetActors.Any(x => x.HasPrefabLink) ? new ReparentAction(targetActors) : null;
|
||||
using (new UndoMultiBlock(ActorNode.Root.Undo, targetActors, "Change actors parent", customAction))
|
||||
{
|
||||
for (int i = 0; i < targetActors.Count; i++)
|
||||
{
|
||||
var targetActor = targetActors[i];
|
||||
targetActor.SetParent(newParent, worldPositionLock);
|
||||
targetActor.OrderInParent = newOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag assets
|
||||
else if (_dragAssets != null && _dragAssets.HasValidDrag)
|
||||
{
|
||||
for (int i = 0; i < _dragAssets.Objects.Count; i++)
|
||||
{
|
||||
var assetItem = _dragAssets.Objects[i];
|
||||
|
||||
if (assetItem.IsOfType<SkinnedModel>())
|
||||
{
|
||||
// Create actor
|
||||
var model = FlaxEngine.Content.LoadAsync<SkinnedModel>(assetItem.ID);
|
||||
var actor = new AnimatedModel();
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.SkinnedModel = model;
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
else if (assetItem.IsOfType<Model>())
|
||||
{
|
||||
// Create actor
|
||||
var model = FlaxEngine.Content.LoadAsync<Model>(assetItem.ID);
|
||||
var actor = new StaticModel();
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.Model = model;
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
else if (assetItem.IsOfType<CollisionData>())
|
||||
{
|
||||
// Create actor
|
||||
var actor = new MeshCollider();
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.CollisionData = FlaxEngine.Content.LoadAsync<CollisionData>(assetItem.ID);
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
else if (assetItem.IsOfType<ParticleSystem>())
|
||||
{
|
||||
// Create actor
|
||||
var actor = new ParticleEffect();
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.ParticleSystem = FlaxEngine.Content.LoadAsync<ParticleSystem>(assetItem.ID);
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
else if (assetItem.IsOfType<SceneAnimation>())
|
||||
{
|
||||
// Create actor
|
||||
var actor = new SceneAnimationPlayer();
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.Animation = FlaxEngine.Content.LoadAsync<SceneAnimation>(assetItem.ID);
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
else if (assetItem.IsOfType<AudioClip>())
|
||||
{
|
||||
// Create actor
|
||||
var actor = new AudioSource();
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.Clip = FlaxEngine.Content.LoadAsync<AudioClip>(assetItem.ID);
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (assetItem.IsOfType<Prefab>())
|
||||
{
|
||||
// Create prefab instance
|
||||
var prefab = FlaxEngine.Content.LoadAsync<Prefab>(assetItem.ID);
|
||||
var actor = PrefabManager.SpawnPrefab(prefab, null);
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
else if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance)
|
||||
{
|
||||
// Create actor
|
||||
var actor = (Actor)visualScriptItem.ScriptType.CreateInstance();
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = assetItem.ShortName;
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
}
|
||||
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag actor type
|
||||
else if (_dragActorType != null && _dragActorType.HasValidDrag)
|
||||
{
|
||||
for (int i = 0; i < _dragActorType.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragActorType.Objects[i];
|
||||
|
||||
// Create actor
|
||||
var actor = item.CreateInstance() as Actor;
|
||||
if (actor == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn actor of type " + item.TypeName);
|
||||
continue;
|
||||
}
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.Name = item.Name;
|
||||
actor.Transform = Actor.Transform;
|
||||
|
||||
// Spawn
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
}
|
||||
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
_dragHandlers.OnDragDrop(null);
|
||||
|
||||
// Check if scene has been modified
|
||||
if (result != DragDropEffect.None)
|
||||
{
|
||||
var node = SceneGraphFactory.FindNode(newParent.ID) as ActorNode;
|
||||
node?.TreeNode.Expand();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool ValidateDragActor(ActorNode actorNode)
|
||||
{
|
||||
// Reject dragging actors not linked to scene (eg. from prefab) or in the opposite way
|
||||
var thisHasScene = ActorNode.ParentScene != null;
|
||||
var otherHasScene = actorNode.ParentScene != null;
|
||||
if (thisHasScene != otherHasScene)
|
||||
return false;
|
||||
|
||||
// Reject dragging parents and itself
|
||||
return actorNode.Actor != null && actorNode != ActorNode && actorNode.Find(Actor) == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the asset for drag and drop into one of the scene tree nodes.
|
||||
/// </summary>
|
||||
/// <param name="assetItem">The item.</param>
|
||||
/// <returns>True if can drag and drop it, otherwise false.</returns>
|
||||
public static bool ValidateDragAsset(AssetItem assetItem)
|
||||
{
|
||||
if (assetItem.IsOfType<SkinnedModel>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<Model>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<AudioClip>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<Prefab>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<CollisionData>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<ParticleSystem>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<SceneAnimation>())
|
||||
return true;
|
||||
if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the type of the actor for drag and drop into one of the scene tree nodes.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of the actor.</param>
|
||||
/// <returns>True if can drag and drop it, otherwise false.</returns>
|
||||
public static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DoDragDrop()
|
||||
{
|
||||
DragData data;
|
||||
var tree = ParentTree;
|
||||
|
||||
// Check if this node is selected
|
||||
if (tree.Selection.Contains(this))
|
||||
{
|
||||
// Get selected actors
|
||||
var actors = new List<ActorNode>();
|
||||
for (var i = 0; i < tree.Selection.Count; i++)
|
||||
{
|
||||
var e = tree.Selection[i];
|
||||
if (e is ActorTreeNode node && node.ActorNode.CanDrag)
|
||||
actors.Add(node.ActorNode);
|
||||
}
|
||||
data = DragActors.GetDragData(actors);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = DragActors.GetDragData(ActorNode);
|
||||
}
|
||||
|
||||
// Start drag operation
|
||||
DoDragDrop(data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_dragActors = null;
|
||||
_dragAssets = null;
|
||||
_dragActorType = null;
|
||||
_dragHandlers?.Clear();
|
||||
_dragHandlers = null;
|
||||
_highlights = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Source/Editor/SceneGraph/GUI/SceneTreeNode.cs
Normal file
40
Source/Editor/SceneGraph/GUI/SceneTreeNode.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ActorTreeNode"/> custom implementation for <see cref="SceneNode"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.SceneGraph.GUI.ActorTreeNode" />
|
||||
public sealed class SceneTreeNode : ActorTreeNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void UpdateText()
|
||||
{
|
||||
base.UpdateText();
|
||||
|
||||
// Append asset name if used
|
||||
var filename = ((Scene)Actor).Filename;
|
||||
if (!string.IsNullOrEmpty(filename))
|
||||
{
|
||||
Text += " (";
|
||||
Text += filename;
|
||||
Text += ")";
|
||||
}
|
||||
|
||||
// Append star character to modified scenes
|
||||
if (ActorNode is SceneNode node && node.IsEdited)
|
||||
Text += "*";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color CacheTextColor()
|
||||
{
|
||||
return Style.Current.Foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
120
Source/Editor/SceneGraph/LocalSceneGraph.cs
Normal file
120
Source/Editor/SceneGraph/LocalSceneGraph.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the manual local scene graph made of actors not added to gameplay but used in editor. Handles creating, updating and removing scene graph nodes for the given root actor that holds the hierarchy.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public class LocalSceneGraph
|
||||
{
|
||||
private ActorNode _main;
|
||||
|
||||
/// <summary>
|
||||
/// The root node.
|
||||
/// </summary>
|
||||
public readonly RootNode Root;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the main actor used to track the hierarchy.
|
||||
/// </summary>
|
||||
public Actor MainActor
|
||||
{
|
||||
get => _main?.Actor;
|
||||
set
|
||||
{
|
||||
if (value != _main?.Actor)
|
||||
{
|
||||
if (_main != null)
|
||||
{
|
||||
_main = null;
|
||||
UnregisterEvents();
|
||||
}
|
||||
Root.DisposeChildNodes();
|
||||
Root.TreeNode.DisposeChildren();
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
var node = SceneGraphFactory.FindNode(value.ID) ?? SceneGraphFactory.BuildActorNode(value);
|
||||
var actorNode = node as ActorNode;
|
||||
if (actorNode == null)
|
||||
throw new Exception("Failed to setup a scene graph for the local actor.");
|
||||
|
||||
actorNode.TreeNode.UnlockChildrenRecursive();
|
||||
actorNode.ParentNode = Root;
|
||||
|
||||
_main = actorNode;
|
||||
RegisterEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the main scene graph node (owns <see cref="MainActor"/>).
|
||||
/// </summary>
|
||||
public ActorNode Main => _main;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalSceneGraph"/> class.
|
||||
/// </summary>
|
||||
/// <param name="root">The root node of the graph. Allows to override some logic for the scene graph section.</param>
|
||||
public LocalSceneGraph(RootNode root)
|
||||
{
|
||||
Root = root;
|
||||
Root.TreeNode.ChildrenIndent = 0;
|
||||
Root.TreeNode.Expand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all created scene graph nodes and unlinks the <see cref="MainActor"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
MainActor = null;
|
||||
Root.Dispose();
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
Level.ActorSpawned += OnActorSpawned;
|
||||
}
|
||||
|
||||
private void UnregisterEvents()
|
||||
{
|
||||
Level.ActorSpawned -= OnActorSpawned;
|
||||
}
|
||||
|
||||
private void OnActorSpawned(Actor actor)
|
||||
{
|
||||
// Skip actors from game
|
||||
if (actor.Scene != null)
|
||||
return;
|
||||
|
||||
// Check if it has parent
|
||||
var parentNode = SceneGraphFactory.FindNode(actor.Parent.ID) as ActorNode;
|
||||
if (parentNode == null)
|
||||
return;
|
||||
|
||||
// Check if actor is not from the local graph (use main actor to verify it)
|
||||
var a = actor.Parent;
|
||||
var main = MainActor;
|
||||
while (a != null && a != main)
|
||||
{
|
||||
a = a.Parent;
|
||||
}
|
||||
if (a == null)
|
||||
return;
|
||||
|
||||
// Add node
|
||||
var actorNode = SceneGraphFactory.BuildActorNode(actor);
|
||||
if (actorNode == null)
|
||||
throw new Exception("Failed to setup a scene graph for the local actor.");
|
||||
actorNode.TreeNode.UnlockChildrenRecursive();
|
||||
actorNode.ParentNode = parentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
147
Source/Editor/SceneGraph/RootNode.cs
Normal file
147
Source/Editor/SceneGraph/RootNode.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents root node of the whole scene graph.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public abstract class RootNode : ActorNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RootNode"/> class.
|
||||
/// </summary>
|
||||
protected RootNode()
|
||||
: base(null, Guid.NewGuid())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RootNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The node id.</param>
|
||||
protected RootNode(Guid id)
|
||||
: base(null, id)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when actor child nodes get released.
|
||||
/// </summary>
|
||||
public event Action<ActorNode> ActorChildNodesDispose;
|
||||
|
||||
/// <summary>
|
||||
/// Called when actor child nodes get released.
|
||||
/// </summary>
|
||||
/// <param name="node">The node.</param>
|
||||
public virtual void OnActorChildNodesDispose(ActorNode node)
|
||||
{
|
||||
ActorChildNodesDispose?.Invoke(node);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Root";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SceneNode ParentScene => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override RootNode Root => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCopyPaste => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDelete => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrag => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActive => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActiveInHierarchy => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Transform Transform
|
||||
{
|
||||
get => Transform.Identity;
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs raycasting over nodes hierarchy trying to get the closest object hit by the given ray.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray.</param>
|
||||
/// <param name="view">The view.</param>
|
||||
/// <param name="distance">The result distance.</param>
|
||||
/// <param name="flags">The raycasting flags.</param>
|
||||
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
||||
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out float distance, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
|
||||
{
|
||||
var data = new RayCastData
|
||||
{
|
||||
Ray = ray,
|
||||
View = view,
|
||||
Flags = flags
|
||||
};
|
||||
return RayCast(ref data, out distance, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs raycasting over nodes hierarchy trying to get the closest object hit by the given ray.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray.</param>
|
||||
/// <param name="view">The view.</param>
|
||||
/// <param name="distance">The result distance.</param>
|
||||
/// <param name="normal">The result intersection surface normal vector.</param>
|
||||
/// <param name="flags">The raycasting flags.</param>
|
||||
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
||||
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out float distance, out Vector3 normal, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
|
||||
{
|
||||
var data = new RayCastData
|
||||
{
|
||||
Ray = ray,
|
||||
View = view,
|
||||
Flags = flags
|
||||
};
|
||||
return RayCast(ref data, out distance, out normal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
distance = 0;
|
||||
normal = Vector3.Up;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Delete()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the specified actor.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <param name="parent">The parent.</param>
|
||||
public abstract void Spawn(Actor actor, Actor parent);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the undo.
|
||||
/// </summary>
|
||||
public abstract Undo Undo { get; }
|
||||
}
|
||||
}
|
||||
171
Source/Editor/SceneGraph/SceneGraphFactory.cs
Normal file
171
Source/Editor/SceneGraph/SceneGraphFactory.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEngine;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory service for Scene Graph nodes creating.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public static class SceneGraphFactory
|
||||
{
|
||||
private static readonly object[] _sharedArgsContainer = new object[1];
|
||||
|
||||
/// <summary>
|
||||
/// The custom nodes types. Key = object type, Value = custom graph node type
|
||||
/// </summary>
|
||||
public static readonly Dictionary<Type, Type> CustomNodesTypes = new Dictionary<Type, Type>();
|
||||
|
||||
/// <summary>
|
||||
/// The nodes collection where key is ID.
|
||||
/// </summary>
|
||||
public static readonly Dictionary<Guid, SceneGraphNode> Nodes = new Dictionary<Guid, SceneGraphNode>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SceneGraphFactory"/> class.
|
||||
/// </summary>
|
||||
static SceneGraphFactory()
|
||||
{
|
||||
// Init default nodes types
|
||||
CustomNodesTypes.Add(typeof(Scene), typeof(SceneNode));
|
||||
CustomNodesTypes.Add(typeof(Camera), typeof(CameraNode));
|
||||
CustomNodesTypes.Add(typeof(EnvironmentProbe), typeof(EnvironmentProbeNode));
|
||||
CustomNodesTypes.Add(typeof(PointLight), typeof(PointLightNode));
|
||||
CustomNodesTypes.Add(typeof(DirectionalLight), typeof(DirectionalLightNode));
|
||||
CustomNodesTypes.Add(typeof(SpotLight), typeof(SpotLightNode));
|
||||
CustomNodesTypes.Add(typeof(Skybox), typeof(SkyboxNode));
|
||||
CustomNodesTypes.Add(typeof(Sky), typeof(SkyNode));
|
||||
CustomNodesTypes.Add(typeof(ExponentialHeightFog), typeof(ExponentialHeightFogNode));
|
||||
CustomNodesTypes.Add(typeof(SkyLight), typeof(SkyLightNode));
|
||||
CustomNodesTypes.Add(typeof(PostFxVolume), typeof(PostFxVolumeNode));
|
||||
CustomNodesTypes.Add(typeof(StaticModel), typeof(StaticModelNode));
|
||||
CustomNodesTypes.Add(typeof(BoxBrush), typeof(BoxBrushNode));
|
||||
CustomNodesTypes.Add(typeof(TextRender), typeof(TextRenderNode));
|
||||
CustomNodesTypes.Add(typeof(AudioListener), typeof(AudioListenerNode));
|
||||
CustomNodesTypes.Add(typeof(AudioSource), typeof(AudioSourceNode));
|
||||
CustomNodesTypes.Add(typeof(BoneSocket), typeof(BoneSocketNode));
|
||||
CustomNodesTypes.Add(typeof(Decal), typeof(DecalNode));
|
||||
CustomNodesTypes.Add(typeof(BoxCollider), typeof(BoxColliderNode));
|
||||
CustomNodesTypes.Add(typeof(SphereCollider), typeof(ColliderNode));
|
||||
CustomNodesTypes.Add(typeof(CapsuleCollider), typeof(ColliderNode));
|
||||
CustomNodesTypes.Add(typeof(MeshCollider), typeof(ColliderNode));
|
||||
CustomNodesTypes.Add(typeof(CharacterController), typeof(ColliderNode));
|
||||
CustomNodesTypes.Add(typeof(UICanvas), typeof(UICanvasNode));
|
||||
CustomNodesTypes.Add(typeof(UIControl), typeof(UIControlNode));
|
||||
CustomNodesTypes.Add(typeof(Terrain), typeof(TerrainNode));
|
||||
CustomNodesTypes.Add(typeof(Foliage), typeof(FoliageNode));
|
||||
CustomNodesTypes.Add(typeof(NavMeshBoundsVolume), typeof(NavMeshBoundsVolumeNode));
|
||||
CustomNodesTypes.Add(typeof(BoxVolume), typeof(BoxVolumeNode));
|
||||
CustomNodesTypes.Add(typeof(NavLink), typeof(NavLinkNode));
|
||||
CustomNodesTypes.Add(typeof(ParticleEffect), typeof(ParticleEffectNode));
|
||||
CustomNodesTypes.Add(typeof(SceneAnimationPlayer), typeof(SceneAnimationPlayerNode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the node by the given ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>Found node or null if cannot.</returns>
|
||||
public static SceneGraphNode FindNode(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
return null;
|
||||
|
||||
SceneGraphNode result;
|
||||
Nodes.TryGetValue(id, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the node for the given object ID or creates a new node for actors (with automatic linkage to the parent).
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>The result node.</returns>
|
||||
public static SceneGraphNode GetNode(Guid id)
|
||||
{
|
||||
SceneGraphNode result;
|
||||
Nodes.TryGetValue(id, out result);
|
||||
if (result == null)
|
||||
{
|
||||
var actor = Object.TryFind<Actor>(ref id);
|
||||
if (actor != null)
|
||||
{
|
||||
result = BuildActorNode(actor);
|
||||
if (result != null && actor.HasParent)
|
||||
{
|
||||
result.ParentNode = FindNode(actor.Parent.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the scene tree.
|
||||
/// </summary>
|
||||
/// <param name="scene">The scene.</param>
|
||||
/// <returns>The root scene node.</returns>
|
||||
public static SceneNode BuildSceneTree(Scene scene)
|
||||
{
|
||||
// TODO: make it faster by calling engine internally only once to gather optimized scene tree
|
||||
return (SceneNode)BuildActorNode(scene);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the actor node. Warning! Don't create duplicated nodes.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <returns>Created node or null if failed.</returns>
|
||||
public static ActorNode BuildActorNode(Actor actor)
|
||||
{
|
||||
ActorNode result = null;
|
||||
Type customType;
|
||||
|
||||
try
|
||||
{
|
||||
// Try to pick custom node type for that actor object
|
||||
if (CustomNodesTypes.TryGetValue(actor.GetType(), out customType))
|
||||
{
|
||||
// Use custom type
|
||||
_sharedArgsContainer[0] = actor;
|
||||
result = (ActorNode)Activator.CreateInstance(customType, _sharedArgsContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use default type for actors
|
||||
result = new ActorNode(actor);
|
||||
}
|
||||
|
||||
// Build children
|
||||
var childrenCount = actor.ChildrenCount;
|
||||
for (int i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = actor.GetChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
|
||||
var childNode = BuildActorNode(child);
|
||||
if (childNode != null)
|
||||
childNode.ParentNode = result;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Error
|
||||
Editor.LogWarning($"Failed to create scene graph node for actor {actor.Name} (type: {actor.GetType()}).");
|
||||
if (CustomNodesTypes.TryGetValue(actor.GetType(), out customType))
|
||||
{
|
||||
Editor.LogWarning($"Custom node type: {customType}");
|
||||
}
|
||||
Editor.LogWarning(ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
358
Source/Editor/SceneGraph/SceneGraphNode.cs
Normal file
358
Source/Editor/SceneGraph/SceneGraphNode.cs
Normal file
@@ -0,0 +1,358 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all leaf node objects which belong to scene graph used by the Editor.
|
||||
/// Scene Graph is directional graph without cyclic references. It's a tree.
|
||||
/// A <see cref="SceneModule"/> class is responsible for Scene Graph management.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public abstract class SceneGraphNode : ITransformable
|
||||
{
|
||||
/// <summary>
|
||||
/// The parent node.
|
||||
/// </summary>
|
||||
protected SceneGraphNode parentNode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children list.
|
||||
/// </summary>
|
||||
public List<SceneGraphNode> ChildNodes { get; } = new List<SceneGraphNode>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SceneGraphNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique node identifier. Cannot be changed at runtime.</param>
|
||||
protected SceneGraphNode(Guid id)
|
||||
{
|
||||
ID = id;
|
||||
SceneGraphFactory.Nodes.Add(id, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier. Must be unique and immutable.
|
||||
/// </summary>
|
||||
public Guid ID { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent scene.
|
||||
/// </summary>
|
||||
public abstract SceneNode ParentScene { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root node of the scene graph (if has).
|
||||
/// </summary>
|
||||
public virtual RootNode Root => ParentNode?.Root;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Transform Transform { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance can be copied or/and pasted.
|
||||
/// </summary>
|
||||
public virtual bool CanCopyPaste => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this node can be deleted by the user.
|
||||
/// </summary>
|
||||
public virtual bool CanDelete => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this node can be dragged by the user.
|
||||
/// </summary>
|
||||
public virtual bool CanDrag => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this node can be transformed by the user.
|
||||
/// </summary>
|
||||
public virtual bool CanTransform => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="SceneGraphNode"/> is active.
|
||||
/// </summary>
|
||||
public abstract bool IsActive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="SceneGraphNode"/> is active and all parent nodes are also active.
|
||||
/// </summary>
|
||||
public abstract bool IsActiveInHierarchy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets order of the node in the parent container.
|
||||
/// </summary>
|
||||
public abstract int OrderInParent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent node.
|
||||
/// </summary>
|
||||
public virtual SceneGraphNode ParentNode
|
||||
{
|
||||
get => parentNode;
|
||||
set
|
||||
{
|
||||
if (parentNode != value)
|
||||
{
|
||||
if (parentNode != null)
|
||||
{
|
||||
parentNode.ChildNodes.Remove(this);
|
||||
}
|
||||
|
||||
parentNode = value;
|
||||
|
||||
if (parentNode != null)
|
||||
{
|
||||
parentNode.ChildNodes.Add(this);
|
||||
}
|
||||
|
||||
OnParentChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object to edit via properties editor when this node is being selected.
|
||||
/// </summary>
|
||||
public virtual object EditableObject => this;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object used to record undo changes.
|
||||
/// </summary>
|
||||
public virtual object UndoRecordObject => EditableObject;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is in a hierarchy (one of the children or lower).
|
||||
/// </summary>
|
||||
/// <param name="node">The node to check,</param>
|
||||
/// <returns>True if given actor is part of the hierarchy, otherwise false.</returns>
|
||||
public virtual bool ContainsInHierarchy(SceneGraphNode node)
|
||||
{
|
||||
if (ChildNodes.Contains(node))
|
||||
return true;
|
||||
|
||||
return ChildNodes.Any(x => x.ContainsInHierarchy(node));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is one of the children.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to check,</param>
|
||||
/// <returns>True if given object is a child, otherwise false.</returns>
|
||||
public virtual bool ContainsChild(SceneGraphNode node)
|
||||
{
|
||||
return ChildNodes.Contains(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the child node.
|
||||
/// </summary>
|
||||
/// <param name="node">The node.</param>
|
||||
public void AddChild(SceneGraphNode node)
|
||||
{
|
||||
node.ParentNode = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The scene graph raycasting data container.
|
||||
/// </summary>
|
||||
public struct RayCastData
|
||||
{
|
||||
/// <summary>
|
||||
/// The raycasting optional flags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FlagTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// The none.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The skip colliders flag. Use it to ignore physics colliders intersections detection.
|
||||
/// </summary>
|
||||
SkipColliders = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The skip editor primitives. Use it to ignore editor icons and primitives intersections detection.
|
||||
/// </summary>
|
||||
SkipEditorPrimitives = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ray.
|
||||
/// </summary>
|
||||
public Ray Ray;
|
||||
|
||||
/// <summary>
|
||||
/// The camera view ray.
|
||||
/// </summary>
|
||||
public Ray View;
|
||||
|
||||
/// <summary>
|
||||
/// The flags.
|
||||
/// </summary>
|
||||
public FlagTypes Flags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs raycasting over child nodes hierarchy trying to get the closest object hit by the given ray.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray casting data.</param>
|
||||
/// <param name="distance">The result distance.</param>
|
||||
/// <param name="normal">The result intersection surface normal vector.</param>
|
||||
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
||||
public virtual SceneGraphNode RayCastChildren(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
distance = 0;
|
||||
normal = Vector3.Up;
|
||||
return null;
|
||||
}
|
||||
|
||||
SceneGraphNode minTarget = null;
|
||||
float minDistance = float.MaxValue;
|
||||
Vector3 minDistanceNormal = Vector3.Up;
|
||||
|
||||
// Check all children
|
||||
for (int i = 0; i < ChildNodes.Count; i++)
|
||||
{
|
||||
var hit = ChildNodes[i].RayCast(ref ray, out distance, out normal);
|
||||
if (hit != null && distance <= minDistance)
|
||||
{
|
||||
minTarget = hit;
|
||||
minDistance = distance;
|
||||
minDistanceNormal = normal;
|
||||
}
|
||||
}
|
||||
|
||||
// Return result
|
||||
distance = minDistance;
|
||||
normal = minDistanceNormal;
|
||||
return minTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs raycasting over nodes hierarchy trying to get the closest object hit by the given ray.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray casting data.</param>
|
||||
/// <param name="distance">The result distance.</param>
|
||||
/// <param name="normal">The result intersection surface normal vector.</param>
|
||||
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
||||
public virtual SceneGraphNode RayCast(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
distance = 0;
|
||||
normal = Vector3.Up;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check itself
|
||||
SceneGraphNode minTarget = null;
|
||||
float minDistance = float.MaxValue;
|
||||
Vector3 minDistanceNormal = Vector3.Up;
|
||||
if (RayCastSelf(ref ray, out distance, out normal))
|
||||
{
|
||||
minTarget = this;
|
||||
minDistance = distance;
|
||||
minDistanceNormal = normal;
|
||||
}
|
||||
|
||||
// Check all children
|
||||
for (int i = 0; i < ChildNodes.Count; i++)
|
||||
{
|
||||
var hit = ChildNodes[i].RayCast(ref ray, out distance, out normal);
|
||||
if (hit != null && distance <= minDistance)
|
||||
{
|
||||
minTarget = hit;
|
||||
minDistance = distance;
|
||||
minDistanceNormal = normal;
|
||||
}
|
||||
}
|
||||
|
||||
// Return result
|
||||
distance = minDistance;
|
||||
normal = minDistanceNormal;
|
||||
return minTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if given ray intersects with the node.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray casting data.</param>
|
||||
/// <param name="distance">The distance.</param>
|
||||
/// <param name="normal">The result intersection surface normal vector.</param>
|
||||
/// <returns>True ray hits this node, otherwise false.</returns>
|
||||
public virtual bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
distance = 0;
|
||||
normal = Vector3.Up;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when selected nodes should draw debug shapes using <see cref="DebugDraw"/> interface.
|
||||
/// </summary>
|
||||
/// <param name="data">The debug draw data.</param>
|
||||
public virtual void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes object represented by this node eg. actor.
|
||||
/// </summary>
|
||||
public virtual void Delete()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the node and the child tree. Disposed all GUI parts and used resources.
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
OnDispose();
|
||||
|
||||
// Unlink from the parent
|
||||
if (parentNode != null)
|
||||
{
|
||||
parentNode.ChildNodes.Remove(this);
|
||||
parentNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when node or parent node is disposing.
|
||||
/// </summary>
|
||||
public virtual void OnDispose()
|
||||
{
|
||||
// Call deeper
|
||||
for (int i = 0; i < ChildNodes.Count; i++)
|
||||
{
|
||||
ChildNodes[i].OnDispose();
|
||||
}
|
||||
|
||||
SceneGraphFactory.Nodes.Remove(ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when parent node gets changed.
|
||||
/// </summary>
|
||||
protected virtual void OnParentChanged()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
129
Source/Editor/SceneGraph/SceneGraphTools.cs
Normal file
129
Source/Editor/SceneGraph/SceneGraphTools.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Set of tools for <see cref="SceneGraphNode"/> processing.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public static class SceneGraphTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for scene graph action execution callback.
|
||||
/// </summary>
|
||||
/// <param name="node">The node.</param>
|
||||
/// <returns>True if call deeper, otherwise skip calling node children.</returns>
|
||||
public delegate bool GraphExecuteCallbackDelegate(SceneGraphNode node);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the custom action on the graph nodes.
|
||||
/// </summary>
|
||||
/// <param name="node">The node</param>
|
||||
/// <param name="callback">The callback.</param>
|
||||
public static void ExecuteOnGraph(this SceneGraphNode node, GraphExecuteCallbackDelegate callback)
|
||||
{
|
||||
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||
{
|
||||
if (callback(node.ChildNodes[i]))
|
||||
{
|
||||
ExecuteOnGraph(node.ChildNodes[i], callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the array made of input nodes that are at the top level. The result collection contains only nodes that don't have parent nodes in the given collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The scene graph node type.</typeparam>
|
||||
/// <param name="nodes">The nodes.</param>
|
||||
/// <returns>The result.</returns>
|
||||
public static List<T> BuildNodesParents<T>(this List<T> nodes)
|
||||
where T : SceneGraphNode
|
||||
{
|
||||
var list = new List<T>();
|
||||
BuildNodesParents(nodes, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the list made of input nodes that are at the top level. The result collection contains only nodes that don't have parent nodes in the given collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The scene graph node type.</typeparam>
|
||||
/// <param name="nodes">The nodes.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
public static void BuildNodesParents<T>(this List<T> nodes, List<T> result)
|
||||
where T : SceneGraphNode
|
||||
{
|
||||
if (nodes == null || result == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
result.Clear();
|
||||
|
||||
// Build solid part of the tree
|
||||
var fullTree = BuildAllNodes(nodes);
|
||||
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var target = nodes[i];
|
||||
|
||||
// If there is no target node parent in the solid tree list,
|
||||
// then it means it's a local root node and can be added to the results.
|
||||
if (!fullTree.Contains(target.ParentNode))
|
||||
result.Add(target);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the array made of all nodes in the input list and child tree. The result collection contains all nodes in the tree.
|
||||
/// </summary>
|
||||
/// <param name="nodes">The nodes.</param>
|
||||
/// <returns>The result.</returns>
|
||||
public static List<SceneGraphNode> BuildAllNodes<T>(this List<T> nodes)
|
||||
where T : SceneGraphNode
|
||||
{
|
||||
var list = new List<SceneGraphNode>();
|
||||
BuildAllNodes(nodes, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void FillTree(SceneGraphNode node, List<SceneGraphNode> result)
|
||||
{
|
||||
result.AddRange(node.ChildNodes);
|
||||
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||
{
|
||||
FillTree(node.ChildNodes[i], result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the list made of all nodes in the input list and child tree. The result collection contains all nodes in the tree.
|
||||
/// </summary>
|
||||
/// <param name="nodes">The nodes.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
public static void BuildAllNodes<T>(this List<T> nodes, List<SceneGraphNode> result)
|
||||
where T : SceneGraphNode
|
||||
{
|
||||
if (nodes == null || result == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
result.Clear();
|
||||
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var target = nodes[i];
|
||||
|
||||
// Check if has been already added
|
||||
if (result.Contains(target))
|
||||
continue;
|
||||
|
||||
// Add whole child tree to the results
|
||||
result.Add(target);
|
||||
FillTree(target, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user