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 #ifndef SCENE_PLUGIN_ECSUTIL_H
17 #define SCENE_PLUGIN_ECSUTIL_H
18 
19 #include <PropertyTools/property_data.h>
20 
21 #include <base/containers/string.h>
22 #include <base/containers/unordered_map.h>
23 #include <base/math/vector.h>
24 #include <core/ecs/entity.h>
25 #include <core/ecs/intf_component_manager.h>
26 #include <core/ecs/intf_ecs.h>
27 #include <core/ecs/intf_entity_manager.h>
28 #include <core/property/intf_property_api.h>
29 #include <core/property/property_types.h>
30 #include <core/property/scoped_handle.h>
31 
CORE_BEGIN_NAMESPACE()32 CORE_BEGIN_NAMESPACE()
33 
34 inline void CloneComponent(CORE_NS::Entity srcEntity, const CORE_NS::IComponentManager& srcManager,
35     CORE_NS::IEcs& dstEcs, CORE_NS::Entity dstEntity)
36 {
37     auto* dstManager = dstEcs.GetComponentManager(srcManager.GetUid());
38     if (dstManager) {
39         // Get copy destiantion property handle.
40         auto componentId = dstManager->GetComponentId(dstEntity);
41         if (componentId == CORE_NS::IComponentManager::INVALID_COMPONENT_ID) {
42             dstManager->Create(dstEntity);
43             componentId = dstManager->GetComponentId(dstEntity);
44         }
45         BASE_ASSERT(componentId != CORE_NS::IComponentManager::INVALID_COMPONENT_ID);
46         const auto* srcHandle = srcManager.GetData(srcEntity);
47         if (srcHandle) {
48             dstManager->SetData(dstEntity, *srcHandle);
49         }
50     }
51 }
52 
CloneComponents(CORE_NS::IEcs & srcEcs,CORE_NS::Entity srcEntity,CORE_NS::IEcs & dstEcs,CORE_NS::Entity dstEntity)53 inline void CloneComponents(
54     CORE_NS::IEcs& srcEcs, CORE_NS::Entity srcEntity, CORE_NS::IEcs& dstEcs, CORE_NS::Entity dstEntity)
55 {
56     BASE_NS::vector<CORE_NS::IComponentManager*> managers;
57     srcEcs.GetComponents(srcEntity, managers);
58     for (auto* srcManager : managers) {
59         CloneComponent(srcEntity, *srcManager, dstEcs, dstEntity);
60     }
61 }
62 
CloneEntity(CORE_NS::IEcs & srcEcs,CORE_NS::Entity src,CORE_NS::IEcs & dstEcs)63 inline CORE_NS::Entity CloneEntity(CORE_NS::IEcs& srcEcs, CORE_NS::Entity src, CORE_NS::IEcs& dstEcs)
64 {
65     CORE_NS::Entity dst = dstEcs.GetEntityManager().Create();
66     CloneComponents(srcEcs, src, dstEcs, dst);
67     return dst;
68 }
69 
CloneEntityReference(CORE_NS::IEcs & srcEcs,CORE_NS::Entity src,CORE_NS::IEcs & dstEcs)70 inline CORE_NS::EntityReference CloneEntityReference(CORE_NS::IEcs& srcEcs, CORE_NS::Entity src, CORE_NS::IEcs& dstEcs)
71 {
72     CORE_NS::EntityReference dst = dstEcs.GetEntityManager().CreateReferenceCounted();
73     CloneComponents(srcEcs, src, dstEcs, dst);
74     return dst;
75 }
76 
77 inline void GatherEntityReferences(BASE_NS::vector<CORE_NS::Entity*>& entities,
78     BASE_NS::vector<CORE_NS::EntityReference*>& entityReferences, const CORE_NS::Property& property,
79     uintptr_t offset = 0)
80 {
81     if (property.type == CORE_NS::PropertyType::ENTITY_T) {
82         entities.emplace_back(reinterpret_cast<CORE_NS::Entity*>(offset));
83     } else if (property.type == CORE_NS::PropertyType::ENTITY_REFERENCE_T) {
84         entityReferences.emplace_back(reinterpret_cast<CORE_NS::EntityReference*>(offset));
85     } else if (property.metaData.containerMethods) {
86         auto& containerProperty = property.metaData.containerMethods->property;
87         if (property.type.isArray) {
88             // Array of properties.
89             for (size_t i = 0; i < property.count; i++) {
90                 uintptr_t ptr = offset + i * containerProperty.size;
91                 GatherEntityReferences(entities, entityReferences, containerProperty, ptr);
92             }
93         } else {
94             // This is a "non trivial container"
95             // (So it needs to read the data and not just the metadata to figure out the data structure).
96             const auto count = property.metaData.containerMethods->size(offset);
97             for (size_t i = 0; i < count; i++) {
98                 uintptr_t ptr = property.metaData.containerMethods->get(offset, i);
99                 GatherEntityReferences(entities, entityReferences, containerProperty, ptr);
100             }
101         }
102 
103     } else if (!property.metaData.memberProperties.empty()) {
104         // Custom type (struct). Process sub properties recursively.
105         for (size_t i = 0; i < property.count; i++) {
106             for (const auto& child : property.metaData.memberProperties) {
107                 GatherEntityReferences(entities, entityReferences, child, offset + child.offset);
108             }
109             offset += property.size / property.count;
110         }
111     }
112 }
113 
RewriteEntityReferences(CORE_NS::IEcs & ecs,CORE_NS::Entity entity,BASE_NS::unordered_map<CORE_NS::Entity,CORE_NS::Entity> & oldToNew)114 inline void RewriteEntityReferences(
115     CORE_NS::IEcs& ecs, CORE_NS::Entity entity, BASE_NS::unordered_map<CORE_NS::Entity, CORE_NS::Entity>& oldToNew)
116 {
117     // Go through the entity properties and update any entity references to point to the ones pointed by the oldToNew
118     // map.
119     auto managers = ecs.GetComponentManagers();
120     for (auto cm : managers) {
121         if (auto id = cm->GetComponentId(entity); id != CORE_NS::IComponentManager::INVALID_COMPONENT_ID) {
122             auto* data = cm->GetData(id);
123             if (data) {
124                 // Find all entity references from this component.
125                 BASE_NS::vector<CORE_NS::Entity*> entities;
126                 BASE_NS::vector<CORE_NS::EntityReference*> entityRefs;
127                 uintptr_t offset = (uintptr_t)data->RLock();
128                 if (offset) {
129                     for (const auto& property : data->Owner()->MetaData()) {
130                         GatherEntityReferences(entities, entityRefs, property, offset + property.offset);
131                     }
132 
133                     // Rewrite old entity values with new ones. Assuming that the memory locations are the same as in
134                     // the RLock. NOTE: Keeping the read access open and we must not change any container sizes.
135                     if (!entities.empty() || !entityRefs.empty()) {
136                         data->WLock();
137                         for (CORE_NS::Entity* entity : entities) {
138                             if (const auto it = oldToNew.find(*entity); it != oldToNew.end()) {
139                                 *entity = it->second;
140                             }
141                         }
142                         for (CORE_NS::EntityReference* entity : entityRefs) {
143                             if (const auto it = oldToNew.find(*entity); it != oldToNew.end()) {
144                                 *entity = ecs.GetEntityManager().GetReferenceCounted(it->second);
145                             }
146                         }
147                         data->WUnlock();
148                     }
149                 }
150 
151                 data->RUnlock();
152             }
153         }
154     }
155 }
156 
CloneEntities(CORE_NS::IEcs & srcEcs,BASE_NS::array_view<const CORE_NS::Entity> src,CORE_NS::IEcs & dstEcs)157 inline BASE_NS::vector<CORE_NS::Entity> CloneEntities(
158     CORE_NS::IEcs& srcEcs, BASE_NS::array_view<const CORE_NS::Entity> src, CORE_NS::IEcs& dstEcs)
159 {
160     BASE_NS::vector<CORE_NS::Entity> clonedEntities;
161     clonedEntities.reserve(src.size());
162     for (const auto& srcEntity : src) {
163         clonedEntities.emplace_back(CloneEntity(srcEcs, srcEntity, dstEcs));
164     }
165     return clonedEntities;
166 }
167 
CloneEntityReferences(CORE_NS::IEcs & srcEcs,BASE_NS::array_view<const CORE_NS::EntityReference> src,CORE_NS::IEcs & dstEcs)168 inline BASE_NS::vector<CORE_NS::EntityReference> CloneEntityReferences(
169     CORE_NS::IEcs& srcEcs, BASE_NS::array_view<const CORE_NS::EntityReference> src, CORE_NS::IEcs& dstEcs)
170 {
171     BASE_NS::vector<CORE_NS::EntityReference> clonedEntities;
172     clonedEntities.reserve(src.size());
173     for (const auto& srcEntity : src) {
174         clonedEntities.emplace_back(CloneEntityReference(srcEcs, srcEntity, dstEcs));
175     }
176     return clonedEntities;
177 }
178 
CloneEntitiesUpdateRefs(CORE_NS::IEcs & srcEcs,BASE_NS::array_view<const CORE_NS::EntityReference> src,CORE_NS::IEcs & dstEcs)179 inline BASE_NS::vector<CORE_NS::EntityReference> CloneEntitiesUpdateRefs(
180     CORE_NS::IEcs& srcEcs, BASE_NS::array_view<const CORE_NS::EntityReference> src, CORE_NS::IEcs& dstEcs)
181 {
182     BASE_NS::unordered_map<CORE_NS::Entity, CORE_NS::Entity> oldToNew;
183 
184     BASE_NS::vector<CORE_NS::EntityReference> clonedEntities;
185     clonedEntities.reserve(src.size());
186     for (const auto& srcEntity : src) {
187         clonedEntities.emplace_back(CloneEntityReference(srcEcs, srcEntity, dstEcs));
188         oldToNew[srcEntity] = clonedEntities.back();
189     }
190 
191     for (auto& entity : clonedEntities) {
192         RewriteEntityReferences(dstEcs, entity, oldToNew);
193     }
194     return clonedEntities;
195 }
196 
isPropertyContainer(const CORE_NS::Property & property)197 inline bool isPropertyContainer(const CORE_NS::Property& property)
198 {
199     return property.type == PROPERTYTYPE(CORE_NS::IPropertyHandle*);
200 }
201 
ResolveContainerProperty(const CORE_NS::IPropertyHandle & handle,const BASE_NS::string & propertyPath,BASE_NS::string & path,BASE_NS::string & name)202 inline CORE_NS::IPropertyHandle* ResolveContainerProperty(const CORE_NS::IPropertyHandle& handle,
203     const BASE_NS::string& propertyPath, BASE_NS::string& path, BASE_NS::string& name)
204 {
205     // Extract property path.
206     auto separatorPosition = propertyPath.find_first_of('.');
207     if (separatorPosition == BASE_NS::string::npos) {
208         return nullptr;
209     }
210 
211     path = propertyPath.substr(0, separatorPosition);
212     name = propertyPath.substr(separatorPosition + 1);
213 
214     CORE_NS::IPropertyHandle* result = nullptr;
215 
216     uintptr_t offset = uintptr_t(handle.RLock());
217 
218     // Get potential container.
219     auto propertyData = CORE_NS::PropertyData::FindProperty(handle.Owner()->MetaData(), path, offset);
220     if (propertyData) {
221         // Ensure it is a container.
222         if (CORE_NS::isPropertyContainer(*propertyData.property)) {
223             // Try to flush value to container.
224             result = *(CORE_NS::IPropertyHandle**)(propertyData.offset);
225         }
226     }
227 
228     handle.RUnlock();
229 
230     return result;
231 }
232 
233 CORE_END_NAMESPACE()
234 
235 #endif
236