1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "entity_collection.h"
17
18 #include <cinttypes>
19 #include <PropertyTools/property_data.h>
20
21 #include <base/containers/fixed_string.h>
22 #include <core/ecs/intf_entity_manager.h>
23 #include <core/log.h>
24 #include <core/property/intf_property_api.h>
25
26 #include "ecs_util.h"
27
28 using namespace BASE_NS;
29 using namespace CORE_NS;
30
SCENE_BEGIN_NAMESPACE()31 SCENE_BEGIN_NAMESPACE()
32
33 EntityCollection::EntityCollection(IEcs& ecs, string_view uri, string_view contextUri)
34 : ecs_(ecs), uri_(uri), contextUri_(contextUri)
35 {}
36
AddListener(IEntityCollection::IListener & listener)37 void EntityCollection::AddListener(IEntityCollection::IListener& listener)
38 {
39 BASE_ASSERT(&listener);
40 listeners_.emplace_back(&listener);
41 }
42
RemoveListener(IEntityCollection::IListener & listener)43 void EntityCollection::RemoveListener(IEntityCollection::IListener& listener)
44 {
45 BASE_ASSERT(&listener);
46 for (size_t i = 0; i < listeners_.size(); ++i) {
47 if (&listener == listeners_[i]) {
48 listeners_.erase(listeners_.begin() + i);
49 return;
50 }
51 }
52
53 // trying to remove a non-existent listener.
54 BASE_ASSERT(true);
55 }
56
GetEcs() const57 IEcs& EntityCollection::GetEcs() const
58 {
59 return ecs_;
60 }
61
GetUri() const62 string EntityCollection::GetUri() const
63 {
64 return uri_;
65 }
66
SetUri(const BASE_NS::string & uri)67 void EntityCollection::SetUri(const BASE_NS::string& uri)
68 {
69 uri_ = uri;
70 }
71
GetContextUri() const72 string EntityCollection::GetContextUri() const
73 {
74 return contextUri_;
75 }
76
GetSrc() const77 string EntityCollection::GetSrc() const
78 {
79 return src_;
80 }
81
SetSrc(string_view src)82 void EntityCollection::SetSrc(string_view src)
83 {
84 src_ = src;
85 MarkModified(true);
86 }
87
GetType() const88 string EntityCollection::GetType() const
89 {
90 return type_;
91 }
92
SetType(string_view type)93 void EntityCollection::SetType(string_view type)
94 {
95 type_ = type;
96 MarkModified(true);
97 }
98
GetEntityCount() const99 size_t EntityCollection::GetEntityCount() const
100 {
101 return entities_.size();
102 }
103
GetEntity(size_t collectionIndex) const104 EntityReference EntityCollection::GetEntity(size_t collectionIndex) const
105 {
106 BASE_ASSERT(collectionIndex < entities_.size());
107 if (collectionIndex >= entities_.size()) {
108 return EntityReference {};
109 }
110 return entities_[collectionIndex];
111 }
112
GetEntity(string_view localContextId) const113 EntityReference EntityCollection::GetEntity(string_view localContextId) const
114 {
115 const auto it = entityIdentifiers_.find(localContextId);
116 if (it != entityIdentifiers_.end()) {
117 return it->second;
118 }
119 const auto itt = namedEntities_.find(localContextId);
120 if (itt != namedEntities_.end()) {
121 return itt->second;
122 }
123 return EntityReference {};
124 }
125
GetEntityRecursive(string_view localContextId) const126 EntityReference EntityCollection::GetEntityRecursive(string_view localContextId) const
127 {
128 auto ret = GetEntity(localContextId);
129
130 if (!CORE_NS::EntityUtil::IsValid(ret)) {
131 for (auto& it : collections_) {
132 ret = it->GetEntityRecursive(localContextId);
133 if (CORE_NS::EntityUtil::IsValid(ret)) {
134 break;
135 }
136 }
137 }
138
139 return ret;
140 }
141
RemoveEntityRecursive(CORE_NS::Entity entity)142 void EntityCollection::RemoveEntityRecursive(CORE_NS::Entity entity)
143 {
144 for (auto entityRef = entities_.cbegin(); entityRef != entities_.cend();) {
145 if (entity == *entityRef) {
146 entityRef = entities_.erase(entityRef);
147 } else {
148 entityRef++;
149 }
150 }
151
152 for (auto entityRef = namedEntities_.cbegin(); entityRef != namedEntities_.cend();) {
153 if (entityRef->second == entity) {
154 entityRef = namedEntities_.erase(entityRef);
155 } else {
156 ++entityRef;
157 }
158 }
159
160 auto uniqueIdentifier = GetUniqueIdentifier(entity);
161 if (!uniqueIdentifier.empty()) {
162 entityIdentifiers_.erase(uniqueIdentifier);
163 }
164
165 for (auto& it : collections_) {
166 it->RemoveEntityRecursive(entity);
167 }
168 }
169
GetEntities() const170 array_view<const EntityReference> EntityCollection::GetEntities() const
171 {
172 return entities_;
173 }
174
AddEntity(EntityReference entity)175 void EntityCollection::AddEntity(EntityReference entity)
176 {
177 AddEntities({ &entity, 1 });
178 }
179
AddEntities(array_view<const EntityReference> entities)180 void EntityCollection::AddEntities(array_view<const EntityReference> entities)
181 {
182 entities_.reserve(entities_.size() + entities.size());
183 for (const auto& entity : entities) {
184 if (entity != Entity {}) {
185 // TODO: make sure that the same entity is not added twice.
186 entities_.emplace_back(entity);
187 } else {
188 CORE_LOG_D("Trying to add null entity reference to a collection");
189 }
190 }
191 MarkModified(true);
192 }
193
RemoveEntity(EntityReference entity)194 bool EntityCollection::RemoveEntity(EntityReference entity)
195 {
196 for (size_t i = 0; i < entities_.size(); ++i) {
197 if (entities_[i] == entity) {
198 entities_.erase(entities_.begin() + i);
199
200 // Also remove any related id mappings.
201 for (auto it = namedEntities_.begin(); it != namedEntities_.end(); ++it) {
202 if (it->second == entity) {
203 namedEntities_.erase(it);
204 break;
205 }
206 }
207
208 auto uniqueIdentifier = GetUniqueIdentifier(entity);
209 if (!uniqueIdentifier.empty()) {
210 entityIdentifiers_.erase(uniqueIdentifier);
211 }
212
213 // TODO: If this collection is overriding another "template" collection, when removing entities, we
214 // need to remember that and the information about deletion needs to be serialized. Maybe check if
215 // (src_.empty()). However this also needs to work with undo
216
217 MarkModified(true);
218 return true;
219 }
220 }
221
222 // Not found. Check the sub-collections.
223 for (auto& collection : collections_) {
224 BASE_ASSERT(collection);
225 if (collection->RemoveEntity(entity)) {
226 MarkModified(true);
227 return true;
228 }
229 }
230
231 return false;
232 }
233
RemoveEntities(array_view<const EntityReference> entities)234 void EntityCollection::RemoveEntities(array_view<const EntityReference> entities)
235 {
236 for (auto& entity : entities) {
237 RemoveEntity(entity);
238 }
239 }
240
SetId(string_view id,EntityReference entity)241 void EntityCollection::SetId(string_view id, EntityReference entity)
242 {
243 namedEntities_[id] = entity;
244 }
245
GetId(Entity entity) const246 string_view EntityCollection::GetId(Entity entity) const
247 {
248 for (auto& it : namedEntities_) {
249 if (it.second == entity) {
250 return it.first;
251 }
252 }
253 return {};
254 }
255
GetIdRecursive(Entity entity) const256 string_view EntityCollection::GetIdRecursive(Entity entity) const
257 {
258 auto ret = GetId(entity);
259 if (ret.empty()) {
260 for (auto& it : collections_) {
261 ret = it->GetIdRecursive(entity);
262 if (!ret.empty()) {
263 break;
264 }
265 }
266 }
267 return ret;
268 }
269
SetUniqueIdentifier(string_view id,EntityReference entity)270 void EntityCollection::SetUniqueIdentifier(string_view id, EntityReference entity)
271 {
272 entityIdentifiers_[id] = entity;
273 }
274
GetUniqueIdentifier(Entity entity) const275 string_view EntityCollection::GetUniqueIdentifier(Entity entity) const
276 {
277 for (auto& it : entityIdentifiers_) {
278 if (it.second == entity) {
279 return it.first;
280 }
281 }
282 return {};
283 }
284
GetUniqueIdentifierRecursive(Entity entity) const285 string_view EntityCollection::GetUniqueIdentifierRecursive(Entity entity) const
286 {
287 auto ret = GetUniqueIdentifier(entity);
288 if (ret.empty()) {
289 for (auto& it : collections_) {
290 ret = it->GetUniqueIdentifierRecursive(entity);
291 if (!ret.empty()) {
292 break;
293 }
294 }
295 }
296 return ret;
297 }
298
GetSubCollectionCount() const299 size_t EntityCollection::GetSubCollectionCount() const
300 {
301 return collections_.size();
302 }
303
GetSubCollection(size_t index)304 IEntityCollection* EntityCollection::GetSubCollection(size_t index)
305 {
306 if (index < 0 || index >= collections_.size()) {
307 return nullptr;
308 }
309 return collections_.at(index).get();
310 }
311
GetSubCollection(size_t index) const312 const IEntityCollection* EntityCollection::GetSubCollection(size_t index) const
313 {
314 if (index < 0 || index >= collections_.size()) {
315 return nullptr;
316 }
317 return collections_.at(index).get();
318 }
319
GetSubCollectionIndex(string_view uri) const320 int32_t EntityCollection::GetSubCollectionIndex(string_view uri) const
321 {
322 for (size_t i = 0; i < collections_.size(); ++i) {
323 BASE_ASSERT(collections_[i]);
324 if (collections_[i]->GetUri() == uri) {
325 return static_cast<int32_t>(i);
326 }
327 }
328 return -1;
329 }
330
GetSubCollectionIndexByRoot(Entity entity) const331 int32_t EntityCollection::GetSubCollectionIndexByRoot(Entity entity) const
332 {
333 if (entity != Entity {}) {
334 for (size_t i = 0; i < collections_.size(); ++i) {
335 BASE_ASSERT(collections_[i]);
336 if (collections_[i]->GetEntity("/") == entity) {
337 return static_cast<int32_t>(i);
338 }
339 }
340 }
341 return -1;
342 }
343
AddSubCollection(string_view uri,string_view contextUri,bool serializable)344 IEntityCollection& EntityCollection::AddSubCollection(string_view uri, string_view contextUri, bool serializable)
345 {
346 auto collection = EntityCollection::Ptr { new EntityCollection(ecs_, uri, contextUri) };
347 collection->SetSerialized(serializable);
348 collections_.emplace_back(move(collection));
349
350 // listen to changes in subcollection
351 collections_.back()->AddListener(*this);
352
353 if (serializable) {
354 MarkModified(true);
355 }
356 return *collections_.back();
357 }
358
AddSubCollectionClone(IEntityCollection & collection,string_view uri)359 IEntityCollection& EntityCollection::AddSubCollectionClone(IEntityCollection& collection, string_view uri)
360 {
361 // TODO: use just the public api
362 collections_.emplace_back(EntityCollection::Ptr { new EntityCollection(ecs_, uri, collection.GetContextUri()) });
363 auto& ec = *collections_.back();
364 static_cast<EntityCollection&>(collection).ClonePrivate(ec);
365
366 // listen to changes in subcollection
367 ec.AddListener(*this);
368
369 MarkModified(true);
370 return ec;
371 }
372
RemoveSubCollection(size_t index)373 void EntityCollection::RemoveSubCollection(size_t index)
374 {
375 BASE_ASSERT(index < collections_.size());
376 if (index < collections_.size()) {
377 // stop listening to changes in subcollection
378 auto& ec = collections_.at(index);
379 ec->RemoveListener(*this);
380
381 collections_.erase(collections_.begin() + index);
382 MarkModified(true);
383 }
384 }
385
GetEntityCountRecursive(bool includeDestroyed,bool includeNonSerialized) const386 size_t EntityCollection::GetEntityCountRecursive(bool includeDestroyed, bool includeNonSerialized) const
387 {
388 if (!includeDestroyed && IsMarkedDestroyed()) {
389 return 0;
390 }
391
392 if (!includeNonSerialized && !IsSerialized()) {
393 return 0;
394 }
395
396 auto size = entities_.size();
397 for (const auto& collection : collections_) {
398 BASE_ASSERT(collection);
399 size += collection->GetEntityCountRecursive(includeDestroyed, includeNonSerialized);
400 }
401 return size;
402 }
403
GetEntitiesRecursive(bool includeDestroyed,vector<EntityReference> & entitiesOut,bool includeNonSerialized) const404 void EntityCollection::GetEntitiesRecursive(
405 bool includeDestroyed, vector<EntityReference>& entitiesOut, bool includeNonSerialized) const
406 {
407 // NOTE: Cloning depends on ordering of entitiesOut.
408 entitiesOut.reserve(entitiesOut.size() + GetEntityCountRecursive(includeDestroyed, includeNonSerialized));
409 DoGetEntitiesRecursive(includeDestroyed, includeNonSerialized, entitiesOut);
410 }
411
DoGetEntitiesRecursive(bool includeDestroyed,bool includeNonSerialized,vector<EntityReference> & entitiesOut) const412 void EntityCollection::DoGetEntitiesRecursive(
413 bool includeDestroyed, bool includeNonSerialized, vector<EntityReference>& entitiesOut) const
414 {
415 if (!includeDestroyed && IsMarkedDestroyed()) {
416 return;
417 }
418
419 if (!includeNonSerialized && !IsSerialized()) {
420 return;
421 }
422
423 entitiesOut.insert(entitiesOut.end(), entities_.begin(), entities_.end());
424 for (const auto& collection : collections_) {
425 BASE_ASSERT(collection);
426 collection->DoGetEntitiesRecursive(includeDestroyed, includeNonSerialized, entitiesOut);
427 }
428 }
429
Contains(Entity entity) const430 bool EntityCollection::Contains(Entity entity) const
431 {
432 for (auto& it : entities_) {
433 if (it == entity) {
434 return true;
435 }
436 }
437 for (const auto& collection : collections_) {
438 if (!collection->IsMarkedDestroyed()) {
439 if (collection->Contains(entity)) {
440 return true;
441 }
442 }
443 }
444 return false;
445 }
446
IsExternal(Entity entity) const447 bool EntityCollection::IsExternal(Entity entity) const
448 {
449 for (auto& it : entities_) {
450 if (it == entity) {
451 return false;
452 }
453 }
454 return true;
455 }
456
isSubCollectionRoot(Entity entity) const457 bool EntityCollection::isSubCollectionRoot(Entity entity) const
458 {
459 if (entity == Entity {}) {
460 return false;
461 }
462
463 for (const auto& collection : collections_) {
464 if (collection->GetEntity("/") == entity) {
465 return true;
466 }
467 }
468
469 return false;
470 }
471
GetReference(CORE_NS::Entity entity) const472 CORE_NS::EntityReference EntityCollection::GetReference(CORE_NS::Entity entity) const
473 {
474 if (Contains(entity)) {
475 auto ref = GetEcs().GetEntityManager().GetReferenceCounted(entity);
476
477 // Check that this entity was reference counted already (it should be part of the collection).
478 CORE_ASSERT(ref.GetRefCount() > 1);
479
480 return ref;
481 }
482 return {};
483 }
484
SetActive(bool active)485 void EntityCollection::SetActive(bool active)
486 {
487 isActive_ = active;
488 const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
489
490 auto& em = ecs_.GetEntityManager();
491 for (auto& entity : entities_) {
492 em.SetActive(entity, effectivelyActive);
493 }
494
495 for (auto& collection : collections_) {
496 BASE_ASSERT(collection);
497 collection->SetActive(active);
498 }
499 }
500
IsActive() const501 bool EntityCollection::IsActive() const
502 {
503 return isActive_;
504 }
505
MarkDestroyed(bool destroyed)506 void EntityCollection::MarkDestroyed(bool destroyed)
507 {
508 MarkModified(true);
509 isMarkedDestroyed_ = destroyed;
510 const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
511
512 // Change the active state of entities without changing the active state of the collection itself.
513 auto& em = ecs_.GetEntityManager();
514 for (auto& entity : entities_) {
515 em.SetActive(entity, effectivelyActive);
516 }
517
518 for (auto& collection : collections_) {
519 BASE_ASSERT(collection);
520 collection->MarkDestroyed(destroyed);
521 }
522 }
523
IsMarkedDestroyed() const524 bool EntityCollection::IsMarkedDestroyed() const
525 {
526 return isMarkedDestroyed_;
527 }
528
MarkModified(bool modified)529 void EntityCollection::MarkModified(bool modified)
530 {
531 if (!IsSerialized()) {
532 return;
533 }
534
535 if (isMarkedModified_ != modified) {
536 isMarkedModified_ = modified;
537 for (auto* l : listeners_) {
538 l->ModifiedChanged(*this, modified);
539 }
540 }
541 }
542
MarkModified(bool modified,bool recursive)543 void EntityCollection::MarkModified(bool modified, bool recursive)
544 {
545 if (!IsSerialized()) {
546 return;
547 }
548
549 if (recursive && !collections_.empty()) {
550 for (auto& c : collections_) {
551 c->MarkModified(modified, true);
552 }
553 }
554 MarkModified(modified);
555 }
556
IsMarkedModified() const557 bool EntityCollection::IsMarkedModified() const
558 {
559 return isMarkedModified_;
560 }
561
Clear()562 void EntityCollection::Clear()
563 {
564 serializationInfo_.clear();
565 namedEntities_.clear();
566 entityIdentifiers_.clear();
567 entities_.clear();
568 collections_.clear();
569 listeners_.clear();
570
571 MarkModified(true);
572 }
573
CopyContents(IEntityCollection & srcCollection)574 void EntityCollection::CopyContents(IEntityCollection& srcCollection)
575 {
576 // TODO: use just the public api
577 static_cast<EntityCollection&>(srcCollection).ClonePrivate(*this);
578 MarkModified(true);
579 }
580
AddEntityToSubcollection(BASE_NS::string_view collection,BASE_NS::string_view name,CORE_NS::Entity entity,bool makeUnique)581 void EntityCollection::AddEntityToSubcollection(
582 BASE_NS::string_view collection, BASE_NS::string_view name, CORE_NS::Entity entity, bool makeUnique)
583 {
584 auto collectionIx = GetSubCollectionIndex(collection);
585 if (collectionIx == -1) {
586 AddSubCollection(collection, {}, !makeUnique);
587 collectionIx = GetSubCollectionCount() - 1;
588 }
589
590 if (auto targetCollection = GetSubCollection(collectionIx)) {
591 BASE_NS::string postFixed(name.data(), name.size());
592 if (makeUnique) {
593 postFixed.append(":");
594 postFixed.append(BASE_NS::to_hex(entity.id));
595 }
596 if (!CORE_NS::EntityUtil::IsValid(targetCollection->GetEntity(postFixed))) {
597 auto ref = ecs_.GetEntityManager().GetReferenceCounted(entity);
598 targetCollection->AddEntity(ref);
599 targetCollection->SetUniqueIdentifier(postFixed, ref);
600 }
601 }
602 }
603
IsSerialized() const604 bool EntityCollection::IsSerialized() const
605 {
606 return isSerialized_;
607 }
608
SetSerialized(bool serialize)609 void EntityCollection::SetSerialized(bool serialize)
610 {
611 isSerialized_ = serialize;
612 }
613
614 // TODO: clean up copying.
615 namespace {
CloneEntitiesFromCollection(IEntityCollection & srcCollection,IEntityCollection & dstCollection,IEntityCollection & srcSerializationCollection,IEntityCollection & dstSerializationCollection,array_view<const EntityReference> entities,unordered_map<Entity,Entity> & oldToNew,vector<EntityReference> & clonedOut)616 void CloneEntitiesFromCollection(IEntityCollection& srcCollection, IEntityCollection& dstCollection,
617 IEntityCollection& srcSerializationCollection, IEntityCollection& dstSerializationCollection,
618 array_view<const EntityReference> entities, unordered_map<Entity, Entity>& oldToNew,
619 vector<EntityReference>& clonedOut)
620 {
621 const size_t entityCount = entities.size();
622 for (size_t i = 0; i < entityCount; ++i) {
623 auto srcEntity = entities[i];
624
625 // Copy the serialization and subcollection info.
626 auto subCollectionIndex = srcCollection.GetSubCollectionIndexByRoot(srcEntity);
627 if (subCollectionIndex >= 0) {
628 auto* srcSubCollection = srcCollection.GetSubCollection(subCollectionIndex);
629 auto& dstSubCollection =
630 dstCollection.AddSubCollection(srcSubCollection->GetUri(), srcSubCollection->GetContextUri());
631 dstSubCollection.SetSrc(srcSubCollection->GetSrc());
632
633 // Recursively copy sub collection.
634 CloneEntitiesFromCollection(*srcSubCollection, dstSubCollection, srcSerializationCollection,
635 dstSerializationCollection, srcSubCollection->GetEntities(), oldToNew, clonedOut);
636
637 // Need to find the collection roots manually.
638 // TODO: a bit messy that the colelctions are iterated separately.
639 // TODO: no easy way to get the root entity.
640 const size_t collectionCount = srcSubCollection->GetSubCollectionCount();
641 for (size_t j = 0; j < collectionCount; ++j) {
642 auto* sc = srcSubCollection->GetSubCollection(j);
643 auto root = sc->GetEntity("/");
644 if (root != Entity {}) {
645 CloneEntitiesFromCollection(*srcSubCollection, dstSubCollection, srcSerializationCollection,
646 dstSerializationCollection, { &root, 1 }, oldToNew, clonedOut);
647 }
648 }
649
650 } else if (!srcCollection.IsExternal(srcEntity)) {
651 auto dstEntity = CloneEntityReference(srcCollection.GetEcs(), srcEntity, dstCollection.GetEcs());
652
653 clonedOut.emplace_back(dstEntity);
654 oldToNew[srcEntity] = dstEntity;
655
656 dstCollection.AddEntity(dstEntity);
657 auto id = srcCollection.GetId(srcEntity);
658 if (!id.empty()) {
659 dstCollection.SetId(id, dstEntity);
660 }
661
662 // Note that the root collections contain all the serialization info.
663 for (auto& cm : srcCollection.GetEcs().GetComponentManagers()) {
664 auto* properties = srcSerializationCollection.GetSerializedProperties(srcEntity, cm->GetUid());
665 if (properties) {
666 dstSerializationCollection.MarkComponentSerialized(dstEntity, cm->GetUid(), true);
667 for (auto& property : *properties) {
668 dstSerializationCollection.MarkPropertySerialized(dstEntity, cm->GetUid(), property, true);
669 }
670 }
671 }
672 }
673 }
674 }
675 } // namespace
676
CopyContentsWithSerialization(IEntityCollection & srcCollection,array_view<const EntityReference> entities)677 vector<EntityReference> EntityCollection::CopyContentsWithSerialization(
678 IEntityCollection& srcCollection, array_view<const EntityReference> entities)
679 {
680 unordered_map<Entity, Entity> oldToNew;
681
682 vector<EntityReference> entitiesOut;
683 entitiesOut.reserve(entities.size());
684 CloneEntitiesFromCollection(srcCollection, *this, srcCollection, *this, entities, oldToNew, entitiesOut);
685
686 for (auto& entity : entitiesOut) {
687 RewriteEntityReferences(GetEcs(), entity, oldToNew);
688 }
689 return entitiesOut;
690 }
691
CopyContentsWithSerialization(IEntityCollection & srcCollection)692 vector<EntityReference> EntityCollection::CopyContentsWithSerialization(IEntityCollection& srcCollection)
693 {
694 vector<EntityReference> entitiesIn;
695 srcCollection.GetEntitiesRecursive(false, entitiesIn);
696 return CopyContentsWithSerialization(srcCollection, entitiesIn);
697 }
698
ClonePrivate(EntityCollection & dst) const699 void EntityCollection::ClonePrivate(EntityCollection& dst) const
700 {
701 // Clone all collections recursively.
702 DoCloneRecursive(dst);
703
704 //
705 // Remap entity properties that are pointing to the src entities to point to cloned ones.
706 //
707 unordered_map<Entity, Entity> oldToNew;
708
709 vector<EntityReference> sourceEntities;
710 GetEntitiesRecursive(false, sourceEntities);
711 vector<EntityReference> clonedEntities;
712 dst.GetEntitiesRecursive(false, clonedEntities);
713
714 // NOTE: Assuming the order in GetEntitiesRecursive is consistent.
715 BASE_ASSERT(sourceEntities.size() == clonedEntities.size());
716 const auto entityCount = sourceEntities.size();
717 for (size_t i = 0; i < entityCount; ++i) {
718 oldToNew[sourceEntities[i]] = clonedEntities[i];
719 }
720 for (auto& entity : clonedEntities) {
721 RewriteEntityReferences(dst.GetEcs(), entity, oldToNew);
722 }
723 }
724
DoCloneRecursive(EntityCollection & dst) const725 void EntityCollection::DoCloneRecursive(EntityCollection& dst) const
726 {
727 // Clone entities.
728 dst.entities_ = CloneEntityReferences(ecs_, { entities_.data(), entities_.size() }, dst.GetEcs());
729
730 // Create id mapping but reference cloned entities instead of the original
731 BASE_ASSERT(entities_.size() == dst.entities_.size());
732 dst.namedEntities_.clear();
733 dst.namedEntities_.reserve(namedEntities_.size());
734 const auto entityCount = entities_.size();
735 for (const auto& it : namedEntities_) {
736 for (size_t i = 0; i < entityCount; ++i) {
737 if (it.second == entities_[i]) {
738 dst.SetId(it.first, dst.entities_[i]);
739 break;
740 }
741 }
742 }
743
744 // Recurse.
745 dst.collections_.reserve(collections_.size());
746 for (auto& collection : collections_) {
747 BASE_ASSERT(collection);
748
749 if (!collection->IsMarkedDestroyed()) {
750 dst.collections_.emplace_back(EntityCollection::Ptr {
751 new EntityCollection(dst.GetEcs(), collection->GetUri(), collection->GetContextUri()) });
752 auto& clonedChild = *dst.collections_.back();
753 collection->DoCloneRecursive(clonedChild);
754 }
755 }
756 }
757
758 namespace {
759
SetPropertyDefined(EntityCollection::PropertyList & pl,string_view propertyPath)760 bool SetPropertyDefined(EntityCollection::PropertyList& pl, string_view propertyPath)
761 {
762 for (const auto& prop : pl) {
763 if (prop == propertyPath) {
764 // Already marked as a property defined by this node.
765 return false;
766 }
767
768 // check if the property we are trying to set is a sub-property of an already defined property
769 auto len1 = prop.length();
770 auto len2 = propertyPath.length();
771 if (len2 > len1) {
772 auto view1 = prop.substr(0, len1);
773 auto view2 = propertyPath.substr(0, len1);
774 if (view1 == view2) {
775 // already defined in a higher level, so no need to define this sub-property
776 return false;
777 }
778 }
779 }
780 pl.push_back(string(propertyPath));
781 return true;
782 }
SetPropertyUndefined(EntityCollection::PropertyList & pl,string_view propertyPath)783 bool SetPropertyUndefined(EntityCollection::PropertyList& pl, string_view propertyPath)
784 {
785 for (size_t i = 0; i < pl.size(); ++i) {
786 if (pl[i] == propertyPath) {
787 pl.erase(pl.begin() + i);
788 return true;
789 }
790 }
791 return false;
792 }
793
794 } // namespace
795
MarkComponentSerialized(Entity entity,Uid component,bool serialize)796 bool EntityCollection::MarkComponentSerialized(Entity entity, Uid component, bool serialize)
797 {
798 bool changed = false;
799 const auto entityInfo = serializationInfo_.find(entity);
800 const bool entityFound = (entityInfo != serializationInfo_.end());
801 if (serialize) {
802 if (!entityFound) {
803 serializationInfo_[entity][component] = {};
804 changed = true;
805 } else {
806 const auto componentInfo = entityInfo->second.find(component);
807 const bool componentFound = (componentInfo != entityInfo->second.end());
808 if (!componentFound) {
809 entityInfo->second[component] = {};
810 changed = true;
811 }
812 }
813 } else {
814 if (entityFound) {
815 entityInfo->second.erase(component);
816 changed = true;
817 }
818 }
819
820 if (changed) {
821 MarkModified(true);
822 }
823
824 return changed;
825 }
826
MarkAllPropertiesSerialized(Entity entity,Uid component)827 bool EntityCollection::MarkAllPropertiesSerialized(Entity entity, Uid component)
828 {
829 bool changed = false;
830
831 auto cm = GetEcs().GetComponentManager(component);
832 if (!cm) {
833 CORE_LOG_W("Set modified: Unrecognized component");
834 return false;
835 }
836
837 auto info = serializationInfo_.find(entity);
838 if (info == serializationInfo_.end()) {
839 serializationInfo_[entity] = {};
840 info = serializationInfo_.find(entity);
841 changed = true;
842 }
843
844 const auto& propertyApi = cm->GetPropertyApi();
845 const auto propertyCount = propertyApi.PropertyCount();
846 for (size_t i = 0; i < propertyCount; ++i) {
847 auto* property = propertyApi.MetaData(i);
848 changed = changed | SetPropertyDefined(info->second[component], property->name);
849 }
850
851 if (changed) {
852 MarkModified(true);
853 }
854
855 return changed;
856 }
857
MarkPropertySerialized(Entity entity,Uid component,string_view propertyPath,bool serialize)858 bool EntityCollection::MarkPropertySerialized(Entity entity, Uid component, string_view propertyPath, bool serialize)
859 {
860 bool changed = false;
861
862 auto* cm = GetEcs().GetComponentManager(component);
863 if (cm) {
864 auto info = serializationInfo_.find(entity);
865 if (serialize) {
866 if (info == serializationInfo_.end()) {
867 serializationInfo_[entity][component] = {};
868 info = serializationInfo_.find(entity);
869 changed = true;
870 }
871 changed = changed | SetPropertyDefined(info->second[component], propertyPath);
872 } else {
873 if (info != serializationInfo_.end()) {
874 changed = changed | SetPropertyUndefined(info->second[component], propertyPath);
875 }
876 }
877 }
878
879 if (changed) {
880 MarkModified(true);
881 }
882
883 return changed;
884 }
885
GetSerializedProperties(Entity entity,Uid component) const886 const IEntityCollection::PropertyList* EntityCollection::GetSerializedProperties(Entity entity, Uid component) const
887 {
888 const auto info = serializationInfo_.find(entity);
889 if (info != serializationInfo_.end()) {
890 const auto props = info->second.find(component);
891 if (props != info->second.end()) {
892 return &props->second;
893 }
894 }
895 return nullptr;
896 }
897
IsPropertySerialized(Entity entity,Uid component,string_view propertyPath)898 bool EntityCollection::IsPropertySerialized(Entity entity, Uid component, string_view propertyPath)
899 {
900 auto info = serializationInfo_.find(entity);
901 if (info == serializationInfo_.end()) {
902 return false;
903 } else {
904 const auto props = info->second.find(component);
905 if (props == info->second.end()) {
906 return false;
907 } else {
908 for (auto& prop : props->second) {
909 if (prop == propertyPath) {
910 return true;
911 }
912 }
913 }
914 }
915 return false;
916 }
917
Destroy()918 void EntityCollection::Destroy()
919 {
920 delete this;
921 }
922
~EntityCollection()923 EntityCollection::~EntityCollection()
924 {
925 Clear();
926 }
927
ModifiedChanged(IEntityCollection & entityCollection,bool modified)928 void EntityCollection::ModifiedChanged(IEntityCollection& entityCollection, bool modified)
929 {
930 // subcollection changed, propagate modified status
931 if (modified) {
932 MarkModified(modified);
933 }
934 }
935
CreateNewEntityCollection(string_view uri,string_view contextUri)936 IEntityCollection::Ptr EntityCollection::CreateNewEntityCollection(string_view uri, string_view contextUri)
937 {
938 return EntityCollection::Ptr { new EntityCollection(ecs_, uri, contextUri) };
939 }
940
941 SCENE_END_NAMESPACE()
942