diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 8d3e3a8ec..902d8c399 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -246,24 +246,51 @@ namespace FlaxEditor.Windows.Assets // Spawning actors options contextMenu.AddSeparator(); - var spawnMenu = contextMenu.AddChildMenu("New"); - var newActorCm = spawnMenu.ContextMenu; - for (int i = 0; i < SceneTreeWindow.SpawnActorsGroups.Length; i++) + + // go through each actor and add it to the context menu if it has the ActorContextMenu attribute + foreach (var actorType in Editor.CodeEditing.Actors.Get()) { - var group = SceneTreeWindow.SpawnActorsGroups[i]; - - if (group.Types.Length == 1) + if (actorType.IsAbstract || !actorType.HasAttribute(typeof(ActorContextMenuAttribute), true)) + continue; + + ActorContextMenuAttribute attribute = null; + foreach (var actorAttribute in actorType.GetAttributes(true)) { - var type = group.Types[0].Value; - newActorCm.AddButton(group.Types[0].Key, () => Spawn(type)); - } - else - { - var groupCm = newActorCm.AddChildMenu(group.Name).ContextMenu; - for (int j = 0; j < group.Types.Length; j++) + if (actorAttribute is ActorContextMenuAttribute actorContextMenuAttribute) { - var type = group.Types[j].Value; - groupCm.AddButton(group.Types[j].Key, () => Spawn(type)); + attribute = actorContextMenuAttribute; + } + } + var splitPath = attribute?.Path.Split('/'); + ContextMenuChildMenu childCM = null; + bool mainCM = true; + for (int i = 0; i < splitPath?.Length; i++) + { + if (i == splitPath.Length - 1) + { + if (mainCM) + { + contextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type)); + mainCM = false; + } + else + { + childCM?.ContextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type)); + childCM.ContextMenu.AutoSort = true; + } + } + else + { + if (mainCM) + { + childCM = contextMenu.GetOrAddChildMenu(splitPath[i].Trim()); + mainCM = false; + } + else + { + childCM = childCM?.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim()); + } + childCM.ContextMenu.AutoSort = true; } } } diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index bef3f1b3c..ce3b42cf7 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -60,23 +60,56 @@ namespace FlaxEditor.Windows if (isSingleActorSelected) { var convertMenu = contextMenu.AddChildMenu("Convert"); - var convertActorCm = convertMenu.ContextMenu; - for (int i = 0; i < SpawnActorsGroups.Length; i++) + convertMenu.ContextMenu.AutoSort = true; + foreach (var actorType in Editor.CodeEditing.Actors.Get()) { - var group = SpawnActorsGroups[i]; - - if (group.Types.Length == 1) + if (actorType.IsAbstract || !actorType.HasAttribute(typeof(ActorContextMenuAttribute), true)) + continue; + + ActorContextMenuAttribute attribute = null; + foreach (var actorAttribute in actorType.GetAttributes(true)) { - var type = group.Types[0].Value; - convertActorCm.AddButton(group.Types[0].Key, () => Editor.SceneEditing.Convert(type)); - } - else - { - var groupCm = convertActorCm.AddChildMenu(group.Name).ContextMenu; - for (int j = 0; j < group.Types.Length; j++) + if (actorAttribute is ActorContextMenuAttribute actorContextMenuAttribute) { - var type = group.Types[j].Value; - groupCm.AddButton(group.Types[j].Key, () => Editor.SceneEditing.Convert(type)); + attribute = actorContextMenuAttribute; + } + } + var splitPath = attribute?.Path.Split('/'); + ContextMenuChildMenu childCM = convertMenu; + bool mainCM = true; + for (int i = 0; i < splitPath?.Length; i++) + { + if (i == splitPath.Length - 1) + { + if (mainCM) + { + convertMenu.ContextMenu.AddButton(splitPath[i].Trim(), () => Editor.SceneEditing.Convert(actorType.Type)); + mainCM = false; + } + else + { + childCM?.ContextMenu.AddButton(splitPath[i].Trim(), () => Editor.SceneEditing.Convert(actorType.Type)); + childCM.ContextMenu.AutoSort = true; + } + } + else + { + // remove new path for converting menu + if (splitPath[i] == "New") + { + continue; + } + + if (mainCM) + { + childCM = convertMenu.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim()); + mainCM = false; + } + else + { + childCM = childCM?.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim()); + } + childCM.ContextMenu.AutoSort = true; } } } @@ -113,25 +146,51 @@ namespace FlaxEditor.Windows // Spawning actors options contextMenu.AddSeparator(); - - var spawnMenu = contextMenu.AddChildMenu("New"); - var newActorCm = spawnMenu.ContextMenu; - for (int i = 0; i < SpawnActorsGroups.Length; i++) + + // go through each actor and add it to the context menu if it has the ActorContextMenu attribute + foreach (var actorType in Editor.CodeEditing.Actors.Get()) { - var group = SpawnActorsGroups[i]; - - if (group.Types.Length == 1) + if (actorType.IsAbstract || !actorType.HasAttribute(typeof(ActorContextMenuAttribute), true)) + continue; + + ActorContextMenuAttribute attribute = null; + foreach (var actorAttribute in actorType.GetAttributes(true)) { - var type = group.Types[0].Value; - newActorCm.AddButton(group.Types[0].Key, () => Spawn(type)); - } - else - { - var groupCm = newActorCm.AddChildMenu(group.Name).ContextMenu; - for (int j = 0; j < group.Types.Length; j++) + if (actorAttribute is ActorContextMenuAttribute actorContextMenuAttribute) { - var type = group.Types[j].Value; - groupCm.AddButton(group.Types[j].Key, () => Spawn(type)); + attribute = actorContextMenuAttribute; + } + } + var splitPath = attribute?.Path.Split('/'); + ContextMenuChildMenu childCM = null; + bool mainCM = true; + for (int i = 0; i < splitPath?.Length; i++) + { + if (i == splitPath.Length - 1) + { + if (mainCM) + { + contextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type)); + mainCM = false; + } + else + { + childCM?.ContextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type)); + childCM.ContextMenu.AutoSort = true; + } + } + else + { + if (mainCM) + { + childCM = contextMenu.GetOrAddChildMenu(splitPath[i].Trim()); + mainCM = false; + } + else + { + childCM = childCM?.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim()); + } + childCM.ContextMenu.AutoSort = true; } } } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 0046782f5..8fd6e33ee 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -22,113 +22,6 @@ namespace FlaxEditor.Windows /// public partial class SceneTreeWindow : SceneEditorWindow { - /// - /// The spawnable actors group. - /// - public struct ActorsGroup - { - /// - /// The group name. - /// - public string Name; - - /// - /// The types to spawn (name and type). - /// - public KeyValuePair[] Types; - } - - /// - /// The Spawnable actors (groups with single entry are inlined without a child menu) - /// - public static readonly ActorsGroup[] SpawnActorsGroups = - { - new ActorsGroup - { - Types = new[] { new KeyValuePair("Actor", typeof(EmptyActor)) } - }, - new ActorsGroup - { - Types = new[] { new KeyValuePair("Model", typeof(StaticModel)) } - }, - new ActorsGroup - { - Types = new[] { new KeyValuePair("Camera", typeof(Camera)) } - }, - new ActorsGroup - { - Name = "Lights", - Types = new[] - { - new KeyValuePair("Directional Light", typeof(DirectionalLight)), - new KeyValuePair("Point Light", typeof(PointLight)), - new KeyValuePair("Spot Light", typeof(SpotLight)), - new KeyValuePair("Sky Light", typeof(SkyLight)), - } - }, - new ActorsGroup - { - Name = "Visuals", - Types = new[] - { - new KeyValuePair("Environment Probe", typeof(EnvironmentProbe)), - new KeyValuePair("Sky", typeof(Sky)), - new KeyValuePair("Skybox", typeof(Skybox)), - new KeyValuePair("Exponential Height Fog", typeof(ExponentialHeightFog)), - new KeyValuePair("PostFx Volume", typeof(PostFxVolume)), - new KeyValuePair("Decal", typeof(Decal)), - new KeyValuePair("Particle Effect", typeof(ParticleEffect)), - } - }, - new ActorsGroup - { - Name = "Physics", - Types = new[] - { - new KeyValuePair("Rigid Body", typeof(RigidBody)), - new KeyValuePair("Character Controller", typeof(CharacterController)), - new KeyValuePair("Box Collider", typeof(BoxCollider)), - new KeyValuePair("Sphere Collider", typeof(SphereCollider)), - new KeyValuePair("Capsule Collider", typeof(CapsuleCollider)), - new KeyValuePair("Mesh Collider", typeof(MeshCollider)), - new KeyValuePair("Fixed Joint", typeof(FixedJoint)), - new KeyValuePair("Distance Joint", typeof(DistanceJoint)), - new KeyValuePair("Slider Joint", typeof(SliderJoint)), - new KeyValuePair("Spherical Joint", typeof(SphericalJoint)), - new KeyValuePair("Hinge Joint", typeof(HingeJoint)), - new KeyValuePair("D6 Joint", typeof(D6Joint)), - } - }, - new ActorsGroup - { - Name = "Other", - Types = new[] - { - new KeyValuePair("Animated Model", typeof(AnimatedModel)), - new KeyValuePair("Bone Socket", typeof(BoneSocket)), - new KeyValuePair("CSG Box Brush", typeof(BoxBrush)), - new KeyValuePair("Audio Source", typeof(AudioSource)), - new KeyValuePair("Audio Listener", typeof(AudioListener)), - new KeyValuePair("Scene Animation", typeof(SceneAnimationPlayer)), - new KeyValuePair("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)), - new KeyValuePair("Nav Link", typeof(NavLink)), - new KeyValuePair("Nav Modifier Volume", typeof(NavModifierVolume)), - new KeyValuePair("Spline", typeof(Spline)), - } - }, - new ActorsGroup - { - Name = "GUI", - Types = new[] - { - new KeyValuePair("UI Control", typeof(UIControl)), - new KeyValuePair("UI Canvas", typeof(UICanvas)), - new KeyValuePair("Text Render", typeof(TextRender)), - new KeyValuePair("Sprite Render", typeof(SpriteRender)), - } - }, - }; - private TextBox _searchBox; private Tree _tree; private Panel _sceneTreePanel; diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.h b/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.h index 4e0fcaa40..cdeb29fa5 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.h +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.h @@ -11,7 +11,7 @@ /// /// The scene animation playback actor. /// -API_CLASS() class FLAXENGINE_API SceneAnimationPlayer : public Actor, public IPostFxSettingsProvider +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Scene Animation\")") class FLAXENGINE_API SceneAnimationPlayer : public Actor, public IPostFxSettingsProvider { DECLARE_SCENE_OBJECT(SceneAnimationPlayer); diff --git a/Source/Engine/Audio/AudioListener.h b/Source/Engine/Audio/AudioListener.h index 4c9f608fa..589c8a676 100644 --- a/Source/Engine/Audio/AudioListener.h +++ b/Source/Engine/Audio/AudioListener.h @@ -7,7 +7,7 @@ /// /// Represents a listener that hears audio sources. For spatial audio the volume and pitch of played audio is determined by the distance, orientation and velocity differences between the source and the listener. /// -API_CLASS() class FLAXENGINE_API AudioListener : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Audio/Audio Listener\")") class FLAXENGINE_API AudioListener : public Actor { DECLARE_SCENE_OBJECT(AudioListener); private: diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index ebc8dc957..ff181674e 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -13,7 +13,7 @@ /// /// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity. /// -API_CLASS() class FLAXENGINE_API AudioSource : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Audio/Audio Source\")") class FLAXENGINE_API AudioSource : public Actor { DECLARE_SCENE_OBJECT(AudioSource); friend class AudioStreamingHandler; diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 16f2740dd..96bcb3ad5 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -12,7 +12,7 @@ /// /// Performs an animation and renders a skinned model. /// -API_CLASS() class FLAXENGINE_API AnimatedModel : public ModelInstanceActor +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Animated Model\")") class FLAXENGINE_API AnimatedModel : public ModelInstanceActor { DECLARE_SCENE_OBJECT(AnimatedModel); friend class AnimationsSystem; diff --git a/Source/Engine/Level/Actors/BoneSocket.h b/Source/Engine/Level/Actors/BoneSocket.h index e3ed1e5f4..88fd3fa1d 100644 --- a/Source/Engine/Level/Actors/BoneSocket.h +++ b/Source/Engine/Level/Actors/BoneSocket.h @@ -7,7 +7,7 @@ /// /// Actor that links to the animated model skeleton node transformation. /// -API_CLASS() class FLAXENGINE_API BoneSocket : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Bone Socket\")") class FLAXENGINE_API BoneSocket : public Actor { DECLARE_SCENE_OBJECT(BoneSocket); diff --git a/Source/Engine/Level/Actors/BoxBrush.h b/Source/Engine/Level/Actors/BoxBrush.h index fc30aedc2..49128ee09 100644 --- a/Source/Engine/Level/Actors/BoxBrush.h +++ b/Source/Engine/Level/Actors/BoxBrush.h @@ -65,7 +65,7 @@ public: /// /// Performs CSG box brush operation that adds or removes geometry. /// -API_CLASS() class FLAXENGINE_API BoxBrush : public Actor, public CSG::Brush +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Box Brush\")") class FLAXENGINE_API BoxBrush : public Actor, public CSG::Brush { DECLARE_SCENE_OBJECT(BoxBrush); private: diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index 465130110..3b6912c43 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -18,7 +18,7 @@ /// /// Describes the camera projection and view. Provides information about how to render scene (viewport location and direction, etc.). /// -API_CLASS(Sealed) class FLAXENGINE_API Camera : public Actor +API_CLASS(Sealed, Attributes = "ActorContextMenu(\"New/Camera\")") class FLAXENGINE_API Camera : public Actor { DECLARE_SCENE_OBJECT(Camera); diff --git a/Source/Engine/Level/Actors/Decal.h b/Source/Engine/Level/Actors/Decal.h index 8cc6c2df7..b0acdd81e 100644 --- a/Source/Engine/Level/Actors/Decal.h +++ b/Source/Engine/Level/Actors/Decal.h @@ -11,7 +11,7 @@ /// /// Actor that draws the can be used to draw a custom decals on top of the other objects. /// -API_CLASS() class FLAXENGINE_API Decal : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Visuals/Decal\")") class FLAXENGINE_API Decal : public Actor { DECLARE_SCENE_OBJECT(Decal); private: diff --git a/Source/Engine/Level/Actors/DirectionalLight.h b/Source/Engine/Level/Actors/DirectionalLight.h index 44a400a13..822005734 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.h +++ b/Source/Engine/Level/Actors/DirectionalLight.h @@ -7,7 +7,7 @@ /// /// Directional light emits light from direction in space. /// -API_CLASS() class FLAXENGINE_API DirectionalLight : public LightWithShadow +API_CLASS(Attributes = "ActorContextMenu(\"New/Lights/Directional Light\")") class FLAXENGINE_API DirectionalLight : public LightWithShadow { DECLARE_SCENE_OBJECT(DirectionalLight); private: diff --git a/Source/Engine/Level/Actors/EmptyActor.h b/Source/Engine/Level/Actors/EmptyActor.h index 00d1cf0a8..1e77816c4 100644 --- a/Source/Engine/Level/Actors/EmptyActor.h +++ b/Source/Engine/Level/Actors/EmptyActor.h @@ -7,7 +7,7 @@ /// /// The empty actor that is useful to create hierarchy and/or hold scripts. See . /// -API_CLASS() class FLAXENGINE_API EmptyActor : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Actor\")") class FLAXENGINE_API EmptyActor : public Actor { DECLARE_SCENE_OBJECT(EmptyActor); public: diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.h b/Source/Engine/Level/Actors/EnvironmentProbe.h index 9284ba328..5991bdaff 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.h +++ b/Source/Engine/Level/Actors/EnvironmentProbe.h @@ -10,7 +10,7 @@ /// /// Environment Probe can capture space around the objects to provide reflections. /// -API_CLASS() class FLAXENGINE_API EnvironmentProbe : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Visuals/Environment Probe\")") class FLAXENGINE_API EnvironmentProbe : public Actor { DECLARE_SCENE_OBJECT(EnvironmentProbe); public: diff --git a/Source/Engine/Level/Actors/ExponentialHeightFog.h b/Source/Engine/Level/Actors/ExponentialHeightFog.h index fe2bf316d..3da3bc570 100644 --- a/Source/Engine/Level/Actors/ExponentialHeightFog.h +++ b/Source/Engine/Level/Actors/ExponentialHeightFog.h @@ -12,7 +12,7 @@ /// /// Used to create fogging effects such as clouds but with a density that is related to the height of the fog. /// -API_CLASS() class FLAXENGINE_API ExponentialHeightFog : public Actor, public IFogRenderer +API_CLASS(Attributes = "ActorContextMenu(\"New/Visuals/Exponential Height Fog\")") class FLAXENGINE_API ExponentialHeightFog : public Actor, public IFogRenderer { DECLARE_SCENE_OBJECT(ExponentialHeightFog); private: diff --git a/Source/Engine/Level/Actors/PointLight.h b/Source/Engine/Level/Actors/PointLight.h index d20fb4ea5..6304175af 100644 --- a/Source/Engine/Level/Actors/PointLight.h +++ b/Source/Engine/Level/Actors/PointLight.h @@ -9,7 +9,7 @@ /// /// Point light emits light from point in all directions. /// -API_CLASS() class FLAXENGINE_API PointLight : public LightWithShadow +API_CLASS(Attributes = "ActorContextMenu(\"New/Lights/Point Light\")") class FLAXENGINE_API PointLight : public LightWithShadow { DECLARE_SCENE_OBJECT(PointLight); private: diff --git a/Source/Engine/Level/Actors/PostFxVolume.h b/Source/Engine/Level/Actors/PostFxVolume.h index bc269eb22..6139b5506 100644 --- a/Source/Engine/Level/Actors/PostFxVolume.h +++ b/Source/Engine/Level/Actors/PostFxVolume.h @@ -9,7 +9,7 @@ /// /// A special type of volume that blends custom set of post process settings into the rendering. /// -API_CLASS() class FLAXENGINE_API PostFxVolume : public BoxVolume, public IPostFxSettingsProvider +API_CLASS(Attributes = "ActorContextMenu(\"New/Visuals/Post Fx Volume\")") class FLAXENGINE_API PostFxVolume : public BoxVolume, public IPostFxSettingsProvider { DECLARE_SCENE_OBJECT(PostFxVolume); private: diff --git a/Source/Engine/Level/Actors/Sky.h b/Source/Engine/Level/Actors/Sky.h index 1d8bbdbf9..69dc88756 100644 --- a/Source/Engine/Level/Actors/Sky.h +++ b/Source/Engine/Level/Actors/Sky.h @@ -14,7 +14,7 @@ class GPUPipelineState; /// /// Sky actor renders atmosphere around the scene with fog and sky. /// -API_CLASS() class FLAXENGINE_API Sky : public Actor, public IAtmosphericFogRenderer, public ISkyRenderer +API_CLASS(Attributes = "ActorContextMenu(\"New/Visuals/Sky\")") class FLAXENGINE_API Sky : public Actor, public IAtmosphericFogRenderer, public ISkyRenderer { DECLARE_SCENE_OBJECT(Sky); private: diff --git a/Source/Engine/Level/Actors/SkyLight.h b/Source/Engine/Level/Actors/SkyLight.h index 239cccd34..649e51d29 100644 --- a/Source/Engine/Level/Actors/SkyLight.h +++ b/Source/Engine/Level/Actors/SkyLight.h @@ -9,7 +9,7 @@ /// /// Sky light captures the distant parts of the scene and applies it as a light. Allows to add ambient light. /// -API_CLASS() class FLAXENGINE_API SkyLight : public Light +API_CLASS(Attributes = "ActorContextMenu(\"New/Lights/Sky Light\")") class FLAXENGINE_API SkyLight : public Light { DECLARE_SCENE_OBJECT(SkyLight); public: diff --git a/Source/Engine/Level/Actors/Skybox.h b/Source/Engine/Level/Actors/Skybox.h index 12faaa3a3..116bf1ca1 100644 --- a/Source/Engine/Level/Actors/Skybox.h +++ b/Source/Engine/Level/Actors/Skybox.h @@ -11,7 +11,7 @@ /// /// Skybox actor renders sky using custom cube texture or material. /// -API_CLASS() class FLAXENGINE_API Skybox : public Actor, public ISkyRenderer +API_CLASS(Attributes = "ActorContextMenu(\"New/Visuals/Sky Box\")") class FLAXENGINE_API Skybox : public Actor, public ISkyRenderer { DECLARE_SCENE_OBJECT(Skybox); private: diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 86f580075..a37d510d4 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -8,7 +8,7 @@ /// /// Spline shape actor that defines spatial curve with utility functions for general purpose usage. /// -API_CLASS() class FLAXENGINE_API Spline : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Spline\")") class FLAXENGINE_API Spline : public Actor { DECLARE_SCENE_OBJECT(Spline); typedef BezierCurveKeyframe Keyframe; diff --git a/Source/Engine/Level/Actors/SpotLight.h b/Source/Engine/Level/Actors/SpotLight.h index 8e2fe0e36..8e8b2717c 100644 --- a/Source/Engine/Level/Actors/SpotLight.h +++ b/Source/Engine/Level/Actors/SpotLight.h @@ -9,7 +9,7 @@ /// /// Spot light emits light from the point in a given direction. /// -API_CLASS() class FLAXENGINE_API SpotLight : public LightWithShadow +API_CLASS(Attributes = "ActorContextMenu(\"New/Lights/Spot Light\")") class FLAXENGINE_API SpotLight : public LightWithShadow { DECLARE_SCENE_OBJECT(SpotLight); private: diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 7ab5263df..ced193bee 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -10,7 +10,7 @@ /// /// Renders model on the screen. /// -API_CLASS() class FLAXENGINE_API StaticModel : public ModelInstanceActor +API_CLASS(Attributes = "ActorContextMenu(\"New/Model\")") class FLAXENGINE_API StaticModel : public ModelInstanceActor { DECLARE_SCENE_OBJECT(StaticModel); private: diff --git a/Source/Engine/Navigation/NavLink.h b/Source/Engine/Navigation/NavLink.h index 50a6cc39e..244ff2f97 100644 --- a/Source/Engine/Navigation/NavLink.h +++ b/Source/Engine/Navigation/NavLink.h @@ -8,7 +8,7 @@ /// The off-mesh link objects used to define a custom point-to-point edge within the navigation graph. /// An off-mesh connection is a user defined traversable connection made up to two vertices, at least one of which resides within a navigation mesh polygon allowing movement outside the navigation mesh. /// -API_CLASS() class FLAXENGINE_API NavLink : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Nav Link\")") class FLAXENGINE_API NavLink : public Actor { DECLARE_SCENE_OBJECT(NavLink); public: diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.h b/Source/Engine/Navigation/NavMeshBoundsVolume.h index 51f2e80eb..e4218a2ca 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.h +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.h @@ -8,7 +8,7 @@ /// /// A special type of volume that defines the area of the scene in which navigation meshes are generated. /// -API_CLASS() class FLAXENGINE_API NavMeshBoundsVolume : public BoxVolume +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Nav Mesh Bounds Volume\")") class FLAXENGINE_API NavMeshBoundsVolume : public BoxVolume { DECLARE_SCENE_OBJECT(NavMeshBoundsVolume); public: diff --git a/Source/Engine/Navigation/NavModifierVolume.h b/Source/Engine/Navigation/NavModifierVolume.h index f06149872..8c7221037 100644 --- a/Source/Engine/Navigation/NavModifierVolume.h +++ b/Source/Engine/Navigation/NavModifierVolume.h @@ -8,7 +8,7 @@ /// /// A special type of volume that defines the area of the scene in which navigation is restricted (eg. higher traversal cost or dynamic obstacle block). /// -API_CLASS() class FLAXENGINE_API NavModifierVolume : public BoxVolume +API_CLASS(Attributes = "ActorContextMenu(\"New/Other/Nav Modifier Volume\")") class FLAXENGINE_API NavModifierVolume : public BoxVolume { DECLARE_SCENE_OBJECT(NavModifierVolume); public: diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index c71775ff7..d0978b92a 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -133,7 +133,7 @@ public: /// /// The particle system instance that plays the particles simulation in the game. /// -API_CLASS() class FLAXENGINE_API ParticleEffect : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/Visuals/Particle Effects\")") class FLAXENGINE_API ParticleEffect : public Actor { DECLARE_SCENE_OBJECT(ParticleEffect); public: diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h index a2ed21948..dd7ab3694 100644 --- a/Source/Engine/Physics/Actors/RigidBody.h +++ b/Source/Engine/Physics/Actors/RigidBody.h @@ -14,7 +14,7 @@ class Collider; /// Physics simulation driven object. /// /// -API_CLASS() class FLAXENGINE_API RigidBody : public Actor, public IPhysicsActor +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Rigid Body\")") class FLAXENGINE_API RigidBody : public Actor, public IPhysicsActor { DECLARE_SCENE_OBJECT(RigidBody); protected: diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h index d49ae33bd..5d6f107e2 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.h +++ b/Source/Engine/Physics/Colliders/BoxCollider.h @@ -9,7 +9,7 @@ /// A box-shaped primitive collider. /// /// -API_CLASS() class FLAXENGINE_API BoxCollider : public Collider +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Box Collider\")") class FLAXENGINE_API BoxCollider : public Collider { DECLARE_SCENE_OBJECT(BoxCollider); private: diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.h b/Source/Engine/Physics/Colliders/CapsuleCollider.h index f62bd4154..8d8c9da78 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.h +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.h @@ -12,7 +12,7 @@ /// Capsules are cylinders with a half-sphere at each end centered at the origin and extending along the X axis, and two hemispherical ends. /// /// -API_CLASS() class FLAXENGINE_API CapsuleCollider : public Collider +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Capsule Collider\")") class FLAXENGINE_API CapsuleCollider : public Collider { DECLARE_SCENE_OBJECT(CapsuleCollider); private: diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 8087d61e8..9169723c5 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -9,7 +9,7 @@ /// Physical objects that allows to easily do player movement constrained by collisions without having to deal with a rigidbody. /// /// -API_CLASS() class FLAXENGINE_API CharacterController : public Collider, public IPhysicsActor +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Character Controller\")") class FLAXENGINE_API CharacterController : public Collider, public IPhysicsActor { DECLARE_SCENE_OBJECT(CharacterController); public: diff --git a/Source/Engine/Physics/Colliders/MeshCollider.h b/Source/Engine/Physics/Colliders/MeshCollider.h index a12746623..a7ecc5bca 100644 --- a/Source/Engine/Physics/Colliders/MeshCollider.h +++ b/Source/Engine/Physics/Colliders/MeshCollider.h @@ -10,7 +10,7 @@ /// A collider represented by an arbitrary mesh. /// /// -API_CLASS() class FLAXENGINE_API MeshCollider : public Collider +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Mesh Collider\")") class FLAXENGINE_API MeshCollider : public Collider { DECLARE_SCENE_OBJECT(MeshCollider); public: diff --git a/Source/Engine/Physics/Colliders/SphereCollider.h b/Source/Engine/Physics/Colliders/SphereCollider.h index 1638300ff..57d4d2898 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.h +++ b/Source/Engine/Physics/Colliders/SphereCollider.h @@ -8,7 +8,7 @@ /// A sphere-shaped primitive collider. /// /// -API_CLASS() class FLAXENGINE_API SphereCollider : public Collider +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Sphere Collider\")") class FLAXENGINE_API SphereCollider : public Collider { DECLARE_SCENE_OBJECT(SphereCollider); private: diff --git a/Source/Engine/Physics/Joints/D6Joint.h b/Source/Engine/Physics/Joints/D6Joint.h index 66819bb0b..b53d3cd28 100644 --- a/Source/Engine/Physics/Joints/D6Joint.h +++ b/Source/Engine/Physics/Joints/D6Joint.h @@ -160,7 +160,7 @@ public: /// It also allows you to constrain limits to only specific axes or completely lock specific axes. /// /// -API_CLASS() class FLAXENGINE_API D6Joint : public Joint +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/D6 Joint\")") class FLAXENGINE_API D6Joint : public Joint { DECLARE_SCENE_OBJECT(D6Joint); private: diff --git a/Source/Engine/Physics/Joints/DistanceJoint.h b/Source/Engine/Physics/Joints/DistanceJoint.h index cb31b8215..d36562796 100644 --- a/Source/Engine/Physics/Joints/DistanceJoint.h +++ b/Source/Engine/Physics/Joints/DistanceJoint.h @@ -37,7 +37,7 @@ DECLARE_ENUM_OPERATORS(DistanceJointFlag); /// Physics joint that maintains an upper or lower (or both) bound on the distance between two bodies. /// /// -API_CLASS() class FLAXENGINE_API DistanceJoint : public Joint +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Distance Joint\")") class FLAXENGINE_API DistanceJoint : public Joint { DECLARE_SCENE_OBJECT(DistanceJoint); private: diff --git a/Source/Engine/Physics/Joints/FixedJoint.h b/Source/Engine/Physics/Joints/FixedJoint.h index 9e6280023..ce617aa40 100644 --- a/Source/Engine/Physics/Joints/FixedJoint.h +++ b/Source/Engine/Physics/Joints/FixedJoint.h @@ -8,7 +8,7 @@ /// Physics joint that maintains a fixed distance and orientation between its two attached bodies. /// /// -API_CLASS() class FLAXENGINE_API FixedJoint : public Joint +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Fixed Joint\")") class FLAXENGINE_API FixedJoint : public Joint { DECLARE_SCENE_OBJECT(FixedJoint); public: diff --git a/Source/Engine/Physics/Joints/HingeJoint.h b/Source/Engine/Physics/Joints/HingeJoint.h index 14e3198e5..fc0719466 100644 --- a/Source/Engine/Physics/Joints/HingeJoint.h +++ b/Source/Engine/Physics/Joints/HingeJoint.h @@ -75,7 +75,7 @@ public: /// Physics joint that removes all but a single rotation degree of freedom from its two attached bodies (for example a door hinge). /// /// -API_CLASS() class FLAXENGINE_API HingeJoint : public Joint +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Hinge Joint\")") class FLAXENGINE_API HingeJoint : public Joint { DECLARE_SCENE_OBJECT(HingeJoint); private: diff --git a/Source/Engine/Physics/Joints/SliderJoint.h b/Source/Engine/Physics/Joints/SliderJoint.h index b909ee925..bab3fcc75 100644 --- a/Source/Engine/Physics/Joints/SliderJoint.h +++ b/Source/Engine/Physics/Joints/SliderJoint.h @@ -27,7 +27,7 @@ DECLARE_ENUM_OPERATORS(SliderJointFlag); /// Physics joint that removes all but a single translational degree of freedom. Bodies are allowed to move along a single axis. /// /// -API_CLASS() class FLAXENGINE_API SliderJoint : public Joint +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Slider Joint\")") class FLAXENGINE_API SliderJoint : public Joint { DECLARE_SCENE_OBJECT(SliderJoint); private: diff --git a/Source/Engine/Physics/Joints/SphericalJoint.h b/Source/Engine/Physics/Joints/SphericalJoint.h index 328575fa2..591ca1357 100644 --- a/Source/Engine/Physics/Joints/SphericalJoint.h +++ b/Source/Engine/Physics/Joints/SphericalJoint.h @@ -29,7 +29,7 @@ DECLARE_ENUM_OPERATORS(SphericalJointFlag); /// rotate around the anchor points, and their rotation can be limited by an elliptical cone. /// /// -API_CLASS() class FLAXENGINE_API SphericalJoint : public Joint +API_CLASS(Attributes = "ActorContextMenu(\"New/Physics/Spherical Joint\")") class FLAXENGINE_API SphericalJoint : public Joint { DECLARE_SCENE_OBJECT(SphericalJoint); private: diff --git a/Source/Engine/Scripting/Attributes/Editor/ActorContextMenu.cs b/Source/Engine/Scripting/Attributes/Editor/ActorContextMenu.cs new file mode 100644 index 000000000..aab4ed92d --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/ActorContextMenu.cs @@ -0,0 +1,26 @@ +using System; + +namespace FlaxEngine +{ + /// + /// This attribute is used to show actors that can be created in the scene and prefab context menus. Separate the subcontext menus with a /. + /// + [Serializable] + [AttributeUsage(AttributeTargets.Class)] + public class ActorContextMenuAttribute : Attribute + { + /// + /// The path to be used in the context menu + /// + public string Path; + + /// + /// Initializes a new instance of the class. + /// + /// The path to use to create the context menu + public ActorContextMenuAttribute(string path) + { + Path = path; + } + } +} diff --git a/Source/Engine/UI/SpriteRender.h b/Source/Engine/UI/SpriteRender.h index bb976da5e..32a2d8b64 100644 --- a/Source/Engine/UI/SpriteRender.h +++ b/Source/Engine/UI/SpriteRender.h @@ -10,7 +10,7 @@ /// /// Sprite rendering object. /// -API_CLASS() class FLAXENGINE_API SpriteRender : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/UI/Sprite Render\")") class FLAXENGINE_API SpriteRender : public Actor { DECLARE_SCENE_OBJECT(SpriteRender); private: diff --git a/Source/Engine/UI/TextRender.h b/Source/Engine/UI/TextRender.h index 82858b580..695df7c01 100644 --- a/Source/Engine/UI/TextRender.h +++ b/Source/Engine/UI/TextRender.h @@ -19,7 +19,7 @@ /// /// Text rendering object. /// -API_CLASS() class FLAXENGINE_API TextRender : public Actor +API_CLASS(Attributes = "ActorContextMenu(\"New/UI/Text Render\")") class FLAXENGINE_API TextRender : public Actor { DECLARE_SCENE_OBJECT(TextRender); private: diff --git a/Source/Engine/UI/UICanvas.h b/Source/Engine/UI/UICanvas.h index 4eff6cebc..1d1aefb7d 100644 --- a/Source/Engine/UI/UICanvas.h +++ b/Source/Engine/UI/UICanvas.h @@ -7,7 +7,7 @@ /// /// Root of the UI structure. Renders GUI and handles input events forwarding. /// -API_CLASS(Sealed, NoConstructor) class FLAXENGINE_API UICanvas : public Actor +API_CLASS(Sealed, NoConstructor, Attributes = "ActorContextMenu(\"New/UI/UI Canvas\")") class FLAXENGINE_API UICanvas : public Actor { DECLARE_SCENE_OBJECT(UICanvas); public: diff --git a/Source/Engine/UI/UIControl.h b/Source/Engine/UI/UIControl.h index 27de5d855..2de1a5455 100644 --- a/Source/Engine/UI/UIControl.h +++ b/Source/Engine/UI/UIControl.h @@ -8,7 +8,7 @@ /// /// Contains a single GUI control (on C# side). /// -API_CLASS(Sealed) class FLAXENGINE_API UIControl : public Actor +API_CLASS(Sealed, Attributes = "ActorContextMenu(\"New/UI/UI Control\")") class FLAXENGINE_API UIControl : public Actor { DECLARE_SCENE_OBJECT(UIControl); private: