• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 <core/util/intf_frustum_util.h>
62 #include <render/device/intf_gpu_resource_manager.h>
63 #include <render/device/intf_shader_manager.h>
64 #include <render/intf_render_context.h>
65 
66 #include "uri_lookup.h"
67 #include "util/component_util_functions.h"
68 
69 namespace {
70 constexpr uint32_t REFLECTION_PROBE_DEFAULT_SIZE { 256U };
71 }
72 
73 CORE3D_BEGIN_NAMESPACE()
74 using namespace BASE_NS;
75 using namespace CORE_NS;
76 using namespace RENDER_NS;
77 
78 namespace CameraMatrixUtil {
CalculateProjectionMatrix(const CameraComponent & cameraComponent,bool & isCameraNegative)79 Math::Mat4X4 CalculateProjectionMatrix(const CameraComponent& cameraComponent, bool& isCameraNegative)
80 {
81     switch (cameraComponent.projection) {
82         case CameraComponent::Projection::ORTHOGRAPHIC: {
83             auto orthoProj = Math::OrthoRhZo(cameraComponent.xMag * -0.5f, cameraComponent.xMag * 0.5f,
84                 cameraComponent.yMag * -0.5f, cameraComponent.yMag * 0.5f, cameraComponent.zNear, cameraComponent.zFar);
85             orthoProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
86             return orthoProj;
87         }
88         case CameraComponent::Projection::PERSPECTIVE: {
89             auto aspect = 1.f;
90             if (cameraComponent.aspect > 0.f) {
91                 aspect = cameraComponent.aspect;
92             } else if (cameraComponent.renderResolution.y > 0U) {
93                 aspect = static_cast<float>(cameraComponent.renderResolution.x) /
94                          static_cast<float>(cameraComponent.renderResolution.y);
95             }
96             auto persProj =
97                 Math::PerspectiveRhZo(cameraComponent.yFov, aspect, cameraComponent.zNear, cameraComponent.zFar);
98             persProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
99             return persProj;
100         }
101         case CameraComponent::Projection::FRUSTUM: {
102             auto aspect = 1.f;
103             if (cameraComponent.aspect > 0.f) {
104                 aspect = cameraComponent.aspect;
105             } else if (cameraComponent.renderResolution.y > 0U) {
106                 aspect = static_cast<float>(cameraComponent.renderResolution.x) /
107                          static_cast<float>(cameraComponent.renderResolution.y);
108             }
109             // with offset 0.5, the camera should offset half the screen
110             const float scale = tan(cameraComponent.yFov * 0.5f) * cameraComponent.zNear;
111             const float xOffset = cameraComponent.xOffset * scale * aspect * 2.0f;
112             const float yOffset = cameraComponent.yOffset * scale * 2.0f;
113             float left = -aspect * scale;
114             float right = aspect * scale;
115             float bottom = -scale;
116             float top = scale;
117             auto persProj = Math::PerspectiveRhZo(left + xOffset, right + xOffset, bottom + yOffset, top + yOffset,
118                 cameraComponent.zNear, cameraComponent.zFar);
119             persProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
120             return persProj;
121         }
122         case CameraComponent::Projection::CUSTOM: {
123             isCameraNegative = Math::Determinant(cameraComponent.customProjectionMatrix) < 0.0f;
124             return cameraComponent.customProjectionMatrix;
125         }
126         default:
127             return Math::IDENTITY_4X4;
128     }
129 }
130 } // namespace CameraMatrixUtil
131 
SceneUtil(IGraphicsContext & graphicsContext)132 SceneUtil::SceneUtil(IGraphicsContext& graphicsContext) : graphicsContext_(graphicsContext) {}
133 
CreateCamera(IEcs & ecs,const Math::Vec3 & position,const Math::Quat & rotation,float zNear,float zFar,float fovDegrees) const134 Entity SceneUtil::CreateCamera(
135     IEcs& ecs, const Math::Vec3& position, const Math::Quat& rotation, float zNear, float zFar, float fovDegrees) const
136 {
137     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
138     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
139     auto ncm = GetManager<INodeComponentManager>(ecs);
140     auto tcm = GetManager<ITransformComponentManager>(ecs);
141     auto ccm = GetManager<ICameraComponentManager>(ecs);
142     if (!lmm || !wmm || !ncm || !tcm || !ccm) {
143         return {};
144     }
145 
146     Entity camera = ecs.GetEntityManager().Create();
147 
148     lmm->Create(camera);
149     wmm->Create(camera);
150     ncm->Create(camera);
151 
152     TransformComponent tc;
153     tc.position = position;
154     tc.rotation = rotation;
155     tcm->Set(camera, tc);
156 
157     CameraComponent cc;
158     cc.sceneFlags |= CameraComponent::SceneFlagBits::ACTIVE_RENDER_BIT;
159     cc.projection = CameraComponent::Projection::PERSPECTIVE;
160     cc.yFov = Math::DEG2RAD * fovDegrees;
161     cc.zNear = zNear;
162     cc.zFar = zFar;
163     ccm->Set(camera, cc);
164 
165     return camera;
166 }
167 
UpdateCameraViewport(IEcs & ecs,Entity entity,const Math::UVec2 & renderResolution) const168 void SceneUtil::UpdateCameraViewport(IEcs& ecs, Entity entity, const Math::UVec2& renderResolution) const
169 {
170     auto ccm = GetManager<ICameraComponentManager>(ecs);
171     if (ccm && CORE_NS::EntityUtil::IsValid(entity)) {
172         ScopedHandle<CameraComponent> cameraHandle = ccm->Write(entity);
173         if (!cameraHandle) {
174             ccm->Create(entity);
175             cameraHandle = ccm->Write(entity);
176             if (!cameraHandle) {
177                 return;
178             }
179         }
180         CameraComponent& cameraComponent = *cameraHandle;
181         cameraComponent.aspect = (renderResolution.y > 0U)
182                                      ? (static_cast<float>(renderResolution.x) / static_cast<float>(renderResolution.y))
183                                      : 1.0f;
184         cameraComponent.renderResolution[0] = renderResolution.x;
185         cameraComponent.renderResolution[1] = renderResolution.y;
186     }
187 }
188 
UpdateCameraViewport(IEcs & ecs,Entity entity,const Math::UVec2 & renderResolution,bool autoAspect,float fovY,float orthoScale) const189 void SceneUtil::UpdateCameraViewport(
190     IEcs& ecs, Entity entity, const Math::UVec2& renderResolution, bool autoAspect, float fovY, float orthoScale) const
191 {
192     auto ccm = GetManager<ICameraComponentManager>(ecs);
193     if (ccm && CORE_NS::EntityUtil::IsValid(entity)) {
194         ScopedHandle<CameraComponent> cameraHandle = ccm->Write(entity);
195         if (!cameraHandle) {
196             ccm->Create(entity);
197             cameraHandle = ccm->Write(entity);
198             if (!cameraHandle) {
199                 return;
200             }
201         }
202         CameraComponent& cameraComponent = *cameraHandle;
203         if (autoAspect) {
204             const float aspectRatio =
205                 (renderResolution.y > 0)
206                     ? (static_cast<float>(renderResolution.x) / static_cast<float>(renderResolution.y))
207                     : 1.0f;
208 
209             // Using the fov value as xfov on portrait screens to keep the object
210             // better in the frame.
211             const float yFov = (aspectRatio > 1.0f) ? fovY : (2.0f * Math::atan(Math::tan(fovY * 0.5f) / aspectRatio));
212 
213             // Update the camera parameters.
214             cameraComponent.aspect = aspectRatio;
215             cameraComponent.yFov = yFov;
216 
217             // The camera can also be in ortho mode. Using a separate zoom value.
218             cameraComponent.yMag = orthoScale;
219             cameraComponent.xMag = cameraComponent.yMag * aspectRatio;
220         }
221 
222         cameraComponent.renderResolution[0] = renderResolution.x;
223         cameraComponent.renderResolution[1] = renderResolution.y;
224     }
225 }
226 
CameraLookAt(IEcs & ecs,Entity entity,const Math::Vec3 & eye,const Math::Vec3 & target,const Math::Vec3 & up) const227 void SceneUtil::CameraLookAt(
228     IEcs& ecs, Entity entity, const Math::Vec3& eye, const Math::Vec3& target, const Math::Vec3& up) const
229 {
230     auto ncm = GetManager<INodeComponentManager>(ecs);
231     auto tcm = GetManager<ITransformComponentManager>(ecs);
232     if (!ncm || !tcm) {
233         return;
234     }
235 
236     auto parentWorld = Math::Mat4X4(1.f);
237     auto getParent = [ncm](Entity entity) {
238         if (auto nodeHandle = ncm->Read(entity)) {
239             return nodeHandle->parent;
240         }
241         return Entity {};
242     };
243 
244     // walk up the hierachy to get an up-to-date world matrix.
245     for (Entity node = getParent(entity); EntityUtil::IsValid(node); node = getParent(node)) {
246         if (auto parentTransformHandle = tcm->Read(node)) {
247             parentWorld = Math::Trs(parentTransformHandle->position, parentTransformHandle->rotation,
248                               parentTransformHandle->scale) *
249                           parentWorld;
250         }
251     }
252 
253     // entity's local transform should invert any parent transformations and apply the transform of rotating towards
254     // target and moving to eye. this transform is the inverse of the look-at matrix.
255     auto worldMatrix = Math::Inverse(parentWorld) * Math::Inverse(Math::LookAtRh(eye, target, up));
256 
257     Math::Vec3 scale;
258     Math::Quat orientation;
259     Math::Vec3 translation;
260     Math::Vec3 skew;
261     Math::Vec4 perspective;
262     if (Math::Decompose(worldMatrix, scale, orientation, translation, skew, perspective)) {
263         if (auto transformHandle = tcm->Write(entity)) {
264             transformHandle->position = translation;
265             transformHandle->rotation = orientation;
266             transformHandle->scale = scale;
267         }
268     }
269 }
270 
CreateLight(IEcs & ecs,const LightComponent & lightComponent,const Math::Vec3 & position,const Math::Quat & rotation) const271 Entity SceneUtil::CreateLight(
272     IEcs& ecs, const LightComponent& lightComponent, const Math::Vec3& position, const Math::Quat& rotation) const
273 {
274     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
275     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
276     auto ncm = GetManager<INodeComponentManager>(ecs);
277     auto nameM = GetManager<INameComponentManager>(ecs);
278     auto tcm = GetManager<ITransformComponentManager>(ecs);
279     auto lcm = GetManager<ILightComponentManager>(ecs);
280     if (!lmm || !wmm || !ncm || !nameM || !tcm || !lcm) {
281         return {};
282     }
283 
284     Entity light = ecs.GetEntityManager().Create();
285 
286     lmm->Create(light);
287     wmm->Create(light);
288     ncm->Create(light);
289     nameM->Create(light);
290     constexpr string_view lightName("Light");
291     nameM->Write(light)->name = lightName;
292 
293     TransformComponent tc;
294     tc.position = position;
295     tc.rotation = rotation;
296     tcm->Set(light, tc);
297 
298     LightComponent lc = lightComponent;
299     lc.shadowEnabled = (lc.type != LightComponent::Type::POINT) && lc.shadowEnabled;
300     // NOTE: range is calculated automatically by the render system if the value is default 0.0
301     lcm->Set(light, lc);
302 
303     return light;
304 }
305 
306 // reflection plane helpers
307 namespace {
308 // default size, updated in render system based on main scene camera
309 constexpr Math::UVec2 DEFAULT_PLANE_TARGET_SIZE { 2u, 2u };
310 
CreateReflectionPlaneGpuImage(IGpuResourceManager & gpuResourceMgr,IRenderHandleComponentManager & handleManager,INameComponentManager & nameManager,const string_view name,const Format format,const ImageUsageFlags usageFlags,const MemoryPropertyFlags memoryPropertyFlags)311 EntityReference CreateReflectionPlaneGpuImage(IGpuResourceManager& gpuResourceMgr,
312     IRenderHandleComponentManager& handleManager, INameComponentManager& nameManager, const string_view name,
313     const Format format, const ImageUsageFlags usageFlags, const MemoryPropertyFlags memoryPropertyFlags)
314 {
315     const auto entity = handleManager.GetEcs().GetEntityManager().CreateReferenceCounted();
316     handleManager.Create(entity);
317 
318     GpuImageDesc desc;
319     desc.width = DEFAULT_PLANE_TARGET_SIZE.x;
320     desc.height = DEFAULT_PLANE_TARGET_SIZE.y;
321     desc.depth = 1U;
322     desc.mipCount = DefaultMaterialCameraConstants::REFLECTION_PLANE_MIP_COUNT;
323     desc.format = format;
324     desc.memoryPropertyFlags = memoryPropertyFlags;
325     desc.usageFlags = usageFlags;
326     desc.imageType = ImageType::CORE_IMAGE_TYPE_2D;
327     desc.imageTiling = ImageTiling::CORE_IMAGE_TILING_OPTIMAL;
328     desc.imageViewType = ImageViewType::CORE_IMAGE_VIEW_TYPE_2D;
329     desc.engineCreationFlags = EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS;
330     handleManager.Write(entity)->reference = gpuResourceMgr.Create(name, desc);
331 
332     nameManager.Create(entity);
333     nameManager.Write(entity)->name = name;
334     return entity;
335 }
336 
CreateReflectionPlaneObjectFromEntity(IEcs & ecs,IGraphicsContext & graphicsContext,const Entity & nodeEntity)337 SceneUtil::ReflectionPlane CreateReflectionPlaneObjectFromEntity(
338     IEcs& ecs, IGraphicsContext& graphicsContext, const Entity& nodeEntity)
339 {
340     SceneUtil::ReflectionPlane plane;
341     auto* renderMeshCM = GetManager<IRenderMeshComponentManager>(ecs);
342     auto* meshCM = GetManager<IMeshComponentManager>(ecs);
343     auto* matCM = GetManager<IMaterialComponentManager>(ecs);
344     auto* gpuHandleCM = GetManager<IRenderHandleComponentManager>(ecs);
345 
346     auto* nameCM = GetManager<INameComponentManager>(ecs);
347     if (!(renderMeshCM && meshCM && matCM && gpuHandleCM && nameCM)) {
348         return plane;
349     }
350 
351     auto& device = graphicsContext.GetRenderContext().GetDevice();
352     auto& gpuResourceMgr = device.GetGpuResourceManager();
353     plane.entity = nodeEntity;
354     plane.colorTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM,
355         DefaultMaterialCameraConstants::CAMERA_COLOR_PREFIX_NAME + to_hex(nodeEntity.id),
356         Format::BASE_FORMAT_B10G11R11_UFLOAT_PACK32,
357         ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT,
358         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
359 
360     // NOTE: uses transient attachement usage flag and cannot be read.
361     plane.depthTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM,
362         DefaultMaterialCameraConstants::CAMERA_DEPTH_PREFIX_NAME + to_hex(nodeEntity.id), Format::BASE_FORMAT_D16_UNORM,
363         ImageUsageFlagBits::CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
364             ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
365         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
366             MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT);
367 
368     const auto meshHandle = [&]() {
369         const auto rmcHandle = renderMeshCM->Read(nodeEntity);
370         if (!rmcHandle) {
371             return ScopedHandle<const MeshComponent> {};
372         }
373         return meshCM->Read(rmcHandle->mesh);
374     }();
375     if (!meshHandle && meshHandle->submeshes.empty()) {
376         return plane;
377     }
378     if (auto matHandle = matCM->Write(meshHandle->submeshes[0].material); matHandle) {
379         MaterialComponent& matComponent = *matHandle;
380         auto* uriCM = GetManager<IUriComponentManager>(ecs);
381         auto* renderHandleCM = GetManager<IRenderHandleComponentManager>(ecs);
382         constexpr const string_view uri = "3dshaders://shader/core3d_dm_fw_reflection_plane.shader";
383         auto shaderEntity = LookupResourceByUri(uri, *uriCM, *renderHandleCM);
384         if (!EntityUtil::IsValid(shaderEntity)) {
385             shaderEntity = ecs.GetEntityManager().Create();
386             renderHandleCM->Create(shaderEntity);
387             renderHandleCM->Write(shaderEntity)->reference = device.GetShaderManager().GetShaderHandle(uri);
388             uriCM->Create(shaderEntity);
389             uriCM->Write(shaderEntity)->uri = uri;
390         }
391         matComponent.materialShader.shader = ecs.GetEntityManager().GetReferenceCounted(shaderEntity);
392 
393         matComponent.textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS].image = plane.colorTarget;
394         matComponent.extraRenderingFlags = MaterialComponent::ExtraRenderingFlagBits::DISCARD_BIT;
395     }
396 
397     return plane;
398 }
399 } // namespace
400 
CreateReflectionPlaneComponent(IEcs & ecs,const Entity & nodeEntity)401 void SceneUtil::CreateReflectionPlaneComponent(IEcs& ecs, const Entity& nodeEntity)
402 {
403     SceneUtil::ReflectionPlane plane = CreateReflectionPlaneObjectFromEntity(ecs, graphicsContext_, nodeEntity);
404     if (EntityUtil::IsValid(plane.entity)) {
405         auto prcm = GetManager<IPlanarReflectionComponentManager>(ecs);
406         PlanarReflectionComponent prc;
407         prc.colorRenderTarget = plane.colorTarget;
408         prc.depthRenderTarget = plane.depthTarget;
409         prc.renderTargetResolution[0] = DEFAULT_PLANE_TARGET_SIZE.x;
410         prc.renderTargetResolution[1] = DEFAULT_PLANE_TARGET_SIZE.y;
411         prcm->Set(plane.entity, prc);
412     }
413 }
414 
415 namespace {
CalculateScalingFactor(IEcs & ecs,Entity targetEntity,Entity sourceEntity)416 float CalculateScalingFactor(IEcs& ecs, Entity targetEntity, Entity sourceEntity)
417 {
418     float scale = 1.f;
419     auto renderMeshManager = GetManager<IRenderMeshComponentManager>(ecs);
420     auto meshManager = GetManager<IMeshComponentManager>(ecs);
421     auto transformManager = GetManager<ITransformComponentManager>(ecs);
422     if (!renderMeshManager || !meshManager || !transformManager) {
423         return scale;
424     }
425 
426     Entity dstMeshEntity;
427     Entity srcMeshEntity;
428     if (auto renderMeshComponent = renderMeshManager->Read(targetEntity); renderMeshComponent) {
429         dstMeshEntity = renderMeshComponent->mesh;
430     }
431     if (auto renderMeshComponent = renderMeshManager->Read(sourceEntity); renderMeshComponent) {
432         srcMeshEntity = renderMeshComponent->mesh;
433     }
434     if (!EntityUtil::IsValid(dstMeshEntity) || !EntityUtil::IsValid(srcMeshEntity)) {
435         return scale;
436     }
437     auto getMeshHeight = [](IMeshComponentManager& meshManager, Entity entity) {
438         if (auto meshComponent = meshManager.Read(entity); meshComponent) {
439             return (meshComponent->aabbMax.y - meshComponent->aabbMin.y);
440         }
441         return 0.f;
442     };
443 
444     const float dstHeight = getMeshHeight(*meshManager, dstMeshEntity);
445     const float srcHeight = getMeshHeight(*meshManager, srcMeshEntity);
446     if ((dstHeight == 0.f) || (srcHeight == 0.f)) {
447         return scale;
448     }
449 
450     auto dstMeshScale = transformManager->Get(targetEntity).scale;
451     auto srcMeshScale = transformManager->Get(sourceEntity).scale;
452     const auto dstSize = dstHeight * dstMeshScale.y;
453     const auto srcSize = srcHeight * srcMeshScale.y;
454     if (srcSize == 0.f) {
455         return scale;
456     }
457     scale = dstSize / srcSize;
458     return scale;
459 }
460 
CreateJointMapping(IEcs & ecs,array_view<const Entity> dstJointEntities,array_view<const Entity> srcJointEntities)461 vector<Entity> CreateJointMapping(
462     IEcs& ecs, array_view<const Entity> dstJointEntities, array_view<const Entity> srcJointEntities)
463 {
464     vector<Entity> srcToDstJointMapping;
465     auto* nameManager = GetManager<INameComponentManager>(ecs);
466     if (!nameManager) {
467         return srcToDstJointMapping;
468     }
469 
470     auto getName = [nameManager](const Entity& jointEntity) -> string_view {
471         if (auto nameComponent = nameManager->Read(jointEntity); nameComponent) {
472             return nameComponent->name;
473         }
474         return {};
475     };
476 
477     vector<string_view> dstJointNames;
478     dstJointNames.reserve(dstJointEntities.size());
479     std::transform(dstJointEntities.begin(), dstJointEntities.end(), std::back_inserter(dstJointNames), getName);
480 
481     vector<string_view> srcJointNames;
482     srcJointNames.reserve(srcJointEntities.size());
483     std::transform(srcJointEntities.begin(), srcJointEntities.end(), std::back_inserter(srcJointNames), getName);
484 
485     srcToDstJointMapping.reserve(srcJointEntities.size());
486 
487     const auto dstJointNamesBegin = dstJointNames.cbegin();
488     const auto dstJointNamesEnd = dstJointNames.cend();
489     for (const auto& srcJointName : srcJointNames) {
490         const auto pos = std::find(dstJointNamesBegin, dstJointNamesEnd, srcJointName);
491         srcToDstJointMapping.push_back(
492             (pos != dstJointNamesEnd) ? dstJointEntities[static_cast<size_t>(std::distance(dstJointNamesBegin, pos))]
493                                       : Entity {});
494         if (pos == dstJointNamesEnd) {
495             CORE_LOG_W("Target skin missing joint %s", srcJointName.data());
496         }
497     }
498     return srcToDstJointMapping;
499 }
500 
UpdateTracks(IEcs & ecs,array_view<EntityReference> targetTracks,array_view<const EntityReference> sourceTracks,array_view<const Entity> srcJointEntities,array_view<const Entity> srcToDstJointMapping,float scale)501 vector<Entity> UpdateTracks(IEcs& ecs, array_view<EntityReference> targetTracks,
502     array_view<const EntityReference> sourceTracks, array_view<const Entity> srcJointEntities,
503     array_view<const Entity> srcToDstJointMapping, float scale)
504 {
505     vector<Entity> trackTargets;
506     auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs);
507     auto animationOutputManager = GetManager<IAnimationOutputComponentManager>(ecs);
508     if (!animationTrackManager || !animationOutputManager) {
509         return trackTargets;
510     }
511     trackTargets.reserve(sourceTracks.size());
512     auto& entityManager = ecs.GetEntityManager();
513     // update tracks to point to target skin's joints.
514     std::transform(sourceTracks.begin(), sourceTracks.end(), targetTracks.begin(),
515         [&entityManager, animationTrackManager, animationOutputManager, &srcJointEntities, &srcToDstJointMapping,
516             &trackTargets, scale](const EntityReference& srcTrackEntity) {
517             const auto srcTrackId = animationTrackManager->GetComponentId(srcTrackEntity);
518             const auto srcTargetEntity = (srcTrackId != IComponentManager::INVALID_COMPONENT_ID)
519                                              ? animationTrackManager->Read(srcTrackId)->target
520                                              : Entity {};
521             if (EntityUtil::IsValid(srcTargetEntity)) {
522                 // check that the src track target is one of the src joints
523                 if (const auto pos = std::find(srcJointEntities.begin(), srcJointEntities.end(), srcTargetEntity);
524                     pos != srcJointEntities.end()) {
525                     auto dstTrackEntity = entityManager.CreateReferenceCounted();
526                     animationTrackManager->Create(dstTrackEntity);
527                     auto dstTrack = animationTrackManager->Write(dstTrackEntity);
528                     auto srcTrack = animationTrackManager->Read(srcTrackId);
529                     *dstTrack = *srcTrack;
530                     const auto jointIndex = static_cast<size_t>(pos - srcJointEntities.begin());
531                     trackTargets.push_back(srcToDstJointMapping[jointIndex]);
532                     dstTrack->target = {};
533 
534                     // joint position track needs to be offset
535                     if ((dstTrack->component == ITransformComponentManager::UID) &&
536                         (dstTrack->property == "position")) {
537                         if (const auto srcOutputId = animationOutputManager->GetComponentId(srcTrack->data);
538                             srcOutputId != IComponentManager::INVALID_COMPONENT_ID) {
539                             // create new animation output with original position corrected by the scale.
540                             dstTrack->data = entityManager.CreateReferenceCounted();
541                             animationOutputManager->Create(dstTrack->data);
542                             const auto dstOutput = animationOutputManager->Write(dstTrack->data);
543                             const auto srcOutput = animationOutputManager->Read(srcOutputId);
544                             dstOutput->type = srcOutput->type;
545                             auto& dst = dstOutput->data;
546                             const auto& src = srcOutput->data;
547                             dst.resize(src.size());
548                             const auto count = dst.size() / sizeof(Math::Vec3);
549                             const auto srcPositions =
550                                 array_view(reinterpret_cast<const Math::Vec3*>(src.data()), count);
551                             auto dstPositions = array_view(reinterpret_cast<Math::Vec3*>(dst.data()), count);
552                             std::transform(srcPositions.begin(), srcPositions.end(), dstPositions.begin(),
553                                 [scale](const Math::Vec3 position) { return position * scale; });
554                         }
555                     }
556                     return dstTrackEntity;
557                 }
558             }
559             CORE_LOG_W("no target for track %" PRIx64, static_cast<Entity>(srcTrackEntity).id);
560             return srcTrackEntity;
561         });
562     return trackTargets;
563 }
564 } // namespace
565 
RetargetSkinAnimation(IEcs & ecs,Entity targetEntity,Entity sourceEntity,Entity animationEntity) const566 IAnimationPlayback* SceneUtil::RetargetSkinAnimation(
567     IEcs& ecs, Entity targetEntity, Entity sourceEntity, Entity animationEntity) const
568 {
569     auto jointsManager = GetManager<ISkinJointsComponentManager>(ecs);
570     auto animationManager = GetManager<IAnimationComponentManager>(ecs);
571     auto transformManager = GetManager<ITransformComponentManager>(ecs);
572     auto animationSystem = GetSystem<IAnimationSystem>(ecs);
573     if (!jointsManager || !animationManager || !transformManager || !animationSystem) {
574         return nullptr;
575     }
576     auto dstJointsComponent = jointsManager->Read(targetEntity);
577     auto srcJointsComponent = jointsManager->Read(sourceEntity);
578     if (!dstJointsComponent || !srcJointsComponent) {
579         return nullptr;
580     }
581     if (!animationManager->HasComponent(animationEntity)) {
582         return nullptr;
583     }
584 
585     auto dstJointEntities = array_view(dstJointsComponent->jointEntities, dstJointsComponent->count);
586     auto srcJointEntities = array_view(srcJointsComponent->jointEntities, srcJointsComponent->count);
587 
588     const vector<Entity> srcToDstJointMapping = CreateJointMapping(ecs, dstJointEntities, srcJointEntities);
589 
590     // calculate a scaling factor based on mesh heights
591     const auto scale = CalculateScalingFactor(ecs, targetEntity, sourceEntity);
592 
593     // compensate difference in root joint positions.
594     // synchronize initial pose by copying src joint rotations to dst joints.
595     {
596         auto srcIt = srcJointEntities.begin();
597         for (const auto& dstE : srcToDstJointMapping) {
598             auto srcTransform = transformManager->Read(*srcIt);
599             auto dstTransform = transformManager->Write(dstE);
600             if (srcTransform && dstTransform) {
601                 dstTransform->position = srcTransform->position * scale;
602                 dstTransform->rotation = srcTransform->rotation;
603             }
604             ++srcIt;
605         }
606     }
607 
608     auto& entityManager = ecs.GetEntityManager();
609     const auto dstAnimationEntity = entityManager.Create();
610     if (auto* nameManager = GetManager<INameComponentManager>(ecs)) {
611         nameManager->Create(dstAnimationEntity);
612         auto srcName = nameManager->Read(animationEntity);
613         nameManager->Write(dstAnimationEntity)->name =
614             (srcName ? srcName->name : string_view("<empty>")) + " retargeted";
615     }
616 
617     vector<Entity> trackTargets;
618     {
619         animationManager->Create(dstAnimationEntity);
620         auto dstAnimationComponent = animationManager->Write(dstAnimationEntity);
621         auto srcAnimationComponent = animationManager->Read(animationEntity);
622         if (!srcAnimationComponent) {
623             return nullptr;
624         }
625         // copy the src animation.
626         *dstAnimationComponent = *srcAnimationComponent;
627         trackTargets = UpdateTracks(ecs, dstAnimationComponent->tracks, srcAnimationComponent->tracks, srcJointEntities,
628             srcToDstJointMapping, scale);
629     }
630 
631     return animationSystem->CreatePlayback(dstAnimationEntity, trackTargets);
632 }
633 
GetDefaultMaterialShaderData(IEcs & ecs,const ISceneUtil::MaterialShaderInfo & info,MaterialComponent::Shader & materialShader,MaterialComponent::Shader & depthShader) const634 void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info,
635     MaterialComponent::Shader& materialShader, MaterialComponent::Shader& depthShader) const
636 {
637     auto* renderHandleMgr = GetManager<IRenderHandleComponentManager>(ecs);
638     if (renderHandleMgr) {
639         IEntityManager& entityMgr = ecs.GetEntityManager();
640         const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager();
641         {
642             const uint32_t renderSlotId =
643                 (info.alphaBlend)
644                     ? shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT)
645                     : shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE);
646 
647             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
648             materialShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
649             RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
650             gs.rasterizationState.cullModeFlags = info.cullModeFlags;
651             gs.rasterizationState.frontFace = info.frontFace;
652             const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
653             const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
654             if (gsHandle) {
655                 materialShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
656             }
657         }
658         if (!info.alphaBlend) {
659             const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH);
660             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
661             depthShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
662             RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
663             gs.rasterizationState.cullModeFlags = info.cullModeFlags;
664             gs.rasterizationState.frontFace = info.frontFace;
665             const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
666             const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
667             if (gsHandle) {
668                 depthShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
669             }
670         }
671     }
672 }
673 
GetDefaultMaterialShaderData(IEcs & ecs,const ISceneUtil::MaterialShaderInfo & info,const string_view renderSlot,MaterialComponent::Shader & shader) const674 void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info,
675     const string_view renderSlot, MaterialComponent::Shader& shader) const
676 {
677     auto* renderHandleMgr = GetManager<IRenderHandleComponentManager>(ecs);
678     if (renderHandleMgr) {
679         IEntityManager& entityMgr = ecs.GetEntityManager();
680         const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager();
681         const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(renderSlot);
682         if (renderSlotId != ~0u) {
683             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
684             if (rsd.shader) {
685                 shader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
686             } else {
687                 CORE_LOG_D("SceneUtil: render slot base shader not found (%s)", renderSlot.data());
688             }
689             if (rsd.graphicsState) {
690                 RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
691                 gs.rasterizationState.cullModeFlags = info.cullModeFlags;
692                 gs.rasterizationState.frontFace = info.frontFace;
693                 const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
694                 const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
695                 shader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
696             } else {
697                 CORE_LOG_D("SceneUtil: render slot base graphics state not found (%s)", renderSlot.data());
698             }
699         } else {
700             CORE_LOG_W("SceneUtil: render slot id not found (%s)", renderSlot.data());
701         }
702     }
703 }
704 
ShareSkin(IEcs & ecs,Entity targetEntity,Entity sourceEntity) const705 void SceneUtil::ShareSkin(IEcs& ecs, Entity targetEntity, Entity sourceEntity) const
706 {
707     auto jointsManager = GetManager<ISkinJointsComponentManager>(ecs);
708     if (!jointsManager) {
709         CORE_LOG_E("Missing ISkinJointsComponentManager.");
710         return;
711     }
712     vector<Entity> dstToSrcJointMapping;
713     {
714         auto dstJointsComponent = jointsManager->Read(targetEntity);
715         auto srcJointsComponent = jointsManager->Read(sourceEntity);
716         if (!dstJointsComponent) {
717             CORE_LOG_E("target doesn't have SkinJointsComponent.");
718             return;
719         }
720         if (!srcJointsComponent) {
721             CORE_LOG_E("source doesn't have SkinJointsComponent.");
722             return;
723         }
724 
725         auto dstJointEntities = array_view(dstJointsComponent->jointEntities, dstJointsComponent->count);
726         auto srcJointEntities = array_view(srcJointsComponent->jointEntities, srcJointsComponent->count);
727 
728         dstToSrcJointMapping = CreateJointMapping(ecs, srcJointEntities, dstJointEntities);
729         if (dstJointsComponent->count != dstToSrcJointMapping.size()) {
730             CORE_LOG_E("couldn't match all joints.");
731         }
732     }
733     auto dstJointsComponent = jointsManager->Write(targetEntity);
734     const auto jointCount = Math::min(dstToSrcJointMapping.size(), countof(dstJointsComponent->jointEntities));
735     std::copy(dstToSrcJointMapping.data(), dstToSrcJointMapping.data() + jointCount, dstJointsComponent->jointEntities);
736 }
737 
RegisterSceneLoader(const ISceneLoader::Ptr & loader)738 void SceneUtil::RegisterSceneLoader(const ISceneLoader::Ptr& loader)
739 {
740     if (auto pos = std::find_if(sceneLoaders_.cbegin(), sceneLoaders_.cend(),
741             [newLoader = loader.get()](const ISceneLoader::Ptr& registered) { return registered.get() == newLoader; });
742         pos == sceneLoaders_.cend()) {
743         sceneLoaders_.push_back(loader);
744     }
745 }
746 
UnregisterSceneLoader(const ISceneLoader::Ptr & loader)747 void SceneUtil::UnregisterSceneLoader(const ISceneLoader::Ptr& loader)
748 {
749     if (auto pos = std::find_if(sceneLoaders_.cbegin(), sceneLoaders_.cend(),
750             [toBeRemoved = loader.get()](
751                 const ISceneLoader::Ptr& registered) { return registered.get() == toBeRemoved; });
752         pos != sceneLoaders_.cend()) {
753         sceneLoaders_.erase(pos);
754     }
755 }
756 
GetSceneLoader(BASE_NS::string_view uri) const757 ISceneLoader::Ptr SceneUtil::GetSceneLoader(BASE_NS::string_view uri) const
758 {
759     for (auto& sceneLoader : sceneLoaders_) {
760         for (const auto& extension : sceneLoader->GetSupportedExtensions()) {
761             if (uri.ends_with(extension)) {
762                 return sceneLoader;
763             }
764         }
765     }
766     return {};
767 }
768 
769 namespace {
CreateTargetGpuImageEnv(IGpuResourceManager & gpuResourceMgr,IEcs & ecs,const Math::UVec2 & size)770 EntityReference CreateTargetGpuImageEnv(IGpuResourceManager& gpuResourceMgr, IEcs& ecs, const Math::UVec2& size)
771 {
772     auto* renderHandleManager = GetManager<IRenderHandleComponentManager>(ecs);
773     if (!renderHandleManager) {
774         return {};
775     }
776     GpuImageDesc envDesc { CORE_IMAGE_TYPE_2D, CORE_IMAGE_VIEW_TYPE_CUBE, BASE_FORMAT_B10G11R11_UFLOAT_PACK32,
777         CORE_IMAGE_TILING_OPTIMAL, CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | CORE_IMAGE_USAGE_SAMPLED_BIT,
778         CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, CORE_IMAGE_CREATE_CUBE_COMPATIBLE_BIT,
779         CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, size.x, size.y, 1U, 1U, 6U, CORE_SAMPLE_COUNT_1_BIT,
780         ComponentMapping {} };
781     auto handle = gpuResourceMgr.Create(envDesc);
782     return GetOrCreateEntityReference(ecs.GetEntityManager(), *renderHandleManager, handle);
783 }
784 
CreateTargetGpuImageDepth(IGpuResourceManager & gpuResourceMgr,IEcs & ecs,const Math::UVec2 & size)785 EntityReference CreateTargetGpuImageDepth(IGpuResourceManager& gpuResourceMgr, IEcs& ecs, const Math::UVec2& size)
786 {
787     auto* renderHandleManager = GetManager<IRenderHandleComponentManager>(ecs);
788     if (!renderHandleManager) {
789         return {};
790     }
791     GpuImageDesc depthDesc { CORE_IMAGE_TYPE_2D, CORE_IMAGE_VIEW_TYPE_CUBE, BASE_FORMAT_D16_UNORM,
792         CORE_IMAGE_TILING_OPTIMAL,
793         CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
794         CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT,
795         CORE_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, size.x, size.y, 1U, 1U, 6U,
796         CORE_SAMPLE_COUNT_1_BIT, ComponentMapping {} };
797     auto handle = gpuResourceMgr.Create(depthDesc);
798     return GetOrCreateEntityReference(ecs.GetEntityManager(), *renderHandleManager, handle);
799 }
800 
CreateReflectionProbeCamera(CORE_NS::IEcs & ecs,IGraphicsContext & graphicsContext,const BASE_NS::Math::Vec3 & position)801 CORE_NS::EntityReference CreateReflectionProbeCamera(
802     CORE_NS::IEcs& ecs, IGraphicsContext& graphicsContext, const BASE_NS::Math::Vec3& position)
803 {
804     auto ccm = GetManager<ICameraComponentManager>(ecs);
805     auto ppcm = GetManager<IPostProcessComponentManager>(ecs);
806     auto tcm = GetManager<ITransformComponentManager>(ecs);
807     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
808     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
809     auto ncm = GetManager<INodeComponentManager>(ecs);
810     if (!ccm || !ppcm || !tcm || !wmm || !lmm || !ncm) {
811         return {};
812     }
813     EntityReference camera = ecs.GetEntityManager().CreateReferenceCounted();
814 
815     CameraComponent cc;
816     auto& gpuResourceMgr = graphicsContext.GetRenderContext().GetDevice().GetGpuResourceManager();
817 
818     auto envEntityRef =
819         CreateTargetGpuImageEnv(gpuResourceMgr, ecs, { REFLECTION_PROBE_DEFAULT_SIZE, REFLECTION_PROBE_DEFAULT_SIZE });
820     auto depthEntityRef = CreateTargetGpuImageDepth(
821         gpuResourceMgr, ecs, { REFLECTION_PROBE_DEFAULT_SIZE, REFLECTION_PROBE_DEFAULT_SIZE });
822     cc.customColorTargets.push_back(envEntityRef);
823     cc.customDepthTarget = depthEntityRef;
824     cc.pipelineFlags = CameraComponent::PipelineFlagBits::CUBEMAP_BIT |
825                        CameraComponent::PipelineFlagBits::CLEAR_COLOR_BIT |
826                        CameraComponent::PipelineFlagBits::CLEAR_DEPTH_BIT;
827     cc.renderingPipeline = CameraComponent::RenderingPipeline::LIGHT_FORWARD;
828     cc.sceneFlags |= CameraComponent::SceneFlagBits::ACTIVE_RENDER_BIT;
829     cc.projection = CameraComponent::Projection::PERSPECTIVE;
830     cc.postProcess = camera;
831     ccm->Set(camera, cc);
832 
833     PostProcessComponent ppc;
834     ppc.enableFlags = 0;
835     ppcm->Set(camera, ppc);
836 
837     TransformComponent tc;
838     tc.position = position;
839     tc.rotation = Math::Quat { 0.f, 0.f, 0.f, 1.f };
840     tcm->Set(camera, tc);
841 
842     lmm->Create(camera);
843     wmm->Create(camera);
844     ncm->Create(camera);
845 
846     return camera;
847 }
848 } // namespace
849 
CreateReflectionProbe(CORE_NS::IEcs & ecs,const BASE_NS::Math::Vec3 & position) const850 CORE_NS::EntityReference SceneUtil::CreateReflectionProbe(CORE_NS::IEcs& ecs, const BASE_NS::Math::Vec3& position) const
851 {
852     auto pcm = GetManager<IReflectionProbeComponentManager>(ecs);
853     if (!pcm) {
854         return {};
855     }
856 
857     auto camera = CreateReflectionProbeCamera(ecs, graphicsContext_, position);
858     if (!camera) {
859         return {};
860     }
861 
862     // cube cam should always have 90 degrees of fov
863     const float viewportAngle { 90.0f };
864     // orthographic multiplier, should not matter with perspective view but for clarity, setting with no scale.
865     const float distortionMultiplier { 0.5f };
866     UpdateCameraViewport(ecs, camera, { REFLECTION_PROBE_DEFAULT_SIZE, REFLECTION_PROBE_DEFAULT_SIZE }, true,
867         Math::DEG2RAD * viewportAngle, viewportAngle * distortionMultiplier);
868 
869     EntityReference probe = ecs.GetEntityManager().CreateReferenceCounted();
870 
871     ReflectionProbeComponent pc;
872     pc.probeCamera = camera;
873     pcm->Set(probe, pc);
874 
875     return probe;
876 }
877 
878 namespace {
879 constexpr uint64_t TYPES[] = { CORE_NS::PropertyType::BOOL_T, CORE_NS::PropertyType::CHAR_T,
880     CORE_NS::PropertyType::INT8_T, CORE_NS::PropertyType::INT16_T, CORE_NS::PropertyType::INT32_T,
881     CORE_NS::PropertyType::INT64_T, CORE_NS::PropertyType::UINT8_T, CORE_NS::PropertyType::UINT16_T,
882     CORE_NS::PropertyType::UINT32_T, CORE_NS::PropertyType::UINT64_T,
883 #ifdef __APPLE__
884     CORE_NS::PropertyType::SIZE_T,
885 #endif
886     CORE_NS::PropertyType::FLOAT_T, CORE_NS::PropertyType::DOUBLE_T, CORE_NS::PropertyType::BOOL_ARRAY_T,
887     CORE_NS::PropertyType::CHAR_ARRAY_T, CORE_NS::PropertyType::INT8_ARRAY_T, CORE_NS::PropertyType::INT16_ARRAY_T,
888     CORE_NS::PropertyType::INT32_ARRAY_T, CORE_NS::PropertyType::INT64_ARRAY_T, CORE_NS::PropertyType::UINT8_ARRAY_T,
889     CORE_NS::PropertyType::UINT16_ARRAY_T, CORE_NS::PropertyType::UINT32_ARRAY_T, CORE_NS::PropertyType::UINT64_ARRAY_T,
890 #ifdef __APPLE__
891     CORE_NS::PropertyType::SIZE_ARRAY_T,
892 #endif
893     CORE_NS::PropertyType::FLOAT_ARRAY_T, CORE_NS::PropertyType::DOUBLE_ARRAY_T, CORE_NS::PropertyType::IVEC2_T,
894     CORE_NS::PropertyType::IVEC3_T, CORE_NS::PropertyType::IVEC4_T, CORE_NS::PropertyType::VEC2_T,
895     CORE_NS::PropertyType::VEC3_T, CORE_NS::PropertyType::VEC4_T, CORE_NS::PropertyType::UVEC2_T,
896     CORE_NS::PropertyType::UVEC3_T, CORE_NS::PropertyType::UVEC4_T, CORE_NS::PropertyType::QUAT_T,
897     CORE_NS::PropertyType::MAT3X3_T, CORE_NS::PropertyType::MAT4X4_T, CORE_NS::PropertyType::UID_T,
898     CORE_NS::PropertyType::STRING_T, CORE_NS::PropertyType::IVEC2_ARRAY_T, CORE_NS::PropertyType::IVEC3_ARRAY_T,
899     CORE_NS::PropertyType::IVEC4_ARRAY_T, CORE_NS::PropertyType::VEC2_ARRAY_T, CORE_NS::PropertyType::VEC3_ARRAY_T,
900     CORE_NS::PropertyType::VEC4_ARRAY_T, CORE_NS::PropertyType::UVEC2_ARRAY_T, CORE_NS::PropertyType::UVEC3_ARRAY_T,
901     CORE_NS::PropertyType::UVEC4_ARRAY_T, CORE_NS::PropertyType::QUAT_ARRAY_T, CORE_NS::PropertyType::MAT3X3_ARRAY_T,
902     CORE_NS::PropertyType::MAT4X4_ARRAY_T, CORE_NS::PropertyType::UID_ARRAY_T, CORE_NS::PropertyType::FLOAT_VECTOR_T,
903     CORE_NS::PropertyType::MAT4X4_VECTOR_T };
904 
905 template<typename EntityHandler, typename EntityReferenceHandler>
FindEntities(const CORE_NS::Property & property,uintptr_t offset,EntityHandler && entityFunc,EntityReferenceHandler && entityRefFunc)906 void FindEntities(const CORE_NS::Property& property, uintptr_t offset, EntityHandler&& entityFunc,
907     EntityReferenceHandler&& entityRefFunc)
908 {
909     if (property.type == CORE_NS::PropertyType::ENTITY_T) {
910         auto ptr = reinterpret_cast<CORE_NS::Entity*>(offset);
911         if (ptr && EntityUtil::IsValid(*ptr)) {
912             entityFunc(ptr);
913         }
914     } else if (property.type == CORE_NS::PropertyType::ENTITY_REFERENCE_T) {
915         auto ptr = reinterpret_cast<CORE_NS::EntityReference*>(offset);
916         if (ptr && EntityUtil::IsValid(*ptr)) {
917             entityRefFunc(ptr);
918         }
919     } else if (std::any_of(std::begin(TYPES), std::end(TYPES),
920                    [&current = property.type](const uint64_t type) { return type == current; })) {
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 
IsSphereInsideCameraFrustum(const IEcs & ecs,Entity cameraEntity,const Math::Vec3 center,const float radius) const1152 bool SceneUtil::IsSphereInsideCameraFrustum(
1153     const IEcs& ecs, Entity cameraEntity, const Math::Vec3 center, const float radius) const
1154 {
1155     if (!EntityUtil::IsValid(cameraEntity)) {
1156         return false;
1157     }
1158     auto* frustumUtil = GetInstance<IFrustumUtil>(UID_FRUSTUM_UTIL);
1159     auto* cameraMgr = GetManager<ICameraComponentManager>(ecs);
1160     auto* worldMatrixMgr = GetManager<IWorldMatrixComponentManager>(ecs);
1161     if (!frustumUtil || !cameraMgr || !worldMatrixMgr) {
1162         return false;
1163     }
1164     auto cameraHandle = cameraMgr->Read(cameraEntity);
1165     auto worldMatrixHandle = worldMatrixMgr->Read(cameraEntity);
1166     if (!cameraHandle || !worldMatrixHandle) {
1167         return false;
1168     }
1169     float determinant = 0.0f;
1170     const Math::Mat4X4 view = Math::Inverse(worldMatrixHandle->matrix, determinant);
1171     bool isCameraNegative = determinant < 0.0f;
1172     const Math::Mat4X4 proj = CameraMatrixUtil::CalculateProjectionMatrix(*cameraHandle, isCameraNegative);
1173     const Frustum frustum = frustumUtil->CreateFrustum(proj * view);
1174     return frustumUtil->SphereFrustumCollision(frustum, center, radius);
1175 }
1176 CORE3D_END_NAMESPACE()
1177