2#include "entt/meta/meta.hpp"
18#include <hpp/type_name.hpp>
19#include <hpp/utility.hpp>
20#include <hpp/finally.hpp>
23#include <imgui/imgui.h>
24#include <imgui/imgui_internal.h>
31auto get_component_icon() -> std::string
34 if constexpr(std::is_same<T, id_component>::value)
38 else if constexpr(std::is_same<T, tag_component>::value)
42 else if constexpr(std::is_same<T, layer_component>::value)
46 else if constexpr(std::is_same<T, prefab_component>::value)
50 else if constexpr(std::is_same<T, prefab_id_component>::value)
55 else if constexpr(std::is_same<T, transform_component>::value)
60 else if constexpr(std::is_same<T, test_component>::value)
65 else if constexpr(std::is_same<T, model_component>::value)
69 else if constexpr(std::is_same<T, submesh_component>::value)
73 else if constexpr(std::is_same<T, camera_component>::value)
77 else if constexpr(std::is_same<T, text_component>::value)
82 else if constexpr(std::is_same<T, animation_component>::value)
86 else if constexpr(std::is_same<T, bone_component>::value)
91 else if constexpr(std::is_same<T, light_component>::value)
95 else if constexpr(std::is_same<T, skylight_component>::value)
99 else if constexpr(std::is_same<T, reflection_probe_component>::value)
104 else if constexpr(std::is_same<T, physics_component>::value)
109 else if constexpr(std::is_same<T, audio_source_component>::value)
113 else if constexpr(std::is_same<T, audio_listener_component>::value)
118 else if constexpr(std::is_same<T, script_component>::value)
123 else if constexpr(std::is_same<T, auto_exposure_component>::value)
127 else if constexpr(std::is_same<T, bloom_component>::value)
131 else if constexpr(std::is_same<T, tonemapping_component>::value)
135 else if constexpr(std::is_same<T, fxaa_component>::value)
139 else if constexpr(std::is_same<T, assao_component>::value)
143 else if constexpr(std::is_same<T, ssr_component>::value)
147 else if constexpr(std::is_same<T, volume_component>::value)
158struct inspect_callbacks
169auto inspect_component(
const std::string&
name,
const inspect_callbacks& callbacks) -> inspect_result
171 inspect_result result{};
175 ImGui::PushID(
name.c_str());
177 auto popup_str =
"COMPONENT_SETTING";
179 bool open_popup =
false;
181 if(!callbacks.can_merge())
183 ImGui::SetNextItemOpen(
true, ImGuiCond_FirstUseEver);
185 auto pos = ImGui::GetCursorPos();
186 auto col_header = ImGui::GetColorU32(ImGuiCol_Header);
187 auto col_header_hovered = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
188 auto col_header_active = ImGui::GetColorU32(ImGuiCol_HeaderActive);
190 auto col_framebg = ImGui::GetColorU32(ImGuiCol_FrameBg);
191 auto col_framebg_hovered = ImGui::GetColorU32(ImGuiCol_FrameBgHovered);
192 auto col_framebg_active = ImGui::GetColorU32(ImGuiCol_FrameBgActive);
194 ImGui::PushStyleColor(ImGuiCol_Header, col_framebg);
195 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, col_framebg_hovered);
196 ImGui::PushStyleColor(ImGuiCol_HeaderActive, col_framebg_active);
199 open = ImGui::CollapsingHeader(fmt::format(
" {}",
name).c_str(),
nullptr, ImGuiTreeNodeFlags_AllowOverlap);
202 ImGui::OpenPopupOnItemClick(popup_str);
203 ImGui::PopStyleColor(3);
205 ImGui::SetCursorPos(pos);
206 ImGui::AlignTextToFramePadding();
207 ImGui::Text(
" %s", callbacks.icon.c_str());
210 auto settings_size = ImGui::CalcTextSize(
ICON_MDI_COG).x + ImGui::GetStyle().FramePadding.x * 2.0f;
212 auto avail = ImGui::GetContentRegionAvail().x + ImGui::GetStyle().FramePadding.x;
213 ImGui::AlignedItem(1.0f,
227 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
228 ImGui::TreePush(
name.c_str());
230 result |= callbacks.on_inspect();
233 ImGui::PopStyleVar();
237 ImGui::OpenPopup(popup_str);
240 bool is_popup_open = ImGui::IsPopupOpen(popup_str);
241 if(is_popup_open && ImGui::BeginPopup(popup_str))
246 bool removal_allowed = callbacks.can_remove();
249 callbacks.on_remove();
252 result.changed =
true;
253 result.edit_finished =
true;
259 callbacks.on_remove();
260 result.changed =
true;
261 result.edit_finished =
true;
270 callbacks.on_remove();
271 result.changed =
true;
272 result.edit_finished =
true;
278auto list_component(ImGuiTextFilter& filter,
const std::string&
name,
const inspect_callbacks& callbacks)
281 inspect_result result{};
282 if(!filter.PassFilter(
name.c_str()))
287 if(ImGui::Selectable(fmt::format(
"{} {}", callbacks.icon,
name).c_str()))
289 callbacks.on_remove();
292 result.changed =
true;
293 result.edit_finished =
true;
295 ImGui::CloseCurrentPopup();
299auto get_entity_pretty_name(entt::handle
entity) ->
const std::string&
303 static const std::string empty =
"None (Entity)";
306 auto&
tag =
entity.get_or_emplace<tag_component>();
310auto process_drag_drop_target(
rtti::context& ctx, entt::handle& obj) ->
bool
312 if(ImGui::IsDragDropPossibleTargetForType(
"entity"))
314 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
319 if(ImGui::BeginDragDropTarget())
321 if(ImGui::IsDragDropPayloadBeingAccepted())
323 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
327 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
331 auto payload = ImGui::AcceptDragDropPayload(
"entity");
332 if(payload !=
nullptr)
334 entt::handle dropped{};
335 std::memcpy(&dropped, payload->Data,
size_t(payload->DataSize));
344 ImGui::EndDragDropTarget();
350auto render_entity_header(
rtti::context& ctx, entt::handle data, prefab_override_context& override_ctx) -> inspect_result
352 inspect_result result{};
359 auto tag_comp = data.try_get<tag_component>();
360 auto trans_comp = data.try_get<transform_component>();
368 if(ImGui::BeginTable(
"EntityHeader", 3, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoClip))
370 ImGui::TableSetupColumn(
"Active", ImGuiTableColumnFlags_WidthFixed, 20.0f);
371 ImGui::TableSetupColumn(
"Icon", ImGuiTableColumnFlags_WidthFixed, 22.0f);
372 ImGui::TableSetupColumn(
"Name", ImGuiTableColumnFlags_WidthStretch);
374 ImGui::TableNextRow();
377 ImGui::TableSetColumnIndex(0);
380 bool is_active = trans_comp->is_active();
383 auto type = entt::resolve<transform_component>();
386 auto prop =
type.data(
"active"_hs);
390 override_ctx.set_component_type(
name, pretty_name);
391 override_ctx.push_segment(prop_name, prop_pretty_name);
393 bool old_active = is_active;
394 if(ImGui::Checkbox(
"##active", &is_active))
396 result.changed =
true;
397 result.edit_finished =
true;
399 auto& em = ctx.get_cached<editing_manager>();
400 em.do_action<entity_set_active_action_t>({},
406 override_ctx.pop_segment();
410 ImGui::PushStyleColor(ImGuiCol_Text, col);
412 ImGui::TableSetColumnIndex(1);
413 ImGui::AlignTextToFramePadding();
417 ImGui::Text(
"%s",
icon.c_str());
420 ImGui::TableSetColumnIndex(2);
422 auto type = entt::resolve<tag_component>();
425 auto prop =
type.data(
"name"_hs);
429 override_ctx.set_component_type(type_name, pretty_name);
430 override_ctx.push_segment(prop_name, prop_pretty_name);
432 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
433 ImGui::SetNextItemWidth(-1.0f);
435 auto old_name = tag_comp->name;
436 if(ImGui::InputTextWidget(
"##name", tag_comp->name,
false))
438 result.changed =
true;
439 result.edit_finished =
true;
441 auto& em = ctx.get_cached<editing_manager>();
443 em.do_action<entity_set_name_action_t>({},
449 ImGui::PopStyleVar();
450 override_ctx.pop_segment();
452 ImGui::PopStyleColor();
459 auto type = entt::resolve<tag_component>();
463 auto prop =
type.data(
"tag"_hs);
467 override_ctx.set_component_type(type_name, pretty_name);
468 override_ctx.push_segment(prop_name, prop_pretty_name);
470 property_layout layout(prop, tag_comp,
true);
473 info.is_property =
true;
474 info.read_only =
false;
476 auto old_tag = tag_comp->tag;
478 auto v_var = entt::forward_as_meta(tag_comp->tag);
481 if(var_result.changed)
483 auto& em = ctx.get_cached<editing_manager>();
485 em.do_action<entity_set_tag_action_t>({},
492 result |= var_result;
494 override_ctx.pop_segment();
504 auto name = get_entity_pretty_name(data);
508 if(ImGui::Button(
ICON_MDI_DELETE, ImVec2(0.0f, ImGui::GetFrameHeight())))
520 if(ImGui::Button(
id.c_str(), ImVec2(ImGui::GetContentRegionAvail().
x, ImGui::GetFrameHeight())))
527 ImGui::SetItemTooltipEx(
"%s",
id.c_str());
529 bool drag_dropped = process_drag_drop_target(ctx, data);
530 result.
changed |= drag_dropped;
543 auto data = var.cast<entt::handle>();
551 auto cleanup = hpp::finally([&]()
559 result = inspect_as_property(ctx, data);
571 result |= render_entity_header(ctx, data, override_ctx);
578 const ImGuiStyle& style = ImGui::GetStyle();
579 const float add_component_footer_height =
580 style.ItemSpacing.y * 4.0f + style.WindowPadding.y + ImGui::GetFrameHeightWithSpacing();
581 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, style.WindowRounding);
582 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
583 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
584 ImGui::BeginChild(
"ENTITY_COMPONENTS",
585 ImVec2(0.0f, -add_component_footer_height),
586 ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding);
587 ImGui::PopStyleVar(2);
588 ImGui::PopStyleColor();
592 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
593 ImGui::TreePush(
"Entity");
596 const auto ent = data.entity();
597 const auto idx = entt::to_entity(ent);
598 const auto ver = entt::to_version(ent);
599 const auto id = entt::to_integral(ent);
602 ImGui::Text(
"Id: %u, Index: %u, Version: %u",
id, idx, ver);
606 ImGui::PopStyleVar();
609 hpp::for_each_tuple_type<all_inspectable_components>(
613 auto component = data.try_get<ctype>();
621 if constexpr(std::is_same_v<ctype, tag_component>)
626 auto type = entt::resolve<ctype>();
631 override_ctx.set_component_type(std::string(
name), pretty_name);
634 inspect_callbacks callbacks;
639 if constexpr(std::is_base_of<owned_component, ctype>::value)
644 ImGui::Text(
"%u", uint32_t(component->get_owner().entity()));
649 comp_var_proxy.
impl->parent = var_proxy.impl;
651 comp_var_proxy.
impl->name = [&]()
653 const auto&
name = var_proxy.impl->name;
658 return fmt::format(
"{}/{}",
name, pretty_name);
660 comp_var_proxy.
impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
663 if(parent_proxy.impl->getter(var) && var)
665 auto data = var.cast<entt::handle>();
668 auto component = data.try_get<ctype>();
671 result = entt::forward_as_meta(*component);
678 comp_var_proxy.
impl->setter = [parent_proxy = var_proxy](
meta_any_proxy& proxy,
const entt::meta_any& value, uint64_t execution_count)
mutable
680 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
684 auto comp_var = entt::forward_as_meta(*component);
688 return ::unravel::inspect_var(ctx, comp_var, comp_var_proxy, comp_info);
692 callbacks.on_add = [&]()
700 callbacks.on_remove = [&]()
702 if(data.all_of<ctype>())
710 callbacks.can_remove = []()
712 return !std::is_same<ctype, id_component>::value && !std::is_same<ctype, tag_component>::value &&
713 !std::is_same<ctype, transform_component>::value && !std::is_same<ctype, prefab_id_component>::value &&
714 !std::is_same<ctype, layer_component>::value && !std::is_same<ctype, bone_component>::value &&
715 !std::is_same<ctype, submesh_component>::value;
718 callbacks.can_merge = []()
720 return std::is_same<ctype, id_component>::value || std::is_same<ctype, tag_component>::value;
724 callbacks.icon = get_component_icon<ctype>();
726 result |= inspect_component(pretty_name, callbacks);
732 const auto& comps = script_comp->get_script_components();
734 int index_to_remove = -1;
735 int index_to_add = -1;
736 for(
size_t i = 0; i < comps.size(); ++i)
739 const auto&
script = comps[i];
740 const auto& mono_obj =
script.pinned->get_object();
741 if(!mono_obj.valid())
743 APPLOG_ERROR(
"Script object is invalid for domain version: {}",
script.pinned->get_domain_version());
746 const auto&
type = mono_obj.get_type();
749 APPLOG_ERROR(
"Script object type is invalid for domain version: {}",
script.pinned->get_domain_version());
752 fs::path source_loc = script_comp->get_script_source_location(
script);
755 const auto& pretty_name =
name;
757 inspect_callbacks callbacks;
762 if(!source_loc.empty())
773 if(ImGui::Button(var.c_str(), ImVec2(-1.0f, ImGui::GetFrameHeight())))
777 em.
focus(source_loc);
778 em.focus_path(source_loc.parent_path());
781 if(ImGui::IsItemDoubleClicked(ImGuiMouseButton_Left))
790 obj_proxy.
impl->parent = var_proxy.impl;
791 obj_proxy.
impl->name = [&]()
793 const auto&
name = var_proxy.impl->name;
798 return fmt::format(
"{}/{}",
name, pretty_name);
800 obj_proxy.
impl->getter = [parent_proxy = var_proxy, i](entt::meta_any& result)
803 if(parent_proxy.impl->getter(var) && var)
805 auto data = var.cast<entt::handle>();
811 const auto& comps = script_comp->get_script_components();
823 obj_proxy.
impl->setter = [parent_proxy = var_proxy](
meta_any_proxy& proxy,
const entt::meta_any& value, uint64_t execution_count)
mutable
825 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
829 entt::meta_any obj_var =
script.pinned;
839 callbacks.on_add = [&]()
844 callbacks.on_remove = [&]()
849 callbacks.can_remove = []()
854 callbacks.can_merge = []()
862 auto script_type = entt::resolve<script_component>();
866 override_ctx.set_component_type(std::string(script_type_name), script_type_pretty_name);
868 std::string segment = fmt::format(
"script_components[{}]/{}", i,
name);
869 std::string pretty_segment = fmt::format(
"Scripts[{}]/{}", i, pretty_name);
870 override_ctx.push_segment(segment, pretty_segment);
872 result |= inspect_component(pretty_name, callbacks);
874 override_ctx.pop_segment();
879 if(index_to_remove != -1)
881 auto comp_to_remove = comps[index_to_remove];
885 auto type = comp_to_remove.
pinned->get_object().get_type();
888 auto script_type_name =
type.get_fullname();
894 if(index_to_add != -1)
901 result.changed |=
true;
902 result.edit_finished |=
true;
910 static const auto label =
"Add Component";
911 auto avail = ImGui::GetContentRegionAvail();
912 ImVec2
size = ImGui::CalcItemSize(label);
914 ImGui::AlignedItem(0.5f,
919 auto pos = ImGui::GetCursorScreenPos();
920 if(ImGui::Button(label, size))
922 ImGui::OpenPopup(
"COMPONENT_MENU");
923 ImGui::SetNextWindowPos(pos);
927 if(ImGui::BeginPopup(
"COMPONENT_MENU"))
929 if(ImGui::IsWindowAppearing())
931 ImGui::SetKeyboardFocusHere();
935 ImGui::DrawItemActivityOutline();
938 ImGui::BeginChild(
"COMPONENT_MENU_CONTEXT", ImVec2(ImGui::GetContentRegionAvail().
x,
size.x));
941 for(
const auto&
type : scr.get_all_scriptable_components())
943 const auto&
name =
type.get_fullname();
945 inspect_callbacks callbacks;
947 callbacks.on_add = [&]()
952 result.changed |=
true;
953 result.edit_finished |=
true;
956 callbacks.on_remove = [&]()
960 callbacks.can_remove = []()
965 callbacks.can_merge = []()
972 result |= list_component(filter_,
name, callbacks);
975 hpp::for_each_tuple_type<all_addable_components>(
982 auto type = entt::resolve<ctype>();
984 inspect_callbacks callbacks;
986 callbacks.on_add = [&]()
993 result.changed |=
true;
994 result.edit_finished |=
true;
997 callbacks.on_remove = [&]()
999 if(data.all_of<ctype>())
1003 result.changed |=
true;
1004 result.edit_finished |=
true;
1010 callbacks.can_remove = []()
1015 callbacks.can_merge = []()
1022 result |= list_component(filter_,
name, callbacks);
1042 if(
auto transform_comp = data.try_get<transform_component>())
1044 transform_comp->mark_consumers_dirty();
static auto get_entity_icon(entt::handle entity) -> std::string
static auto get_entity_display_color(entt::handle entity) -> ImVec4
Manages ImGui layout for property inspection in the editor.
Class that contains core data for audio listeners. There can only be one instance of it per scene.
#define ICON_MDI_FILTER_OUTLINE
#define ICON_MDI_CUBE_OUTLINE
#define ICON_MDI_LIGHTBULB
#define ICON_MDI_LANGUAGE_CSHARP
#define ICON_MDI_AXIS_ARROW
#define ICON_MDI_VOLUME_HIGH
#define ICON_MDI_BRIGHTNESS_AUTO
#define ICON_MDI_ANIMATION
#define ICON_MDI_SHAPE_OUTLINE
#define ICON_MDI_VIEW_AGENDA
#define ICON_MDI_IDENTIFIER
#define ICON_MDI_EAR_HEARING
#define ICON_MDI_WHITE_BALANCE_SUNNY
#define ICON_MDI_WEATHER_SUNNY
#define ICON_MDI_BRIGHTNESS_5
#define ICON_MDI_REFLECT_HORIZONTAL
#define ICON_MDI_SELECT_SEARCH
std::function< bool()> can_merge
std::function< inspect_result()> on_inspect
std::function< void()> on_remove
std::function< bool()> can_remove
std::function< void()> on_add
#define APPLOG_ERROR(...)
void PushFont(Font::Enum _font)
auto MenuItemIcon(const char *icon, const char *label, const char *shortcut, bool enabled) -> bool
void PushReadonly(bool _enabled)
auto get_pretty_name(const meta_type &t) -> std::string
auto get_name(const meta_type &t) -> std::string
std::tuple< test_component, model_component, animation_component, camera_component, volume_component, auto_exposure_component, tonemapping_component, assao_component, bloom_component, fxaa_component, taa_component, ssr_component, ssil_component, light_component, skylight_component, reflection_probe_component, physics_component, character_controller_component, audio_source_component, audio_listener_component, text_component, particle_emitter_component, ui_document_component > all_addable_components
auto is_debug_view() -> bool
Checks if currently in debug view mode.
std::tuple< id_component, tag_component, layer_component, prefab_component, prefab_id_component, transform_component, test_component, model_component, animation_component, bone_component, submesh_component, camera_component, volume_component, auto_exposure_component, tonemapping_component, assao_component, bloom_component, fxaa_component, taa_component, ssr_component, ssil_component, light_component, skylight_component, reflection_probe_component, physics_component, character_controller_component, audio_source_component, audio_listener_component, text_component, particle_emitter_component, ui_document_component > all_inspectable_components
auto make_proxy(entt::meta_any &var, const std::string &name) -> meta_any_proxy
Creates a simple proxy for direct variable access.
auto inspect_var(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result
Main entry point for inspecting any variable with automatic type resolution.
void do_action(const std::string &name, const std::function< void()> &action)
void focus(entt::meta_any object)
Selects an object. Can be anything.
static void open_workspace_on_file(const fs::path &file, int line=0)
Result of an inspection operation indicating what changes occurred.
bool changed
Whether the value was modified during inspection.
bool edit_finished
Whether user finished editing (e.g., released mouse or pressed enter)
entt::registry * inspected_registry
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect_as_property(rtti::context &ctx, entt::handle &data) -> inspect_result
Component that holds a reference to a prefab asset and tracks property overrides.
Global context for tracking prefab override changes during inspection.
Represents a generic prefab with a buffer for serialized data.
dotnet::object_pinned_ptr pinned
Metadata about a variable being inspected.
bool is_property
Whether this is a property that can be overridden in prefabs.
bool read_only
Whether the variable should be displayed as read-only.