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 <cinttypes>
19 
20 #include <3d/ecs/components/animation_component.h>
21 #include <3d/ecs/components/animation_output_component.h>
22 #include <3d/ecs/components/animation_track_component.h>
23 #include <3d/ecs/components/camera_component.h>
24 #include <3d/ecs/components/light_component.h>
25 #include <3d/ecs/components/local_matrix_component.h>
26 #include <3d/ecs/components/material_component.h>
27 #include <3d/ecs/components/mesh_component.h>
28 #include <3d/ecs/components/name_component.h>
29 #include <3d/ecs/components/node_component.h>
30 #include <3d/ecs/components/planar_reflection_component.h>
31 #include <3d/ecs/components/render_handle_component.h>
32 #include <3d/ecs/components/render_mesh_component.h>
33 #include <3d/ecs/components/skin_joints_component.h>
34 #include <3d/ecs/components/transform_component.h>
35 #include <3d/ecs/components/uri_component.h>
36 #include <3d/ecs/components/world_matrix_component.h>
37 #include <3d/ecs/systems/intf_animation_system.h>
38 #include <3d/intf_graphics_context.h>
39 #include <3d/render/default_material_constants.h>
40 #include <3d/util/intf_mesh_util.h>
41 #include <base/containers/fixed_string.h>
42 #include <base/math/matrix_util.h>
43 #include <core/ecs/intf_ecs.h>
44 #include <core/ecs/intf_entity_manager.h>
45 #include <core/intf_engine.h>
46 #include <core/log.h>
47 #include <core/namespace.h>
48 #include <core/property/intf_property_handle.h>
49 #include <render/device/intf_gpu_resource_manager.h>
50 #include <render/device/intf_shader_manager.h>
51 #include <render/intf_render_context.h>
52 
53 #include "uri_lookup.h"
54 #include "util/component_util_functions.h"
55 #include "util/string_util.h"
56 
57 CORE3D_BEGIN_NAMESPACE()
58 using namespace BASE_NS;
59 using namespace CORE_NS;
60 using namespace RENDER_NS;
61 
62 namespace CameraMatrixUtil {
CalculateProjectionMatrix(const CameraComponent & cameraComponent,bool & isCameraNegative)63 Math::Mat4X4 CalculateProjectionMatrix(const CameraComponent& cameraComponent, bool& isCameraNegative)
64 {
65     switch (cameraComponent.projection) {
66         case CameraComponent::Projection::ORTHOGRAPHIC: {
67             auto orthoProj = Math::OrthoRhZo(cameraComponent.xMag * -0.5f, cameraComponent.xMag * 0.5f,
68                 cameraComponent.yMag * -0.5f, cameraComponent.yMag * 0.5f, cameraComponent.zNear, cameraComponent.zFar);
69             orthoProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
70             return orthoProj;
71         }
72         case CameraComponent::Projection::PERSPECTIVE: {
73             auto aspect = 1.f;
74             if (cameraComponent.aspect > 0.f) {
75                 aspect = cameraComponent.aspect;
76             } else if (cameraComponent.renderResolution.y > 0U) {
77                 aspect = static_cast<float>(cameraComponent.renderResolution.x) /
78                          static_cast<float>(cameraComponent.renderResolution.y);
79             }
80             auto persProj =
81                 Math::PerspectiveRhZo(cameraComponent.yFov, aspect, cameraComponent.zNear, cameraComponent.zFar);
82             persProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
83             return persProj;
84         }
85         case CameraComponent::Projection::FRUSTUM: {
86             auto aspect = 1.f;
87             if (cameraComponent.aspect > 0.f) {
88                 aspect = cameraComponent.aspect;
89             } else if (cameraComponent.renderResolution.y > 0U) {
90                 aspect = static_cast<float>(cameraComponent.renderResolution.x) /
91                          static_cast<float>(cameraComponent.renderResolution.y);
92             }
93             // with offset 0.5, the camera should offset half the screen
94             const float scale = tan(cameraComponent.yFov * 0.5f) * cameraComponent.zNear;
95             const float xOffset = cameraComponent.xOffset * scale * aspect * 2.0f;
96             const float yOffset = cameraComponent.yOffset * scale * 2.0f;
97             float left = -aspect * scale;
98             float right = aspect * scale;
99             float bottom = -scale;
100             float top = scale;
101             auto persProj = Math::PerspectiveRhZo(left + xOffset, right + xOffset, bottom + yOffset, top + yOffset,
102                 cameraComponent.zNear, cameraComponent.zFar);
103             persProj[1][1] *= -1.f; // left-hand NDC while Vulkan right-handed -> flip y
104             return persProj;
105         }
106         case CameraComponent::Projection::CUSTOM: {
107             isCameraNegative = Math::Determinant(cameraComponent.customProjectionMatrix) < 0.0f;
108             return cameraComponent.customProjectionMatrix;
109         }
110         default:
111             return Math::IDENTITY_4X4;
112     }
113 }
114 } // namespace CameraMatrixUtil
115 
SceneUtil(IGraphicsContext & graphicsContext)116 SceneUtil::SceneUtil(IGraphicsContext& graphicsContext) : graphicsContext_(graphicsContext) {}
117 
CreateCamera(IEcs & ecs,const Math::Vec3 & position,const Math::Quat & rotation,float zNear,float zFar,float fovDegrees) const118 Entity SceneUtil::CreateCamera(
119     IEcs& ecs, const Math::Vec3& position, const Math::Quat& rotation, float zNear, float zFar, float fovDegrees) const
120 {
121     IEntityManager& em = ecs.GetEntityManager();
122     const Entity camera = em.Create();
123 
124     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
125     lmm->Create(camera);
126 
127     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
128     wmm->Create(camera);
129 
130     auto ncm = GetManager<INodeComponentManager>(ecs);
131     ncm->Create(camera);
132 
133     auto tcm = GetManager<ITransformComponentManager>(ecs);
134     TransformComponent tc;
135     tc.position = position;
136     tc.rotation = rotation;
137     tcm->Set(camera, tc);
138 
139     auto ccm = GetManager<ICameraComponentManager>(ecs);
140     CameraComponent cc;
141     cc.sceneFlags |= CameraComponent::SceneFlagBits::ACTIVE_RENDER_BIT;
142     cc.projection = CameraComponent::Projection::PERSPECTIVE;
143     cc.yFov = Math::DEG2RAD * fovDegrees;
144     cc.zNear = zNear;
145     cc.zFar = zFar;
146     ccm->Set(camera, cc);
147 
148     return camera;
149 }
150 
UpdateCameraViewport(IEcs & ecs,Entity entity,const Math::UVec2 & renderResolution) const151 void SceneUtil::UpdateCameraViewport(IEcs& ecs, Entity entity, const Math::UVec2& renderResolution) const
152 {
153     auto ccm = GetManager<ICameraComponentManager>(ecs);
154     if (ccm && CORE_NS::EntityUtil::IsValid(entity)) {
155         ScopedHandle<CameraComponent> cameraHandle = ccm->Write(entity);
156         if (!cameraHandle) {
157             ccm->Create(entity);
158             cameraHandle = ccm->Write(entity);
159             if (!cameraHandle) {
160                 return;
161             }
162         }
163         CameraComponent& cameraComponent = *cameraHandle;
164         cameraComponent.aspect = (renderResolution.y > 0U)
165                                      ? (static_cast<float>(renderResolution.x) / static_cast<float>(renderResolution.y))
166                                      : 1.0f;
167         cameraComponent.renderResolution[0] = renderResolution.x;
168         cameraComponent.renderResolution[1] = renderResolution.y;
169     }
170 }
171 
UpdateCameraViewport(IEcs & ecs,Entity entity,const Math::UVec2 & renderResolution,bool autoAspect,float fovY,float orthoScale) const172 void SceneUtil::UpdateCameraViewport(
173     IEcs& ecs, Entity entity, const Math::UVec2& renderResolution, bool autoAspect, float fovY, float orthoScale) const
174 {
175     auto ccm = GetManager<ICameraComponentManager>(ecs);
176     if (ccm && CORE_NS::EntityUtil::IsValid(entity)) {
177         ScopedHandle<CameraComponent> cameraHandle = ccm->Write(entity);
178         if (!cameraHandle) {
179             ccm->Create(entity);
180             cameraHandle = ccm->Write(entity);
181             if (!cameraHandle) {
182                 return;
183             }
184         }
185         CameraComponent& cameraComponent = *cameraHandle;
186         if (autoAspect) {
187             const float aspectRatio =
188                 (renderResolution.y > 0)
189                     ? (static_cast<float>(renderResolution.x) / static_cast<float>(renderResolution.y))
190                     : 1.0f;
191 
192             // Using the fov value as xfov on portrait screens to keep the object
193             // better in the frame.
194             const float yFov = (aspectRatio > 1.0f) ? fovY : (2.0f * Math::atan(Math::tan(fovY * 0.5f) / aspectRatio));
195 
196             // Update the camera parameters.
197             cameraComponent.aspect = aspectRatio;
198             cameraComponent.yFov = yFov;
199 
200             // The camera can also be in ortho mode. Using a separate zoom value.
201             cameraComponent.yMag = orthoScale;
202             cameraComponent.xMag = cameraComponent.yMag * aspectRatio;
203         }
204 
205         cameraComponent.renderResolution[0] = renderResolution.x;
206         cameraComponent.renderResolution[1] = renderResolution.y;
207     }
208 }
209 
CameraLookAt(IEcs & ecs,Entity entity,const Math::Vec3 & eye,const Math::Vec3 & target,const Math::Vec3 & up)210 void SceneUtil::CameraLookAt(
211     IEcs& ecs, Entity entity, const Math::Vec3& eye, const Math::Vec3& target, const Math::Vec3& up)
212 {
213     auto parentWorld = Math::Mat4X4(1.f);
214 
215     auto getParent = [ncm = GetManager<INodeComponentManager>(ecs)](Entity entity) {
216         if (auto nodeHandle = ncm->Read(entity)) {
217             return nodeHandle->parent;
218         }
219         return Entity {};
220     };
221 
222     // walk up the hierachy to get an up-to-date world matrix.
223     auto tcm = GetManager<ITransformComponentManager>(ecs);
224     for (Entity node = getParent(entity); EntityUtil::IsValid(node); node = getParent(node)) {
225         if (auto parentTransformHandle = tcm->Read(node)) {
226             parentWorld = Math::Trs(parentTransformHandle->position, parentTransformHandle->rotation,
227                               parentTransformHandle->scale) *
228                           parentWorld;
229         }
230     }
231 
232     // entity's local transform should invert any parent transformations and apply the transform of rotating towards
233     // target and moving to eye. this transform is the inverse of the look-at matrix.
234     auto worldMatrix = Math::Inverse(parentWorld) * Math::Inverse(Math::LookAtRh(eye, target, up));
235 
236     Math::Vec3 scale;
237     Math::Quat orientation;
238     Math::Vec3 translation;
239     Math::Vec3 skew;
240     Math::Vec4 perspective;
241     if (Math::Decompose(worldMatrix, scale, orientation, translation, skew, perspective)) {
242         if (auto transformHandle = tcm->Write(entity)) {
243             transformHandle->position = translation;
244             transformHandle->rotation = orientation;
245             transformHandle->scale = scale;
246         }
247     }
248 }
249 
CreateLight(IEcs & ecs,const LightComponent & lightComponent,const Math::Vec3 & position,const Math::Quat & rotation) const250 Entity SceneUtil::CreateLight(
251     IEcs& ecs, const LightComponent& lightComponent, const Math::Vec3& position, const Math::Quat& rotation) const
252 {
253     IEntityManager& em = ecs.GetEntityManager();
254     const Entity light = em.Create();
255 
256     auto lmm = GetManager<ILocalMatrixComponentManager>(ecs);
257     lmm->Create(light);
258 
259     auto wmm = GetManager<IWorldMatrixComponentManager>(ecs);
260     wmm->Create(light);
261 
262     auto ncm = GetManager<INodeComponentManager>(ecs);
263     ncm->Create(light);
264 
265     auto nameM = GetManager<INameComponentManager>(ecs);
266     nameM->Create(light);
267     constexpr string_view lightName("Light");
268     nameM->Write(light)->name = lightName;
269 
270     auto tcm = GetManager<ITransformComponentManager>(ecs);
271     TransformComponent tc;
272     tc.position = position;
273     tc.rotation = rotation;
274     tcm->Set(light, tc);
275 
276     auto lcm = GetManager<ILightComponentManager>(ecs);
277     LightComponent lc = lightComponent;
278     lc.shadowEnabled = (lc.type == LightComponent::Type::POINT) ? false : lc.shadowEnabled;
279     lc.range = ComponentUtilFunctions::CalculateSafeLightRange(lc.range, lc.intensity);
280     lcm->Set(light, lc);
281 
282     return light;
283 }
284 
285 // reflection plane helpers
286 namespace {
287 // default size, updated in render system based on main scene camera
288 constexpr Math::UVec2 DEFAULT_PLANE_TARGET_SIZE { 2u, 2u };
289 
CreateReflectionPlaneGpuImage(IGpuResourceManager & gpuResourceMgr,IRenderHandleComponentManager & handleManager,INameComponentManager & nameManager,const string_view name,const Format format,const ImageUsageFlags usageFlags,const MemoryPropertyFlags memoryPropertyFlags)290 EntityReference CreateReflectionPlaneGpuImage(IGpuResourceManager& gpuResourceMgr,
291     IRenderHandleComponentManager& handleManager, INameComponentManager& nameManager, const string_view name,
292     const Format format, const ImageUsageFlags usageFlags, const MemoryPropertyFlags memoryPropertyFlags)
293 {
294     const auto entity = handleManager.GetEcs().GetEntityManager().CreateReferenceCounted();
295     handleManager.Create(entity);
296 
297     GpuImageDesc desc;
298     desc.width = DEFAULT_PLANE_TARGET_SIZE.x;
299     desc.height = DEFAULT_PLANE_TARGET_SIZE.y;
300     desc.depth = 1U;
301     desc.mipCount = DefaultMaterialCameraConstants::REFLECTION_PLANE_MIP_COUNT;
302     desc.format = format;
303     desc.memoryPropertyFlags = memoryPropertyFlags;
304     desc.usageFlags = usageFlags;
305     desc.imageType = ImageType::CORE_IMAGE_TYPE_2D;
306     desc.imageTiling = ImageTiling::CORE_IMAGE_TILING_OPTIMAL;
307     desc.imageViewType = ImageViewType::CORE_IMAGE_VIEW_TYPE_2D;
308     desc.engineCreationFlags = EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS;
309     handleManager.Write(entity)->reference = gpuResourceMgr.Create(name, desc);
310 
311     nameManager.Create(entity);
312     nameManager.Write(entity)->name = name;
313     return entity;
314 }
315 
CreateReflectionPlaneObjectFromEntity(IEcs & ecs,IGraphicsContext & graphicsContext,const Entity & nodeEntity)316 SceneUtil::ReflectionPlane CreateReflectionPlaneObjectFromEntity(
317     IEcs& ecs, IGraphicsContext& graphicsContext, const Entity& nodeEntity)
318 {
319     SceneUtil::ReflectionPlane plane;
320     IRenderMeshComponentManager* renderMeshCM = GetManager<IRenderMeshComponentManager>(ecs);
321     IMeshComponentManager* meshCM = GetManager<IMeshComponentManager>(ecs);
322     IMaterialComponentManager* matCM = GetManager<IMaterialComponentManager>(ecs);
323     IRenderHandleComponentManager* gpuHandleCM = GetManager<IRenderHandleComponentManager>(ecs);
324 
325     INameComponentManager* nameCM = GetManager<INameComponentManager>(ecs);
326     if (!(renderMeshCM && meshCM && matCM && gpuHandleCM && nameCM)) {
327         return plane;
328     }
329 
330     auto& device = graphicsContext.GetRenderContext().GetDevice();
331     auto& gpuResourceMgr = device.GetGpuResourceManager();
332     plane.entity = nodeEntity;
333     plane.colorTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM,
334         DefaultMaterialCameraConstants::CAMERA_COLOR_PREFIX_NAME + to_hex(nodeEntity.id),
335         Format::BASE_FORMAT_B10G11R11_UFLOAT_PACK32,
336         ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT,
337         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
338 
339     // NOTE: uses transient attachement usage flag and cannot be read.
340     plane.depthTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM,
341         DefaultMaterialCameraConstants::CAMERA_DEPTH_PREFIX_NAME + to_hex(nodeEntity.id), Format::BASE_FORMAT_D16_UNORM,
342         ImageUsageFlagBits::CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
343             ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
344         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
345             MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT);
346 
347     if (const auto rmcHandle = renderMeshCM->Read(nodeEntity); rmcHandle) {
348         const RenderMeshComponent& rmc = *rmcHandle;
349         if (const auto meshHandle = meshCM->Read(rmc.mesh); meshHandle) {
350             const MeshComponent& meshComponent = *meshHandle;
351             if (!meshComponent.submeshes.empty()) {
352                 if (auto matHandle = matCM->Write(meshComponent.submeshes[0].material); matHandle) {
353                     MaterialComponent& matComponent = *matHandle;
354                     auto* uriCM = GetManager<IUriComponentManager>(ecs);
355                     auto* renderHandleCM = GetManager<IRenderHandleComponentManager>(ecs);
356                     constexpr const string_view uri = "3dshaders://shader/core3d_dm_fw_reflection_plane.shader";
357                     auto shaderEntity = LookupResourceByUri(uri, *uriCM, *renderHandleCM);
358                     if (!EntityUtil::IsValid(shaderEntity)) {
359                         shaderEntity = ecs.GetEntityManager().Create();
360                         renderHandleCM->Create(shaderEntity);
361                         renderHandleCM->Write(shaderEntity)->reference = device.GetShaderManager().GetShaderHandle(uri);
362                         uriCM->Create(shaderEntity);
363                         uriCM->Write(shaderEntity)->uri = uri;
364                     }
365                     matComponent.materialShader.shader = ecs.GetEntityManager().GetReferenceCounted(shaderEntity);
366 
367                     matComponent.textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS].image =
368                         plane.colorTarget;
369                     matComponent.extraRenderingFlags = MaterialComponent::ExtraRenderingFlagBits::DISCARD_BIT;
370                 }
371             }
372         }
373     }
374 
375     return plane;
376 }
377 } // namespace
378 
CreateReflectionPlaneComponent(IEcs & ecs,const Entity & nodeEntity)379 void SceneUtil::CreateReflectionPlaneComponent(IEcs& ecs, const Entity& nodeEntity)
380 {
381     SceneUtil::ReflectionPlane plane = CreateReflectionPlaneObjectFromEntity(ecs, graphicsContext_, nodeEntity);
382     if (EntityUtil::IsValid(plane.entity)) {
383         auto prcm = GetManager<IPlanarReflectionComponentManager>(ecs);
384         PlanarReflectionComponent prc;
385         prc.colorRenderTarget = plane.colorTarget;
386         prc.depthRenderTarget = plane.depthTarget;
387         prc.renderTargetResolution[0] = DEFAULT_PLANE_TARGET_SIZE.x;
388         prc.renderTargetResolution[1] = DEFAULT_PLANE_TARGET_SIZE.y;
389         prcm->Set(plane.entity, prc);
390     }
391 }
392 
CalculateScalingFactor(IEcs & ecs,Entity targetEntity,Entity sourceEntity)393 float CalculateScalingFactor(IEcs& ecs, Entity targetEntity, Entity sourceEntity)
394 {
395     float scale = 1.f;
396     auto renderMeshManager = GetManager<IRenderMeshComponentManager>(ecs);
397     auto meshManager = GetManager<IMeshComponentManager>(ecs);
398     auto transformManager = GetManager<ITransformComponentManager>(ecs);
399     if (!renderMeshManager || !meshManager || !transformManager) {
400         return scale;
401     }
402 
403     Entity dstMeshEntity;
404     Entity srcMeshEntity;
405     if (auto renderMeshComponent = renderMeshManager->Read(targetEntity); renderMeshComponent) {
406         dstMeshEntity = renderMeshComponent->mesh;
407     }
408     if (auto renderMeshComponent = renderMeshManager->Read(sourceEntity); renderMeshComponent) {
409         srcMeshEntity = renderMeshComponent->mesh;
410     }
411     if (!EntityUtil::IsValid(dstMeshEntity) || !EntityUtil::IsValid(srcMeshEntity)) {
412         return scale;
413     }
414     auto getMeshHeight = [](IMeshComponentManager& meshManager, Entity entity) {
415         if (auto meshComponent = meshManager.Read(entity); meshComponent) {
416             return (meshComponent->aabbMax.y - meshComponent->aabbMin.y);
417         }
418         return 0.f;
419     };
420 
421     const float dstHeight = getMeshHeight(*meshManager, dstMeshEntity);
422     const float srcHeight = getMeshHeight(*meshManager, srcMeshEntity);
423     if ((dstHeight == 0.f) || (srcHeight == 0.f)) {
424         return scale;
425     }
426 
427     auto dstMeshScale = transformManager->Get(targetEntity).scale;
428     auto srcMeshScale = transformManager->Get(sourceEntity).scale;
429     const auto dstSize = dstHeight * dstMeshScale.y;
430     const auto srcSize = srcHeight * srcMeshScale.y;
431     if (srcSize == 0.f) {
432         return scale;
433     }
434     scale = dstSize / srcSize;
435     return scale;
436 }
437 
CreateJointMapping(IEcs & ecs,array_view<const Entity> dstJointEntities,array_view<const Entity> srcJointEntities)438 vector<Entity> CreateJointMapping(
439     IEcs& ecs, array_view<const Entity> dstJointEntities, array_view<const Entity> srcJointEntities)
440 {
441     vector<Entity> srcToDstJointMapping;
442 
443     auto getName = [nameManager = GetManager<INameComponentManager>(ecs)](const Entity& jointEntity) -> string_view {
444         if (auto nameComponent = nameManager->Read(jointEntity); nameComponent) {
445             return nameComponent->name;
446         }
447         return {};
448     };
449 
450     vector<string_view> dstJointNames;
451     dstJointNames.reserve(dstJointEntities.size());
452     std::transform(dstJointEntities.begin(), dstJointEntities.end(), std::back_inserter(dstJointNames), getName);
453 
454     vector<string_view> srcJointNames;
455     srcJointNames.reserve(srcJointEntities.size());
456     std::transform(srcJointEntities.begin(), srcJointEntities.end(), std::back_inserter(srcJointNames), getName);
457 
458     srcToDstJointMapping.reserve(srcJointEntities.size());
459 
460     const auto dstJointNamesBegin = dstJointNames.cbegin();
461     const auto dstJointNamesEnd = dstJointNames.cend();
462     for (const auto& srcJointName : srcJointNames) {
463         const auto pos = std::find(dstJointNamesBegin, dstJointNamesEnd, srcJointName);
464         srcToDstJointMapping.push_back(
465             (pos != dstJointNamesEnd) ? dstJointEntities[static_cast<size_t>(std::distance(dstJointNamesBegin, pos))]
466                                       : Entity {});
467         if (pos == dstJointNamesEnd) {
468             CORE_LOG_W("Target skin missing joint %s", srcJointName.data());
469         }
470     }
471     return srcToDstJointMapping;
472 }
473 
UpdateTracks(IEcs & ecs,array_view<EntityReference> targetTracks,array_view<const EntityReference> sourceTracks,array_view<const Entity> srcJointEntities,array_view<const Entity> srcToDstJointMapping,float scale)474 vector<Entity> UpdateTracks(IEcs& ecs, array_view<EntityReference> targetTracks,
475     array_view<const EntityReference> sourceTracks, array_view<const Entity> srcJointEntities,
476     array_view<const Entity> srcToDstJointMapping, float scale)
477 {
478     vector<Entity> trackTargets;
479     trackTargets.reserve(sourceTracks.size());
480     auto& entityManager = ecs.GetEntityManager();
481     auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs);
482     auto animationOutputManager = GetManager<IAnimationOutputComponentManager>(ecs);
483     // update tracks to point to target skin's joints.
484     std::transform(sourceTracks.begin(), sourceTracks.end(), targetTracks.begin(),
485         [&entityManager, animationTrackManager, animationOutputManager, &srcJointEntities, &srcToDstJointMapping,
486             &trackTargets, scale](const EntityReference& srcTrackEntity) {
487             const auto srcTrackId = animationTrackManager->GetComponentId(srcTrackEntity);
488             const auto srcTargetEntity = (srcTrackId != IComponentManager::INVALID_COMPONENT_ID)
489                                              ? animationTrackManager->Read(srcTrackId)->target
490                                              : Entity {};
491             if (EntityUtil::IsValid(srcTargetEntity)) {
492                 // check that the src track target is one of the src joints
493                 if (const auto pos = std::find(srcJointEntities.begin(), srcJointEntities.end(), srcTargetEntity);
494                     pos != srcJointEntities.end()) {
495                     auto dstTrackEntity = entityManager.CreateReferenceCounted();
496                     animationTrackManager->Create(dstTrackEntity);
497                     auto dstTrack = animationTrackManager->Write(dstTrackEntity);
498                     auto srcTrack = animationTrackManager->Read(srcTrackId);
499                     *dstTrack = *srcTrack;
500                     const auto jointIndex = static_cast<size_t>(pos - srcJointEntities.begin());
501                     trackTargets.push_back(srcToDstJointMapping[jointIndex]);
502                     dstTrack->target = {};
503 
504                     // joint position track needs to be offset
505                     if ((dstTrack->component == ITransformComponentManager::UID) &&
506                         (dstTrack->property == "position")) {
507                         if (const auto srcOutputId = animationOutputManager->GetComponentId(srcTrack->data);
508                             srcOutputId != IComponentManager::INVALID_COMPONENT_ID) {
509                             // create new animation output with original position corrected by the scale.
510                             dstTrack->data = entityManager.CreateReferenceCounted();
511                             animationOutputManager->Create(dstTrack->data);
512                             const auto dstOutput = animationOutputManager->Write(dstTrack->data);
513                             const auto srcOutput = animationOutputManager->Read(srcOutputId);
514                             dstOutput->type = srcOutput->type;
515                             auto& dst = dstOutput->data;
516                             const auto& src = srcOutput->data;
517                             dst.resize(src.size());
518                             const auto count = dst.size() / sizeof(Math::Vec3);
519                             const auto srcPositions =
520                                 array_view(reinterpret_cast<const Math::Vec3*>(src.data()), count);
521                             auto dstPositions = array_view(reinterpret_cast<Math::Vec3*>(dst.data()), count);
522                             std::transform(srcPositions.begin(), srcPositions.end(), dstPositions.begin(),
523                                 [scale](const Math::Vec3 position) { return position * scale; });
524                         }
525                     }
526                     return dstTrackEntity;
527                 }
528             }
529             CORE_LOG_W("no target for track %" PRIx64, static_cast<Entity>(srcTrackEntity).id);
530             return srcTrackEntity;
531         });
532     return trackTargets;
533 }
534 
RetargetSkinAnimation(IEcs & ecs,Entity targetEntity,Entity sourceEntity,Entity animationEntity) const535 IAnimationPlayback* SceneUtil::RetargetSkinAnimation(
536     IEcs& ecs, Entity targetEntity, Entity sourceEntity, Entity animationEntity) const
537 {
538     auto jointsManager = GetManager<ISkinJointsComponentManager>(ecs);
539     auto dstJointsComponent = jointsManager->Read(targetEntity);
540     auto srcJointsComponent = jointsManager->Read(sourceEntity);
541     if (!dstJointsComponent || !srcJointsComponent) {
542         return nullptr;
543     }
544     if (!GetManager<IAnimationComponentManager>(ecs)->HasComponent(animationEntity)) {
545         return nullptr;
546     }
547 
548     auto dstJointEntities = array_view(dstJointsComponent->jointEntities, dstJointsComponent->count);
549     auto srcJointEntities = array_view(srcJointsComponent->jointEntities, srcJointsComponent->count);
550 
551     const vector<Entity> srcToDstJointMapping = CreateJointMapping(ecs, dstJointEntities, srcJointEntities);
552 
553     // calculate a scaling factor based on mesh heights
554     const auto scale = CalculateScalingFactor(ecs, targetEntity, sourceEntity);
555 
556     // compensate difference in root joint positions.
557     // synchronize initial pose by copying src joint rotations to dst joints.
558     {
559         auto transformManager = GetManager<ITransformComponentManager>(ecs);
560         auto srcIt = srcJointEntities.begin();
561         for (const auto& dstE : srcToDstJointMapping) {
562             auto srcTransform = transformManager->Read(*srcIt);
563             auto dstTransform = transformManager->Write(dstE);
564             if (srcTransform && dstTransform) {
565                 dstTransform->position = srcTransform->position * scale;
566                 dstTransform->rotation = srcTransform->rotation;
567             }
568             ++srcIt;
569         }
570     }
571 
572     auto& entityManager = ecs.GetEntityManager();
573     const auto dstAnimationEntity = entityManager.Create();
574     {
575         INameComponentManager* nameManager = GetManager<INameComponentManager>(ecs);
576         nameManager->Create(dstAnimationEntity);
577         auto srcName = nameManager->Read(animationEntity);
578         nameManager->Write(dstAnimationEntity)->name =
579             (srcName ? srcName->name : string_view("<empty>")) + " retargeted";
580     }
581 
582     vector<Entity> trackTargets;
583     {
584         auto animationManager = GetManager<IAnimationComponentManager>(ecs);
585         animationManager->Create(dstAnimationEntity);
586         auto dstAnimationComponent = animationManager->Write(dstAnimationEntity);
587         auto srcAnimationComponent = animationManager->Read(animationEntity);
588         if (!srcAnimationComponent) {
589             return nullptr;
590         }
591         // copy the src animation.
592         *dstAnimationComponent = *srcAnimationComponent;
593         trackTargets = UpdateTracks(ecs, dstAnimationComponent->tracks, srcAnimationComponent->tracks, srcJointEntities,
594             srcToDstJointMapping, scale);
595     }
596     auto animationSystem = GetSystem<IAnimationSystem>(ecs);
597     return animationSystem->CreatePlayback(dstAnimationEntity, trackTargets);
598 }
599 
GetDefaultMaterialShaderData(IEcs & ecs,const ISceneUtil::MaterialShaderInfo & info,MaterialComponent::Shader & materialShader,MaterialComponent::Shader & depthShader) const600 void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info,
601     MaterialComponent::Shader& materialShader, MaterialComponent::Shader& depthShader) const
602 {
603     IRenderHandleComponentManager* renderHandleMgr = GetManager<IRenderHandleComponentManager>(ecs);
604     if (renderHandleMgr) {
605         IEntityManager& entityMgr = ecs.GetEntityManager();
606         const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager();
607         {
608             const uint32_t renderSlotId =
609                 (info.alphaBlend)
610                     ? shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT)
611                     : shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE);
612 
613             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
614             materialShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
615             RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
616             gs.rasterizationState.cullModeFlags = info.cullModeFlags;
617             gs.rasterizationState.frontFace = info.frontFace;
618             const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
619             const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
620             if (gsHandle) {
621                 materialShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
622             }
623         }
624         if (!info.alphaBlend) {
625             const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH);
626             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
627             depthShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
628             RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
629             gs.rasterizationState.cullModeFlags = info.cullModeFlags;
630             gs.rasterizationState.frontFace = info.frontFace;
631             const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
632             const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
633             if (gsHandle) {
634                 depthShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
635             }
636         }
637     }
638 }
639 
GetDefaultMaterialShaderData(IEcs & ecs,const ISceneUtil::MaterialShaderInfo & info,const string_view renderSlot,MaterialComponent::Shader & shader) const640 void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info,
641     const string_view renderSlot, MaterialComponent::Shader& shader) const
642 {
643     IRenderHandleComponentManager* renderHandleMgr = GetManager<IRenderHandleComponentManager>(ecs);
644     if (renderHandleMgr) {
645         IEntityManager& entityMgr = ecs.GetEntityManager();
646         const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager();
647         const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(renderSlot);
648         if (renderSlotId != ~0u) {
649             const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId);
650             if (rsd.shader) {
651                 shader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader);
652             } else {
653                 CORE_LOG_D("SceneUtil: render slot base shader not found (%s)", renderSlot.data());
654             }
655             if (rsd.graphicsState) {
656                 RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState);
657                 gs.rasterizationState.cullModeFlags = info.cullModeFlags;
658                 gs.rasterizationState.frontFace = info.frontFace;
659                 const uint64_t gsHash = shaderMgr.HashGraphicsState(gs);
660                 const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash);
661                 shader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle);
662             } else {
663                 CORE_LOG_D("SceneUtil: render slot base graphics state not found (%s)", renderSlot.data());
664             }
665         } else {
666             CORE_LOG_W("SceneUtil: render slot id not found (%s)", renderSlot.data());
667         }
668     }
669 }
670 
ShareSkin(IEcs & ecs,Entity targetEntity,Entity sourceEntity) const671 void SceneUtil::ShareSkin(IEcs& ecs, Entity targetEntity, Entity sourceEntity) const
672 {
673     vector<Entity> dstToSrcJointMapping;
674 
675     auto jointsManager = GetManager<ISkinJointsComponentManager>(ecs);
676     {
677         auto dstJointsComponent = jointsManager->Read(targetEntity);
678         auto srcJointsComponent = jointsManager->Read(sourceEntity);
679         if (!dstJointsComponent) {
680             CORE_LOG_E("target doesn't have SkinJointsComponent.");
681             return;
682         }
683         if (!srcJointsComponent) {
684             CORE_LOG_E("source doesn't have SkinJointsComponent.");
685             return;
686         }
687 
688         auto dstJointEntities = array_view(dstJointsComponent->jointEntities, dstJointsComponent->count);
689         auto srcJointEntities = array_view(srcJointsComponent->jointEntities, srcJointsComponent->count);
690 
691         dstToSrcJointMapping = CreateJointMapping(ecs, srcJointEntities, dstJointEntities);
692         if (dstJointsComponent->count != dstToSrcJointMapping.size()) {
693             CORE_LOG_E("couldn't match all joints.");
694         }
695     }
696     auto dstJointsComponent = jointsManager->Write(targetEntity);
697     std::copy(dstToSrcJointMapping.data(), dstToSrcJointMapping.data() + dstToSrcJointMapping.size(),
698         dstJointsComponent->jointEntities);
699 }
700 
RegisterSceneLoader(const ISceneLoader::Ptr & loader)701 void SceneUtil::RegisterSceneLoader(const ISceneLoader::Ptr& loader)
702 {
703     if (auto pos = std::find_if(sceneLoaders_.cbegin(), sceneLoaders_.cend(),
704             [newLoader = loader.get()](const ISceneLoader::Ptr& registered) { return registered.get() == newLoader; });
705         pos == sceneLoaders_.cend()) {
706         sceneLoaders_.push_back(loader);
707     }
708 }
709 
UnregisterSceneLoader(const ISceneLoader::Ptr & loader)710 void SceneUtil::UnregisterSceneLoader(const ISceneLoader::Ptr& loader)
711 {
712     if (auto pos = std::find_if(sceneLoaders_.cbegin(), sceneLoaders_.cend(),
713             [toBeRemoved = loader.get()](
714                 const ISceneLoader::Ptr& registered) { return registered.get() == toBeRemoved; });
715         pos != sceneLoaders_.cend()) {
716         sceneLoaders_.erase(pos);
717     }
718 }
719 
GetSceneLoader(BASE_NS::string_view uri) const720 ISceneLoader::Ptr SceneUtil::GetSceneLoader(BASE_NS::string_view uri) const
721 {
722     for (auto& sceneLoader : sceneLoaders_) {
723         for (const auto& extension : sceneLoader->GetSupportedExtensions()) {
724             if (uri.ends_with(extension)) {
725                 return sceneLoader;
726             }
727         }
728     }
729     return {};
730 }
731 CORE3D_END_NAMESPACE()
732