/* * Copyright (C) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "util/scene_util.h" #include <3d/ecs/components/animation_component.h> #include <3d/ecs/components/animation_output_component.h> #include <3d/ecs/components/animation_track_component.h> #include <3d/ecs/components/camera_component.h> #include <3d/ecs/components/light_component.h> #include <3d/ecs/components/local_matrix_component.h> #include <3d/ecs/components/material_component.h> #include <3d/ecs/components/mesh_component.h> #include <3d/ecs/components/name_component.h> #include <3d/ecs/components/node_component.h> #include <3d/ecs/components/planar_reflection_component.h> #include <3d/ecs/components/render_handle_component.h> #include <3d/ecs/components/render_mesh_component.h> #include <3d/ecs/components/skin_joints_component.h> #include <3d/ecs/components/transform_component.h> #include <3d/ecs/components/uri_component.h> #include <3d/ecs/components/world_matrix_component.h> #include <3d/ecs/systems/intf_animation_system.h> #include <3d/intf_graphics_context.h> #include <3d/render/default_material_constants.h> #include <3d/util/intf_mesh_util.h> #include #include #include #include #include #include #include #include #include #include "uri_lookup.h" #include "util/component_util_functions.h" #include "util/string_util.h" CORE3D_BEGIN_NAMESPACE() using namespace BASE_NS; using namespace CORE_NS; using namespace RENDER_NS; SceneUtil::SceneUtil(IGraphicsContext& graphicsContext) : graphicsContext_(graphicsContext) {} Entity SceneUtil::CreateCamera( IEcs& ecs, const Math::Vec3& position, const Math::Quat& rotation, float zNear, float zFar, float fovDegrees) const { IEntityManager& em = ecs.GetEntityManager(); const Entity camera = em.Create(); auto lmm = GetManager(ecs); lmm->Create(camera); auto wmm = GetManager(ecs); wmm->Create(camera); auto ncm = GetManager(ecs); ncm->Create(camera); auto tcm = GetManager(ecs); TransformComponent tc; tc.position = position; tc.rotation = rotation; tcm->Set(camera, tc); auto ccm = GetManager(ecs); CameraComponent cc; cc.sceneFlags |= CameraComponent::SceneFlagBits::ACTIVE_RENDER_BIT; cc.projection = CameraComponent::Projection::PERSPECTIVE; cc.yFov = Math::DEG2RAD * fovDegrees; cc.zNear = zNear; cc.zFar = zFar; ccm->Set(camera, cc); return camera; } void SceneUtil::UpdateCameraViewport(IEcs& ecs, Entity entity, const Math::UVec2& renderResolution) const { auto ccm = GetManager(ecs); if (ccm && CORE_NS::EntityUtil::IsValid(entity)) { CameraComponent cameraComponent = ccm->Get(entity); cameraComponent.aspect = (renderResolution.y > 0) ? (static_cast(renderResolution.x) / renderResolution.y) : 1.0f; cameraComponent.renderResolution[0] = renderResolution.x; cameraComponent.renderResolution[1] = renderResolution.y; ccm->Set(entity, cameraComponent); } } void SceneUtil::UpdateCameraViewport( IEcs& ecs, Entity entity, const Math::UVec2& renderResolution, bool autoAspect, float fovY, float orthoScale) const { auto ccm = GetManager(ecs); if (ccm && CORE_NS::EntityUtil::IsValid(entity)) { CameraComponent cameraComponent = ccm->Get(entity); if (autoAspect) { const float aspectRatio = (renderResolution.y > 0) ? (static_cast(renderResolution.x) / renderResolution.y) : 1.0f; // Using the fov value as xfov on portrait screens to keep the object // better in the frame. const float yFov = (aspectRatio > 1.0f) ? fovY : (2.0f * Math::atan(Math::tan(fovY * 0.5f) / aspectRatio)); // Update the camera parameters. cameraComponent.aspect = aspectRatio; cameraComponent.yFov = yFov; // The camera can also be in ortho mode. Using a separate zoom value. cameraComponent.yMag = orthoScale; cameraComponent.xMag = cameraComponent.yMag * aspectRatio; } cameraComponent.renderResolution[0] = renderResolution.x; cameraComponent.renderResolution[1] = renderResolution.y; ccm->Set(entity, cameraComponent); } } Entity SceneUtil::CreateLight( IEcs& ecs, const LightComponent& lightComponent, const Math::Vec3& position, const Math::Quat& rotation) const { IEntityManager& em = ecs.GetEntityManager(); const Entity light = em.Create(); auto lmm = GetManager(ecs); lmm->Create(light); auto wmm = GetManager(ecs); wmm->Create(light); auto ncm = GetManager(ecs); ncm->Create(light); auto nameM = GetManager(ecs); nameM->Create(light); constexpr string_view lightName("Light"); nameM->Write(light)->name = lightName; auto tcm = GetManager(ecs); TransformComponent tc; tc.position = position; tc.rotation = rotation; tcm->Set(light, tc); auto lcm = GetManager(ecs); LightComponent lc = lightComponent; lc.shadowEnabled = (lc.type == LightComponent::Type::POINT) ? false : lc.shadowEnabled; lc.range = ComponentUtilFunctions::CalculateSafeLightRange(lc.range, lc.intensity); lcm->Set(light, lc); return light; } // reflection plane helpers namespace { // default size, updated in render system based on main scene camera constexpr Math::UVec2 DEFAULT_PLANE_TARGET_SIZE { 2u, 2u }; EntityReference CreateReflectionPlaneGpuImage(IGpuResourceManager& gpuResourceMgr, IRenderHandleComponentManager& handleManager, INameComponentManager& nameManager, const string_view name, const Format format, const ImageUsageFlags usageFlags) { const auto entity = handleManager.GetEcs().GetEntityManager().CreateReferenceCounted(); handleManager.Create(entity); GpuImageDesc desc; desc.width = DEFAULT_PLANE_TARGET_SIZE.x; desc.height = DEFAULT_PLANE_TARGET_SIZE.y; desc.depth = 1; desc.format = format; desc.memoryPropertyFlags = MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; desc.usageFlags = usageFlags; desc.imageType = ImageType::CORE_IMAGE_TYPE_2D; desc.imageTiling = ImageTiling::CORE_IMAGE_TILING_OPTIMAL; desc.imageViewType = ImageViewType::CORE_IMAGE_VIEW_TYPE_2D; desc.engineCreationFlags = EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS; handleManager.Write(entity)->reference = gpuResourceMgr.Create(name, desc); nameManager.Create(entity); nameManager.Write(entity)->name = name; return entity; } SceneUtil::ReflectionPlane CreateReflectionPlaneObjectFromEntity( IEcs& ecs, IGraphicsContext& graphicsContext, const Entity& nodeEntity) { SceneUtil::ReflectionPlane plane; IRenderMeshComponentManager* renderMeshCM = GetManager(ecs); IMeshComponentManager* meshCM = GetManager(ecs); IMaterialComponentManager* matCM = GetManager(ecs); IRenderHandleComponentManager* gpuHandleCM = GetManager(ecs); INameComponentManager* nameCM = GetManager(ecs); if (!(renderMeshCM && meshCM && matCM && gpuHandleCM && nameCM)) { return plane; } auto& device = graphicsContext.GetRenderContext().GetDevice(); auto& gpuResourceMgr = device.GetGpuResourceManager(); plane.entity = nodeEntity; plane.colorTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM, DefaultMaterialCameraConstants::CAMERA_COLOR_PREFIX_NAME + to_hex(nodeEntity.id), Format::BASE_FORMAT_R8G8B8A8_SRGB, ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT); // NOTE: uses transient attachement usage flag and cannot be read. plane.depthTarget = CreateReflectionPlaneGpuImage(gpuResourceMgr, *gpuHandleCM, *nameCM, DefaultMaterialCameraConstants::CAMERA_DEPTH_PREFIX_NAME + to_hex(nodeEntity.id), Format::BASE_FORMAT_D16_UNORM, ImageUsageFlagBits::CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT); if (const auto rmcHandle = renderMeshCM->Read(nodeEntity); rmcHandle) { const RenderMeshComponent& rmc = *rmcHandle; if (const auto meshHandle = meshCM->Read(rmc.mesh); meshHandle) { const MeshComponent& meshComponent = *meshHandle; if (!meshComponent.submeshes.empty()) { if (auto matHandle = matCM->Write(meshComponent.submeshes[0].material); matHandle) { MaterialComponent& matComponent = *matHandle; auto* uriCM = GetManager(ecs); auto* renderHandleCM = GetManager(ecs); constexpr const string_view uri = "3dshaders://shader/core3d_dm_fw_reflection_plane.shader"; auto shaderEntity = LookupResourceByUri(uri, *uriCM, *renderHandleCM); if (!EntityUtil::IsValid(shaderEntity)) { shaderEntity = ecs.GetEntityManager().Create(); renderHandleCM->Create(shaderEntity); renderHandleCM->Write(shaderEntity)->reference = device.GetShaderManager().GetShaderHandle(uri); uriCM->Create(shaderEntity); uriCM->Write(shaderEntity)->uri = uri; } matComponent.materialShader.shader = ecs.GetEntityManager().GetReferenceCounted(shaderEntity); matComponent.textures[MaterialComponent::TextureIndex::SHEEN].image = plane.colorTarget; matComponent.extraRenderingFlags = MaterialComponent::ExtraRenderingFlagBits::DISCARD_BIT; } } } } return plane; } } // namespace void SceneUtil::CreateReflectionPlaneComponent(IEcs& ecs, const Entity& nodeEntity) { SceneUtil::ReflectionPlane plane = CreateReflectionPlaneObjectFromEntity(ecs, graphicsContext_, nodeEntity); if (EntityUtil::IsValid(plane.entity)) { auto prcm = GetManager(ecs); PlanarReflectionComponent prc; prc.colorRenderTarget = plane.colorTarget; prc.depthRenderTarget = plane.depthTarget; prc.renderTargetResolution[0] = DEFAULT_PLANE_TARGET_SIZE.x; prc.renderTargetResolution[1] = DEFAULT_PLANE_TARGET_SIZE.y; prcm->Set(plane.entity, prc); } } float CalculateScalingFactor(IEcs& ecs, Entity targetEntity, Entity sourceEntity) { float scale = 1.f; auto renderMeshManager = GetManager(ecs); auto meshManager = GetManager(ecs); auto transformManager = GetManager(ecs); if (!renderMeshManager || !meshManager || !transformManager) { return scale; } Entity dstMeshEntity; Entity srcMeshEntity; if (auto renderMeshComponent = renderMeshManager->Read(targetEntity); renderMeshComponent) { dstMeshEntity = renderMeshComponent->mesh; } if (auto renderMeshComponent = renderMeshManager->Read(sourceEntity); renderMeshComponent) { srcMeshEntity = renderMeshComponent->mesh; } if (!EntityUtil::IsValid(dstMeshEntity) || !EntityUtil::IsValid(srcMeshEntity)) { return scale; } auto getMeshHeight = [](IMeshComponentManager& meshManager, Entity entity) { if (auto meshComponent = meshManager.Read(entity); meshComponent) { return (meshComponent->aabbMax.y - meshComponent->aabbMin.y); } return 0.f; }; const float dstHeight = getMeshHeight(*meshManager, dstMeshEntity); const float srcHeight = getMeshHeight(*meshManager, srcMeshEntity); if ((dstHeight == 0.f) || (srcHeight == 0.f)) { return scale; } auto dstMeshScale = transformManager->Get(targetEntity).scale; auto srcMeshScale = transformManager->Get(sourceEntity).scale; const auto dstSize = dstHeight * dstMeshScale.y; const auto srcSize = srcHeight * srcMeshScale.y; if (srcSize == 0.f) { return scale; } scale = dstSize / srcSize; return scale; } vector CreateJointMapping( IEcs& ecs, array_view dstJointEntities, array_view srcJointEntities) { vector srcToDstJointMapping; auto getName = [nameManager = GetManager(ecs)](const Entity& jointEntity) -> string_view { if (auto nameComponent = nameManager->Read(jointEntity); nameComponent) { return nameComponent->name; } return {}; }; vector dstJointNames; dstJointNames.reserve(dstJointEntities.size()); std::transform(dstJointEntities.begin(), dstJointEntities.end(), std::back_inserter(dstJointNames), getName); vector srcJointNames; srcJointNames.reserve(srcJointEntities.size()); std::transform(srcJointEntities.begin(), srcJointEntities.end(), std::back_inserter(srcJointNames), getName); srcToDstJointMapping.reserve(srcJointEntities.size()); const auto dstJointNamesBegin = dstJointNames.cbegin(); const auto dstJointNamesEnd = dstJointNames.cend(); for (const auto& srcJointName : srcJointNames) { const auto pos = std::find(dstJointNamesBegin, dstJointNamesEnd, srcJointName); srcToDstJointMapping.push_back( (pos != dstJointNamesEnd) ? dstJointEntities[static_cast(std::distance(dstJointNamesBegin, pos))] : Entity {}); if (pos == dstJointNamesEnd) { CORE_LOG_W("Target skin missing joint %s", srcJointName.data()); } } return srcToDstJointMapping; } vector UpdateTracks(IEcs& ecs, array_view targetTracks, array_view sourceTracks, array_view srcJointEntities, array_view srcToDstJointMapping, float scale) { vector trackTargets; trackTargets.reserve(sourceTracks.size()); auto& entityManager = ecs.GetEntityManager(); auto animationTrackManager = GetManager(ecs); auto animationOutputManager = GetManager(ecs); // update tracks to point to target skin's joints. std::transform(sourceTracks.begin(), sourceTracks.end(), targetTracks.begin(), [&entityManager, animationTrackManager, animationOutputManager, &srcJointEntities, &srcToDstJointMapping, &trackTargets, scale](const EntityReference& srcTrackEntity) { const auto srcTargetEntity = animationTrackManager->Read(srcTrackEntity)->target; // check that the src track target is one of the src joints if (const auto pos = std::find(srcJointEntities.begin(), srcJointEntities.end(), srcTargetEntity); pos != srcJointEntities.end()) { auto dstTrackEntity = entityManager.CreateReferenceCounted(); animationTrackManager->Create(dstTrackEntity); auto dstTrack = animationTrackManager->Write(dstTrackEntity); auto srcTrack = animationTrackManager->Read(srcTrackEntity); *dstTrack = *srcTrack; const auto jointIndex = (pos - srcJointEntities.begin()); trackTargets.push_back(srcToDstJointMapping[jointIndex]); dstTrack->target = {}; // joint position track needs to be offset if ((dstTrack->component == ITransformComponentManager::UID) && (dstTrack->property == "position")) { // create new animation output with original position corrected by the scale. dstTrack->data = entityManager.CreateReferenceCounted(); animationOutputManager->Create(dstTrack->data); const auto dstOutput = animationOutputManager->Write(dstTrack->data); const auto srcOutput = animationOutputManager->Read(srcTrack->data); dstOutput->type = srcOutput->type; auto& dst = dstOutput->data; const auto& src = srcOutput->data; dst.resize(src.size()); const auto count = dst.size() / sizeof(Math::Vec3); const auto srcPositions = array_view(reinterpret_cast(src.data()), count); auto dstPositions = array_view(reinterpret_cast(dst.data()), count); std::transform(srcPositions.begin(), srcPositions.end(), dstPositions.begin(), [scale](const Math::Vec3 position) { return position * scale; }); } return dstTrackEntity; } return srcTrackEntity; }); return trackTargets; } IAnimationPlayback* SceneUtil::RetargetSkinAnimation( IEcs& ecs, Entity targetEntity, Entity sourceEntity, Entity animationEntity) const { auto jointsManager = GetManager(ecs); auto dstJointsComponent = jointsManager->Read(targetEntity); auto srcJointsComponent = jointsManager->Read(sourceEntity); if (!dstJointsComponent || !srcJointsComponent) { return nullptr; } if (!GetManager(ecs)->HasComponent(animationEntity)) { return nullptr; } auto dstJointEntities = array_view(dstJointsComponent->jointEntities, dstJointsComponent->count); auto srcJointEntities = array_view(srcJointsComponent->jointEntities, srcJointsComponent->count); const vector srcToDstJointMapping = CreateJointMapping(ecs, dstJointEntities, srcJointEntities); // calculate a scaling factor based on mesh heights const auto scale = CalculateScalingFactor(ecs, targetEntity, sourceEntity); // compensate difference in root joint positions. // synchronize initial pose by copying src joint rotations to dst joints. { auto transformManager = GetManager(ecs); auto srcIt = srcJointEntities.begin(); for (const auto& dstE : srcToDstJointMapping) { auto srcTransform = transformManager->Read(*srcIt); auto dstTransform = transformManager->Write(dstE); if (srcTransform && dstTransform) { dstTransform->position = srcTransform->position * scale; dstTransform->rotation = srcTransform->rotation; } ++srcIt; } } auto& entityManager = ecs.GetEntityManager(); const auto dstAnimationEntity = entityManager.Create(); { INameComponentManager* nameManager = GetManager(ecs); nameManager->Create(dstAnimationEntity); nameManager->Write(dstAnimationEntity)->name = nameManager->Read(animationEntity)->name + " retargeted"; } vector trackTargets; { auto animationManager = GetManager(ecs); animationManager->Create(dstAnimationEntity); auto dstAnimationComponent = animationManager->Write(dstAnimationEntity); auto srcAnimationComponent = animationManager->Read(animationEntity); if (!srcAnimationComponent) { return nullptr; } // copy the src animation. *dstAnimationComponent = *srcAnimationComponent; trackTargets = UpdateTracks(ecs, dstAnimationComponent->tracks, srcAnimationComponent->tracks, srcJointEntities, srcToDstJointMapping, scale); } auto animationSystem = GetSystem(ecs); return animationSystem->CreatePlayback(dstAnimationEntity, trackTargets); } void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info, MaterialComponent::Shader& materialShader, MaterialComponent::Shader& depthShader) const { IRenderHandleComponentManager* renderHandleMgr = GetManager(ecs); if (renderHandleMgr) { IEntityManager& entityMgr = ecs.GetEntityManager(); const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager(); { const uint32_t renderSlotId = (info.alphaBlend) ? shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT) : shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE); const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId); materialShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader); RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState); gs.rasterizationState.cullModeFlags = info.cullModeFlags; gs.rasterizationState.frontFace = info.frontFace; const uint64_t gsHash = shaderMgr.HashGraphicsState(gs); const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash); if (gsHandle) { materialShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle); } } if (!info.alphaBlend) { const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH); const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId); depthShader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader); RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState); gs.rasterizationState.cullModeFlags = info.cullModeFlags; gs.rasterizationState.frontFace = info.frontFace; const uint64_t gsHash = shaderMgr.HashGraphicsState(gs); const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash); if (gsHandle) { depthShader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle); } } } } void SceneUtil::GetDefaultMaterialShaderData(IEcs& ecs, const ISceneUtil::MaterialShaderInfo& info, const string_view renderSlot, MaterialComponent::Shader& shader) const { IRenderHandleComponentManager* renderHandleMgr = GetManager(ecs); if (renderHandleMgr) { IEntityManager& entityMgr = ecs.GetEntityManager(); const IShaderManager& shaderMgr = graphicsContext_.GetRenderContext().GetDevice().GetShaderManager(); const uint32_t renderSlotId = shaderMgr.GetRenderSlotId(renderSlot); if (renderSlotId != ~0u) { const IShaderManager::RenderSlotData rsd = shaderMgr.GetRenderSlotData(renderSlotId); if (rsd.shader) { shader.shader = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, rsd.shader); } else { CORE_LOG_D("SceneUtil: render slot base shader not found (%s)", renderSlot.data()); } if (rsd.graphicsState) { RENDER_NS::GraphicsState gs = shaderMgr.GetGraphicsState(rsd.graphicsState); gs.rasterizationState.cullModeFlags = info.cullModeFlags; gs.rasterizationState.frontFace = info.frontFace; const uint64_t gsHash = shaderMgr.HashGraphicsState(gs); const RenderHandleReference gsHandle = shaderMgr.GetGraphicsStateHandleByHash(gsHash); shader.graphicsState = GetOrCreateEntityReference(entityMgr, *renderHandleMgr, gsHandle); } else { CORE_LOG_D("SceneUtil: render slot base graphics state not found (%s)", renderSlot.data()); } } else { CORE_LOG_W("SceneUtil: render slot id not found (%s)", renderSlot.data()); } } } CORE3D_END_NAMESPACE()