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