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