Unravel Engine C++ Reference
Loading...
Searching...
No Matches
entity.cpp
Go to the documentation of this file.
1#include "entity.hpp"
2
3#include <chrono>
10#include <engine/engine.h>
11#include <engine/events.h>
16
17#include "entt/entity/fwd.hpp"
18#include "logging/logging.h"
20#include "uuid/uuid.h"
21
22#include <hpp/utility.hpp>
23#include <sstream>
24#include <utility>
25
26namespace unravel
27{
28
29auto const_handle_cast(entt::const_handle chandle) -> entt::handle
30{
31 entt::handle handle(*const_cast<entt::handle::registry_type*>(chandle.registry()), chandle.entity());
32 return handle;
33}
34
35
36auto as_span(const std::vector<entity_data<entt::handle>>& entities) -> hpp::span<const entt::handle>
37{
38 // pointer to the first entity_data
39 auto* base = entities.data();
40
41 // entity_data is just a tag type around entt::handle, so they must have the same size
42 static_assert(sizeof(entity_data<entt::handle>) == sizeof(entt::handle), "entity_data<entt::handle> and entt::handle must have the same size");
43 static_assert(alignof(entity_data<entt::handle>) == alignof(entt::handle), "entity_data<entt::handle> and entt::handle must have the same alignment");
44 // compute the address of the first entt::handle
45 auto* first_handle = reinterpret_cast<const entt::handle*>(base);
46
47 // build a span over all of them
48 hpp::span<const entt::handle> handles{first_handle, entities.size()};
49 return handles;
50}
51
52thread_local load_context* load_ctx_ptr{};
53thread_local save_context* save_ctx_ptr{};
55std::atomic_bool writing = false;
56std::atomic_bool reading = false;
57
58auto push_load_context(entt::registry& registry) -> bool
59{
60 if(load_ctx_ptr)
61 {
62 return false;
63 }
65 load_ctx_ptr->reg = &registry;
66 return true;
67}
68
69void pop_load_context(bool push_result)
70{
71 if(push_result && load_ctx_ptr)
72 {
73 auto entities = std::move(load_ctx_ptr->entities);
74 delete load_ctx_ptr;
75 load_ctx_ptr = {};
76
77
78 if(!entities.empty())
79 {
80 auto callbacks = get_post_load_callbacks();
81 if(callbacks && callbacks->callback)
82 {
83 callbacks->callback(as_span(entities));
84 }
85 }
86
87 }
88}
89
91{
92 assert(load_ctx_ptr);
93 return *load_ctx_ptr;
94}
95
97{
99 {
100 return;
101 }
103}
105{
107 {
110 }
111}
112
117
118auto push_save_context() -> bool
119{
120 if(save_ctx_ptr)
121 {
122 return false;
123 }
125
126 return true;
127}
128
129void pop_save_context(bool push_result)
130{
131 if(push_result && save_ctx_ptr)
132 {
133 delete save_ctx_ptr;
134 save_ctx_ptr = {};
135 }
136}
137
138
140{
141 assert(save_ctx_ptr);
142 return *save_ctx_ptr;
143}
144void add_to_uid_mapping(entt::handle& obj, bool recursive = true)
145{
146 auto& load_ctx = get_load_context();
147
148 auto id_comp = obj.try_get<prefab_id_component>();
149 if(id_comp)
150 {
151 id_comp->generate_if_nil();
152 load_ctx.mapping_by_prefab_uid[id_comp->id].handle = obj;
153 }
154
155
156 if(auto prefab_comp = obj.try_get<prefab_component>())
157 {
158 for(auto& entity_uuid : prefab_comp->removed_entities)
159 {
160 load_ctx.mapping_by_prefab_uid[entity_uuid].handle = {};
161 }
162 }
163
164 if(recursive )
165 {
166 if(auto trans_comp = obj.try_get<transform_component>())
167 {
168 for(auto child : trans_comp->get_children())
169 {
170 add_to_uid_mapping(child, recursive);
171 }
172 }
173 }
174}
175
177{
178
179 auto& load_ctx = get_load_context();
180 for(auto& [uid, mapping] : load_ctx.mapping_by_prefab_uid)
181 {
182 if(!mapping.consumed && mapping.handle)
183 {
184 // APPLOG_TRACE("destroying entity: {}", uid.to_string());
185 mapping.handle.destroy();
186 }
187 }
188}
189
190
191auto is_parent(entt::const_handle potential_parent, entt::const_handle child) -> bool
192{
193 if(!potential_parent)
194 {
195 return false;
196 }
197 // Traverse up the hierarchy from `child`
198 while(true)
199 {
200 // Access the transform component once per entity
201 const auto* transform = child.try_get<transform_component>();
202 if(!transform)
203 {
204 return false; // Reached the root without finding `potential_parent`
205 }
206 auto parent = transform->get_parent();
207 if(!parent)
208 {
209 return false;
210 }
211
212 if(parent == potential_parent)
213 {
214 return true; // Found the parent relationship
215 }
216
217 child = parent; // Move up the hierarchy
218 }
219}
220auto find_root(entt::const_handle e) -> entt::const_handle
221{
222 // Loop to find the root entity
223 while(true)
224 {
225 // Access the `transform_component` once
226 const auto* transform = e.try_get<transform_component>();
227 if(!transform || !transform->get_parent())
228 {
229 break; // If no parent, we are at the root
230 }
231 e = transform->get_parent(); // Move to the parent entity
232 }
233 return e; // Root entity
234}
235
236auto are_related(entt::const_handle lhs, entt::const_handle rhs) -> bool
237{
238 return find_root(lhs) == find_root(rhs);
239}
240
247
248auto push_entity_path(entt::const_handle obj) -> bool
249{
251 if(ctx)
252 {
253 if(auto id = obj.try_get<prefab_id_component>())
254 {
255 ctx->push_segment(id->id.to_string());
256 return true;
257 }
258 }
259 return false;
260}
261
262void pop_entity_path(bool pushed)
263{
265 if(pushed && ctx)
266 {
267 ctx->pop_segment();
268 }
269}
270
271} // namespace unravel
272
273using namespace unravel;
274
275namespace ser20
276{
277
278template<typename Archive>
279void save_entity_id(Archive& ar, const entt::const_handle& obj)
280{
281 entt::handle::entity_type id = obj.valid() ? obj.entity() : entt::null;
282 try_save(ar, ser20::make_nvp("id", id));
283}
284
285template<typename Archive>
286void save_entity_prefab_uid(Archive& ar, const entt::const_handle& obj)
287{
288 if(obj)
289 {
290 auto& id_comp = const_handle_cast(obj).get_or_emplace<prefab_id_component>();
291 id_comp.generate_if_nil();
292
293 try_save(ar, ser20::make_nvp("prefab_uid", id_comp.id));
294 }
295 else
296 {
297 try_save(ar, ser20::make_nvp("prefab_uid", hpp::uuid{}));
298 }
299}
300
301template<typename Archive>
302void save_entity_uuid(Archive& ar, const entt::const_handle& obj)
303{
304 if(obj)
305 {
306 auto& id_comp = const_handle_cast(obj).get_or_emplace<id_component>();
307 id_comp.generate_if_nil();
308 try_save(ar, ser20::make_nvp("uid", id_comp.id));
309 }
310 else
311 {
312 try_save(ar, ser20::make_nvp("uid", hpp::uuid{}));
313 }
314}
315
316template<typename Archive>
317void save_entity(Archive& ar, const entt::const_handle& obj, entity_flags flags)
318{
319 auto& save_ctx = get_save_context();
320 if(save_ctx.is_saving_to_prefab())
321 {
322 save_entity_prefab_uid(ar, obj);
323 }
324 if(!save_ctx.is_cloning())
325 {
326 save_entity_uuid(ar, obj);
327 }
328 else
329 {
330 save_entity_id(ar, obj);
331 }
332}
333
334template<typename Archive>
335auto load_entity_from_id(Archive& ar, entt::handle& obj, entity_flags flags) -> bool
336{
337 entt::handle::entity_type id{};
338 bool valid = try_load(ar, ser20::make_nvp("id", id));
339
340 valid &= id != entt::null && id != entt::handle::entity_type(0);
341 if(valid)
342 {
343 auto& load_ctx = get_load_context();
344 auto it = load_ctx.mapping_by_eid.find(id);
345 if(it != load_ctx.mapping_by_eid.end())
346 {
347 obj = it->second;
348 // APPLOG_TRACE("found in cache entity from id: {}", uint32_t(id));
349 }
350 else if(obj)
351 {
352 load_ctx.mapping_by_eid[id] = obj;
353 // APPLOG_TRACE("added to cache entity from id: {}", uint32_t(id));
354 }
355 else
356 {
357 if(flags == entity_flags::resolve_with_existing)
358 {
359 entt::handle check_entity(*load_ctx.reg, id);
360 if(check_entity)
361 {
362 obj = check_entity;
363 load_ctx.mapping_by_eid[id] = obj;
364 // APPLOG_TRACE("added to cache entity from id: {}", uint32_t(id));
365 }
366 else
367 {
368 obj = {};
369 }
370 }
371 else
372 {
373 obj = entt::handle(*load_ctx.reg, load_ctx.reg->create());
374 load_ctx.mapping_by_eid[id] = obj;
375 // APPLOG_TRACE("created and added to cache entity from id: {}", uint32_t(id));
376 }
377 }
378 }
379
380
381 return valid;
382}
383
384template<typename Archive>
385auto load_entity_from_prefab_uid(Archive& ar, entt::handle& obj, entity_flags flags) -> bool
386{
387 hpp::uuid uid;
388 try_load(ar, ser20::make_nvp("prefab_uid", uid));
389
390
391 auto& load_ctx = get_load_context();
392 auto it = load_ctx.mapping_by_prefab_uid.find(uid);
393 if(it != load_ctx.mapping_by_prefab_uid.end())
394 {
395 // APPLOG_TRACE("found in cache entity from uid: {}", uid.to_string());
396 obj = it->second.handle;
397 it->second.consumed = true;
398 return true;
399 }
400
401 return false;
402}
403
404template<typename Archive>
405auto load_entity_from_uuid(Archive& ar, entt::handle& obj, entity_flags flags) -> bool
406{
407 hpp::uuid uuid;
408 if(!try_load(ar, ser20::make_nvp("uid", uuid)))
409 {
410 return false;
411 }
412 if(uuid.is_nil())
413 {
414 return false;
415 }
416 auto& load_ctx = get_load_context();
417 auto it = load_ctx.mapping_by_uid.find(uuid);
418 if(it != load_ctx.mapping_by_uid.end())
419 {
420 obj = it->second;
421 }
422 else if(obj)
423 {
424 load_ctx.mapping_by_uid[uuid] = obj;
425 }
426 else
427 {
428 if(flags == entity_flags::resolve_with_existing)
429 {
430 auto view = load_ctx.reg->view<id_component>();
431 for(auto e : view)
432 {
433 if(view.get<id_component>(e).id == uuid)
434 {
435 obj = entt::handle(*load_ctx.reg, e);
436 load_ctx.mapping_by_uid[uuid] = obj;
437 return true;
438 }
439 }
440 obj = {};
441 return false;
442 }
443 obj = entt::handle(*load_ctx.reg, load_ctx.reg->create());
444 load_ctx.mapping_by_uid[uuid] = obj;
445 }
446 return true;
447}
448
449template<typename Archive>
450void load_entity(Archive& ar, entt::handle& obj, entity_flags flags)
451{
452 bool valid = false;
453 auto& load_ctx = get_load_context();
454 if(load_ctx.is_updating_prefab())
455 {
456 valid = load_entity_from_prefab_uid(ar, obj, flags);
457 }
458 if(!valid && !load_ctx.is_cloning())
459 {
460 valid = load_entity_from_uuid(ar, obj, flags);
461 }
462 if(!valid)
463 {
464 valid = load_entity_from_id(ar, obj, flags);
465 }
466 if(!valid)
467 {
468 obj = {};
469 }
470}
471
472template<typename Component>
473auto should_save_component(const entt::const_handle& obj) -> bool
474{
475 if constexpr(std::is_same_v<Component, prefab_component>)
476 {
477 // if we are saving to prefab, we don't want to save the prefab component
478 auto& save_ctx = get_save_context();
479
480 if(save_ctx.is_saving_to_prefab())
481 {
482 return false;
483 }
484 }
485 else if constexpr(std::is_same_v<Component, prefab_id_component>)
486 {
487 // if we are cloning
488 // we need to generate a new id for the entity
489 auto& save_ctx = get_save_context();
490 if(save_ctx.is_cloning())
491 {
492 if(save_ctx.get_clone_mode() != clone_mode_t::cloning_prefab_instance)
493 {
494 return false;
495 }
496 }
497 }
498 return true;
499}
500
501template<typename Component>
502auto should_load_component(const entt::handle& obj) -> bool
503{
504 if constexpr(std::is_same_v<Component, id_component>)
505 {
506 // Instance entity UIDs are per-instance (implicit override). When syncing /
507 // loading into an existing prefab hierarchy, never overwrite them from the
508 // prefab asset. New entities still get a UID via generate_if_nil.
509 auto& load_ctx = get_load_context();
510 if(load_ctx.is_updating_prefab())
511 {
512 auto& id_comp = obj.get_or_emplace<id_component>();
513 id_comp.generate_if_nil();
514 return false;
515 }
516 }
517 return true;
518}
519
520SAVE(entt::const_handle)
521{
522 save_entity(ar, obj, entity_flags::none);
523}
526
527LOAD(entt::handle)
528{
529
530 load_entity(ar, obj, entity_flags::none);
531}
532
535
537{
538 // Saving entity links is a little more complex than just entities
539 // The rule is as follows.
540 // If we are saving as single entity hierarch :
541 // If the entity link is not part of it :
542 // -> if we are saving to prefab, break the link
543 // -> if we are duplicating resolve the link on load with exsisting scene.
544 entity_flags flags = entity_flags::resolve_with_loaded;
545 entt::const_handle to_save = obj.handle;
546
547 auto& save_ctx = get_save_context();
548
549 bool is_saving_single = save_ctx.save_source.valid();
550 if(is_saving_single)
551 {
552 // is the entity a child of the hierarchy that we are saving?
553 bool save_source_is_parent = is_parent(save_ctx.save_source, obj.handle);
554
555 // if it is an external entity
556 if(!save_source_is_parent)
557 {
558 if(save_ctx.is_saving_to_prefab())
559 {
560 // when saving prefabs, external entities
561 // should not be saved
562 to_save = {};
563 }
564 else
565 {
566 // when saving entities for duplication purpose, external entities
567 // should not be resolved from the existing scene
568 flags = entity_flags::resolve_with_existing;
569 }
570 }
571 else
572 {
573 if(!save_ctx.is_saving_to_prefab())
574 {
575 flags = entity_flags::resolve_with_existing;
576 }
577 }
578 }
579
580 try_save(ar, ser20::make_nvp("flags", flags));
581 save_entity(ar, to_save, flags);
582}
585
587{
588 entity_flags flags{};
589 try_load(ar, ser20::make_nvp("flags", flags));
590
591 load_entity(ar, obj.handle, flags);
592}
593
596
597
598
600{
601 hpp::for_each_tuple_type<unravel::all_serializeable_components>(
602 [&](auto index)
603 {
604 using ctype = std::tuple_element_t<decltype(index)::value, unravel::all_serializeable_components>;
605
606 if(!should_save_component<ctype>(obj.entity))
607 {
608 return;
609 }
610
611 auto component = obj.entity.try_get<ctype>();
612
613 const auto type = entt::resolve<ctype>();
614 auto name = entt::get_name(type);
615
616 if(component)
617 {
618 try_save(ar, ser20::make_nvp("has_" + name, true));
619 try_save(ar, ser20::make_nvp(name, *component));
620 }
621
622 });
623}
626
628{
629 hpp::for_each_tuple_type<unravel::all_serializeable_components>(
630 [&](auto index)
631 {
632 using ctype = std::tuple_element_t<decltype(index)::value, unravel::all_serializeable_components>;
633
634 if(!should_load_component<ctype>(obj.entity))
635 {
636 return;
637 }
638
639
640 auto component_type = entt::resolve<ctype>();
641 auto name = entt::get_name(component_type);
642 auto pretty_name = entt::get_pretty_name(component_type);
643
644 auto has_name = "has_" + name;
645 auto pretty_has_name = "Has" + pretty_name;
646
647
648 bool has_component = false;
649 {
650 bool success_has_component = false;
651
652 // success_has_component |= serialize_check(has_name, [&]() -> bool
653 // {
654 // return try_serialize_direct(ar, ser20::make_nvp(pretty_has_name, has_component));
655 // });
656
657 if(!success_has_component)
658 {
659 success_has_component |= serialize_check(has_name, [&]() -> bool
660 {
661 return try_serialize_direct(ar, ser20::make_nvp(has_name, has_component));
662 });
663 }
664 }
665
666 if(has_component)
667 {
668 auto& component = obj.entity.get_or_emplace<ctype>();
669
670 bool success_component = false;
671 {
672 // Legacy support with pretty name
673 // success_component |= serialize_check(name, [&]() -> bool
674 // {
675 // return try_serialize_direct(ar, ser20::make_nvp(pretty_name, component));
676 // });
677 }
678
679 if(!success_component)
680 {
681 success_component |= serialize_check(name, [&]() -> bool
682 {
683 return try_serialize_direct(ar, ser20::make_nvp(name, component));
684 });
685 }
686
687 emit_on_load<ctype>(*obj.entity.registry(), obj.entity.entity());
688
689 }
690
691 if constexpr(std::is_same_v<ctype, id_component>)
692 {
693 auto& comp = obj.entity.get_or_emplace<ctype>();
694 comp.generate_if_nil();
695 }
696 if constexpr(std::is_same_v<ctype, tag_component>)
697 {
698 auto& comp = obj.entity.get_or_emplace<ctype>();
699 (void)comp;
700 }
701 if constexpr(std::is_same_v<ctype, layer_component>)
702 {
703 auto& comp = obj.entity.get_or_emplace<ctype>();
704 (void)comp;
705 }
706
707
708
709 });
710
711
712 // if we are cloning
713 // we need to generate a new id for the entity
714 auto& load_ctx = get_load_context();
715 if(load_ctx.is_cloning())
716 {
717 if(load_ctx.get_clone_mode() != clone_mode_t::cloning_prefab_instance)
718 {
719 // and are not the root of the prefab,
720 obj.entity.remove<prefab_id_component>();
721 }
722
723 auto id_comp = obj.entity.try_get<id_component>();
724 if(id_comp)
725 {
726 id_comp->regenerate_id();
727 }
728 }
729}
732
734{
735 SAVE_FUNCTION_NAME(ar, obj.components.entity);
736 try_save(ar, ser20::make_nvp("components", obj.components));
737}
740
742{
743 entt::handle e;
744 LOAD_FUNCTION_NAME(ar, e);
745
746 if(e)
747 {
748 bool pushed = push_entity_path(e);
749 obj.components.entity = e;
750 try_load(ar, ser20::make_nvp("components", obj.components));
751 pop_entity_path(pushed);
752 }
753}
756
757} // namespace ser20
758
759namespace unravel
760{
761namespace
762{
763
764void flatten_hierarchy(entt::const_handle obj, std::vector<entity_data<entt::const_handle>>& entities)
765{
766 auto& trans_comp = obj.get<transform_component>();
767 const auto& children = trans_comp.get_children();
768
770 data.components.entity = obj;
771
772 entities.emplace_back(data);
773
774 entities.reserve(entities.size() + children.size());
775 for(const auto& child : children)
776 {
777 flatten_hierarchy(child, entities);
778 }
779}
780
781template<typename Archive>
782void save_to_archive(Archive& ar, entt::const_handle obj)
783{
784 bool pushed = push_save_context();
785
786 bool is_root = obj.all_of<root_component>();
787 if(!is_root)
788 {
789 const_handle_cast(obj).emplace<root_component>();
790 }
791
792 auto& trans_comp = obj.get<transform_component>();
793
794 std::vector<entity_data<entt::const_handle>> entities;
795 flatten_hierarchy(obj, entities);
796
797 try_save(ar, ser20::make_nvp("entities", entities));
798
799 static const std::string version = "1.0.0";
800 try_save(ar, ser20::make_nvp("version", version));
801
802 if(!is_root)
803 {
804 const_handle_cast(obj).erase<root_component>();
805 }
806
807 pop_save_context(pushed);
808}
809
810template<typename Archive>
811auto load_from_archive_impl(Archive& ar, entt::registry& registry) -> entt::handle
812{
813 std::vector<entity_data<entt::handle>> entities;
814 try_load(ar, ser20::make_nvp("entities", entities));
815
816 std::string version;
817 try_load(ar, ser20::make_nvp("version", version));
818
819 entt::handle result{};
820 if(!entities.empty())
821 {
822 result = entities.front().components.entity;
823 }
824
825 auto& load_ctx = get_load_context();
826 load_ctx.entities.reserve(load_ctx.entities.size() + entities.size());
827 std::move(entities.begin(), entities.end(), std::back_inserter(load_ctx.entities));
828
829 return result;
830}
831
832template<typename Archive>
833void load_from_archive_start(Archive& ar, entt::registry& registry, entt::handle& e)
834{
835 bool pushed = push_load_context(registry);
836
837 e = load_from_archive_impl(ar, registry);
838
839 pop_load_context(pushed);
840}
841
842template<typename Archive>
843auto load_from_archive_start(Archive& ar, entt::registry& registry) -> entt::handle
844{
845 bool pushed = push_load_context(registry);
846
847 auto obj = load_from_archive_impl(ar, registry);
848
849 pop_load_context(pushed);
850
851 return obj;
852}
853
854template<typename Archive>
855void load_from_archive(Archive& ar, entt::handle& obj)
856{
857 obj = load_from_archive_start(ar, *obj.registry());
858}
859
860template<typename Archive>
861void save_to_archive(Archive& ar, const entt::registry& reg)
862{
863 bool pushed = push_save_context();
864
865 size_t count = 0;
866 reg.view<root_component, transform_component>().each(
867 [&](auto e, auto&& comp1, auto&& comp2)
868 {
869 count++;
870 });
871
872 try_save(ar, ser20::make_nvp("entities_count", count));
873 reg.view<root_component, transform_component>().each(
874 [&](auto e, auto&& comp1, auto&& comp2)
875 {
876 save_to_archive(ar, entt::const_handle(reg, e));
877 });
878
879 pop_save_context(pushed);
880}
881
882template<typename Archive>
883void load_from_archive(Archive& ar, entt::registry& reg)
884{
885 size_t count = 0;
886 try_load(ar, ser20::make_nvp("entities_count", count));
887
888 bool pushed = push_load_context(reg);
889
890 for(size_t i = 0; i < count; ++i)
891 {
892 entt::handle e(reg, reg.create());
893
894 load_from_archive(ar, e);
895 }
896
897 pop_load_context(pushed);
898}
899
900} // namespace
901
902void save_to_stream(std::ostream& stream, entt::const_handle obj)
903{
904 if(stream.good())
905 {
906 // APPLOG_INFO_PERF(std::chrono::microseconds);
907
908 try
909 {
910 auto ar = ser20::create_oarchive_associative(stream);
911 save_to_archive(ar, obj);
912 }
913 catch(const std::exception& e)
914 {
915 APPLOG_ERROR("Failed to save entity to stream: {}", e.what());
916 }
917 }
918}
919
920void save_to_file(const std::string& absolute_path, entt::const_handle obj)
921{
922 writing = true;
923 {
924 std::ofstream stream(absolute_path);
925
926 bool pushed = push_save_context();
927 auto& save_ctx = get_save_context();
928 save_ctx.save_source = obj;
929 save_ctx.to_prefab = true;
930
931 save_to_stream(stream, obj);
932
933 save_ctx.to_prefab = false;
934 save_ctx.save_source = {};
935 pop_save_context(pushed);
936 }
937 writing = false;
938}
939
940void save_to_stream_bin(std::ostream& stream, entt::const_handle obj)
941{
942 if(stream.good())
943 {
944 ser20::oarchive_binary_t ar(stream);
945
946 save_to_archive(ar, obj);
947 }
948}
949
950void save_to_file_bin(const std::string& absolute_path, entt::const_handle obj)
951{
952 std::ofstream stream(absolute_path, std::ios::binary);
953
954 bool pushed = push_save_context();
955 auto& save_ctx = get_save_context();
956 save_ctx.save_source = obj;
957 save_ctx.to_prefab = true;
958
959 save_to_stream_bin(stream, obj);
960 save_ctx.to_prefab = false;
961 save_ctx.save_source = {};
962 pop_save_context(pushed);
963}
964
965void load_from_view(std::string_view view, entt::handle& obj)
966{
967 if(!view.empty())
968 {
969 // APPLOG_INFO_PERF(std::chrono::microseconds);
970
971 try
972 {
973 auto ar = ser20::create_iarchive_associative(view.data(), view.size());
974 load_from_archive(ar, obj);
975 }
976 catch(const std::exception& e)
977 {
978 APPLOG_ERROR("Failed to load entity from view: {}", e.what());
979 }
980 }
981}
982
983void load_from_stream(std::istream& stream, entt::handle& obj)
984{
985 if(stream.good())
986 {
987 // APPLOG_INFO_PERF(std::chrono::microseconds);
988 try
989 {
990 auto ar = ser20::create_iarchive_associative(stream);
991 load_from_archive(ar, obj);
992 }
993 catch(const std::exception& e)
994 {
995 APPLOG_ERROR("Failed to load entity from stream: {}", e.what());
996 }
997 }
998}
999
1000void load_from_file(const std::string& absolute_path, entt::handle& obj)
1001{
1002 fs::file_istream input(absolute_path);
1003 if(!input.is_open())
1004 {
1005 return;
1006 }
1007 load_from_stream(input, obj);
1008}
1009
1010void load_from_stream_bin(std::istream& stream, entt::handle& obj)
1011{
1012 if(stream.good())
1013 {
1014 // APPLOG_INFO_PERF(std::chrono::microseconds);
1015 try
1016 {
1017 ser20::iarchive_binary_t ar(stream);
1018 load_from_archive(ar, obj);
1019 }
1020 catch(const std::exception& e)
1021 {
1022 APPLOG_ERROR("Failed to load entity from stream: {}", e.what());
1023 }
1024 }
1025}
1026
1027void load_from_file_bin(const std::string& absolute_path, entt::handle& obj)
1028{
1029 fs::file_istream input(absolute_path, std::ios::binary);
1030 if(!input.is_open())
1031 {
1032 return;
1033 }
1035}
1036
1038 entt::registry& registry,
1039 entt::handle& obj) -> bool
1040{
1041 reading = true;
1042 bool result = true;
1043
1044 // copy here to keep it alive
1045 auto prefab = pfb.get();
1046 const auto& buffer = prefab->buffer.data;
1047
1048 if(!buffer.empty())
1049 {
1050 // APPLOG_INFO_PERF(std::chrono::microseconds);
1051
1052 try
1053 {
1054 auto ar = ser20::create_iarchive_associative(buffer.data(), buffer.size());
1055
1056 bool pushed = push_load_context(registry);
1057
1058 auto& load_ctx = get_load_context();
1059
1060 add_to_uid_mapping(obj);
1061
1062 load_from_archive_start(ar, registry, obj);
1063
1065
1066 pop_load_context(pushed);
1067
1068
1069 if(obj)
1070 {
1071 auto& pfb_comp = obj.get_or_emplace<prefab_component>();
1072 pfb_comp.source = pfb;
1073 }
1074 }
1075 catch(const std::exception& e)
1076 {
1077 result = false;
1078 bool r = reading;
1079 bool w = writing;
1080 APPLOG_ERROR("Broken prefab {}", pfb.id());
1081 }
1082 }
1083
1084 reading = false;
1085 return result;
1086}
1087
1088void regenerate_entity_uids(entt::handle obj)
1089{
1090 auto& id_comp = obj.get_or_emplace<id_component>();
1091 id_comp.regenerate_id();
1092 if(auto transform = obj.try_get<transform_component>())
1093 {
1094 for(auto child : transform->get_children())
1095 {
1097 }
1098 }
1099}
1100
1101auto load_from_prefab(const asset_handle<prefab>& pfb, entt::registry& registry) -> entt::handle
1102{
1103 reading = true;
1104 entt::handle obj;
1105
1106 // copy here to keep it alive
1107 auto prefab = pfb.get();
1108 const auto& buffer = prefab->buffer.data;
1109
1110 if(!buffer.empty())
1111 {
1112 // APPLOG_INFO_PERF(std::chrono::microseconds);
1113
1114 try
1115 {
1116 auto ar = ser20::create_iarchive_associative(buffer.data(), buffer.size());
1117
1118 obj = load_from_archive_start(ar, registry);
1119
1120 if(obj)
1121 {
1123 auto& pfb_comp = obj.get_or_emplace<prefab_component>();
1124 pfb_comp.source = pfb;
1125 }
1126 }
1127 catch(const std::exception& e)
1128 {
1129 bool r = reading;
1130 bool w = writing;
1131 APPLOG_ERROR("Broken prefab {}", pfb.id());
1132 }
1133 }
1134
1135 reading = false;
1136
1137 return obj;
1138}
1139auto load_from_prefab_bin(const asset_handle<prefab>& pfb, entt::registry& registry) -> entt::handle
1140{
1141 entt::handle obj;
1142
1143 // copy here to keep it alive
1144 auto prefab = pfb.get();
1145 auto buffer = prefab->buffer.get_stream_buf();
1146 std::istream stream(&buffer);
1147 if(stream.good())
1148 {
1149 // APPLOG_INFO_PERF(std::chrono::microseconds);
1150
1151 try
1152 {
1153 ser20::iarchive_binary_t ar(stream);
1154
1155 obj = load_from_archive_start(ar, registry);
1156
1157 if(obj)
1158 {
1160 auto& pfb_comp = obj.get_or_emplace<prefab_component>();
1161 pfb_comp.source = pfb;
1162 }
1163 }
1164 catch(const std::exception& e)
1165 {
1166 APPLOG_ERROR("Broken prefab {}", pfb.id());
1167 }
1168 }
1169
1170 return obj;
1171}
1172
1173auto has_prefab_component(const entt::const_handle& obj) -> bool
1174{
1175
1176 if(!obj.valid())
1177 {
1178 return false;
1179 }
1180 if(obj.all_of<prefab_component>())
1181 {
1182 return true;
1183 }
1184
1185 if(auto transform_comp = obj.try_get<transform_component>())
1186 {
1187 for(auto child : transform_comp->get_children())
1188 {
1189 if(has_prefab_component(child))
1190 {
1191 return true;
1192 }
1193 }
1194 }
1195 return false;
1196}
1197
1198void clone_entity_from_stream(entt::const_handle src_obj, entt::handle& dst_obj)
1199{
1200 // APPLOG_INFO_PERF(std::chrono::microseconds);
1201
1202 bool has_prefab_instances = has_prefab_component(src_obj);
1203
1204 auto clone_mode = has_prefab_instances ? clone_mode_t::cloning_prefab_instance : clone_mode_t::cloning_object;
1205 bool pushed = push_save_context();
1206 auto& save_ctx = get_save_context();
1207 save_ctx.save_source = src_obj;
1208 save_ctx.to_prefab = false;
1209 save_ctx.clone_mode = clone_mode;
1210 std::stringstream ss;
1211 {
1212 save_to_stream(ss, src_obj);
1213 }
1214 save_ctx.to_prefab = false;
1215 save_ctx.save_source = {};
1216 save_ctx.clone_mode = clone_mode_t::none;
1217 pop_save_context(pushed);
1218
1219
1220 pushed = push_load_context(*dst_obj.registry());
1221 auto& load_ctx = get_load_context();
1222 load_ctx.clone_mode = clone_mode;
1223
1224 {
1225 load_from(ss, dst_obj);
1226 }
1227 load_ctx.clone_mode = clone_mode_t::none;
1228 pop_load_context(pushed);
1229}
1230
1231void save_to_stream(std::ostream& stream, const scene& scn)
1232{
1233 if(stream.good())
1234 {
1235 // APPLOG_INFO_PERF(std::chrono::microseconds);
1236
1237 try
1238 {
1239 auto ar = ser20::create_oarchive_associative(stream);
1240 save_to_archive(ar, *scn.registry);
1241 }
1242 catch(const std::exception& e)
1243 {
1244 APPLOG_ERROR("Failed to save scene to stream: {}", e.what());
1245 }
1246 }
1247}
1248void save_to_file(const std::string& absolute_path, const scene& scn)
1249{
1250 // APPLOG_INFO_PERF(std::chrono::microseconds);
1251
1252 std::ofstream stream(absolute_path);
1253 save_to_stream(stream, scn);
1254}
1255void save_to_stream_bin(std::ostream& stream, const scene& scn)
1256{
1257 if(stream.good())
1258 {
1259 // APPLOG_INFO_PERF(std::chrono::microseconds);
1260
1261 try
1262 {
1263 ser20::oarchive_binary_t ar(stream);
1264 save_to_archive(ar, *scn.registry);
1265 }
1266 catch(const std::exception& e)
1267 {
1268 APPLOG_ERROR("Failed to save scene to stream: {}", e.what());
1269 }
1270 }
1271}
1272void save_to_file_bin(const std::string& absolute_path, const scene& scn)
1273{
1274 std::ofstream stream(absolute_path, std::ios::binary);
1275 save_to_stream_bin(stream, scn);
1276}
1277
1278void load_from_view(std::string_view view, scene& scn)
1279{
1280 if(!view.empty())
1281 {
1282 // APPLOG_INFO_PERF(std::chrono::microseconds);
1283
1284 try
1285 {
1286 auto ar = ser20::create_iarchive_associative(view.data(), view.size());
1287 load_from_archive(ar, *scn.registry);
1288 }
1289 catch(const std::exception& e)
1290 {
1291 APPLOG_ERROR("Failed to load scene from view: {}", e.what());
1292 }
1293 }
1294}
1295
1296void load_from_stream(std::istream& stream, scene& scn)
1297{
1298 if(stream.good())
1299 {
1300 // APPLOG_INFO_PERF(std::chrono::microseconds);
1301
1302 stream.seekg(0);
1303
1304 try
1305 {
1306 auto ar = ser20::create_iarchive_associative(stream);
1307 load_from_archive(ar, *scn.registry);
1308 }
1309 catch(const std::exception& e)
1310 {
1311 APPLOG_ERROR("Failed to load scene from stream: {}", e.what());
1312 }
1313 }
1314}
1315void load_from_file(const std::string& absolute_path, scene& scn)
1316{
1317 fs::file_istream input(absolute_path);
1318 if(!input.is_open())
1319 {
1320 return;
1321 }
1322 load_from_stream(input, scn);
1323}
1324void load_from_stream_bin(std::istream& stream, scene& scn)
1325{
1326 if(stream.good())
1327 {
1328 // APPLOG_INFO_PERF(std::chrono::microseconds);
1329
1330 stream.seekg(0);
1331
1332 try
1333 {
1334 scn.unload();
1335 ser20::iarchive_binary_t ar(stream);
1336 load_from_archive(ar, *scn.registry);
1337 }
1338 catch(const std::exception& e)
1339 {
1340 APPLOG_ERROR("Failed to load scene from stream: {}", e.what());
1341 }
1342 }
1343}
1344void load_from_file_bin(const std::string& absolute_path, scene& scn)
1345{
1346 fs::file_istream input(absolute_path, std::ios::binary);
1347 if(!input.is_open())
1348 {
1349 return;
1350 }
1352}
1353
1355{
1356 // copy here to keep it alive
1357 auto prefab = pfb.get();
1358 const auto& buffer = prefab->buffer.data;
1359
1360 if(!buffer.empty())
1361 {
1362 // APPLOG_INFO_PERF(std::chrono::microseconds);
1363 try
1364 {
1365 scn.unload();
1366 auto ar = ser20::create_iarchive_associative(buffer.data(), buffer.size());
1367 load_from_archive(ar, *scn.registry);
1368 }
1369 catch(const std::exception& e)
1370 {
1371 APPLOG_ERROR("Failed to load scene from prefab: {}", e.what());
1372 }
1373 }
1374
1375 return true;
1376}
1378{
1379 // copy here to keep it alive
1380 auto prefab = pfb.get();
1381 auto buffer = prefab->buffer.get_stream_buf();
1382
1383 // APPLOG_INFO_PERF(std::chrono::microseconds);
1384 std::istream stream(&buffer);
1385 if(!stream.good())
1386 {
1387 return false;
1388 }
1389
1390 load_from_stream_bin(stream, scn);
1391
1392 return true;
1393}
1394
1395void clone_scene_from_stream(const scene& src_scene, scene& dst_scene)
1396{
1397 dst_scene.unload();
1398
1399 auto& src = src_scene.registry;
1400 auto& dst = dst_scene.registry;
1401
1402 // APPLOG_INFO_PERF(std::chrono::microseconds);
1403
1404 src->view<root_component, transform_component>().each(
1405 [&](auto e, auto&& comp1, auto&& comp2)
1406 {
1407 std::stringstream ss;
1408 save_to_stream(ss, src_scene.create_handle(e));
1409
1410 auto e_clone = dst_scene.registry->create();
1411 auto e_clone_obj = dst_scene.create_handle(e_clone);
1412
1413 load_from(ss, e_clone_obj);
1414 });
1415}
1416} // namespace unravel
stdio-backed input stream. Accepts std::ios open flags (e.g. std::ios::binary) when opening a
Component that handles transformations (position, rotation, scale, etc.) in the ACE framework.
auto get_children() const noexcept -> const std::vector< entt::handle > &
Gets the child entities.
const char * id
uint16_t view
uint16_t index
std::vector< render_pass_node > children
std::string name
Definition hub.cpp:33
#define APPLOG_ERROR(...)
Definition logging.h:20
texture_job_type type
auto get_pretty_name(const meta_type &t) -> std::string
auto get_name(const meta_type &t) -> std::string
Definition yaml.hpp:46
auto load_entity_from_prefab_uid(Archive &ar, entt::handle &obj, entity_flags flags) -> bool
Definition entity.cpp:385
auto load_entity_from_uuid(Archive &ar, entt::handle &obj, entity_flags flags) -> bool
Definition entity.cpp:405
void load_entity(Archive &ar, entt::handle &obj, entity_flags flags)
Definition entity.cpp:450
auto load_entity_from_id(Archive &ar, entt::handle &obj, entity_flags flags) -> bool
Definition entity.cpp:335
auto create_oarchive_associative(std::ostream &stream)
BinaryInputArchive iarchive_binary_t
void save_entity_uuid(Archive &ar, const entt::const_handle &obj)
Definition entity.cpp:302
auto create_iarchive_associative(std::istream &stream)
auto should_load_component(const entt::handle &obj) -> bool
Definition entity.cpp:502
simd::JSONOutputArchive oarchive_associative_t
auto should_save_component(const entt::const_handle &obj) -> bool
Definition entity.cpp:473
BinaryOutputArchive oarchive_binary_t
simd::JSONInputArchive iarchive_associative_t
void save_entity(Archive &ar, const entt::const_handle &obj, entity_flags flags)
Definition entity.cpp:317
void save_entity_id(Archive &ar, const entt::const_handle &obj)
Definition entity.cpp:279
void save_entity_prefab_uid(Archive &ar, const entt::const_handle &obj)
Definition entity.cpp:286
auto get_path_context() -> path_context *
auto push_save_context() -> bool
Definition entity.cpp:118
std::atomic_bool writing
Definition entity.cpp:55
void regenerate_entity_uids(entt::handle obj)
Definition entity.cpp:1088
@ resolve_with_loaded
Definition entity.cpp:245
@ resolve_with_existing
Definition entity.cpp:244
auto are_related(entt::const_handle lhs, entt::const_handle rhs) -> bool
Definition entity.cpp:236
void save_to_stream_bin(std::ostream &stream, entt::const_handle obj)
Definition entity.cpp:940
void load_from(Stream &stream, T &scn)
Definition entity.hpp:158
auto push_entity_path(entt::const_handle obj) -> bool
Definition entity.cpp:248
void clone_scene_from_stream(const scene &src_scene, scene &dst_scene)
Definition entity.cpp:1395
thread_local post_load_callbacks * post_load_callbacks_ptr
Definition entity.cpp:54
void pop_load_context(bool push_result)
Definition entity.cpp:69
void save_to_file_bin(const std::string &absolute_path, const animation_clip &obj)
auto get_post_load_callbacks() -> const post_load_callbacks *
Definition entity.cpp:113
auto push_load_context(entt::registry &registry) -> bool
Definition entity.cpp:58
void pop_on_load_callbacks()
Definition entity.cpp:104
void pop_entity_path(bool pushed)
Definition entity.cpp:262
auto is_parent(entt::const_handle potential_parent, entt::const_handle child) -> bool
Definition entity.cpp:191
thread_local load_context * load_ctx_ptr
Definition entity.cpp:52
auto get_save_context() -> save_context &
Definition entity.cpp:139
void load_from_file(const std::string &absolute_path, animation_clip &obj)
auto get_load_context() -> load_context &
Definition entity.cpp:90
void save_to_file(const std::string &absolute_path, const animation_clip &obj)
std::atomic_bool reading
Definition entity.cpp:56
auto load_from_prefab_bin(const asset_handle< prefab > &pfb, entt::registry &registry) -> entt::handle
Definition entity.cpp:1139
void add_to_uid_mapping(entt::handle &obj, bool recursive=true)
Definition entity.cpp:144
auto load_from_stream(std::istream &stream, entt::handle e, script_component::script_object &obj) -> bool
void load_from_stream_bin(std::istream &stream, entt::handle &obj)
Definition entity.cpp:1010
auto load_from_prefab_out(const asset_handle< prefab > &pfb, entt::registry &registry, entt::handle &obj) -> bool
Definition entity.cpp:1037
thread_local save_context * save_ctx_ptr
Definition entity.cpp:53
void load_from_file_bin(const std::string &absolute_path, animation_clip &obj)
void pop_save_context(bool push_result)
Definition entity.cpp:129
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, script_component > all_serializeable_components
auto has_prefab_component(const entt::const_handle &obj) -> bool
Definition entity.cpp:1173
auto find_root(entt::const_handle e) -> entt::const_handle
Definition entity.cpp:220
auto const_handle_cast(entt::const_handle chandle) -> entt::handle
Definition entity.cpp:29
void load_from_view(std::string_view view, entt::handle &obj)
Definition entity.cpp:965
void push_on_load_callbacks(const post_load_callbacks &callbacks)
Definition entity.cpp:96
void emit_on_load(entt::registry &reg, entt::entity e)
Definition scene.h:56
auto as_span(const std::vector< entity_data< entt::handle > > &entities) -> hpp::span< const entt::handle >
Definition entity.cpp:36
void cleanup_uid_mapping()
Definition entity.cpp:176
auto load_from_prefab(const asset_handle< prefab > &pfb, entt::registry &registry) -> entt::handle
Definition entity.cpp:1101
void clone_entity_from_stream(entt::const_handle src_obj, entt::handle &dst_obj)
Definition entity.cpp:1198
auto save_to_stream(std::ostream &stream, entt::const_handle e, const script_component::script_object &obj) -> bool
#define SAVE_INSTANTIATE(cls, Archive)
#define LOAD(cls)
auto try_save(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
auto try_serialize_direct(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
#define LOAD_INSTANTIATE(cls, Archive)
#define LOAD_FUNCTION_NAME
auto serialize_check(const std::string &name, F &&serialize_callback) -> bool
#define SAVE(cls)
auto try_load(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
#define SAVE_FUNCTION_NAME
Thread-safe handle to an asset.
auto get_stream_buf() const -> membuf
Definition filesystem.h:36
entity_components< Entity > components
Definition entity.hpp:23
hpp::uuid id
The unique identifier for the entity.
Component that provides a unique identifier (UUID) for an entity.
entt::registry * reg
Definition entity.hpp:73
std::vector< entity_data< entt::handle > > entities
Definition entity.hpp:71
Component that holds a reference to a prefab asset and tracks property overrides.
asset_handle< prefab > source
Handle to the prefab asset.
Component that provides a unique identifier (UUID) for a prefab.
Represents a generic prefab with a buffer for serialized data.
Definition prefab.h:18
fs::stream_buffer< std::vector< uint8_t > > buffer
Buffer to store serialized data of the prefab.
Definition prefab.h:22
Root component structure for the ACE framework, serves as the base component.
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:70
void unload()
Unloads the scene, removing all entities.
Definition scene.cpp:200
std::unique_ptr< entt::registry > registry
The registry that manages all entities in the scene.
Definition scene.h:187
auto create_handle(entt::entity e) -> entt::handle
Creates an entity in the scene.
Definition scene.cpp:428
gfx::uniform_handle handle
Definition uniform.cpp:9
YAML input and output archives.