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 [¤t = 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