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