Unravel Engine C++ Reference
Loading...
Searching...
No Matches
inspector_assets.cpp
Go to the documentation of this file.
1#include "inspector_assets.h"
2#include "inspectors.h"
3
8#include <engine/engine.h>
9#include <engine/events.h>
12#include <engine/ui/ui_tree.h>
13
14
27
28
32
33// must be below all
35
37#include <graphics/texture.h>
38#include <imgui/imgui.h>
39#include <imgui/imgui_internal.h>
40#include <imgui_widgets/sequencer/imgui_neo_sequencer.h>
41#include <logging/logging.h>
42
43namespace unravel
44{
45namespace
46{
47template<typename T>
48auto process_drag_drop_target(asset_manager& am, asset_handle<T>& entry) -> bool
49{
50 for(const auto& type : ex::get_suported_formats<T>())
51 {
52 if(ImGui::IsDragDropPossibleTargetForType(type.c_str()))
53 {
54 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
55 break;
56 }
57 }
58
59 bool result = false;
60 if(ImGui::BeginDragDropTarget())
61 {
62 if(ImGui::IsDragDropPayloadBeingAccepted())
63 {
64 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
65 }
66 else
67 {
68 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
69 }
70
71 for(const auto& type : ex::get_suported_formats<T>())
72 {
73 auto payload = ImGui::AcceptDragDropPayload(type.c_str());
74 if(payload)
75 {
76 std::string absolute_path(reinterpret_cast<const char*>(payload->Data), std::size_t(payload->DataSize));
77
78 std::string key = fs::convert_to_protocol(fs::path(absolute_path)).generic_string();
79 const auto& entry_future = am.template find_asset<T>(key);
80 if(entry_future.is_ready())
81 {
82 entry = entry_future;
83 }
84
85 if(entry.is_valid())
86 {
87 result = true;
88 break;
89 }
90 }
91 }
92 ImGui::EndDragDropTarget();
93 }
94 return result;
95}
96
97template<typename T>
98auto pick_asset(ImGuiTextFilter& filter,
99 editing_manager& em,
100 thumbnail_manager& tm,
101 asset_manager& am,
102 asset_handle<T>& data,
103 const std::string& type) -> inspect_result
104{
105 inspect_result result{};
106
107 auto fh = ImGui::GetFrameHeight();
108 ImVec2 item_size = ImVec2(fh, fh) * 2.0f;
109 ImGui::BeginGroup();
110 if(data)
111 {
112 const auto& thumbnail = tm.get_thumbnail(data);
113
114 if(!data.is_ready())
115 {
116 auto spinner_size = item_size.x;
117 ImSpinner::Spinner<ImSpinner::SpinnerTypeT::e_st_eclipse>("spinner",
118 ImSpinner::Radius{spinner_size * 0.5f},
119 ImSpinner::Thickness{6.0f},
120 ImSpinner::Color{ImSpinner::white},
121 ImSpinner::Speed{6.0f});
122 }
123 else
124 {
125 ImVec2 texture_size = ImGui::GetSize(thumbnail, item_size);
126
127 ImGui::ContentItem citem{};
128 citem.texId = ImGui::ToId(thumbnail);
129 citem.texture_size = texture_size;
130 citem.image_size = item_size;
131
132 if(ImGui::ContentButtonItem(citem))
133 {
134 em.foucs_asset(data);
135 }
136 ImGui::SetItemTooltipEx("Locate the asset in the content browser.\n%s", data.id().c_str());
137
138 ImGui::DrawItemActivityOutline();
139 }
140 }
141 else
142 {
143 ImGui::Button("##None", item_size);
144
145 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImGuiCol_TextDisabled), 1.0f);
146 }
147
148 bool drag_dropped = process_drag_drop_target(am, data);
149 result.changed |= drag_dropped;
150 result.edit_finished |= drag_dropped;
151
152 ImGui::SameLine();
153
154 std::string item = data ? data.name() : fmt::format("None ({})", type);
155 ImGui::BeginGroup();
156 ImGui::AlignTextToFramePadding();
157
158 auto popup_name = fmt::format("Pick {}", type);
159 bool clicked = ImGui::Button(item.c_str(), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight()));
160 ImGui::DrawItemActivityOutline();
161
162 ImGui::SetItemTooltipEx("%s\n\nPick an Asset", item.c_str());
163 if(clicked)
164 {
165 filter.Clear();
166 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->Size * 0.4f);
167 ImGui::OpenPopup(popup_name.c_str());
168 }
169
170 if(ImGui::Button(ICON_MDI_FILE_FIND))
171 {
172 em.foucs_asset(data);
173 }
174 ImGui::DrawItemActivityOutline();
175
176 ImGui::SetItemTooltipEx("Locate the asset in the content browser.\n%s", data.id().c_str());
177
178 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
179
180 if(ImGui::Button(ICON_MDI_UNDO_VARIANT))
181 {
182 if(data)
183 {
185 result.changed = true;
186 result.edit_finished = true;
187 }
188 }
189 ImGui::DrawItemActivityOutline();
190
191 ImGui::SetItemTooltipEx("Reset to default.");
192
193 ImGui::EndGroup();
194
195 bool open = true;
196 ImGui::PushStyleVar(ImGuiStyleVar_WindowTitleAlign, ImVec2(0.5f, 0.5f));
197 if(ImGui::BeginPopupModal(popup_name.c_str(), &open))
198 {
199 if(!open)
200 {
201 ImGui::CloseCurrentPopup();
202 }
203
204 if(ImGui::IsWindowAppearing())
205 {
206 ImGui::SetKeyboardFocusHere();
207 }
208
209 ImGui::DrawFilterWithHint(filter, "Search...", ImGui::GetContentRegionAvail().x);
210 ImGui::DrawItemActivityOutline();
211
212 auto assets = am.get_assets_with_predicate<T>(
213 [&](const auto& asset)
214 {
215 const auto& id = asset.id();
216 hpp::string_view id_view(id);
217 return !id_view.starts_with("editor:/") && filter.PassFilter(asset.name().c_str());
218 });
219
220 const float size = 100.0f;
221
222 ImGui::BeginChild("##items", {-1.0f, -1.0f});
223 ImGui::ItemBrowser(size,
224 assets.size(),
225 [&](int index)
226 {
227 auto& asset = assets[index];
228 const auto& thumbnail = tm.get_thumbnail(asset);
229
230
231 if(asset && !asset.is_ready())
232 {
233 auto spinner_size = size;
234 ImSpinner::Spinner<ImSpinner::SpinnerTypeT::e_st_eclipse>(
235 "spinner",
236 ImSpinner::Radius{spinner_size * 0.5f},
237 ImSpinner::Thickness{6.0f},
238 ImSpinner::Color{ImSpinner::white},
239 ImSpinner::Speed{6.0f});
240 }
241 else
242 {
243 ImVec2 texture_size = ImGui::GetSize(thumbnail, item_size);
244 ImVec2 item_size = {size, size};
245
246 // copy so that we can pass c_str
247 auto name = asset.name();
248 auto description = asset.uid().to_string();
249 ImGui::ContentItem citem{};
250 citem.texId = ImGui::ToId(thumbnail);
251 citem.name = name.c_str();
252 citem.description = description.c_str();
253 citem.texture_size = texture_size;
254 citem.image_size = item_size;
255
256 if(ImGui::ContentButtonItem(citem))
257 {
258 data = asset;
259 result.changed = true;
260 result.edit_finished = true;
261 ImGui::CloseCurrentPopup();
262 }
263
264 ImGui::SetItemTooltipEx("%s", asset.name().c_str());
265 }
266 });
267
268 ImGui::EndChild();
269
270 ImGui::EndPopup();
271 }
272 ImGui::PopStyleVar();
273
274 ImGui::EndGroup();
275
276 return result;
277}
278
279template<typename T>
280auto make_asset_instance_proxy(entt::meta_any& var, const meta_any_proxy& var_proxy) -> meta_any_proxy
281{
282 meta_any_proxy data_var_proxy;
283 data_var_proxy.impl->parent = var_proxy.impl;
284 data_var_proxy.impl->type_name = entt::get_pretty_name(var.type());
285 data_var_proxy.impl->name = var_proxy.impl->name;
286 data_var_proxy.impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
287 {
288 entt::meta_any var;
289 if(parent_proxy.impl->getter(var) && var)
290 {
291 auto data = var.cast<std::shared_ptr<T>>();
292 result = entt::forward_as_meta(*data);
293 return true;
294 }
295 return false;
296 };
297 data_var_proxy.impl->setter =
298 [parent_proxy = var_proxy](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
299 {
300 entt::meta_any var;
301 if(proxy.impl->getter(var) && var)
302 {
303 var.assign(value);
304 return parent_proxy.impl->setter(parent_proxy, var, execution_count);
305 }
306 return false;
307 };
308 return data_var_proxy;
309}
310
311template<typename T>
312auto make_asset_proxy(entt::meta_any& var, const meta_any_proxy& var_proxy) -> meta_any_proxy
313{
314 auto& data = var.cast<asset_handle<T>&>();
315 meta_any_proxy data_var_proxy;
316 data_var_proxy.impl->parent = var_proxy.impl;
317 data_var_proxy.impl->type_name = entt::get_pretty_name(var.type());
318 data_var_proxy.impl->name = var_proxy.impl->name;
319 data_var_proxy.impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
320 {
321 entt::meta_any var;
322 if(parent_proxy.impl->getter(var) && var)
323 {
324 auto& data = var.cast<asset_handle<T>&>();
325 if(data)
326 {
327 auto mat = data.get(false);
328 if(mat)
329 {
330 result = entt::forward_as_meta(*mat);
331 return true;
332 }
333 }
334 }
335 return false;
336 };
337 data_var_proxy.impl->setter =
338 [parent_proxy = var_proxy](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
339 {
340 // entt::meta_any var;
341 // proxy.impl->getter(var);
342 // if(var)
343 // {
344 // var = value;
345 // parent_proxy.impl->setter(parent_proxy, var, execution_count);
346 // }
347 return false;
348 };
349 return data_var_proxy;
350}
351
352template<typename T>
353auto make_mutable_asset_proxy(entt::meta_any& var, const meta_any_proxy& var_proxy) -> meta_any_proxy
354{
355 auto& data = var.cast<asset_handle<T>&>();
356 meta_any_proxy data_var_proxy;
357 data_var_proxy.impl->parent = var_proxy.impl;
358 data_var_proxy.impl->type_name = entt::get_pretty_name(var.type());
359 data_var_proxy.impl->name = var_proxy.impl->name;
360 data_var_proxy.impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
361 {
362 entt::meta_any var;
363 if(parent_proxy.impl->getter(var) && var)
364 {
365 auto& data = var.cast<asset_handle<T>&>();
366 if(data)
367 {
368 auto mat = data.get(false);
369 if(mat)
370 {
371 result = entt::forward_as_meta(*mat);
372 return true;
373 }
374 }
375 }
376 return false;
377 };
378
379 data_var_proxy.impl->setter = [data, parent_proxy = var_proxy](meta_any_proxy& proxy,
380 const entt::meta_any& value,
381 uint64_t execution_count) mutable
382 {
383 entt::meta_any var;
384 if(proxy.impl->getter(var) && var)
385 {
386 var.assign(value);
387 parent_proxy.impl->setter(parent_proxy, var, execution_count);
388
389 // Get the asset and mutate it
390 auto data_asset = data.get(true);
391 if(data_asset)
392 {
393 *data_asset = var.cast<T&>();
394 }
395
396 if(execution_count > 1)
397 {
398 // Do this after the setter is called to ensure the asset is mutable
399 auto& ctx = engine::context();
400 auto& tm = ctx.get_cached<thumbnail_manager>();
401 tm.regenerate_thumbnail(data.uid());
402 asset_writer::atomic_save_to_file(data.id(), data);
403 }
404
405 return true;
406 }
407 return false;
408 };
409
410 return data_var_proxy;
411}
412
413} // namespace
414
415void inspector_asset_handle_texture::draw_image(const asset_handle<gfx::texture>& data, ImVec2 size)
416{
417 if(data.is_ready())
418 {
419 auto sz = ImGui::GetSize(data, size);
420 ImGui::ImageWithAspect(ImGui::ToId(data, inspected_mip_), sz, size);
421
422 const auto tex = data.get(false);
423 if(tex)
424 {
425 if(tex->info.numMips > 1)
426 {
427 ImGui::KnobSliderScalarT("Mip", &inspected_mip_, 0, tex->info.numMips - 1);
428 }
429 }
430 return;
431 }
432
433 ImGui::Dummy(size);
434 ImGui::RenderFrameBorder(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
435}
436
437auto inspector_asset_handle_texture::inspect_as_property(rtti::context& ctx, asset_handle<gfx::texture>& data)
439{
440 auto& am = ctx.get_cached<asset_manager>();
441 auto& tm = ctx.get_cached<thumbnail_manager>();
442 auto& em = ctx.get_cached<editing_manager>();
443
444 inspect_result result{};
445 result |= pick_asset(filter, em, tm, am, data, ex::get_type<gfx::texture>());
446
447 return result;
448}
449
450auto inspector_asset_handle_texture::inspect(rtti::context& ctx,
451 entt::meta_any& var,
452 const meta_any_proxy& var_proxy,
453 const var_info& info,
454 const entt::meta_custom& custom) -> inspect_result
455{
456 auto& data = var.cast<asset_handle<gfx::texture>&>();
457
458 if(info.is_property)
459 {
460 return inspect_as_property(ctx, data);
461 }
462
463 bool changed = false;
464 if(inspected_asset_ != data || inspected_version_ != data.version())
465 {
466 inspected_asset_ = data;
467 inspected_version_ = data.version();
468 importer_ = nullptr;
469 inspected_mip_ = 0;
470 }
471
472 auto& am = ctx.get_cached<unravel::asset_manager>();
473 inspect_result result{};
474
475 auto available = ImGui::GetContentRegionAvail();
476
477 if(ImGui::BeginTabBar("asset_handle_texture",
478 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_FittingPolicyScroll))
479 {
480 if(ImGui::BeginTabItem(ex::get_type(data.extension()).c_str()))
481 {
482 ImGui::BeginChild(ex::get_type(data.extension()).c_str());
483
484 draw_image(data, available);
485
486 if(data.is_ready())
487 {
488 var_info tex_var_info;
489 tex_var_info.read_only = true;
490 tex_var_info.is_copyable = false;
491
492 auto tex_var_proxy = make_asset_proxy<gfx::texture>(var, var_proxy);
493
494 entt::meta_any tex_var;
495 if(tex_var_proxy.impl->getter(tex_var))
496 {
497 result |= ::unravel::inspect_var(ctx, tex_var, tex_var_proxy, tex_var_info);
498 }
499 }
500 ImGui::EndChild();
501 ImGui::EndTabItem();
502 }
503 if(ImGui::BeginTabItem("Import"))
504 {
505 auto meta = am.get_metadata(data.uid());
506
507 auto base_importer = meta.meta.importer;
508
509 auto importer = std::static_pointer_cast<texture_importer_meta>(base_importer);
510
511 if(importer)
512 {
513 if(!importer_)
514 {
515 importer_ = std::make_shared<texture_importer_meta>(*importer);
516 }
517
518 result |= ::unravel::inspect(ctx, *importer_);
519 }
520
521 if(ImGui::Button("Revert"))
522 {
523 importer_ = {};
524 }
525 ImGui::SameLine();
526 if(ImGui::Button("Apply"))
527 {
528 if(importer_)
529 {
530 *importer = *importer_;
531 }
532
533 auto meta_absolute_path = asset_writer::resolve_meta_file(data);
534 asset_writer::atomic_save_to_file(meta_absolute_path.string(), meta.meta);
535 }
536 ImGui::EndTabItem();
537 }
538 ImGui::EndTabBar();
539 }
540
541 return result;
542}
543
544auto inspector_asset_handle_material::inspect_as_property(rtti::context& ctx, asset_handle<material>& data)
546{
547 auto& am = ctx.get_cached<asset_manager>();
548 auto& tm = ctx.get_cached<thumbnail_manager>();
549 auto& em = ctx.get_cached<editing_manager>();
550
551 inspect_result result{};
552
553 result |= pick_asset(filter, em, tm, am, data, ex::get_type<material>());
554
555 return result;
556}
557
558auto inspector_asset_handle_material::inspect(rtti::context& ctx,
559 entt::meta_any& var,
560 const meta_any_proxy& var_proxy,
561 const var_info& info,
562 const entt::meta_custom& custom) -> inspect_result
563{
564 auto& data = var.cast<asset_handle<material>&>();
565
566 if(info.is_property)
567 {
568 return inspect_as_property(ctx, data);
569 }
570
571 inspect_result result{};
572
573 if(data.is_ready())
574 {
575 auto data_var_proxy = make_mutable_asset_proxy<material>(var, var_proxy);
576
577 entt::meta_any data_var;
578 if(data_var_proxy.impl->getter(data_var))
579 {
580 result |= ::unravel::inspect_var(ctx, data_var, data_var_proxy);
581 }
582
583 if(result.changed)
584 {
585 auto& tm = ctx.get_cached<thumbnail_manager>();
586 tm.regenerate_thumbnail(data.uid());
587 }
588 }
589 if(result.edit_finished)
590 {
591 asset_writer::atomic_save_to_file(data.id(), data);
592 }
593
594 return result;
595}
596
597auto inspector_shared_material::inspect(rtti::context& ctx,
598 entt::meta_any& var,
599 const meta_any_proxy& var_proxy,
600 const var_info& info,
601 const entt::meta_custom& custom) -> inspect_result
602{
603 auto& data = var.cast<std::shared_ptr<material>&>();
604
605 inspect_result result{};
606 {
607 if(data)
608 {
609 if(ImGui::Button(ICON_MDI_DELETE))
610 {
611 data.reset();
612 result.changed = true;
613 result.edit_finished = true;
614 }
615 ImGui::SameLine();
616 if(ImGui::TreeNodeEx("Material Instance", ImGuiTreeNodeFlags_AllowOverlap))
617 {
618 auto data_var_proxy = make_asset_instance_proxy<material>(var, var_proxy);
619 auto data_var = entt::forward_as_meta(*data);
620 result |= ::unravel::inspect_var(ctx, data_var, data_var_proxy);
621
622 ImGui::TreePop();
623 }
624 }
625 else
626 {
627 if(ImGui::Button("Create Instance"))
628 {
629 data = std::make_shared<pbr_material>();
630 result.changed = true;
631 result.edit_finished = true;
632 }
633 }
634 }
635
636 return result;
637}
638
639auto inspector_asset_handle_mesh::inspect_as_property(rtti::context& ctx, asset_handle<mesh>& data) -> inspect_result
640{
641 auto& am = ctx.get_cached<asset_manager>();
642 auto& tm = ctx.get_cached<thumbnail_manager>();
643 auto& em = ctx.get_cached<editing_manager>();
644
645 inspect_result result{};
646
647 result |= pick_asset(filter, em, tm, am, data, ex::get_type<mesh>());
648 return result;
649}
650
651auto inspector_asset_handle_mesh::inspect(rtti::context& ctx,
652 entt::meta_any& var,
653 const meta_any_proxy& var_proxy,
654 const var_info& info,
655 const entt::meta_custom& custom) -> inspect_result
656{
657 auto& data = var.cast<asset_handle<mesh>&>();
658
659 if(info.is_property)
660 {
661 return inspect_as_property(ctx, data);
662 }
663
664 if(inspected_asset_ != data || inspected_version_ != data.version())
665 {
666 inspected_asset_ = data;
667 inspected_version_ = data.version();
668 importer_ = nullptr;
669 }
670
671 auto& am = ctx.get_cached<asset_manager>();
672 inspect_result result{};
673
674 if(ImGui::BeginTabBar("asset_handle_mesh",
675 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_FittingPolicyScroll))
676 {
677 if(ImGui::BeginTabItem(ex::get_type(data.extension()).c_str()))
678 {
679 ImGui::BeginChild(ex::get_type(data.extension()).c_str());
680
681 if(data)
682 {
683 var_info mesh_var_info;
684 mesh_var_info.read_only = true;
685 mesh_var_info.is_copyable = false;
686
687 auto mesh_var_proxy = make_asset_proxy<mesh>(var, var_proxy);
688
689 entt::meta_any mesh_var;
690 if(mesh_var_proxy.impl->getter(mesh_var))
691 {
692 result |= ::unravel::inspect_var(ctx, mesh_var, mesh_var_proxy, mesh_var_info);
693 }
694 }
695
696 ImGui::EndChild();
697 ImGui::EndTabItem();
698 }
699
700 if(ImGui::BeginTabItem("Import"))
701 {
702 auto meta = am.get_metadata(data.uid());
703
704 auto base_importer = meta.meta.importer;
705
706 auto importer = std::static_pointer_cast<mesh_importer_meta>(base_importer);
707
708 if(importer)
709 {
710 if(!importer_)
711 {
712 importer_ = std::make_shared<mesh_importer_meta>(*importer);
713 }
714
715 if(ImGui::BeginTabBar("asset_handle_mesh_import",
716 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton |
717 ImGuiTabBarFlags_FittingPolicyScroll))
718 {
719 if(ImGui::BeginTabItem("Model"))
720 {
721 result |= ::unravel::inspect(ctx, importer_->model);
722
723 ImGui::EndTabItem();
724 }
725
726 if(ImGui::BeginTabItem("Rig"))
727 {
728 result |= ::unravel::inspect(ctx, importer_->rig);
729
730 ImGui::EndTabItem();
731 }
732
733 if(ImGui::BeginTabItem("Animations"))
734 {
735 result |= ::unravel::inspect(ctx, importer_->animations);
736
737 ImGui::EndTabItem();
738 }
739
740 if(ImGui::BeginTabItem("Materials"))
741 {
742 result |= ::unravel::inspect(ctx, importer_->materials);
743
744 ImGui::EndTabItem();
745 }
746
747 ImGui::EndTabBar();
748 }
749 }
750
751 if(ImGui::Button("Revert"))
752 {
753 importer_ = {};
754 }
755 ImGui::SameLine();
756 if(ImGui::Button("Apply"))
757 {
758 if(importer_)
759 {
760 *importer = *importer_;
761 }
762
763 auto meta_absolute_path = asset_writer::resolve_meta_file(data);
764 asset_writer::atomic_save_to_file(meta_absolute_path.string(), meta.meta);
765 }
766
767 ImGui::EndTabItem();
768 }
769 ImGui::EndTabBar();
770 }
771 return result;
772}
773
774auto inspector_asset_handle_animation::inspect_as_property(rtti::context& ctx, asset_handle<animation_clip>& data)
776{
777 auto& am = ctx.get_cached<asset_manager>();
778 auto& tm = ctx.get_cached<thumbnail_manager>();
779 auto& em = ctx.get_cached<editing_manager>();
780
781 inspect_result result{};
782 result |= pick_asset(filter, em, tm, am, data, ex::get_type<animation_clip>());
783
784 return result;
785}
786
787auto inspector_asset_handle_animation::inspect(rtti::context& ctx,
788 entt::meta_any& var,
789 const meta_any_proxy& var_proxy,
790 const var_info& info,
791 const entt::meta_custom& custom) -> inspect_result
792{
793 auto& data = var.cast<asset_handle<animation_clip>&>();
794
795 if(info.is_property)
796 {
797 return inspect_as_property(ctx, data);
798 }
799
800 if(inspected_asset_ != data || inspected_version_ != data.version())
801 {
802 inspected_asset_ = data;
803 inspected_version_ = data.version();
804 importer_ = nullptr;
805 }
806
807 auto& am = ctx.get_cached<asset_manager>();
808 inspect_result result{};
809
810 if(ImGui::BeginTabBar("asset_handle_animation",
811 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_FittingPolicyScroll))
812 {
813 if(ImGui::BeginTabItem(ex::get_type(data.extension()).c_str()))
814 {
815 if(data)
816 {
817 var_info clip_var_info;
818 clip_var_info.read_only = true;
819
820 auto clip_var_proxy = make_asset_proxy<animation_clip>(var, var_proxy);
821
822 entt::meta_any clip_var;
823 if(clip_var_proxy.impl->getter(clip_var))
824 {
825 result |= ::unravel::inspect_var(ctx, clip_var, clip_var_proxy, clip_var_info);
826 }
827 }
828 ImGui::EndTabItem();
829 }
830 if(ImGui::BeginTabItem("Import"))
831 {
832 auto meta = am.get_metadata(data.uid());
833 auto base_importer = meta.meta.importer;
834
835 auto importer = std::static_pointer_cast<animation_importer_meta>(base_importer);
836
837 if(importer)
838 {
839 if(!importer_)
840 {
841 importer_ = std::make_shared<animation_importer_meta>(*importer);
842 }
843
844 if(ImGui::BeginTabBar("asset_handle_mesh_import",
845 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton |
846 ImGuiTabBarFlags_FittingPolicyScroll))
847 {
848 if(ImGui::BeginTabItem("Root Motion"))
849 {
850 result |= ::unravel::inspect(ctx, importer_->root_motion);
851
852 ImGui::EndTabItem();
853 }
854
855 ImGui::EndTabBar();
856 }
857 }
858
859 if(ImGui::Button("Revert"))
860 {
861 importer_ = {};
862 }
863 ImGui::SameLine();
864 if(ImGui::Button("Apply"))
865 {
866 if(importer_)
867 {
868 *importer = *importer_;
869 }
870
871 auto meta_absolute_path = asset_writer::resolve_meta_file(data);
872 asset_writer::atomic_save_to_file(meta_absolute_path.string(), meta.meta);
873 }
874
875 ImGui::EndTabItem();
876 }
877 ImGui::EndTabBar();
878 }
879 return result;
880}
881
882auto inspector_asset_handle_prefab::get_prefab_entity(rtti::context& ctx, const asset_handle<prefab>& prefab)
883 -> entt::handle
884{
885 entt::handle instance{};
886 auto view = inspected_scene_.registry->view<prefab_component>();
887 view.each(
888 [&](auto e, auto&& comp)
889 {
890 if(comp.source == prefab && comp.source.version() == inspected_version_)
891 {
892 instance = inspected_scene_.create_handle(e);
893 }
894 });
895 if(!instance)
896 {
897 inspected_scene_.unload();
898 instance = inspected_scene_.instantiate(prefab, false);
899 inspected_version_ = prefab.version();
900 }
901
902 return instance;
903}
904
905auto inspector_asset_handle_prefab::inspect_as_property(rtti::context& ctx, asset_handle<prefab>& data)
907{
908 auto& am = ctx.get_cached<asset_manager>();
909 auto& tm = ctx.get_cached<thumbnail_manager>();
910 auto& em = ctx.get_cached<editing_manager>();
911
912 inspect_result result{};
913 result |= pick_asset(filter, em, tm, am, data, ex::get_type<prefab>());
914
915 return result;
916}
917
918auto inspector_asset_handle_prefab::inspect(rtti::context& ctx,
919 entt::meta_any& var,
920 const meta_any_proxy& var_proxy,
921 const var_info& info,
922 const entt::meta_custom& custom) -> inspect_result
923{
924 auto& data = var.cast<asset_handle<prefab>&>();
925
926 if(info.is_property)
927 {
928 return inspect_as_property(ctx, data);
929 }
930
931 auto prefab_entity = get_prefab_entity(ctx, data);
932
933 auto& am = ctx.get_cached<asset_manager>();
934 inspect_result result{};
935
936 if(ImGui::BeginTabBar("asset_handle_prefab",
937 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_FittingPolicyScroll))
938 {
939 if(ImGui::BeginTabItem(ex::get_type(data.extension()).c_str()))
940 {
941 ImGui::BeginChild(ex::get_type(data.extension()).c_str());
942
943 if(data)
944 {
945 result |= ::unravel::inspect(ctx, prefab_entity);
946
947 if(result.edit_finished)
948 {
949 fs::path absolute_key = fs::absolute(fs::resolve_protocol(data.id()));
950 asset_writer::atomic_save_to_file(absolute_key.string(), prefab_entity);
951 }
952 }
953 ImGui::EndChild();
954 ImGui::EndTabItem();
955 }
956 if(ImGui::BeginTabItem("Import"))
957 {
958 ImGui::TextUnformatted("Import options");
959
960 if(ImGui::Button("Reimport"))
961 {
962 asset_actions::reimport(data);
963 }
964
965 ImGui::EndTabItem();
966 }
967 ImGui::EndTabBar();
968 }
969 return result;
970}
971
972auto inspector_asset_handle_scene_prefab::inspect_as_property(rtti::context& ctx, asset_handle<scene_prefab>& data)
974{
975 auto& am = ctx.get_cached<asset_manager>();
976 auto& tm = ctx.get_cached<thumbnail_manager>();
977 auto& em = ctx.get_cached<editing_manager>();
978
979 inspect_result result{};
980
981 result |= pick_asset(filter, em, tm, am, data, ex::get_type<scene_prefab>());
982
983 return result;
984}
985
986auto inspector_asset_handle_scene_prefab::inspect(rtti::context& ctx,
987 entt::meta_any& var,
988 const meta_any_proxy& var_proxy,
989 const var_info& info,
990 const entt::meta_custom& custom) -> inspect_result
991{
992 auto& data = var.cast<asset_handle<scene_prefab>&>();
993
994 if(info.is_property)
995 {
996 return inspect_as_property(ctx, data);
997 }
998
999 auto& am = ctx.get_cached<asset_manager>();
1000 inspect_result result{};
1001
1002 if(ImGui::BeginTabBar("asset_handle_scene_prefab",
1003 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_FittingPolicyScroll))
1004 {
1005 if(ImGui::BeginTabItem(ex::get_type(data.extension()).c_str()))
1006 {
1007 ImGui::EndTabItem();
1008 }
1009 if(ImGui::BeginTabItem("Import"))
1010 {
1011 ImGui::BeginChild("Import");
1012
1013 ImGui::TextUnformatted("Import options");
1014
1015 if(ImGui::Button("Reimport"))
1016 {
1017 asset_actions::reimport(data);
1018 }
1019
1020 ImGui::EndChild();
1021 ImGui::EndTabItem();
1022 }
1023 ImGui::EndTabBar();
1024 }
1025 return result;
1026}
1027
1028auto inspector_asset_handle_physics_material::inspect_as_property(rtti::context& ctx,
1031{
1032 auto& am = ctx.get_cached<asset_manager>();
1033 auto& tm = ctx.get_cached<thumbnail_manager>();
1034 auto& em = ctx.get_cached<editing_manager>();
1035
1036 inspect_result result{};
1037 result |= pick_asset(filter, em, tm, am, data, ex::get_type<physics_material>());
1038
1039 return result;
1040}
1041
1042auto inspector_asset_handle_physics_material::inspect(rtti::context& ctx,
1043 entt::meta_any& var,
1044 const meta_any_proxy& var_proxy,
1045 const var_info& info,
1046 const entt::meta_custom& custom) -> inspect_result
1047{
1048 auto& data = var.cast<asset_handle<physics_material>&>();
1049
1050 if(info.is_property)
1051 {
1052 return inspect_as_property(ctx, data);
1053 }
1054
1055 inspect_result result{};
1056
1057 {
1058 auto data_var_proxy = make_mutable_asset_proxy<physics_material>(var, var_proxy);
1059
1060 entt::meta_any data_var;
1061 if(data_var_proxy.impl->getter(data_var))
1062 {
1063 result |= ::unravel::inspect_var(ctx, data_var, data_var_proxy);
1064 }
1065 }
1066 if(result.edit_finished)
1067 {
1068 asset_writer::atomic_save_to_file(data.id(), data);
1069 }
1070
1071 return result;
1072}
1073
1074auto inspector_asset_handle_audio_clip::inspect_as_property(rtti::context& ctx, asset_handle<audio_clip>& data)
1076{
1077 auto& am = ctx.get_cached<asset_manager>();
1078 auto& tm = ctx.get_cached<thumbnail_manager>();
1079 auto& em = ctx.get_cached<editing_manager>();
1080
1081 inspect_result result{};
1082 result |= pick_asset(filter, em, tm, am, data, ex::get_type<audio_clip>());
1083
1084 return result;
1085}
1086
1087void inspector_asset_handle_audio_clip::inspect_clip(const std::shared_ptr<audio_clip>& var)
1088{
1089 if(!source_)
1090 {
1091 source_ = std::make_shared<audio::source>();
1092 }
1093 source_->update(audio::duration_t(0.0166));
1094
1095 property_layout layout("clip",
1096 [&]()
1097 {
1098 ImGui::BeginGroup();
1099
1100 if(ImGui::Button(ICON_MDI_PLAY))
1101 {
1102 if(source_->is_playing())
1103 {
1104 source_->resume();
1105 }
1106 else
1107 {
1108 source_->bind(*var);
1109 source_->play();
1110 }
1111 }
1112 ImGui::SameLine();
1113 if(ImGui::Button(ICON_MDI_PAUSE))
1114 {
1115 source_->pause();
1116 }
1117 ImGui::SameLine();
1118 if(ImGui::Button(ICON_MDI_STOP))
1119 {
1120 source_->stop();
1121 }
1122 ImGui::EndGroup();
1123 });
1124
1125 auto duration = source_->has_bound_sound() ? source_->get_playback_duration() : var->get_info().duration;
1126
1127 float total_time = floorf(float(duration.count()) * 100.0f) / 100.0f;
1128
1129 auto current_time = float(source_->get_playback_position().count());
1130
1131 if(ImGui::KnobSliderScalarT("##playing_offset", &current_time, 0.0f, total_time))
1132 {
1133 source_->set_playback_position(audio::duration_t(current_time));
1134 }
1135}
1136
1137auto inspector_asset_handle_audio_clip::inspect(rtti::context& ctx,
1138 entt::meta_any& var,
1139 const meta_any_proxy& var_proxy,
1140 const var_info& info,
1141 const entt::meta_custom& custom) -> inspect_result
1142{
1143 auto& data = var.cast<asset_handle<audio_clip>&>();
1144
1145 if(info.is_property)
1146 {
1147 return inspect_as_property(ctx, data);
1148 }
1149
1150 if(inspected_asset_ != data || inspected_version_ != data.version())
1151 {
1152 inspected_asset_ = data;
1153 inspected_version_ = data.version();
1154 importer_ = nullptr;
1155 }
1156
1157 auto& am = ctx.get_cached<asset_manager>();
1158 inspect_result result{};
1159
1160 if(ImGui::BeginTabBar("asset_handle_audio_clip",
1161 ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_FittingPolicyScroll))
1162 {
1163 if(ImGui::BeginTabItem(ex::get_type(data.extension()).c_str()))
1164 {
1165 ImGui::BeginChild(ex::get_type(data.extension()).c_str());
1166
1167 auto data_var = data.get(false);
1168 if(data_var)
1169 {
1170 var_info data_var_info;
1171 data_var_info.read_only = true;
1172 data_var_info.is_copyable = false;
1173
1174 auto data_var_proxy = make_asset_proxy<audio_clip>(var, var_proxy);
1175
1176 entt::meta_any data_var;
1177 if(data_var_proxy.impl->getter(data_var))
1178 {
1179 result |= ::unravel::inspect_var(ctx, data_var, data_var_proxy, data_var_info);
1180 }
1181
1182 auto clip = data.get(false);
1183
1184 if(clip)
1185 {
1186 inspect_clip(clip);
1187 }
1188 }
1189
1190 ImGui::EndChild();
1191 ImGui::EndTabItem();
1192 }
1193 if(ImGui::BeginTabItem("Import"))
1194 {
1195 auto meta = am.get_metadata(data.uid());
1196 auto base_importer = meta.meta.importer;
1197
1198 auto importer = std::static_pointer_cast<audio_importer_meta>(base_importer);
1199
1200 if(importer)
1201 {
1202 if(!importer_)
1203 {
1204 importer_ = std::make_shared<audio_importer_meta>(*importer);
1205 }
1206
1207 result |= ::unravel::inspect(ctx, *importer_);
1208 }
1209
1210 if(ImGui::Button("Revert"))
1211 {
1212 importer_ = {};
1213 }
1214 ImGui::SameLine();
1215 if(ImGui::Button("Apply"))
1216 {
1217 if(importer_)
1218 {
1219 *importer = *importer_;
1220 }
1221
1222 auto meta_absolute_path = asset_writer::resolve_meta_file(data);
1223 asset_writer::atomic_save_to_file(meta_absolute_path.string(), meta.meta);
1224 }
1225
1226 ImGui::EndTabItem();
1227 }
1228 ImGui::EndTabBar();
1229 }
1230
1231 return result;
1232}
1233
1234auto inspector_asset_handle_font::inspect_as_property(rtti::context& ctx, asset_handle<font>& data) -> inspect_result
1235{
1236 auto& am = ctx.get_cached<asset_manager>();
1237 auto& tm = ctx.get_cached<thumbnail_manager>();
1238 auto& em = ctx.get_cached<editing_manager>();
1239
1240 inspect_result result{};
1241 result |= pick_asset(filter, em, tm, am, data, ex::get_type<font>());
1242
1243 return result;
1244}
1245
1246auto inspector_asset_handle_font::inspect(rtti::context& ctx,
1247 entt::meta_any& var,
1248 const meta_any_proxy& var_proxy,
1249 const var_info& info,
1250 const entt::meta_custom& custom) -> inspect_result
1251{
1252 auto& data = var.cast<asset_handle<font>&>();
1253
1254 if(info.is_property)
1255 {
1256 return inspect_as_property(ctx, data);
1257 }
1258
1259 inspect_result result{};
1260
1261 {
1262 auto data_var = data.get(false);
1263 if(data_var)
1264 {
1265 var_info data_var_info;
1266 data_var_info.read_only = true;
1267 data_var_info.is_copyable = false;
1268
1269 auto data_var_proxy = make_asset_proxy<font>(var, var_proxy);
1270 entt::meta_any data_var;
1271 if(data_var_proxy.impl->getter(data_var))
1272 {
1273 result |= ::unravel::inspect_var(ctx, data_var, data_var_proxy, data_var_info);
1274 }
1275 }
1276 }
1277
1278 return result;
1279}
1280
1281auto inspector_asset_handle_ui_tree::inspect_as_property(rtti::context& ctx, asset_handle<ui_tree>& data)
1283{
1284 auto& am = ctx.get_cached<asset_manager>();
1285 auto& tm = ctx.get_cached<thumbnail_manager>();
1286 auto& em = ctx.get_cached<editing_manager>();
1287
1288 inspect_result result{};
1289 result |= pick_asset(filter, em, tm, am, data, ex::get_type<ui_tree>());
1290
1291 return result;
1292}
1293
1294auto inspector_asset_handle_ui_tree::inspect(rtti::context& ctx,
1295 entt::meta_any& var,
1296 const meta_any_proxy& var_proxy,
1297 const var_info& info,
1298 const entt::meta_custom& custom) -> inspect_result
1299{
1300 auto& data = var.cast<asset_handle<ui_tree>&>();
1301
1302 if(info.is_property)
1303 {
1304 return inspect_as_property(ctx, data);
1305 }
1306
1307 inspect_result result{};
1308
1309 {
1310 auto data_var_proxy = make_mutable_asset_proxy<ui_tree>(var, var_proxy);
1311
1312 entt::meta_any data_var;
1313 if(data_var_proxy.impl->getter(data_var))
1314 {
1315 result |= ::unravel::inspect_var(ctx, data_var, data_var_proxy);
1316 }
1317 }
1318 if(result.edit_finished)
1319 {
1320 asset_writer::atomic_save_to_file(data.id(), data);
1321 }
1322
1323 return result;
1324}
1325
1326auto inspector_asset_handle_style_sheet::inspect_as_property(rtti::context& ctx, asset_handle<style_sheet>& data)
1328{
1329 auto& am = ctx.get_cached<asset_manager>();
1330 auto& tm = ctx.get_cached<thumbnail_manager>();
1331 auto& em = ctx.get_cached<editing_manager>();
1332
1333 inspect_result result{};
1334 result |= pick_asset(filter, em, tm, am, data, ex::get_type<style_sheet>());
1335
1336 return result;
1337}
1338
1339auto inspector_asset_handle_style_sheet::inspect(rtti::context& ctx,
1340 entt::meta_any& var,
1341 const meta_any_proxy& var_proxy,
1342 const var_info& info,
1343 const entt::meta_custom& custom) -> inspect_result
1344{
1345 auto& data = var.cast<asset_handle<style_sheet>&>();
1346
1347 if(info.is_property)
1348 {
1349 return inspect_as_property(ctx, data);
1350 }
1351
1352 inspect_result result{};
1353
1354 {
1355 auto data_var_proxy = make_mutable_asset_proxy<style_sheet>(var, var_proxy);
1356
1357 entt::meta_any data_var;
1358 if(data_var_proxy.impl->getter(data_var))
1359 {
1360 result |= ::unravel::inspect_var(ctx, data_var, data_var_proxy);
1361 }
1362 }
1363 if(result.edit_finished)
1364 {
1365 asset_writer::atomic_save_to_file(data.id(), data);
1366 }
1367
1368 return result;
1369}
1370
1371} // namespace unravel
Manages assets, including loading, unloading, and storage.
float x
const char * description
uint16_t view
uint16_t index
std::string name
Definition hub.cpp:33
#define ICON_MDI_STOP
#define ICON_MDI_UNDO_VARIANT
#define ICON_MDI_PAUSE
#define ICON_MDI_FILE_FIND
#define ICON_MDI_DELETE
#define ICON_MDI_PLAY
texture_job_type type
ImTextureID ToId(gfx::texture_handle _handle, uint8_t _mip=0, uint8_t _flags=IMGUI_FLAGS_ALPHA_BLEND)
Definition imgui.h:103
ImVec2 GetSize(const gfx::texture &tex, const ImVec2 &fallback={})
Definition imgui.h:149
auto get_pretty_name(const meta_type &t) -> std::string
auto get_type() -> const std::string &
auto get_suported_formats() -> const std::vector< std::string > &
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
auto inspect(rtti::context &ctx, T &obj, const std::string &name={}) -> inspect_result
Convenience template function for inspecting objects of known type.
Definition inspectors.h:402
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.
Thread-safe handle to an asset.
static auto get_empty() -> const asset_handle &
Gets an empty asset handle.
auto get(bool wait=true) const -> std::shared_ptr< T >
Gets the shared pointer to the asset.
auto name() const -> std::string
Gets the name of the asset derived from its path.
auto is_ready() const -> bool
Checks if the task is ready.
size()=default
Result of an inspection operation indicating what changes occurred.
Definition inspector.h:164
Safe deferred property access proxy for arbitrary object properties.
Definition inspector.h:216
Represents a generic prefab with a buffer for serialized data.
Definition prefab.h:18
void regenerate_thumbnail(const hpp::uuid &uid)
Metadata about a variable being inspected.
Definition inspector.h:151
bool read_only
Whether the variable should be displayed as read-only.
Definition inspector.h:153
std::vector< GitHubAsset > assets