4#include "imgui/imgui.h"
5#include "imgui_widgets/tooltips.h"
6#include <imgui/imgui_internal.h>
37bool prev_edit_label{};
40auto update_editing() ->
void
42 prev_edit_label = edit_label_;
45auto is_just_started_editing_label() ->
bool
47 return edit_label_ && edit_label_ != prev_edit_label;
50auto is_editing_label() ->
bool
77 entt::handle parent_entity,
78 const std::string& action_name,
80 bool start_label_edit =
true)
83 em.push_undo_stack_enabled(
true);
84 em.queue_action<create_entities_action_t>(
86 [&ctx, parent_uh =
entt::make_uhandle(parent_entity), producer = std::move(producer), start_label_edit]() -> entt::handle
88 auto& em = ctx.get_cached<editing_manager>();
89 auto* active_scene = em.get_active_scene(ctx);
95 auto new_entity = producer(ctx, *active_scene);
103 if(
auto parent = parent_uh.resolve())
105 if(
auto* tr = new_entity.try_get<transform_component>())
107 tr->set_parent(parent,
false);
113 start_editing_label(ctx, new_entity);
117 em.select(new_entity);
121 em.pop_undo_stack_enabled();
124void create_empty_entity(
rtti::context& ctx, entt::handle parent_entity)
126 queue_create_with_parent(ctx, parent_entity,
"Create Entity",
129 return scn.create_entity();
133void create_empty_parent_entity(
rtti::context& ctx, entt::handle child_entity)
141 auto current_parent = child_entity.get<transform_component>().get_parent();
144 auto created_uh = std::make_shared<entt::uhandle>();
146 auto seq = std::make_shared<sequence_action_t>();
152 [&ctx, current_parent, created_uh]() -> std::shared_ptr<editing_action_t>
154 return std::make_shared<create_entities_action_t>(
155 [&ctx, current_parent, created_uh]() -> entt::handle
158 auto* active_scene = em.get_active_scene(ctx);
163 auto new_entity = active_scene->create_entity();
170 new_entity.get<transform_component>().set_parent(current_parent,
false);
173 start_editing_label(ctx, new_entity);
182 [child_entity, current_parent, created_uh]() -> std::shared_ptr<editing_action_t>
184 auto new_wrapper = created_uh->resolve();
189 return std::make_shared<transform_set_parent_action_t>(child_entity, current_parent, new_wrapper);
192 em.push_undo_stack_enabled(
true);
193 em.queue_action(
"Create Parent Entity",
seq);
194 em.pop_undo_stack_enabled();
198void create_mesh_entity(
rtti::context& ctx, entt::handle parent_entity,
const std::string& mesh_name)
200 queue_create_with_parent(ctx, parent_entity,
"Create Mesh Entity",
207void create_text_entity(
rtti::context& ctx, entt::handle parent_entity)
209 queue_create_with_parent(ctx, parent_entity,
"Create Text Entity",
216void create_particle_emitter_entity(
rtti::context& ctx, entt::handle parent_entity)
218 queue_create_with_parent(ctx, parent_entity,
"Create Particle Emitter Entity",
227 queue_create_with_parent(ctx, parent_entity,
"Create Light Entity",
236 queue_create_with_parent(ctx, parent_entity,
"Create Reflection Probe Entity",
243void create_camera_entity(
rtti::context& ctx, entt::handle parent_entity)
245 queue_create_with_parent(ctx, parent_entity,
"Create Camera Entity",
252void create_volume_entity(
rtti::context& ctx, entt::handle parent_entity)
254 queue_create_with_parent(ctx, parent_entity,
"Create Volume Entity",
261void create_audio_source_entity(
rtti::context& ctx, entt::handle parent_entity)
263 queue_create_with_parent(ctx, parent_entity,
"Create Audio Source Entity",
270void create_ui_document_entity(
rtti::context& ctx, entt::handle parent_entity)
272 queue_create_with_parent(ctx, parent_entity,
"Create UI Document Entity",
279void create_terrain_entity(
rtti::context& ctx, entt::handle parent_entity)
281 queue_create_with_parent(ctx, parent_entity,
"Create Terrain Entity",
292auto process_drag_drop_source(entt::handle
entity) ->
bool
294 if(
entity && ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
297 ImGui::SetDragDropPayload(
"entity", &
entity,
sizeof(
entity));
298 ImGui::EndDragDropSource();
305void handle_entity_drop(
rtti::context& ctx, entt::handle target_entity, entt::handle dropped_entity)
309 auto do_action = [&](entt::handle dropped)
312 auto action = std::make_shared<transform_set_parent_action_t>(dropped, dropped.get<transform_component>().get_parent(), target_entity);
313 em.push_undo_stack_enabled(
true);
314 em.queue_action(
"", std::move(action));
315 em.pop_undo_stack_enabled();
318 if(em.is_selected(dropped_entity))
320 for(
auto e : em.try_get_selections_as<entt::handle>())
330 do_action(dropped_entity);
334void handle_mesh_drop(
rtti::context& ctx,
const std::string& absolute_path)
336 queue_create_with_parent(ctx, entt::handle{},
"Drop Mesh",
345void handle_prefab_drop(
rtti::context& ctx,
const std::string& absolute_path)
347 queue_create_with_parent(ctx, entt::handle{},
"Drop Prefab",
358 if(!ImGui::BeginDragDropTarget())
363 if(ImGui::IsDragDropPayloadBeingAccepted())
365 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
369 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
373 auto payload = ImGui::AcceptDragDropPayload(
"entity");
374 if(payload !=
nullptr)
376 entt::handle dropped{};
377 std::memcpy(&dropped, payload->Data,
size_t(payload->DataSize));
380 handle_entity_drop(ctx,
entity, dropped);
387 auto mesh_payload = ImGui::AcceptDragDropPayload(
type.c_str());
388 if(mesh_payload !=
nullptr)
390 std::string absolute_path(
reinterpret_cast<const char*
>(mesh_payload->Data), std::size_t(mesh_payload->DataSize));
391 handle_mesh_drop(ctx, absolute_path);
398 auto prefab_payload = ImGui::AcceptDragDropPayload(
type.c_str());
399 if(prefab_payload !=
nullptr)
401 std::string absolute_path(
reinterpret_cast<const char*
>(prefab_payload->Data), std::size_t(prefab_payload->DataSize));
402 handle_prefab_drop(ctx, absolute_path);
406 ImGui::EndDragDropTarget();
411 if(!process_drag_drop_source(
entity))
413 process_drag_drop_target(ctx,
entity);
421void draw_3d_objects_menu(
rtti::context& ctx, entt::handle parent_entity)
428 static const std::vector<std::pair<std::string, std::vector<std::string>>> menu_objects = {
430 {
"Cube Rounded", {
"Cube Rounded"}},
431 {
"Sphere", {
"Sphere"}},
432 {
"Plane", {
"Plane"}},
433 {
"Cylinder", {
"Cylinder"}},
434 {
"Capsule_1m", {
"Capsule_1m"}},
435 {
"Capsule_2m", {
"Capsule_2m"}},
437 {
"Torus", {
"Torus"}},
438 {
"Teapot", {
"Teapot"}},
440 {
"Polygon", {
"Icosahedron",
"Dodecahedron"}},
441 {
"Icosphere", {
"Icosphere0",
"Icosphere1",
"Icosphere2",
"Icosphere3",
"Icosphere4",
442 "Icosphere5",
"Icosphere6",
"Icosphere7",
"Icosphere8",
"Icosphere9",
443 "Icosphere10",
"Icosphere11",
"Icosphere12",
"Icosphere13",
"Icosphere14",
444 "Icosphere15",
"Icosphere16",
"Icosphere17",
"Icosphere18",
"Icosphere19"}}};
446 for(
const auto& p : menu_objects)
448 const auto&
name =
p.first;
449 const auto& objects_name =
p.second;
451 if(
name ==
"Separator")
455 else if(
name ==
"New Line")
459 else if(objects_name.size() == 1)
461 if(ImGui::MenuItem(
name.c_str()))
463 create_mesh_entity(ctx, parent_entity,
name);
468 if(ImGui::BeginMenu(
name.c_str()))
470 for(
const auto& n : objects_name)
472 if(ImGui::MenuItem(
n.c_str()))
474 create_mesh_entity(ctx, parent_entity, n);
485 if(ImGui::MenuItem(
"Text"))
487 create_text_entity(ctx, parent_entity);
493 if(ImGui::MenuItem(
"Terrain"))
495 create_terrain_entity(ctx, parent_entity);
501void draw_lighting_menu(
rtti::context& ctx, entt::handle parent_entity)
509 if(ImGui::BeginMenu(
"Light"))
511 static const std::vector<std::pair<std::string, light_type>> light_objects = {
516 for(
const auto& p : light_objects)
518 const auto&
name =
p.first;
519 const auto&
type =
p.second;
520 if(ImGui::MenuItem(
name.c_str()))
522 create_light_entity(ctx, parent_entity,
type,
name);
529 if(ImGui::BeginMenu(
"Reflection Probes"))
531 static const std::vector<std::pair<std::string, probe_type>> reflection_probes = {
535 for(
const auto& p : reflection_probes)
537 const auto&
name =
p.first;
538 const auto&
type =
p.second;
540 if(ImGui::MenuItem(
name.c_str()))
542 create_reflection_probe_entity(ctx, parent_entity,
type,
name);
551void draw_common_menu_items(
rtti::context& ctx, entt::handle parent_entity)
555 create_empty_entity(ctx, parent_entity);
558 draw_3d_objects_menu(ctx, parent_entity);
559 draw_lighting_menu(ctx, parent_entity);
563 create_camera_entity(ctx, parent_entity);
568 create_volume_entity(ctx, parent_entity);
573 create_audio_source_entity(ctx, parent_entity);
578 create_particle_emitter_entity(ctx, parent_entity);
583 create_ui_document_entity(ctx, parent_entity);
587void draw_entity_context_menu(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
589 if(ImGui::BeginPopupContextItem(
"Entity Context Menu"))
596 create_empty_parent_entity(ctx,
entity);
599 draw_common_menu_items(ctx,
entity);
606 em.queue_action(
"Rename Entity",
609 start_editing_label(ctx,
entity);
617 panels->get_scene_panel().duplicate_entities({
entity});
622 panels->get_scene_panel().delete_entities({
entity});
627 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
632 if(
entity.any_of<prefab_component, prefab_id_component>())
637 em.queue_action(
"Open Prefab",
638 [&ctx,
entity, panels]()
mutable
643 auto prefab = prefab_root.get<prefab_component>().source;
647 em.enter_prefab_mode(ctx, prefab,
true);
656 em.queue_action(
"Unlink from Prefab",
659 entity.remove<prefab_component, prefab_id_component>();
668void draw_window_context_menu(
rtti::context& ctx, imgui_panels* panels)
670 if(ImGui::BeginPopupContextWindow(
nullptr, ImGuiPopupFlags_MouseButtonRight))
675 draw_common_menu_items(ctx, {});
685 draw_entity_context_menu(ctx, panels,
entity);
689 draw_window_context_menu(ctx, panels);
697void draw_activity(
rtti::context& ctx, transform_component& trans_comp)
699 bool is_active_local = trans_comp.is_active();
702 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
707 trans_comp.set_active(!is_active_local);
709 auto entity = trans_comp.get_owner();
712 em.push_undo_stack_enabled(
true);
714 em.queue_action<entity_set_active_action_t>({},
719 em.pop_undo_stack_enabled();
725 ImGui::PopStyleColor();
732 auto focus = em.try_get_active_focus_as<entt::handle>();
744auto get_entity_tree_node_flags(
rtti::context& ctx, entt::handle
entity,
bool has_children) -> ImGuiTreeNodeFlags
747 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_OpenOnArrow;
749 if(em.is_selected(
entity))
751 flags |= ImGuiTreeNodeFlags_Selected;
756 flags |= ImGuiTreeNodeFlags_Leaf;
759 flags |= ImGuiTreeNodeFlags_DrawLinesToNodes;
764auto get_entity_display_label(entt::handle
entity) -> std::string
769 const auto ent =
entity.entity();
770 const auto id = entt::to_integral(ent);
772 return icon +
name +
"###" + std::to_string(
id);
778 auto mode = em.get_select_mode();
779 em.queue_action(
"Select Entity",
780 [&ctx,
entity, mode]()
mutable
782 stop_editing_label(ctx,
entity);
788void handle_entity_keyboard_shortcuts(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
793 em.queue_action(
"Rename Entity",
796 start_editing_label(ctx,
entity);
802 panels->get_scene_panel().delete_entities({
entity});
807 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
812 panels->get_scene_panel().duplicate_entities({
entity});
816void handle_entity_mouse_interactions(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity,
bool is_item_clicked_middle,
bool is_item_double_clicked_left)
818 if(is_item_clicked_middle)
820 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
823 if(is_item_double_clicked_left)
825 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
829void draw_entity_name_editor(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity,
const ImVec2& pos)
832 if(!em.is_selected(
entity) || !is_editing_label())
837 if(is_just_started_editing_label())
839 ImGui::SetKeyboardFocusHere();
842 ImGui::SetCursorScreenPos(pos);
843 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().
x);
846 auto old_name = edit_name;
847 ImGui::InputTextWidget(
"##rename", edit_name,
false, ImGuiInputTextFlags_AutoSelectAll);
849 if(ImGui::IsItemDeactivatedAfterEdit())
853 em.push_undo_stack_enabled(
true);
854 em.queue_action<entity_set_name_action_t>({},
858 em.pop_undo_stack_enabled();
859 stop_editing_label(ctx,
entity);
862 ImGui::PopItemWidth();
864 if(ImGui::IsItemDeactivated())
866 stop_editing_label(ctx,
entity);
878 ImGui::PushID(
static_cast<int>(
entity.entity()));
880 auto& trans_comp =
entity.get<transform_component>();
881 bool has_children = !trans_comp.get_children().empty();
883 ImGuiTreeNodeFlags flags = get_entity_tree_node_flags(ctx,
entity, has_children);
885 if(is_parent_of_focused(ctx,
entity))
887 ImGui::SetNextItemOpen(
true, 0);
891 auto pos = ImGui::GetCursorScreenPos() + ImVec2(ImGui::GetTextLineHeightWithSpacing(), 0.0f);
892 ImGui::AlignTextToFramePadding();
894 auto label = get_entity_display_label(
entity);
897 ImGui::PushStyleColor(ImGuiCol_Text, col);
898 ImGui::PushStyleVarX(ImGuiStyleVar_ItemInnerSpacing, 0.0f);
899 bool opened = ImGui::TreeNodeEx(label.c_str(), flags);
900 ImGui::PopStyleVar();
902 if(ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
904 const auto ent =
entity.entity();
905 const auto idx = entt::to_entity(ent);
906 const auto ver = entt::to_version(ent);
907 const auto id = entt::to_integral(ent);
909 ImGui::SetItemTooltipEx(
"Id: %d\nIndex: %d\nVersion: %d",
id, idx, ver);
912 ImGui::PopStyleColor();
916 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
918 if(!ImGui::IsItemVisible())
920 ImGui::SetScrollHereY();
925 if(!is_editing_label())
928 check_context_menu(ctx, panels,
entity);
932 bool is_item_focus_changed = ImGui::IsItemFocusChanged();
933 bool is_item_released_left = ImGui::IsItemReleased(ImGuiMouseButton_Left);
934 bool is_item_clicked_middle = ImGui::IsItemClicked(ImGuiMouseButton_Middle);
935 bool is_item_double_clicked_left = ImGui::IsItemDoubleClicked(ImGuiMouseButton_Left);
936 bool activity_hovered =
false;
939 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
940 ImGui::AlignedItem(1.0f,
941 ImGui::GetContentRegionAvail().
x - ImGui::GetStyle().FramePadding.x,
942 ImGui::GetFrameHeight(),
945 draw_activity(ctx, trans_comp);
946 activity_hovered = ImGui::IsItemHovered();
950 if(!activity_hovered)
952 if(is_item_released_left || is_item_focus_changed)
954 handle_entity_selection(ctx,
entity);
957 if(em.is_selected(
entity))
959 handle_entity_mouse_interactions(ctx, panels,
entity, is_item_clicked_middle, is_item_double_clicked_left);
960 handle_entity_keyboard_shortcuts(ctx, panels,
entity);
965 draw_entity_name_editor(ctx, panels,
entity, pos);
972 const auto&
children = trans_comp.get_children();
977 draw_entity(ctx, panels, child);
1002void hierarchy_panel::draw_prefab_mode_header(
rtti::context& ctx)
const
1006 if(!em.is_prefab_mode())
1011 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
1016 ImGui::PopStyleColor();
1018 if (em.edited_prefab)
1021 ImGui::Text(
"Editing Prefab: %s", fs::path(em.edited_prefab.id()).filename().string().c_str());
1027auto hierarchy_panel::get_scene_display_name(
const editing_manager& em,
scene* target_scene)
const -> std::string
1031 if (em.is_prefab_mode())
1033 name = fs::path(em.edited_prefab.id()).filename().string();
1041 name = target_scene->source.name();
1048 if(em.has_unsaved_changes())
1057void hierarchy_panel::draw_scene_hierarchy(
rtti::context& ctx)
const
1059 auto& em = ctx.
get_cached<editing_manager>();
1060 scene* target_scene = em.get_active_scene(ctx);
1067 std::string scene_name = get_scene_display_name(em, target_scene);
1069 ImGui::SetNextItemOpen(
true, ImGuiCond_Appearing);
1070 if(ImGui::CollapsingHeader(scene_name.c_str()))
1074 target_scene->registry->sort<root_component>(
1075 [](
auto const& lhs,
auto const& rhs)
1078 return lhs.order < rhs.order;
1085 target_scene->registry->view<root_component, transform_component>().each(
1086 [&](
auto e,
auto&& root,
auto&& comp)
1088 draw_entity(ctx,
parent_, comp.get_owner());
1092 handle_window_empty_click(ctx);
1095void hierarchy_panel::handle_window_empty_click(
rtti::context& ctx)
const
1097 auto& em = ctx.
get_cached<editing_manager>();
1098 if(ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
1100 if(!ImGui::IsAnyItemHovered())
1120 draw_prefab_mode_header(ctx);
1122 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
1123 ImGuiWindowFlags_NoSavedSettings;
1125 if(ImGui::BeginChild(
"hierarchy_content", ImGui::GetContentRegionAvail(), 0, flags))
1127 check_context_menu(ctx,
parent_, {});
1128 draw_scene_hierarchy(ctx);
1132 check_drag(ctx, {});
static auto get_entity_icon(entt::handle entity) -> std::string
static auto get_entity_name(entt::handle entity) -> std::string
Gets the entity name from tag component.
static auto get_entity_display_color(entt::handle entity) -> ImVec4
void on_after_render(rtti::context &ctx) override
void draw_ui(rtti::context &ctx) override
void init(rtti::context &ctx)
hierarchy_panel(imgui_panels *parent, const char *name)
auto get_window_flags() const -> ImGuiWindowFlags override
#define ICON_MDI_VOLUME_HIGH
#define ICON_MDI_OPEN_IN_NEW
#define ICON_MDI_ARRANGE_BRING_FORWARD
#define ICON_MDI_LINK_OFF
#define ICON_MDI_CONTENT_COPY
#define ICON_MDI_FILE_DOCUMENT
#define ICON_MDI_LIGHTBULB_ON
#define ICON_MDI_CROSSHAIRS_GPS
#define ICON_MDI_PLUS_BOX_OUTLINE
#define ICON_MDI_KEYBOARD_RETURN
auto BeginMenuIcon(const char *icon, const char *label, bool enabled) -> bool
auto MenuItemIcon(const char *icon, const char *label, const char *shortcut, bool enabled) -> bool
auto make_uhandle(entt::handle handle) -> uhandle
auto get_type() -> const std::string &
auto get_suported_formats() -> const std::vector< std::string > &
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
Provides a sequence-based action management system for controlling and scheduling actions.
constexpr ImGuiKey delete_item
const ImGuiKeyCombination duplicate_item
constexpr ImGuiKey rename_item
constexpr ImGuiKey focus_selected
auto is_roots_order_changed() -> bool
void reset_roots_order_changed()
probe_type
Enum class representing the type of reflection probe.
@ sphere
Sphere type reflection probe.
@ box
Box type reflection probe.
light_type
Enum representing the type of light.
static auto create_reflection_probe_entity(rtti::context &ctx, scene &scn, probe_type type, const std::string &name) -> entt::handle
Creates a reflection probe entity.
static auto create_ui_document_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a UI document entity.
static auto create_text_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a text entity.
static auto create_audio_source_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a audio source entity.
static auto create_terrain(rtti::context &ctx, scene &scn, math::vec3 pos={0.0f, 0.0f, 0.0f}) -> entt::handle
Creates a test heightfield terrain entity (embedded procedural mesh).
static auto create_volume_entity(rtti::context &ctx, scene &scn, const std::string &name, volume_mode mode=volume_mode::local) -> entt::handle
Creates a post process volume entity.
static auto create_embedded_mesh_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates an embedded mesh entity.
static auto create_mesh_entity_at(rtti::context &ctx, scene &scn, const std::string &key, const camera &cam, math::vec2 pos, bool align_to_surface=false) -> entt::handle
Creates a mesh entity at a specified position.
static auto create_particle_emitter_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a particle emitter entity.
static auto create_light_entity(rtti::context &ctx, scene &scn, light_type type, const std::string &name) -> entt::handle
Creates a light entity.
static auto create_camera_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a camera entity.
static auto create_prefab_at(rtti::context &ctx, scene &scn, const std::string &key, const camera &cam, math::vec2 pos, bool align_to_surface=false) -> entt::handle
Creates a prefab entity at a specified position.
static auto find_prefab_root_entity(entt::handle entity) -> entt::handle
Finds the prefab root entity by traversing up the parent hierarchy.