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