• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "util/scene_util.h"
17 
18 #include <algorithm>
19 #include <cinttypes>
20 #include <unordered_set>
21 
22 #include <3d/ecs/components/animation_component.h>
23 #include <3d/ecs/components/animation_output_component.h>
24 #include <3d/ecs/components/animation_track_component.h>
25 #include <3d/ecs/components/camera_component.h>
26 #include <3d/ecs/components/light_component.h>
27 #include <3d/ecs/components/local_matrix_component.h>
28 #include <3d/ecs/components/material_component.h>
29 #include <3d/ecs/components/mesh_component.h>
30 #include <3d/ecs/components/name_component.h>
31 #include <3d/ecs/components/node_component.h>
32 #include <3d/ecs/components/planar_reflection_component.h>
33 #include <3d/ecs/components/post_process_component.h>
34 #include <3d/ecs/components/reflection_probe_component.h>
35 #include <3d/ecs/components/render_handle_component.h>
36 #include <3d/ecs/components/render_mesh_component.h>
37 #include <3d/ecs/components/skin_joints_component.h>
38 #include <3d/ecs/components/transform_component.h>
39 #include <3d/ecs/components/uri_component.h>
40 #include <3d/ecs/components/world_matrix_component.h>
41 #include <3d/ecs/systems/intf_animation_system.h>
42 #include <3d/ecs/systems/intf_node_system.h>
43 #include <3d/intf_graphics_context.h>
44 #include <3d/render/default_material_constants.h>
45 #include <3d/util/intf_mesh_util.h>
46 #include <base/containers/fixed_string.h>
47 #include <base/containers/unordered_map.h>
48 #include <base/containers/vector.h>
49 #include <base/math/matrix_util.h>
50 #include <base/math/quaternion_util.h>
51 #include <base/util/algorithm.h>
52 #include <base/util/uid_util.h>
53 #include <core/ecs/intf_ecs.h>
54 #include <core/ecs/intf_entity_manager.h>
55 #include <core/intf_engine.h>
56 #include <core/log.h>
57 #include <core/namespace.h>
58 #include <core/property/intf_property_api.h>
59 #include <core/property/intf_property_handle.h>
60 #include <core/property/property_types.h>
61 #include <render/device/intf_gpu_resource_manager.h>
62 #include <render/device/intf_shader_manager.h>
63 #include <render/intf_render_context.h>
64 
65 #include "uri_lookup.h"
66 #include "util/component_util_functions.h"
67 
68 namespace {
69 constexpr uint32_t REFLECTION_PROBE_DEFAULT_SIZE { 256U };
70 }
71 
72 CORE3D_BEGIN_NAMESPACE()
73 using namespace BASE_NS;
74 using namespace CORE_NS;
75 using namespace RENDER_NS;
76 
77 namespace CameraMatrixUtil {
CalculateProjectionMatrix(const CameraComponent & cameraComponent,bool & isCameraNegative)78 Math::Mat4X4 CalculateProjectionMatrix(const CameraComponent& cameraComponent, bool& isCameraNegative)
79 {
80     switch (cameraComponent.projection) {
81         case CameraComponent::Projection::ORTHOGRAPHIC: {
82             auto orthoProj = Math::OrthoRhZo(cameraComponent.xMag * -0.5f, cameraComponent.xMag * 0.5f,
83                 cameraComponent.yMag * -0.5f, cameraComponent.yMag * 0.5f, cameraComponent.zNear, cameraComponent.zFar);
84             orthoProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
85             return orthoProj;
86         }
87         case CameraComponent::Projection::PERSPECTIVE: {
88             auto aspect = 1.f;
89             if (cameraComponent.aspect > 0.f) {
90                 aspect = cameraComponent.aspect;
91             } else if (cameraComponent.renderResolution.y > 0U) {
92                 aspect = static_cast<float>(cameraComponent.renderResolution.x) /
93                          static_cast<float>(cameraComponent.renderResolution.y);
94             }
95             auto persProj =
96                 Math::PerspectiveRhZo(cameraComponent.yFov, aspect, cameraComponent.zNear, cameraComponent.zFar);
97             persProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
98             return persProj;
99         }
100         case CameraComponent::Projection::FRUSTUM: {
101             auto aspect = 1.f;
102             if (cameraComponent.aspect > 0.f) {
103                 aspect = cameraComponent.aspect;
104             } else if (cameraComponent.renderResolution.y > 0U) {
105                 aspect = static_cast<float>(cameraComponent.renderResolution.x) /
106                          static_cast<float>(cameraComponent.renderResolution.y);
107             }
108             // with offset 0.5, the camera should offset half the screen
109             const float scale = tan(cameraComponent.yFov * 0.5f) * cameraComponent.zNear;
110             const float xOffset = cameraComponent.xOffset * scale * aspect * 2.0f;
111             const float yOffset = cameraComponent.yOffset * scale * 2.0f;
112             float left = -aspect * scale;
113             float right = aspect * scale;
114             float bottom = -scale;
115             float top = scale;
116             auto persProj = Math::PerspectiveRhZo(left + xOffset, right + xOffset, bottom + yOffset, top + yOffset,
117                 cameraComponent.zNear, cameraComponent.zFar);
118             persProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
119             return persProj;
120         }
121         case CameraComponent::Projection::CUSTOM: {
122             isCameraNegative = Math::Determinant(cameraComponent.customProjectionMatrix) < 0.0f;
123             return cameraComponent.customProjectionMatrix;
124         }
125         default:
126             return Math::IDENTITY_4X4;
127     }
128 }
129 } // namespace CameraMatrixUtil
130 
SceneUtil(IGraphicsContext & graphicsContext)131 SceneUtil::SceneUtil(IGraphicsContext& graphicsContext) : graphicsContext_(graphicsContext) {}
132 
CreateCamera(IEcs & ecs,const Math::Vec3 & position,const Math::Quat & rotation,float zNear,float zFar,float fovDegrees) const133 Entity SceneUtil::CreateCamera(
134     IEcs& ecs, const Math::Vec3& position, const Math::Quat& rotation, float zNear, float zFar, float fovDegrees) const
135 {
136     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
137     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
138     auto ncm = GetManager<INodeComponentManager>(ecs);
139     auto tcm = GetManager<ITransformComponentManager>(ecs);
140     auto ccm = GetManager<ICameraComponentManager>(ecs);
141     if (!lmm || !wmm || !ncm || !tcm || !ccm) {
142         return {};
143     }
144 
145     Entity camera = ecs.GetEntityManager().Create();
146 
147     lmm->Create(camera);
148     wmm->Create(camera);
149     ncm->Create(camera);
150 
151     TransformComponent tc;
152     tc.position = position;
153     tc.rotation = rotation;
154     tcm->Set(camera, tc);
155 
156     CameraComponent cc;
157     cc.sceneFlags |= CameraComponent::SceneFlagBits::ACTIVE_RENDER_BIT;
158     cc.projection = CameraComponent::Projection::PERSPECTIVE;
159     cc.yFov = Math::DEG2RAD * fovDegrees;
160     cc.zNear = zNear;
161     cc.zFar = zFar;
162     ccm->Set(camera, cc);
163 
164     return camera;
165 }
166 
UpdateCameraViewport(IEcs & ecs,Entity entity,const Math::UVec2 & renderResolution) const167 void SceneUtil::UpdateCameraViewport(IEcs& ecs, Entity entity, const Math::UVec2& renderResolution) const
168 {
169     auto ccm = GetManager<ICameraComponentManager>(ecs);
170     if (ccm && CORE_NS::EntityUtil::IsValid(entity)) {
171         ScopedHandle<CameraComponent> cameraHandle = ccm->Write(entity);
172         if (!cameraHandle) {
173             ccm->Create(entity);
174             cameraHandle = ccm->Write(entity);
175             if (!cameraHandle) {
176                 return;
177             }
178         }
179         CameraComponent& cameraComponent = *cameraHandle;
180         cameraComponent.aspect = (renderResolution.y > 0U)
181                                      ? (static_cast<float>(renderResolution.x) / static_cast<float>(renderResolution.y))
182                                      : 1.0f;
183         cameraComponent.renderResolution[0] = renderResolution.x;
184         cameraComponent.renderResolution[1] = renderResolution.y;
185     }
186 }
187 
UpdateCameraViewport(IEcs & ecs,Entity entity,const Math::UVec2 & renderResolution,bool autoAspect,float fovY,float orthoScale) const188 void SceneUtil::UpdateCameraViewport(
189     IEcs& ecs, Entity entity, const Math::UVec2& renderResolution, bool autoAspect, float fovY, float orthoScale) const
190 {
191     auto ccm = GetManager<ICameraComponentManager>(ecs);
192     if (ccm && CORE_NS::EntityUtil::IsValid(entity)) {
193         ScopedHandle<CameraComponent> cameraHandle = ccm->Write(entity);
194         if (!cameraHandle) {
195             ccm->Create(entity);
196             cameraHandle = ccm->Write(entity);
197             if (!cameraHandle) {
198                 return;
199             }
200         }
201         CameraComponent& cameraComponent = *cameraHandle;
202         if (autoAspect) {
203             const float aspectRatio =
204                 (renderResolution.y > 0)
205                     ? (static_cast<float>(renderResolution.x) / static_cast<float>(renderResolution.y))
206                     : 1.0f;
207 
208             // Using the fov value as xfov on portrait screens to keep the object
209             // better in the frame.
210             const float yFov = (aspectRatio > 1.0f) ? fovY : (2.0f * Math::atan(Math::tan(fovY * 0.5f) / aspectRatio));
211 
212             // Update the camera parameters.
213             cameraComponent.aspect = aspectRatio;
214             cameraComponent.yFov = yFov;
215 
216             // The camera can also be in ortho mode. Using a separate zoom value.
217             cameraComponent.yMag = orthoScale;
218             cameraComponent.xMag = cameraComponent.yMag * aspectRatio;
219         }
220 
221         cameraComponent.renderResolution[0] = renderResolution.x;
222         cameraComponent.renderResolution[1] = renderResolution.y;
223     }
224 }
225 
CameraLookAt(IEcs & ecs,Entity entity,const Math::Vec3 & eye,const Math::Vec3 & target,const Math::Vec3 & up)226 void SceneUtil::CameraLookAt(
227     IEcs& ecs, Entity entity, const Math::Vec3& eye, const Math::Vec3& target, const Math::Vec3& up)
228 {
229     auto ncm = GetManager<INodeComponentManager>(ecs);
230     auto tcm = GetManager<ITransformComponentManager>(ecs);
231     if (!ncm || !tcm) {
232         return;
233     }
234 
235     auto parentWorld = Math::Mat4X4(1.f);
236     auto getParent = [ncm](Entity entity) {
237         if (auto nodeHandle = ncm->Read(entity)) {
238             return nodeHandle->parent;
239         }
240         return Entity {};
241     };
242 
243     // walk up the hierachy to get an up-to-date world matrix.
244     for (Entity node = getParent(entity); EntityUtil::IsValid(node); node = getParent(node)) {
245         if (auto parentTransformHandle = tcm->Read(node)) {
246             parentWorld = Math::Trs(parentTransformHandle->position, parentTransformHandle->rotation,
247                 parentTransformHandle->scale) * parentWorld;
248         }
249     }
250 
251     // entity's local transform should invert any parent transformations and apply the transform of rotating towards
252     // target and moving to eye. this transform is the inverse of the look-at matrix.
253     auto worldMatrix = Math::Inverse(parentWorld) * Math::Inverse(Math::LookAtRh(eye, target, up));
254 
255     Math::Vec3 scale;
256     Math::Quat orientation;
257     Math::Vec3 translation;
258     Math::Vec3 skew;
259     Math::Vec4 perspective;
260     if (Math::Decompose(worldMatrix, scale, orientation, translation, skew, perspective)) {
261         if (auto transformHandle = tcm->Write(entity)) {
262             transformHandle->position = translation;
263             transformHandle->rotation = orientation;
264             transformHandle->scale = scale;
265         }
266     }
267 }
268 
CreateLight(IEcs & ecs,const LightComponent & lightComponent,const Math::Vec3 & position,const Math::Quat & rotation) const269 Entity SceneUtil::CreateLight(
270     IEcs& ecs, const LightComponent& lightComponent, const Math::Vec3& position, const Math::Quat& rotation) const
271 {
272     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
273     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
274     auto ncm = GetManager<INodeComponentManager>(ecs);
275     auto nameM = GetManager<INameComponentManager>(ecs);
276     auto tcm = GetManager<ITransformComponentManager>(ecs);
277     auto lcm = GetManager<ILightComponentManager>(ecs);
278     if (!lmm || !wmm || !ncm || !nameM || !tcm || !lcm) {
279         return {};
280     }
281 
282     Entity light = ecs.GetEntityManager().Create();
283 
284     lmm->Create(light);
285     wmm->Create(light);
286     ncm->Create(light);
287     nameM->Create(light);
288     constexpr string_view lightName("Light");
289     nameM->Write(light)->name = lightName;
290 
291     TransformComponent tc;
292     tc.position = position;
293     tc.rotation = rotation;
294     tcm->Set(light, tc);
295 
296     LightComponent lc = lightComponent;
297     lc.shadowEnabled = (lc.type != LightComponent::Type::POINT) && lc.shadowEnabled;
298     lc.range = ComponentUtilFunctions::CalculateSafeLightRange(lc.range, lc.intensity);
299     lcm->Set(light, lc);
300 
301     return light;
302 }
303 
304 // reflection plane helpers
305 namespace {
306 // default size, updated in render system based on main scene camera
307 constexpr Math::UVec2 DEFAULT_PLANE_TARGET_SIZE { 2u, 2u };
308 
CreateReflectionPlaneGpuImage(IGpuResourceManager & gpuResourceMgr,IRenderHandleComponentManager & handleManager,INameComponentManager & nameManager,const string_view name,const Format format,const ImageUsageFlags usageFlags,const MemoryPropertyFlags memoryPropertyFlags)309 EntityReference CreateReflectionPlaneGpuImage(IGpuResourceManager& gpuResourceMgr,
310     IRenderHandleComponentManager& handleManager, INameComponentManager& nameManager, const string_view name,
311     const Format format, const ImageUsageFlags usageFlags, const MemoryPropertyFlags memoryPropertyFlags)
312 {
313     const auto entity = handleManager.GetEcs().GetEntityManager().CreateReferenceCounted();
314     handleManager.Create(entity);
315 
316     GpuImageDesc desc;
317     desc.width = DEFAULT_PLANE_TARGET_SIZE.x;
318     desc.height = DEFAULT_PLANE_TARGET_SIZE.y;
319     desc.depth = 1U;
320     desc.mipCount = DefaultMaterialCameraConstants::REFLECTION_PLANE_MIP_COUNT;
321     desc.format = format;
322     desc.memoryPropertyFlags = memoryPropertyFlags;
323     desc.usageFlags = usageFlags;
324     desc.imageType = ImageType::CORE_IMAGE_TYPE_2D;
325     desc.imageTiling = ImageTiling::CORE_IMAGE_TILING_OPTIMAL;
326     desc.imageViewType = ImageViewType::CORE_IMAGE_VIEW_TYPE_2D;
327     desc.engineCreationFlags = EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS;
328     handleManager.Write(entity)->reference = gpuResourceMgr.Create(name, desc);
329 
330     nameManager.Create(entity);
331     nameManager.Write(entity)->name = name;
332     return entity;
333 }
334 
CreateReflectionPlaneObjectFromEntity(IEcs & ecs,IGraphicsContext & graphicsContext,const Entity & nodeEntity)335 SceneUtil::ReflectionPlane CreateReflectionPlaneObjectFromEntity(
336     IEcs& ecs, IGraphicsContext& graphicsContext, const Entity& nodeEntity)
337 {
338     SceneUtil::ReflectionPlane plane;
339     auto* renderMeshCM = GetManager<IRenderMeshComponentManager>(ecs);
340     auto* meshCM = GetManager<IMeshComponentManager>(ecs);
341     auto* matCM = GetManager<IMaterialComponentManager>(ecs);
342     auto* gpuHandleCM = GetManager<IRenderHandleComponentManager>(ecs);
343 
344     auto* nameCM = GetManager<INameComponentManager>(ecs);
345     if (!(renderMeshCM && meshCM && matCM && gpuHandleCM && nameCM)) {
346         return plane;
347     }
348 
349     auto& device = graphicsContext.GetRenderContext().GetDevice();
350     auto& gpuResourceMgr = device.GetGpuResourceManager();
351     plane.entity = nodeEntity;
352     plane.colorTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM,
353         DefaultMaterialCameraConstants::CAMERA_COLOR_PREFIX_NAME + to_hex(nodeEntity.id),
354         Format::BASE_FORMAT_B10G11R11_UFLOAT_PACK32,
355         ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT,
356         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
357 
358     // NOTE: uses transient attachement usage flag and cannot be read.
359     plane.depthTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM,
360         DefaultMaterialCameraConstants::CAMERA_DEPTH_PREFIX_NAME + to_hex(nodeEntity.id), Format::BASE_FORMAT_D16_UNORM,
361         ImageUsageFlagBits::CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
362             ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
363         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
364             MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT);
365 
366     const auto meshHandle = [&]() {
367         const auto rmcHandle = renderMeshCM->Read(nodeEntity);
368         if (!rmcHandle) {
369             return ScopedHandle<const MeshComponent> {};
370         }
371         return meshCM->Read(rmcHandle->mesh);
372     }();
373     if (!meshHandle && meshHandle->submeshes.empty()) {
374         return plane;
375     }
376     if (auto matHandle = matCM->Write(meshHandle->submeshes[0].material); matHandle) {
377         MaterialComponent& matComponent = *matHandle;
378         auto* uriCM = GetManager<IUriComponentManager>(ecs);
379         auto* renderHandleCM = GetManager<IRenderHandleComponentManager>(ecs);
380         constexpr const string_view uri = "3dshaders://shader/core3d_dm_fw_reflection_plane.shader";
381         auto shaderEntity = LookupResourceByUri(uri, *uriCM, *renderHandleCM);
382         if (!EntityUtil::IsValid(shaderEntity)) {
383             shaderEntity = ecs.GetEntityManager().Create();
384             renderHandleCM->Create(shaderEntity);
385             renderHandleCM->Write(shaderEntity)->reference = device.GetShaderManager().GetShaderHandle(uri);
386             uriCM->Create(shaderEntity);
387             uriCM->Write(shaderEntity)->uri = uri;
388         }
389         matComponent.materialShader.shader = ecs.GetEntityManager().GetReferenceCounted(shaderEntity);
390 
391         matComponent.textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS].image = plane.colorTarget;
392         matComponent.extraRenderingFlags = MaterialComponent::ExtraRenderingFlagBits::DISCARD_BIT;
393     }
394 
395     return plane;
396 }
397 } // namespace
398 
CreateReflectionPlaneComponent(IEcs & ecs,const Entity & nodeEntity)399 void SceneUtil::CreateReflectionPlaneComponent(IEcs& ecs, const Entity& nodeEntity)
400 {
401     SceneUtil::ReflectionPlane plane = CreateReflectionPlaneObjectFromEntity(ecs, graphicsContext_, nodeEntity);
402     if (EntityUtil::IsValid(plane.entity)) {
403         auto prcm = GetManager<IPlanarReflectionComponentManager>(ecs);
404         PlanarReflectionComponent prc;
405         prc.colorRenderTarget = plane.colorTarget;
406         prc.depthRenderTarget = plane.depthTarget;
407         prc.renderTargetResolution[0] = DEFAULT_PLANE_TARGET_SIZE.x;
408         prc.renderTargetResolution[1] = DEFAULT_PLANE_TARGET_SIZE.y;
409         prcm->Set(plane.entity, prc);
410     }
411 }
412 
413 namespace {
CalculateScalingFactor(IEcs & ecs,Entity targetEntity,Entity sourceEntity)414 float CalculateScalingFactor(IEcs& ecs, Entity targetEntity, Entity sourceEntity)
415 {
416     float scale = 1.f;
417     auto renderMeshManager = GetManager<IRenderMeshComponentManager>(ecs);
418     auto meshManager = GetManager<IMeshComponentManager>(ecs);
419     auto transformManager = GetManager<ITransformComponentManager>(ecs);
420     if (!renderMeshManager || !meshManager || !transformManager) {
421         return scale;
422     }
423 
424     Entity dstMeshEntity;
425     Entity srcMeshEntity;
426     if (auto renderMeshComponent = renderMeshManager->Read(targetEntity); renderMeshComponent) {
427         dstMeshEntity = renderMeshComponent->mesh;
428     }
429     if (auto renderMeshComponent = renderMeshManager->Read(sourceEntity); renderMeshComponent) {
430         srcMeshEntity = renderMeshComponent->mesh;
431     }
432     if (!EntityUtil::IsValid(dstMeshEntity) || !EntityUtil::IsValid(srcMeshEntity)) {
433         return scale;
434     }
435     auto getMeshHeight = [](IMeshComponentManager& meshManager, Entity entity) {
436         if (auto meshComponent = meshManager.Read(entity); meshComponent) {
437             return (meshComponent->aabbMax.y - meshComponent->aabbMin.y);
438         }
439         return 0.f;
440     };
441 
442     const float dstHeight = getMeshHeight(*meshManager, dstMeshEntity);
443     const float srcHeight = getMeshHeight(*meshManager, srcMeshEntity);
444     if ((dstHeight == 0.f) || (srcHeight == 0.f)) {
445         return scale;
446     }
447 
448     auto dstMeshScale = transformManager->Get(targetEntity).scale;
449     auto srcMeshScale = transformManager->Get(sourceEntity).scale;
450     const auto dstSize = dstHeight * dstMeshScale.y;
451     const auto srcSize = srcHeight * srcMeshScale.y;
452     if (srcSize == 0.f) {
453         return scale;
454     }
455     scale = dstSize / srcSize;
456     return scale;
457 }
458 
CreateJointMapping(IEcs & ecs,array_view<const Entity> dstJointEntities,array_view<const Entity> srcJointEntities)459 vector<Entity> CreateJointMapping(
460     IEcs& ecs, array_view<const Entity> dstJointEntities, array_view<const Entity> srcJointEntities)
461 {
462     vector<Entity> srcToDstJointMapping;
463     auto* nameManager = GetManager<INameComponentManager>(ecs);
464     if (!nameManager) {
465         return srcToDstJointMapping;
466     }
467 
468     auto getName = [nameManager](const Entity& jointEntity) -> string_view {
469         if (auto nameComponent = nameManager->Read(jointEntity); nameComponent) {
470             return nameComponent->name;
471         }
472         return {};
473     };
474 
475     vector<string_view> dstJointNames;
476     dstJointNames.reserve(dstJointEntities.size());
477     std::transform(dstJointEntities.begin(), dstJointEntities.end(), std::back_inserter(dstJointNames), getName);
478 
479     vector<string_view> srcJointNames;
480     srcJointNames.reserve(srcJointEntities.size());
481     std::transform(srcJointEntities.begin(), srcJointEntities.end(), std::back_inserter(srcJointNames), getName);
482 
483     srcToDstJointMapping.reserve(srcJointEntities.size());
484 
485     const auto dstJointNamesBegin = dstJointNames.cbegin();
486     const auto dstJointNamesEnd = dstJointNames.cend();
487     for (const auto& srcJointName : srcJointNames) {
488         const auto pos = std::find(dstJointNamesBegin, dstJointNamesEnd, srcJointName);
489         srcToDstJointMapping.push_back(
490             (pos != dstJointNamesEnd) ? dstJointEntities[static_cast<size_t>(std::distance(dstJointNamesBegin, pos))]
491                                       : Entity {});
492         if (pos == dstJointNamesEnd) {
493             CORE_LOG_W("Target skin missing joint %s", srcJointName.data());
494         }
495     }
496     return srcToDstJointMapping;
497 }
498 
UpdateTracks(IEcs & ecs,array_view<EntityReference> targetTracks,array_view<const EntityReference> sourceTracks,array_view<const Entity> srcJointEntities,array_view<const Entity> srcToDstJointMapping,float scale)499 vector<Entity> UpdateTracks(IEcs& ecs, array_view<EntityReference> targetTracks,
500     array_view<const EntityReference> sourceTracks, array_view<const Entity> srcJointEntities,
501     array_view<const Entity> srcToDstJointMapping, float scale)
502 {
503     vector<Entity> trackTargets;
504     auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs);
505     auto animationOutputManager = GetManager<IAnimationOutputComponentManager>(ecs);
506     if (!animationTrackManager || !animationOutputManager) {
507         return trackTargets;
508     }
509     trackTargets.reserve(sourceTracks.size());
510     auto& entityManager = ecs.GetEntityManager();
511     // update tracks to point to target skin's joints.
512     std::transform(sourceTracks.begin(), sourceTracks.end(), targetTracks.begin(),
513         [&entityManager, animationTrackManager, animationOutputManager, &srcJointEntities, &srcToDstJointMapping,
514             &trackTargets, scale](const EntityReference& srcTrackEntity) {
515             const auto srcTrackId = animationTrackManager->GetComponentId(srcTrackEntity);
516             const auto srcTargetEntity = (srcTrackId != IComponentManager::INVALID_COMPONENT_ID)
517                                              ? animationTrackManager->Read(srcTrackId)->target
518                                              : Entity {};
519             if (EntityUtil::IsValid(srcTargetEntity)) {
520                 // check that the src track target is one of the src joints
521                 if (const auto pos = std::find(srcJointEntities.begin(), srcJointEntities.end(), srcTargetEntity);
522                     pos != srcJointEntities.end()) {
523                     auto dstTrackEntity = entityManager.CreateReferenceCounted();
524                     animationTrackManager->Create(dstTrackEntity);
525                     auto dstTrack = animationTrackManager->Write(dstTrackEntity);
526                     auto srcTrack = animationTrackManager->Read(srcTrackId);
527                     *dstTrack = *srcTrack;
528                     const auto jointIndex = static_cast<size_t>(pos - srcJointEntities.begin());
529                     trackTargets.push_back(srcToDstJointMapping[jointIndex]);
530                     dstTrack->target = {};
531 
532                     // joint position track needs to be offset
533                     if ((dstTrack->component == ITransformComponentManager::UID) &&
534                         (dstTrack->property == "position")) {
535                         if (const auto srcOutputId = animationOutputManager->GetComponentId(srcTrack->data);
536                             srcOutputId != IComponentManager::INVALID_COMPONENT_ID) {
537                             // create new animation output with original position corrected by the scale.
538                             dstTrack->data = entityManager.CreateReferenceCounted();
539                             animationOutputManager->Create(dstTrack->data);
540                             const auto dstOutput = animationOutputManager->Write(dstTrack->data);
541                             const auto srcOutput = animationOutputManager->Read(srcOutputId);
542                             dstOutput->type = srcOutput->type;
543                             auto& dst = dstOutput->data;
544                             const auto& src = srcOutput->data;
545                             dst.resize(src.size());
546                             const auto count = dst.size() / sizeof(Math::Vec3);
547                             const auto srcPositions =
548                                 array_view(reinterpret_cast<const Math::Vec3*>(src.data()), count);
549                             auto dstPositions = array_view(reinterpret_cast<Math::Vec3*>(dst.data()), count);
550                             std::transform(srcPositions.begin(), srcPositions.end(), dstPositions.begin(),
551                                 [scale](const Math::Vec3 position) { return position * scale; });
552                         }
553                     }
554                     return dstTrackEntity;
555                 }
556             }
557             CORE_LOG_W("no target for track %" PRIx64, static_cast<Entity>(srcTrackEntity).id);
558             return srcTrackEntity;
559         });
560     return trackTargets;
561 }
562 } // namespace
563 
RetargetSkinAnimation(IEcs & ecs,Entity targetEntity,Entity sourceEntity,Entity animationEntity) const564 IAnimationPlayback* SceneUtil::RetargetSkinAnimation(
565     IEcs& ecs, Entity targetEntity, Entity sourceEntity, Entity animationEntity) const
566 {
567     auto jointsManager = GetManager<ISkinJointsComponentManager>(ecs);
568     auto animationManager = GetManager<IAnimationComponentManager>(ecs);
569     auto transformManager = GetManager<ITransformComponentManager>(ecs);
570     auto animationSystem = GetSystem<IAnimationSystem>(ecs);
571     if (!jointsManager || !animationManager || !transformManager || !animationSystem) {
572         return nullptr;
573     }
574     auto dstJointsComponent = jointsManager->Read(targetEntity);
575     auto srcJointsComponent = jointsManager->Read(sourceEntity);
576     if (!dstJointsComponent || !srcJointsComponent) {
577         return nullptr;
578     }
579     if (!animationManager->HasComponent(animationEntity)) {
580         return nullptr;
581     }
582 
583     auto dstJointEntities = array_view(dstJointsComponent->jointEntities, dstJointsComponent->count);
584     auto srcJointEntities = array_view(srcJointsComponent->jointEntities, srcJointsComponent->count);
585 
586     const vector<Entity> srcToDstJointMapping = CreateJointMapping(ecs, dstJointEntities, srcJointEntities);
587 
588     // calculate a scaling factor based on mesh heights
589     const auto scale = CalculateScalingFactor(ecs, targetEntity, sourceEntity);
590 
591     // compensate difference in root joint positions.
592     // synchronize initial pose by copying src joint rotations to dst joints.
593     {
594         auto srcIt = srcJointEntities.begin();
595         for (const auto& dstE : srcToDstJointMapping) {
596             auto srcTransform = transformManager->Read(*srcIt);
597             auto dstTransform = transformManager->Write(dstE);
598             if (srcTransform && dstTransform) {
599                 dstTransform->position = srcTransform->position * scale;
600                 dstTransform->rotation = srcTransform->rotation;
601             }
602             ++srcIt;
603         }
604     }
605 
606     auto& entityManager = ecs.GetEntityManager();
607     const auto dstAnimationEntity = entityManager.Create();
608     if (auto* nameManager = GetManager<INameComponentManager>(ecs)) {
609         nameManager->Create(dstAnimationEntity);
610         auto srcName = nameManager->Read(animationEntity);
611         nameManager->Write(dstAnimationEntity)->name =
612             (srcName ? srcName->name : string_view("<empty>")) + " retargeted";
613     }
614 
615     vector<Entity> trackTargets;
616     {
617         animationManager->Create(dstAnimationEntity);
618         auto dstAnimationComponent = animationManager->Write(dstAnimationEntity);
619         auto srcAnimationComponent = animationManager->Read(animationEntity);
620         if (!srcAnimationComponent) {
621             return nullptr;
622         }
623         // copy the src animation.
624         *dstAnimationComponent = *srcAnimationComponent;
625         trackTargets = UpdateTracks(ecs, dstAnimationComponent->tracks, srcAnimationComponent->tracks, srcJointEntities,
626             srcToDstJointMapping, scale);
627     }
628 
629     return animationSystem->CreatePlayback(dstAnimationEntity, trackTargets);
630 }
631 
GetDefaultMaterialShaderData(IEcs & ecs,const ISceneUtil::MaterialShaderInfo & info,MaterialComponent::Shader & materialShader,MaterialComponent::Shader & depthShader) const632 void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info,
633     MaterialComponent::Shader& materialShader, MaterialComponent::Shader& depthShader) const
634 {
635     auto* renderHandleMgr = GetManager<IRenderHandleComponentManager>(ecs);
636     if (renderHandleMgr) {
637         IEntityManager& entityMgr = ecs.GetEntityManager();
638         const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager();
639         {
640             const uint32_t renderSlotId =
641                 (info.alphaBlend)
642                     ? shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT)
643                     : shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE);
644 
645             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
646             materialShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
647             RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
648             gs.rasterizationState.cullModeFlags = info.cullModeFlags;
649             gs.rasterizationState.frontFace = info.frontFace;
650             const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
651             const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
652             if (gsHandle) {
653                 materialShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
654             }
655         }
656         if (!info.alphaBlend) {
657             const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH);
658             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
659             depthShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
660             RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
661             gs.rasterizationState.cullModeFlags = info.cullModeFlags;
662             gs.rasterizationState.frontFace = info.frontFace;
663             const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
664             const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
665             if (gsHandle) {
666                 depthShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
667             }
668         }
669     }
670 }
671 
GetDefaultMaterialShaderData(IEcs & ecs,const ISceneUtil::MaterialShaderInfo & info,const string_view renderSlot,MaterialComponent::Shader & shader) const672 void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info,
673     const string_view renderSlot, MaterialComponent::Shader& shader) const
674 {
675     auto* renderHandleMgr = GetManager<IRenderHandleComponentManager>(ecs);
676     if (renderHandleMgr) {
677         IEntityManager& entityMgr = ecs.GetEntityManager();
678         const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager();
679         const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(renderSlot);
680         if (renderSlotId != ~0u) {
681             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
682             if (rsd.shader) {
683                 shader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
684             } else {
685                 CORE_LOG_D("SceneUtil: render slot base shader not found (%s)", renderSlot.data());
686             }
687             if (rsd.graphicsState) {
688                 RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
689                 gs.rasterizationState.cullModeFlags = info.cullModeFlags;
690                 gs.rasterizationState.frontFace = info.frontFace;
691                 const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
692                 const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
693                 shader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
694             } else {
695                 CORE_LOG_D("SceneUtil: render slot base graphics state not found (%s)", renderSlot.data());
696             }
697         } else {
698             CORE_LOG_W("SceneUtil: render slot id not found (%s)", renderSlot.data());
699         }
700     }
701 }
702 
ShareSkin(IEcs & ecs,Entity targetEntity,Entity sourceEntity) const703 void SceneUtil::ShareSkin(IEcs& ecs, Entity targetEntity, Entity sourceEntity) const
704 {
705     auto jointsManager = GetManager<ISkinJointsComponentManager>(ecs);
706     if (!jointsManager) {
707         CORE_LOG_E("Missing ISkinJointsComponentManager.");
708         return;
709     }
710     vector<Entity> dstToSrcJointMapping;
711     {
712         auto dstJointsComponent = jointsManager->Read(targetEntity);
713         auto srcJointsComponent = jointsManager->Read(sourceEntity);
714         if (!dstJointsComponent) {
715             CORE_LOG_E("target doesn't have SkinJointsComponent.");
716             return;
717         }
718         if (!srcJointsComponent) {
719             CORE_LOG_E("source doesn't have SkinJointsComponent.");
720             return;
721         }
722 
723         auto dstJointEntities = array_view(dstJointsComponent->jointEntities, dstJointsComponent->count);
724         auto srcJointEntities = array_view(srcJointsComponent->jointEntities, srcJointsComponent->count);
725 
726         dstToSrcJointMapping = CreateJointMapping(ecs, srcJointEntities, dstJointEntities);
727         if (dstJointsComponent->count != dstToSrcJointMapping.size()) {
728             CORE_LOG_E("couldn't match all joints.");
729         }
730     }
731     auto dstJointsComponent = jointsManager->Write(targetEntity);
732     const auto jointCount = Math::min(dstToSrcJointMapping.size(), countof(dstJointsComponent->jointEntities));
733     std::copy(dstToSrcJointMapping.data(), dstToSrcJointMapping.data() + jointCount, dstJointsComponent->jointEntities);
734 }
735 
RegisterSceneLoader(const ISceneLoader::Ptr & loader)736 void SceneUtil::RegisterSceneLoader(const ISceneLoader::Ptr& loader)
737 {
738     if (auto pos = std::find_if(sceneLoaders_.cbegin(), sceneLoaders_.cend(),
739             [newLoader = loader.get()](const ISceneLoader::Ptr& registered) { return registered.get() == newLoader; });
740         pos == sceneLoaders_.cend()) {
741         sceneLoaders_.push_back(loader);
742     }
743 }
744 
UnregisterSceneLoader(const ISceneLoader::Ptr & loader)745 void SceneUtil::UnregisterSceneLoader(const ISceneLoader::Ptr& loader)
746 {
747     if (auto pos = std::find_if(sceneLoaders_.cbegin(), sceneLoaders_.cend(),
748             [toBeRemoved = loader.get()](
749                 const ISceneLoader::Ptr& registered) { return registered.get() == toBeRemoved; });
750         pos != sceneLoaders_.cend()) {
751         sceneLoaders_.erase(pos);
752     }
753 }
754 
GetSceneLoader(BASE_NS::string_view uri) const755 ISceneLoader::Ptr SceneUtil::GetSceneLoader(BASE_NS::string_view uri) const
756 {
757     for (auto& sceneLoader : sceneLoaders_) {
758         for (const auto& extension : sceneLoader->GetSupportedExtensions()) {
759             if (uri.ends_with(extension)) {
760                 return sceneLoader;
761             }
762         }
763     }
764     return {};
765 }
766 
767 namespace {
CreateTargetGpuImageEnv(IGpuResourceManager & gpuResourceMgr,IEcs & ecs,const Math::UVec2 & size)768 EntityReference CreateTargetGpuImageEnv(IGpuResourceManager& gpuResourceMgr, IEcs& ecs, const Math::UVec2& size)
769 {
770     auto* renderHandleManager = GetManager<IRenderHandleComponentManager>(ecs);
771     if (!renderHandleManager) {
772         return {};
773     }
774     GpuImageDesc envDesc { CORE_IMAGE_TYPE_2D, CORE_IMAGE_VIEW_TYPE_CUBE, BASE_FORMAT_B10G11R11_UFLOAT_PACK32,
775         CORE_IMAGE_TILING_OPTIMAL, CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | CORE_IMAGE_USAGE_SAMPLED_BIT,
776         CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, CORE_IMAGE_CREATE_CUBE_COMPATIBLE_BIT,
777         CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, size.x, size.y, 1U, 1U, 6U, CORE_SAMPLE_COUNT_1_BIT,
778         ComponentMapping {} };
779     auto handle = gpuResourceMgr.Create(envDesc);
780     return GetOrCreateEntityReference(ecs.GetEntityManager(), *renderHandleManager, handle);
781 }
782 
CreateTargetGpuImageDepth(IGpuResourceManager & gpuResourceMgr,IEcs & ecs,const Math::UVec2 & size)783 EntityReference CreateTargetGpuImageDepth(IGpuResourceManager& gpuResourceMgr, IEcs& ecs, const Math::UVec2& size)
784 {
785     auto* renderHandleManager = GetManager<IRenderHandleComponentManager>(ecs);
786     if (!renderHandleManager) {
787         return {};
788     }
789     GpuImageDesc depthDesc { CORE_IMAGE_TYPE_2D, CORE_IMAGE_VIEW_TYPE_CUBE, BASE_FORMAT_D16_UNORM,
790         CORE_IMAGE_TILING_OPTIMAL,
791         CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
792         CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT,
793         CORE_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, size.x, size.y, 1U, 1U, 6U,
794         CORE_SAMPLE_COUNT_1_BIT, ComponentMapping {} };
795     auto handle = gpuResourceMgr.Create(depthDesc);
796     return GetOrCreateEntityReference(ecs.GetEntityManager(), *renderHandleManager, handle);
797 }
798 
CreateReflectionProbeCamera(CORE_NS::IEcs & ecs,IGraphicsContext & graphicsContext,const BASE_NS::Math::Vec3 & position)799 CORE_NS::EntityReference CreateReflectionProbeCamera(
800     CORE_NS::IEcs& ecs, IGraphicsContext& graphicsContext, const BASE_NS::Math::Vec3& position)
801 {
802     auto ccm = GetManager<ICameraComponentManager>(ecs);
803     auto ppcm = GetManager<IPostProcessComponentManager>(ecs);
804     auto tcm = GetManager<ITransformComponentManager>(ecs);
805     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
806     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
807     auto ncm = GetManager<INodeComponentManager>(ecs);
808     if (!ccm || !ppcm || !tcm || !wmm || !lmm || !ncm) {
809         return {};
810     }
811     EntityReference camera = ecs.GetEntityManager().CreateReferenceCounted();
812 
813     CameraComponent cc;
814     auto& gpuResourceMgr = graphicsContext.GetRenderContext().GetDevice().GetGpuResourceManager();
815 
816     auto envEntityRef =
817         CreateTargetGpuImageEnv(gpuResourceMgr, ecs, { REFLECTION_PROBE_DEFAULT_SIZE, REFLECTION_PROBE_DEFAULT_SIZE });
818     auto depthEntityRef = CreateTargetGpuImageDepth(
819         gpuResourceMgr, ecs, { REFLECTION_PROBE_DEFAULT_SIZE, REFLECTION_PROBE_DEFAULT_SIZE });
820     cc.customColorTargets.push_back(envEntityRef);
821     cc.customDepthTarget = depthEntityRef;
822     cc.pipelineFlags = CameraComponent::PipelineFlagBits::CUBEMAP_BIT |
823                        CameraComponent::PipelineFlagBits::CLEAR_COLOR_BIT |
824                        CameraComponent::PipelineFlagBits::CLEAR_DEPTH_BIT;
825     cc.renderingPipeline = CameraComponent::RenderingPipeline::LIGHT_FORWARD;
826     cc.sceneFlags |= CameraComponent::SceneFlagBits::ACTIVE_RENDER_BIT;
827     cc.projection = CameraComponent::Projection::PERSPECTIVE;
828     cc.postProcess = camera;
829     ccm->Set(camera, cc);
830 
831     PostProcessComponent ppc;
832     ppc.enableFlags = 0;
833     ppcm->Set(camera, ppc);
834 
835     TransformComponent tc;
836     tc.position = position;
837     tc.rotation = Math::Quat { 0.f, 0.f, 0.f, 1.f };
838     tcm->Set(camera, tc);
839 
840     lmm->Create(camera);
841     wmm->Create(camera);
842     ncm->Create(camera);
843 
844     return camera;
845 }
846 } // namespace
847 
CreateReflectionProbe(CORE_NS::IEcs & ecs,const BASE_NS::Math::Vec3 & position) const848 CORE_NS::EntityReference SceneUtil::CreateReflectionProbe(CORE_NS::IEcs& ecs, const BASE_NS::Math::Vec3& position) const
849 {
850     auto pcm = GetManager<IReflectionProbeComponentManager>(ecs);
851     if (!pcm) {
852         return {};
853     }
854 
855     auto camera = CreateReflectionProbeCamera(ecs, graphicsContext_, position);
856     if (!camera) {
857         return {};
858     }
859 
860     // cube cam should always have 90 degrees of fov
861     const float viewportAngle { 90.0f };
862     // orthographic multiplier, should not matter with perspective view but for clarity, setting with no scale.
863     const float distortionMultiplier { 0.5f };
864     UpdateCameraViewport(ecs, camera, { REFLECTION_PROBE_DEFAULT_SIZE, REFLECTION_PROBE_DEFAULT_SIZE }, true,
865         Math::DEG2RAD * viewportAngle, viewportAngle * distortionMultiplier);
866 
867     EntityReference probe = ecs.GetEntityManager().CreateReferenceCounted();
868 
869     ReflectionProbeComponent pc;
870     pc.probeCamera = camera;
871     pcm->Set(probe, pc);
872 
873     return probe;
874 }
875 
876 namespace {
877 constexpr uint64_t TYPES[] = { CORE_NS::PropertyType::BOOL_T, CORE_NS::PropertyType::CHAR_T,
878     CORE_NS::PropertyType::INT8_T, CORE_NS::PropertyType::INT16_T, CORE_NS::PropertyType::INT32_T,
879     CORE_NS::PropertyType::INT64_T, CORE_NS::PropertyType::UINT8_T, CORE_NS::PropertyType::UINT16_T,
880     CORE_NS::PropertyType::UINT32_T, CORE_NS::PropertyType::UINT64_T,
881 #ifdef __APPLE__
882     CORE_NS::PropertyType::SIZE_T,
883 #endif
884     CORE_NS::PropertyType::FLOAT_T, CORE_NS::PropertyType::DOUBLE_T, CORE_NS::PropertyType::BOOL_ARRAY_T,
885     CORE_NS::PropertyType::CHAR_ARRAY_T, CORE_NS::PropertyType::INT8_ARRAY_T, CORE_NS::PropertyType::INT16_ARRAY_T,
886     CORE_NS::PropertyType::INT32_ARRAY_T, CORE_NS::PropertyType::INT64_ARRAY_T, CORE_NS::PropertyType::UINT8_ARRAY_T,
887     CORE_NS::PropertyType::UINT16_ARRAY_T, CORE_NS::PropertyType::UINT32_ARRAY_T, CORE_NS::PropertyType::UINT64_ARRAY_T,
888 #ifdef __APPLE__
889     CORE_NS::PropertyType::SIZE_ARRAY_T,
890 #endif
891     CORE_NS::PropertyType::FLOAT_ARRAY_T, CORE_NS::PropertyType::DOUBLE_ARRAY_T, CORE_NS::PropertyType::IVEC2_T,
892     CORE_NS::PropertyType::IVEC3_T, CORE_NS::PropertyType::IVEC4_T, CORE_NS::PropertyType::VEC2_T,
893     CORE_NS::PropertyType::VEC3_T, CORE_NS::PropertyType::VEC4_T, CORE_NS::PropertyType::UVEC2_T,
894     CORE_NS::PropertyType::UVEC3_T, CORE_NS::PropertyType::UVEC4_T, CORE_NS::PropertyType::QUAT_T,
895     CORE_NS::PropertyType::MAT3X3_T, CORE_NS::PropertyType::MAT4X4_T, CORE_NS::PropertyType::UID_T,
896     CORE_NS::PropertyType::STRING_T, CORE_NS::PropertyType::IVEC2_ARRAY_T, CORE_NS::PropertyType::IVEC3_ARRAY_T,
897     CORE_NS::PropertyType::IVEC4_ARRAY_T, CORE_NS::PropertyType::VEC2_ARRAY_T, CORE_NS::PropertyType::VEC3_ARRAY_T,
898     CORE_NS::PropertyType::VEC4_ARRAY_T, CORE_NS::PropertyType::UVEC2_ARRAY_T, CORE_NS::PropertyType::UVEC3_ARRAY_T,
899     CORE_NS::PropertyType::UVEC4_ARRAY_T, CORE_NS::PropertyType::QUAT_ARRAY_T, CORE_NS::PropertyType::MAT3X3_ARRAY_T,
900     CORE_NS::PropertyType::MAT4X4_ARRAY_T, CORE_NS::PropertyType::UID_ARRAY_T, CORE_NS::PropertyType::FLOAT_VECTOR_T,
901     CORE_NS::PropertyType::MAT4X4_VECTOR_T };
902 
903 template<typename EntityHandler, typename EntityReferenceHandler>
FindEntities(const CORE_NS::Property & property,uintptr_t offset,EntityHandler && entityFunc,EntityReferenceHandler && entityRefFunc)904 void FindEntities(const CORE_NS::Property& property, uintptr_t offset, EntityHandler&& entityFunc,
905     EntityReferenceHandler&& entityRefFunc)
906 {
907     if (property.type == CORE_NS::PropertyType::ENTITY_T) {
908         auto ptr = reinterpret_cast<CORE_NS::Entity*>(offset);
909         if (ptr && EntityUtil::IsValid(*ptr)) {
910             entityFunc(ptr);
911         }
912     } else if (property.type == CORE_NS::PropertyType::ENTITY_REFERENCE_T) {
913         auto ptr = reinterpret_cast<CORE_NS::EntityReference*>(offset);
914         if (ptr && EntityUtil::IsValid(*ptr)) {
915             entityRefFunc(ptr);
916         }
917     } else if (std::any_of(std::begin(TYPES), std::end(TYPES),
918         [&current = property.type](const uint64_t type) {
919             return type == current;
920         })) {
921         // One of the basic types so no further processing needed.
922     } else if (property.metaData.containerMethods) {
923         auto& containerProperty = property.metaData.containerMethods->property;
924         if (property.type.isArray) {
925             // Array of properties.
926             for (size_t i = 0; i < property.count; i++) {
927                 uintptr_t ptr = offset + i * containerProperty.size;
928                 FindEntities(containerProperty, ptr, BASE_NS::forward<EntityHandler>(entityFunc),
929                     BASE_NS::forward<EntityReferenceHandler>(entityRefFunc));
930             }
931         } else {
932             // This is a "non trivial container"
933             // (So it needs to read the data and not just the metadata to figure out the data structure).
934             const auto count = property.metaData.containerMethods->size(offset);
935             for (size_t i = 0; i < count; i++) {
936                 uintptr_t ptr = property.metaData.containerMethods->get(offset, i);
937                 FindEntities(containerProperty, ptr, BASE_NS::forward<EntityHandler>(entityFunc),
938                     BASE_NS::forward<EntityReferenceHandler>(entityRefFunc));
939             }
940         }
941     } else if (!property.metaData.memberProperties.empty()) {
942         // Custom type (struct). Process sub properties recursively.
943         for (size_t i = 0; i < property.count; i++) {
944             const auto ptr = offset;
945             offset += property.size / property.count;
946             for (const auto& child : property.metaData.memberProperties) {
947                 FindEntities(child, ptr + child.offset, BASE_NS::forward<EntityHandler>(entityFunc),
948                     BASE_NS::forward<EntityReferenceHandler>(entityRefFunc));
949             }
950         }
951     }
952 }
953 
GatherEntities(const IEcs & source,const Entity sourceEntity)954 vector<Entity> GatherEntities(const IEcs& source, const Entity sourceEntity)
955 {
956     vector<Entity> entities;
957     if (!CORE_NS::EntityUtil::IsValid(sourceEntity)) {
958         return entities;
959     }
960     auto nodeSystem = GetSystem<INodeSystem>(source);
961     if (!nodeSystem) {
962         CORE_LOG_W("Missing INodeSystem");
963         return entities;
964     }
965     vector<IComponentManager*> managers;
966 
967     vector<Entity> stack;
968     stack.push_back(sourceEntity);
969     while (!stack.empty()) {
970         const Entity entity = stack.back();
971         stack.pop_back();
972 
973         if (auto pos = LowerBound(entities.cbegin(), entities.cend(), entity);
974             (pos != entities.cend()) && (*pos == entity)) {
975             continue;
976         } else {
977             entities.insert(pos, entity);
978         }
979         source.GetComponents(entity, managers);
980         for (const auto& srcManager : managers) {
981             if (srcManager->GetUid() == INodeComponentManager::UID) {
982                 // for the hierarchy we need either ask the children via NodeSystem/SceneNode or construct the hierarchy
983                 // based on parent Entity.
984                 if (auto sceneNode = nodeSystem->GetNode(entity)) {
985                     auto children = sceneNode->GetChildren();
986                     stack.reserve(stack.size() + children.size());
987                     for (const auto child : children) {
988                         stack.push_back(child->GetEntity());
989                     }
990                 }
991             } else if (auto* data = srcManager->GetData(entity)) {
992                 // for other component types it's fine to go through all the properties.
993                 if (const auto base = reinterpret_cast<uintptr_t>(data->RLock())) {
994                     for (const auto& property : data->Owner()->MetaData()) {
995                         FindEntities(
996                             property, base + property.offset,
997                             [&stack](const Entity* entity) { stack.push_back(*entity); },
998                             [&stack](const EntityReference* entity) { stack.push_back(*entity); });
999                     }
1000                 }
1001                 data->RUnlock();
1002             }
1003         }
1004     }
1005 
1006     return entities;
1007 }
1008 
GatherEntities(const IEcs & source)1009 vector<Entity> GatherEntities(const IEcs& source)
1010 {
1011     vector<Entity> entities;
1012     auto& entityManager = source.GetEntityManager();
1013     auto it = entityManager.Begin();
1014     if (!it || it->Compare(entityManager.End())) {
1015         return entities;
1016     }
1017     do {
1018         entities.push_back(it->Get());
1019     } while (it->Next());
1020     return entities;
1021 }
1022 
UpdateEntities(IComponentManager * manager,Entity entity,const BASE_NS::unordered_map<CORE_NS::Entity,CORE_NS::Entity> & oldToNew)1023 void UpdateEntities(
1024     IComponentManager* manager, Entity entity, const BASE_NS::unordered_map<CORE_NS::Entity, CORE_NS::Entity>& oldToNew)
1025 {
1026     auto* data = manager->GetData(entity);
1027     if (!data) {
1028         return;
1029     }
1030     const auto base = reinterpret_cast<uintptr_t>(data->RLock());
1031     if (!base) {
1032         data->RUnlock();
1033         return;
1034     }
1035     // as multiple properties can point to the same value, use set to track which have been updates.
1036     std::unordered_set<uintptr_t> updatedProperties;
1037     auto& em = manager->GetEcs().GetEntityManager();
1038     for (const auto& property : data->Owner()->MetaData()) {
1039         FindEntities(
1040             property, base + property.offset,
1041             [oldToNew, data, &updatedProperties](Entity* entity) {
1042                 if (updatedProperties.count(reinterpret_cast<uintptr_t>(entity))) {
1043                     return;
1044                 }
1045                 updatedProperties.insert(reinterpret_cast<uintptr_t>(entity));
1046                 if (const auto it = oldToNew.find(*entity); it != oldToNew.end()) {
1047                     data->WLock();
1048                     *entity = it->second;
1049                     data->WUnlock();
1050                 } else {
1051                     CORE_LOG_D("couldn't find %s", to_hex(entity->id).data());
1052                 }
1053             },
1054             [oldToNew, data, &updatedProperties, &em](EntityReference* entity) {
1055                 if (updatedProperties.count(reinterpret_cast<uintptr_t>(entity))) {
1056                     return;
1057                 }
1058                 updatedProperties.insert(reinterpret_cast<uintptr_t>(entity));
1059                 if (const auto it = oldToNew.find(*entity); it != oldToNew.end()) {
1060                     data->WLock();
1061                     *entity = em.GetReferenceCounted(it->second);
1062                     data->WUnlock();
1063                 } else {
1064                     CORE_LOG_D("couldn't find %s", to_hex(static_cast<const Entity>(*entity).id).data());
1065                 }
1066             });
1067     }
1068     data->RUnlock();
1069 }
1070 
1071 struct CloneResults {
1072     vector<Entity> newEntities;
1073     unordered_map<Entity, Entity> oldToNew;
1074 };
1075 
CloneEntities(IEcs & destination,const IEcs & source,vector<Entity> srcEntities)1076 CloneResults CloneEntities(IEcs& destination, const IEcs& source, vector<Entity> srcEntities)
1077 {
1078     unordered_map<Entity, Entity> srcToDst;
1079     vector<Entity> newEntities;
1080     newEntities.reserve(srcEntities.size());
1081     vector<IComponentManager*> managers;
1082     while (!srcEntities.empty()) {
1083         const Entity srcEntity = srcEntities.back();
1084         srcEntities.pop_back();
1085 
1086         const Entity dstEntity = destination.GetEntityManager().Create();
1087         newEntities.push_back(dstEntity);
1088         srcToDst[srcEntity] = dstEntity;
1089 
1090         source.GetComponents(srcEntity, managers);
1091         for (const auto& srcManager : managers) {
1092             const auto* srcData = srcManager->GetData(srcEntity);
1093             if (!srcData) {
1094                 continue;
1095             }
1096             auto* dstManager = destination.GetComponentManager(srcManager->GetUid());
1097             if (!dstManager) {
1098                 CORE_LOG_W("ComponentManager %s missing from destination.", to_string(srcManager->GetUid()).data());
1099                 continue;
1100             }
1101             dstManager->Create(dstEntity);
1102             dstManager->SetData(dstEntity, *srcData);
1103         }
1104     }
1105 
1106     for (const auto& newEntity : newEntities) {
1107         destination.GetComponents(newEntity, managers);
1108         for (const auto& dstManager : managers) {
1109             UpdateEntities(dstManager, newEntity, srcToDst);
1110         }
1111     }
1112     return CloneResults { BASE_NS::move(newEntities), BASE_NS::move(srcToDst) };
1113 }
1114 } // namespace
1115 
Clone(IEcs & destination,const Entity parentEntity,const IEcs & source,const Entity sourceEntity) const1116 Entity SceneUtil::Clone(
1117     IEcs& destination, const Entity parentEntity, const IEcs& source, const Entity sourceEntity) const
1118 {
1119     if (&destination == &source) {
1120         return {};
1121     }
1122     if (CORE_NS::EntityUtil::IsValid(parentEntity) && !destination.GetEntityManager().IsAlive(parentEntity)) {
1123         return {};
1124     }
1125     if (!CORE_NS::EntityUtil::IsValid(sourceEntity) || !source.GetEntityManager().IsAlive(sourceEntity)) {
1126         return {};
1127     }
1128     auto result = CloneEntities(destination, source, GatherEntities(source, sourceEntity));
1129     Entity destinationEntity = result.oldToNew[sourceEntity];
1130     auto* nodeManager = GetManager<INodeComponentManager>(destination);
1131     if (nodeManager) {
1132         auto handle = nodeManager->Write(destinationEntity);
1133         if (handle) {
1134             handle->parent = parentEntity;
1135         }
1136     } else {
1137         CORE_LOG_W("Failed to reparent: missing INodeComponentManager");
1138     }
1139     return destinationEntity;
1140 }
1141 
Clone(IEcs & destination,const IEcs & source) const1142 vector<Entity> SceneUtil::Clone(IEcs& destination, const IEcs& source) const
1143 {
1144     if (&destination == &source) {
1145         return {};
1146     }
1147 
1148     auto result = CloneEntities(destination, source, GatherEntities(source));
1149     return result.newEntities;
1150 }
1151 CORE3D_END_NAMESPACE()
1152