Unravel Engine C++ Reference
Loading...
Searching...
No Matches
inspector_entity.cpp
Go to the documentation of this file.
1#include "inspector_entity.h"
2#include "entt/meta/meta.hpp"
3#include "inspector.h"
4#include "inspectors.h"
6
13#include <engine/engine.h>
17
18#include <hpp/type_name.hpp>
19#include <hpp/utility.hpp>
20#include <hpp/finally.hpp>
21#include <string_utils/utils.h>
22
23#include <imgui/imgui.h>
24#include <imgui/imgui_internal.h>
25namespace unravel
26{
27
28namespace
29{
30template<typename T>
31auto get_component_icon() -> std::string
32{
33 // Core components
34 if constexpr(std::is_same<T, id_component>::value)
35 {
37 }
38 else if constexpr(std::is_same<T, tag_component>::value)
39 {
40 return ICON_MDI_TAG;
41 }
42 else if constexpr(std::is_same<T, layer_component>::value)
43 {
44 return ICON_MDI_LAYERS;
45 }
46 else if constexpr(std::is_same<T, prefab_component>::value)
47 {
48 return ICON_MDI_CUBE;
49 }
50 else if constexpr(std::is_same<T, prefab_id_component>::value)
51 {
53 }
54 // Transform
55 else if constexpr(std::is_same<T, transform_component>::value)
56 {
58 }
59 // Test/Debug
60 else if constexpr(std::is_same<T, test_component>::value)
61 {
62 return ICON_MDI_BUG;
63 }
64 // Rendering components
65 else if constexpr(std::is_same<T, model_component>::value)
66 {
67 return ICON_MDI_SHAPE;
68 }
69 else if constexpr(std::is_same<T, submesh_component>::value)
70 {
72 }
73 else if constexpr(std::is_same<T, camera_component>::value)
74 {
75 return ICON_MDI_CAMERA;
76 }
77 else if constexpr(std::is_same<T, text_component>::value)
78 {
79 return ICON_MDI_TEXT;
80 }
81 // Animation
82 else if constexpr(std::is_same<T, animation_component>::value)
83 {
84 return ICON_MDI_ANIMATION;
85 }
86 else if constexpr(std::is_same<T, bone_component>::value)
87 {
88 return ICON_MDI_BONE;
89 }
90 // Lighting
91 else if constexpr(std::is_same<T, light_component>::value)
92 {
93 return ICON_MDI_LIGHTBULB;
94 }
95 else if constexpr(std::is_same<T, skylight_component>::value)
96 {
98 }
99 else if constexpr(std::is_same<T, reflection_probe_component>::value)
100 {
102 }
103 // Physics
104 else if constexpr(std::is_same<T, physics_component>::value)
105 {
106 return ICON_MDI_ATOM;
107 }
108 // Audio
109 else if constexpr(std::is_same<T, audio_source_component>::value)
110 {
112 }
113 else if constexpr(std::is_same<T, audio_listener_component>::value)
114 {
116 }
117 // Scripting
118 else if constexpr(std::is_same<T, script_component>::value)
119 {
121 }
122 // Post-processing effects (using similar icons for consistency)
123 else if constexpr(std::is_same<T, auto_exposure_component>::value)
124 {
126 }
127 else if constexpr(std::is_same<T, bloom_component>::value)
128 {
130 }
131 else if constexpr(std::is_same<T, tonemapping_component>::value)
132 {
134 }
135 else if constexpr(std::is_same<T, fxaa_component>::value)
136 {
137 return ICON_MDI_FILTER;
138 }
139 else if constexpr(std::is_same<T, assao_component>::value)
140 {
142 }
143 else if constexpr(std::is_same<T, ssr_component>::value)
144 {
145 return ICON_MDI_MIRROR;
146 }
147 else if constexpr(std::is_same<T, volume_component>::value)
148 {
150 }
151 else
152 {
153 // Default fallback icon
155 }
156}
157
158struct inspect_callbacks
159{
160 std::function<inspect_result()> on_inspect;
161 std::function<void()> on_add;
162 std::function<void()> on_remove;
163 std::function<bool()> can_remove;
164 std::function<bool()> can_merge;
165
166 std::string icon;
167};
168
169auto inspect_component(const std::string& name, const inspect_callbacks& callbacks) -> inspect_result
170{
171 inspect_result result{};
172
173 bool opened = true;
174
175 ImGui::PushID(name.c_str());
176
177 auto popup_str = "COMPONENT_SETTING";
178
179 bool open_popup = false;
180 bool open = true;
181 if(!callbacks.can_merge())
182 {
183 ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver);
184
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);
189
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);
193
194 ImGui::PushStyleColor(ImGuiCol_Header, col_framebg);
195 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, col_framebg_hovered);
196 ImGui::PushStyleColor(ImGuiCol_HeaderActive, col_framebg_active);
197
199 open = ImGui::CollapsingHeader(fmt::format(" {}", name).c_str(), nullptr, ImGuiTreeNodeFlags_AllowOverlap);
200 ImGui::PopFont();
201
202 ImGui::OpenPopupOnItemClick(popup_str);
203 ImGui::PopStyleColor(3);
204
205 ImGui::SetCursorPos(pos);
206 ImGui::AlignTextToFramePadding();
207 ImGui::Text(" %s", callbacks.icon.c_str());
208
209 ImGui::SameLine();
210 auto settings_size = ImGui::CalcTextSize(ICON_MDI_COG).x + ImGui::GetStyle().FramePadding.x * 2.0f;
211
212 auto avail = ImGui::GetContentRegionAvail().x + ImGui::GetStyle().FramePadding.x;
213 ImGui::AlignedItem(1.0f,
214 avail,
215 settings_size,
216 [&]()
217 {
218 if(ImGui::Button(ICON_MDI_COG))
219 {
220 open_popup = true;
221 }
222 });
223 }
224
225 if(open)
226 {
227 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
228 ImGui::TreePush(name.c_str());
229
230 result |= callbacks.on_inspect();
231
232 ImGui::TreePop();
233 ImGui::PopStyleVar();
234 }
235 if(open_popup)
236 {
237 ImGui::OpenPopup(popup_str);
238 }
239
240 bool is_popup_open = ImGui::IsPopupOpen(popup_str);
241 if(is_popup_open && ImGui::BeginPopup(popup_str))
242 {
243 {
245
246 bool removal_allowed = callbacks.can_remove();
247 if(ImGui::MenuItemIcon(ICON_MDI_RESTORE, "Reset", nullptr, removal_allowed))
248 {
249 callbacks.on_remove();
250 callbacks.on_add();
251
252 result.changed = true;
253 result.edit_finished = true;
254 }
255
256 ImGui::Separator();
257 if(ImGui::MenuItemIcon(ICON_MDI_DELETE, "Remove Component", nullptr, removal_allowed))
258 {
259 callbacks.on_remove();
260 result.changed = true;
261 result.edit_finished = true;
262 }
263 }
264 ImGui::EndPopup();
265 }
266
267 ImGui::PopID();
268 if(!opened)
269 {
270 callbacks.on_remove();
271 result.changed = true;
272 result.edit_finished = true;
273 }
274
275 return result;
276}
277
278auto list_component(ImGuiTextFilter& filter, const std::string& name, const inspect_callbacks& callbacks)
279 -> inspect_result
280{
281 inspect_result result{};
282 if(!filter.PassFilter(name.c_str()))
283 {
284 return result;
285 }
286
287 if(ImGui::Selectable(fmt::format("{} {}", callbacks.icon, name).c_str()))
288 {
289 callbacks.on_remove();
290 callbacks.on_add();
291
292 result.changed = true;
293 result.edit_finished = true;
294
295 ImGui::CloseCurrentPopup();
296 }
297 return result;
298}
299auto get_entity_pretty_name(entt::handle entity) -> const std::string&
300{
301 if(!entity)
302 {
303 static const std::string empty = "None (Entity)";
304 return empty;
305 }
306 auto& tag = entity.get_or_emplace<tag_component>();
307 return tag.name;
308}
309
310auto process_drag_drop_target(rtti::context& ctx, entt::handle& obj) -> bool
311{
312 if(ImGui::IsDragDropPossibleTargetForType("entity"))
313 {
314 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
315 }
316
317 bool result = false;
318
319 if(ImGui::BeginDragDropTarget())
320 {
321 if(ImGui::IsDragDropPayloadBeingAccepted())
322 {
323 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
324 }
325 else
326 {
327 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
328 }
329
330 {
331 auto payload = ImGui::AcceptDragDropPayload("entity");
332 if(payload != nullptr)
333 {
334 entt::handle dropped{};
335 std::memcpy(&dropped, payload->Data, size_t(payload->DataSize));
336 if(dropped)
337 {
338 obj = dropped;
339 result = true;
340 }
341 }
342 }
343
344 ImGui::EndDragDropTarget();
345 }
346
347 return result;
348}
349
350auto render_entity_header(rtti::context& ctx, entt::handle data, prefab_override_context& override_ctx) -> inspect_result
351{
352 inspect_result result{};
353
354 if(!data)
355 {
356 return result;
357 }
358
359 auto tag_comp = data.try_get<tag_component>();
360 auto trans_comp = data.try_get<transform_component>();
361
362 if(!tag_comp)
363 {
364 return result;
365 }
366
367 // Create a table for proper alignment
368 if(ImGui::BeginTable("EntityHeader", 3, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoClip))
369 {
370 ImGui::TableSetupColumn("Active", ImGuiTableColumnFlags_WidthFixed, 20.0f);
371 ImGui::TableSetupColumn("Icon", ImGuiTableColumnFlags_WidthFixed, 22.0f);
372 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
373
374 ImGui::TableNextRow();
375
376 // Active checkbox column
377 ImGui::TableSetColumnIndex(0);
378 if(trans_comp)
379 {
380 bool is_active = trans_comp->is_active();
381
382 // Track component type for prefab override context
383 auto type = entt::resolve<transform_component>();
384 auto name = entt::get_name(type);
385 auto pretty_name = entt::get_pretty_name(type);
386 auto prop = type.data("active"_hs);
387 auto prop_name = entt::get_name(prop);
388 auto prop_pretty_name = entt::get_pretty_name(prop);
389
390 override_ctx.set_component_type(name, pretty_name);
391 override_ctx.push_segment(prop_name, prop_pretty_name);
392
393 bool old_active = is_active;
394 if(ImGui::Checkbox("##active", &is_active))
395 {
396 result.changed = true;
397 result.edit_finished = true;
398
399 auto& em = ctx.get_cached<editing_manager>();
400 em.do_action<entity_set_active_action_t>({},
401 data,
402 old_active,
403 is_active);
404 }
405
406 override_ctx.pop_segment();
407 }
408
410 ImGui::PushStyleColor(ImGuiCol_Text, col);
411 // Icon column
412 ImGui::TableSetColumnIndex(1);
413 ImGui::AlignTextToFramePadding();
414 {
416
417 ImGui::Text("%s", icon.c_str());
418 }
419 // Name field column
420 ImGui::TableSetColumnIndex(2);
421 {
422 auto type = entt::resolve<tag_component>();
423 auto type_name = entt::get_name(type);
424 auto pretty_name = entt::get_pretty_name(type);
425 auto prop = type.data("name"_hs);
426 auto prop_name = entt::get_name(prop);
427 auto prop_pretty_name = entt::get_pretty_name(prop);
428
429 override_ctx.set_component_type(type_name, pretty_name);
430 override_ctx.push_segment(prop_name, prop_pretty_name);
431
432 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
433 ImGui::SetNextItemWidth(-1.0f);
434
435 auto old_name = tag_comp->name;
436 if(ImGui::InputTextWidget("##name", tag_comp->name, false))
437 {
438 result.changed = true;
439 result.edit_finished = true;
440
441 auto& em = ctx.get_cached<editing_manager>();
442
443 em.do_action<entity_set_name_action_t>({},
444 data,
445 old_name,
446 tag_comp->name);
447 }
448
449 ImGui::PopStyleVar();
450 override_ctx.pop_segment();
451 }
452 ImGui::PopStyleColor();
453
454 ImGui::EndTable();
455 }
456
457 // Tag field using traditional property_layout approach
458 {
459 auto type = entt::resolve<tag_component>();
460 auto type_name = entt::get_name(type);
461 auto pretty_name = entt::get_pretty_name(type);
462
463 auto prop = type.data("tag"_hs);
464 auto prop_name = entt::get_name(prop);
465 auto prop_pretty_name = entt::get_pretty_name(prop);
466
467 override_ctx.set_component_type(type_name, pretty_name);
468 override_ctx.push_segment(prop_name, prop_pretty_name);
469
470 property_layout layout(prop, tag_comp, true);
471
472 var_info info;
473 info.is_property = true;
474 info.read_only = false;
475
476 auto old_tag = tag_comp->tag;
477
478 auto v_var = entt::forward_as_meta(tag_comp->tag);
479 auto var_result = ::unravel::inspect_var(ctx, v_var, make_proxy(v_var), info);
480
481 if(var_result.changed)
482 {
483 auto& em = ctx.get_cached<editing_manager>();
484
485 em.do_action<entity_set_tag_action_t>({},
486 data,
487 old_tag,
488 tag_comp->tag);
489
490 }
491
492 result |= var_result;
493
494 override_ctx.pop_segment();
495 }
496
497 return result;
498}
499
500} // namespace
501
503{
504 auto name = get_entity_pretty_name(data);
505
506 inspect_result result;
507
508 if(ImGui::Button(ICON_MDI_DELETE, ImVec2(0.0f, ImGui::GetFrameHeight())))
509 {
510 if(data)
511 {
512 data = {};
513 result.changed = true;
514 result.edit_finished = true;
515 }
516 }
517
518 ImGui::SameLine();
519 auto id = fmt::format("{} {}", ICON_MDI_CUBE, name);
520 if(ImGui::Button(id.c_str(), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight())))
521 {
522 auto& em = ctx.get_cached<editing_manager>();
523
524 em.focus(data);
525 }
526
527 ImGui::SetItemTooltipEx("%s", id.c_str());
528
529 bool drag_dropped = process_drag_drop_target(ctx, data);
530 result.changed |= drag_dropped;
531 result.edit_finished |= drag_dropped;
532
533 return result;
534}
535
537 entt::meta_any& var,
538 const meta_any_proxy& var_proxy,
539 const var_info& info,
540 const entt::meta_custom& custom) -> inspect_result
541{
542 inspect_result result{};
543 auto data = var.cast<entt::handle>();
544
545 if(data)
546 {
547 auto& inspector_ctx = ctx.get_cached<inspector_context>();
548 inspector_ctx.inspected_registry = data.registry();
549 }
550
551 auto cleanup = hpp::finally([&]()
552 {
553 auto& inspector_ctx = ctx.get_cached<inspector_context>();
554 inspector_ctx.inspected_registry = nullptr;
555 });
556
557 if(info.is_property)
558 {
559 result = inspect_as_property(ctx, data);
560 }
561 else
562 {
563 if(!data)
564 {
565 return result;
566 }
567
568 auto& override_ctx = ctx.get_cached<prefab_override_context>();
569
570 // Render Unity-style entity header (active checkbox, icon, name, tag)
571 result |= render_entity_header(ctx, data, override_ctx);
572
573 ImGui::Spacing();
574 ImGui::Separator();
575 ImGui::Spacing();
576
577 // Keep "Add Component" pinned below the scrollable component list.
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();
589
590 if(is_debug_view())
591 {
592 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
593 ImGui::TreePush("Entity");
594 {
595 property_layout layout("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);
600
601 //ImGui::SetItemTooltipEx("Id: %d\nIndex: %d\nVersion: %d", id, idx, ver);
602 ImGui::Text("Id: %u, Index: %u, Version: %u", id, idx, ver);
603 }
604
605 ImGui::TreePop();
606 ImGui::PopStyleVar();
607 }
608
609 hpp::for_each_tuple_type<all_inspectable_components>(
610 [&](auto index)
611 {
612 using ctype = std::tuple_element_t<decltype(index)::value, all_inspectable_components>;
613 auto component = data.try_get<ctype>();
614
615 if(!component)
616 {
617 return;
618 }
619
620 // Skip tag_component as it's handled in the Unity-style header
621 if constexpr(std::is_same_v<ctype, tag_component>)
622 {
623 return;
624 }
625
626 auto type = entt::resolve<ctype>();
627 auto name = entt::get_name(type);
628 auto pretty_name = entt::get_pretty_name(type);
629
630 // Track component type for prefab override context
631 override_ctx.set_component_type(std::string(name), pretty_name);
632
633
634 inspect_callbacks callbacks;
635
636 callbacks.on_inspect = [&]() -> inspect_result
637 {
638
639 if constexpr(std::is_base_of<owned_component, ctype>::value)
640 {
641 if(is_debug_view())
642 {
643 property_layout layout("Owner");
644 ImGui::Text("%u", uint32_t(component->get_owner().entity()));
645 }
646 }
647
648 meta_any_proxy comp_var_proxy;
649 comp_var_proxy.impl->parent = var_proxy.impl;
650 comp_var_proxy.impl->type_name = entt::get_pretty_name(type);
651 comp_var_proxy.impl->name = [&]()
652 {
653 const auto& name = var_proxy.impl->name;
654 if(name.empty())
655 {
656 return pretty_name;
657 }
658 return fmt::format("{}/{}", name, pretty_name);
659 }();
660 comp_var_proxy.impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
661 {
662 entt::meta_any var;
663 if(parent_proxy.impl->getter(var) && var)
664 {
665 auto data = var.cast<entt::handle>();
666 if(data)
667 {
668 auto component = data.try_get<ctype>();
669 if(component)
670 {
671 result = entt::forward_as_meta(*component);
672 return true;
673 }
674 }
675 }
676 return false;
677 };
678 comp_var_proxy.impl->setter = [parent_proxy = var_proxy](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
679 {
680 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
681 };
682 // entt::meta_any comp_var;
683 // call_var_getter(comp_var, comp_var_getter);
684 auto comp_var = entt::forward_as_meta(*component);
685
686 var_info comp_info;
687 comp_info.is_copyable = false;
688 return ::unravel::inspect_var(ctx, comp_var, comp_var_proxy, comp_info);
689
690 };
691
692 callbacks.on_add = [&]()
693 {
694 // data.emplace<ctype>();
695
696 auto& em = ctx.get_cached<editing_manager>();
698 };
699
700 callbacks.on_remove = [&]()
701 {
702 if(data.all_of<ctype>())
703 {
704 auto& em = ctx.get_cached<editing_manager>();
706 }
707
708 };
709
710 callbacks.can_remove = []()
711 {
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;
716 };
717
718 callbacks.can_merge = []()
719 {
720 return std::is_same<ctype, id_component>::value || std::is_same<ctype, tag_component>::value;
721 };
722
723
724 callbacks.icon = get_component_icon<ctype>();
725
726 result |= inspect_component(pretty_name, callbacks);
727 });
728
729 auto script_comp = data.try_get<script_component>();
730 if(script_comp)
731 {
732 const auto& comps = script_comp->get_script_components();
733
734 int index_to_remove = -1;
735 int index_to_add = -1;
736 for(size_t i = 0; i < comps.size(); ++i)
737 {
738 ImGui::PushID(i);
739 const auto& script = comps[i];
740 const auto& mono_obj = script.pinned->get_object();
741 if(!mono_obj.valid())
742 {
743 APPLOG_ERROR("Script object is invalid for domain version: {}", script.pinned->get_domain_version());
744 continue;
745 }
746 const auto& type = mono_obj.get_type();
747 if(!type.valid())
748 {
749 APPLOG_ERROR("Script object type is invalid for domain version: {}", script.pinned->get_domain_version());
750 continue;
751 }
752 fs::path source_loc = script_comp->get_script_source_location(script);
753
754 auto name = type.get_fullname();
755 const auto& pretty_name = name;
756
757 inspect_callbacks callbacks;
758 callbacks.on_inspect = [&]() -> inspect_result
759 {
760 inspect_result inspect_res{};
761
762 if(!source_loc.empty())
763 {
764 var_info field_info;
765 field_info.is_property = true;
766 field_info.read_only = true;
767 ImGui::PushReadonly(field_info.read_only);
768
769 std::string var = ICON_MDI_LANGUAGE_CSHARP " " + source_loc.stem().string();
770 {
771 property_layout layout("Script");
772
773 if(ImGui::Button(var.c_str(), ImVec2(-1.0f, ImGui::GetFrameHeight())))
774 {
775 auto& em = ctx.get_cached<editing_manager>();
776
777 em.focus(source_loc);
778 em.focus_path(source_loc.parent_path());
779 }
780
781 if(ImGui::IsItemDoubleClicked(ImGuiMouseButton_Left))
782 {
784 }
785 }
787 }
788
789 meta_any_proxy obj_proxy;
790 obj_proxy.impl->parent = var_proxy.impl;
791 obj_proxy.impl->name = [&]()
792 {
793 const auto& name = var_proxy.impl->name;
794 if(name.empty())
795 {
796 return pretty_name;
797 }
798 return fmt::format("{}/{}", name, pretty_name);
799 }();
800 obj_proxy.impl->getter = [parent_proxy = var_proxy, i](entt::meta_any& result)
801 {
802 entt::meta_any var;
803 if(parent_proxy.impl->getter(var) && var)
804 {
805 auto data = var.cast<entt::handle>();
806 if(data)
807 {
808 auto script_comp = data.try_get<script_component>();
809 if(script_comp)
810 {
811 const auto& comps = script_comp->get_script_components();
812 if(i < comps.size())
813 {
814 auto& script = comps[i];
815 result = script.pinned;//entt::forward_as_meta(script.pinned);
816 return true;
817 }
818 }
819 }
820 }
821 return false;
822 };
823 obj_proxy.impl->setter = [parent_proxy = var_proxy](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
824 {
825 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
826 };
827 // entt::meta_any obj_var;
828 // call_var_getter(obj_var, obj_getter);
829 entt::meta_any obj_var = script.pinned;//entt::forward_as_meta(*script.pinned);
830 obj_proxy.impl->type_name = entt::get_pretty_name(obj_var.type());
831
832 var_info obj_info;
833 obj_info.is_copyable = false;
834
835 inspect_res |= ::unravel::inspect_var(ctx, obj_var, obj_proxy, obj_info);
836 return inspect_res;
837 };
838
839 callbacks.on_add = [&]()
840 {
841 index_to_add = i;
842 };
843
844 callbacks.on_remove = [&]()
845 {
846 index_to_remove = i;
847 };
848
849 callbacks.can_remove = []()
850 {
851 return true;
852 };
853
854 callbacks.can_merge = []()
855 {
856 return false;
857 };
858
859 callbacks.icon = ICON_MDI_LANGUAGE_CSHARP;
860
861
862 auto script_type = entt::resolve<script_component>();
863 auto script_type_name = entt::get_name(script_type);
864 auto script_type_pretty_name = entt::get_pretty_name(script_type);
865 // Track component type for prefab override context
866 override_ctx.set_component_type(std::string(script_type_name), script_type_pretty_name);
867
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);
871
872 result |= inspect_component(pretty_name, callbacks);
873
874 override_ctx.pop_segment();
875
876 ImGui::PopID();
877 }
878
879 if(index_to_remove != -1)
880 {
881 auto comp_to_remove = comps[index_to_remove];
882
884
885 auto type = comp_to_remove.pinned->get_object().get_type();
886
887 auto& em = ctx.get_cached<editing_manager>();
888 auto script_type_name = type.get_fullname();
889 em.do_action<entity_remove_script_component_action_t>({}, data, script_type_name, index_to_remove);
890
891 // script_comp->remove_script_component(comp_to_remove.pinned->object);
892 // script_comp->process_pending_deletions();
893
894 if(index_to_add != -1)
895 {
896 // script_comp->add_script_component(type);
897
898 em.do_action<entity_add_script_component_action_t>({}, data, script_type_name);
899 }
900
901 result.changed |= true;
902 result.edit_finished |= true;
903 }
904 }
905
906 ImGui::EndChild();
907
908 ImGui::Spacing();
909 ImGui::Spacing();
910 static const auto label = "Add Component";
911 auto avail = ImGui::GetContentRegionAvail();
912 ImVec2 size = ImGui::CalcItemSize(label);
913 size.x *= 2.0f;
914 ImGui::AlignedItem(0.5f,
915 avail.x,
916 size.x,
917 [&]()
918 {
919 auto pos = ImGui::GetCursorScreenPos();
920 if(ImGui::Button(label, size))
921 {
922 ImGui::OpenPopup("COMPONENT_MENU");
923 ImGui::SetNextWindowPos(pos);
924 }
925 });
926
927 if(ImGui::BeginPopup("COMPONENT_MENU"))
928 {
929 if(ImGui::IsWindowAppearing())
930 {
931 ImGui::SetKeyboardFocusHere();
932 }
933
934 ImGui::DrawFilterWithHint(filter_, ICON_MDI_SELECT_SEARCH " Search...", size.x);
935 ImGui::DrawItemActivityOutline();
936
937 ImGui::Separator();
938 ImGui::BeginChild("COMPONENT_MENU_CONTEXT", ImVec2(ImGui::GetContentRegionAvail().x, size.x));
939
940 const auto& scr = ctx.get_cached<script_system>();
941 for(const auto& type : scr.get_all_scriptable_components())
942 {
943 const auto& name = type.get_fullname();
944
945 inspect_callbacks callbacks;
946
947 callbacks.on_add = [&]()
948 {
949 //data.get_or_emplace<script_component>().add_script_component(type);
950 auto& em = ctx.get_cached<editing_manager>();
952 result.changed |= true;
953 result.edit_finished |= true;
954 };
955
956 callbacks.on_remove = [&]()
957 {
958 };
959
960 callbacks.can_remove = []()
961 {
962 return true;
963 };
964
965 callbacks.can_merge = []()
966 {
967 return false;
968 };
969
970 callbacks.icon = ICON_MDI_LANGUAGE_CSHARP;
971
972 result |= list_component(filter_, name, callbacks);
973 }
974
975 hpp::for_each_tuple_type<all_addable_components>(
976 [&](auto index)
977 {
978 using ctype = std::tuple_element_t<decltype(index)::value, all_addable_components>;
979
980 auto name = entt::get_pretty_name(entt::resolve<ctype>());
981
982 auto type = entt::resolve<ctype>();
983
984 inspect_callbacks callbacks;
985
986 callbacks.on_add = [&]()
987 {
988 // data.emplace<ctype>();
989 auto& em = ctx.get_cached<editing_manager>();
991
992
993 result.changed |= true;
994 result.edit_finished |= true;
995 };
996
997 callbacks.on_remove = [&]()
998 {
999 if(data.all_of<ctype>())
1000 {
1001 auto& em = ctx.get_cached<editing_manager>();
1003 result.changed |= true;
1004 result.edit_finished |= true;
1005 }
1006
1007
1008 };
1009
1010 callbacks.can_remove = []()
1011 {
1012 return true;
1013 };
1014
1015 callbacks.can_merge = []()
1016 {
1017 return false;
1018 };
1019
1020 // callbacks.icon = ICON_MDI_GRID;
1021
1022 result |= list_component(filter_, name, callbacks);
1023 });
1024
1025 ImGui::EndChild();
1026 ImGui::EndPopup();
1027 }
1028 }
1029
1030 if(result.changed)
1031 {
1032 if(data)
1033 {
1034 if(auto prefab = data.try_get<prefab_component>())
1035 {
1036 prefab->changed = true;
1037 }
1038
1039 // Component data was edited in place - wake the indexed transform-dirty
1040 // consumers so derived render data is re-consumed (e.g. the model pose
1041 // refresh picks up submesh_component entry edits on armature nodes).
1042 if(auto transform_comp = data.try_get<transform_component>())
1043 {
1044 transform_comp->mark_consumers_dirty();
1045 }
1046 }
1047
1048 var = data;
1049 }
1050
1051 return result;
1052}
1053} // namespace unravel
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.
Definition inspector.h:25
Class that contains core data for audio listeners. There can only be one instance of it per scene.
float x
const char * icon
uint16_t index
std::string name
Definition hub.cpp:33
std::string tag
Definition hub.cpp:32
#define ICON_MDI_FILTER_OUTLINE
#define ICON_MDI_MIRROR
#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_CAMERA
#define ICON_MDI_ANIMATION
#define ICON_MDI_ATOM
#define ICON_MDI_SHAPE_OUTLINE
#define ICON_MDI_RESTORE
#define ICON_MDI_LAYERS
#define ICON_MDI_VIEW_AGENDA
#define ICON_MDI_IDENTIFIER
#define ICON_MDI_SHAPE
#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_FILTER
#define ICON_MDI_SELECT_SEARCH
#define ICON_MDI_COG
#define ICON_MDI_DELETE
#define ICON_MDI_BONE
#define ICON_MDI_TAG
#define ICON_MDI_TEXT
#define ICON_MDI_BUG
#define ICON_MDI_CUBE
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(...)
Definition logging.h:20
texture_job_type type
void PushFont(Font::Enum _font)
Definition imgui.cpp:646
auto MenuItemIcon(const char *icon, const char *label, const char *shortcut, bool enabled) -> bool
void PopReadonly()
Definition imgui.cpp:687
void PushReadonly(bool _enabled)
Definition imgui.cpp:678
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.
entt::handle entity
@ SemiBold
Definition imgui.h:53
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.
Definition inspector.h:164
bool changed
Whether the value was modified during inspection.
Definition inspector.h:166
bool edit_finished
Whether user finished editing (e.g., released mouse or pressed enter)
Definition inspector.h:168
entt::registry * inspected_registry
Definition inspectors.h:88
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
Safe deferred property access proxy for arbitrary object properties.
Definition inspector.h:216
std::shared_ptr< meta_any_proxy_impl > impl
Definition inspector.h:274
Component that holds a reference to a prefab asset and tracks property overrides.
Global context for tracking prefab override changes during inspection.
Definition inspectors.h:99
Represents a generic prefab with a buffer for serialized data.
Definition prefab.h:18
Metadata about a variable being inspected.
Definition inspector.h:151
bool is_property
Whether this is a property that can be overridden in prefabs.
Definition inspector.h:155
bool read_only
Whether the variable should be displayed as read-only.
Definition inspector.h:153