#include #include #include #include #include using namespace XCEngine::Components; using namespace XCEngine::Rendering; namespace { RenderContext CreateValidContext() { RenderContext context; context.device = reinterpret_cast(1); context.commandList = reinterpret_cast(1); context.commandQueue = reinterpret_cast(1); return context; } } // namespace TEST(SceneRenderRequestPlanner_Test, FallsBackToSceneCamerasWhenOverrideCameraIsNotUsable) { Scene scene("SceneRenderRequestPlannerOverrideFallback"); GameObject* overrideObject = scene.CreateGameObject("Override"); auto* overrideCamera = overrideObject->AddComponent(); overrideCamera->SetDepth(-10.0f); overrideCamera->SetEnabled(false); GameObject* sceneCameraObject = scene.CreateGameObject("SceneCamera"); auto* sceneCamera = sceneCameraObject->AddComponent(); sceneCamera->SetDepth(3.0f); SceneRenderRequestPlanner planner; const std::vector cameras = planner.CollectCameras(scene, overrideCamera); ASSERT_EQ(cameras.size(), 1u); EXPECT_EQ(cameras[0], sceneCamera); } TEST(SceneRenderRequestPlanner_Test, UsesOverrideCameraExclusivelyWhenItIsUsable) { Scene scene("SceneRenderRequestPlannerOverrideOnly"); GameObject* overrideObject = scene.CreateGameObject("Override"); auto* overrideCamera = overrideObject->AddComponent(); overrideCamera->SetDepth(9.0f); GameObject* sceneCameraObject = scene.CreateGameObject("SceneCamera"); auto* sceneCamera = sceneCameraObject->AddComponent(); sceneCamera->SetDepth(-5.0f); SceneRenderRequestPlanner planner; const std::vector cameras = planner.CollectCameras(scene, overrideCamera); ASSERT_EQ(cameras.size(), 1u); EXPECT_EQ(cameras[0], overrideCamera); } TEST(SceneRenderRequestPlanner_Test, CollectsOnlyUsableSceneCamerasInStableRenderOrder) { Scene scene("SceneRenderRequestPlannerCollectScene"); GameObject* inactiveObject = scene.CreateGameObject("Inactive"); auto* inactiveCamera = inactiveObject->AddComponent(); inactiveObject->SetActive(false); GameObject* highBaseObject = scene.CreateGameObject("HighBase"); auto* highBase = highBaseObject->AddComponent(); highBase->SetDepth(10.0f); highBase->SetStackType(CameraStackType::Base); GameObject* firstTieObject = scene.CreateGameObject("FirstTie"); auto* firstTie = firstTieObject->AddComponent(); firstTie->SetDepth(2.0f); firstTie->SetStackType(CameraStackType::Base); GameObject* secondTieObject = scene.CreateGameObject("SecondTie"); auto* secondTie = secondTieObject->AddComponent(); secondTie->SetDepth(2.0f); secondTie->SetStackType(CameraStackType::Base); GameObject* overlayObject = scene.CreateGameObject("Overlay"); auto* overlay = overlayObject->AddComponent(); overlay->SetDepth(-10.0f); overlay->SetStackType(CameraStackType::Overlay); SceneRenderRequestPlanner planner; const std::vector cameras = planner.CollectCameras(scene, nullptr); (void)inactiveCamera; ASSERT_EQ(cameras.size(), 4u); EXPECT_EQ(cameras[0], firstTie); EXPECT_EQ(cameras[1], secondTie); EXPECT_EQ(cameras[2], highBase); EXPECT_EQ(cameras[3], overlay); } TEST(SceneRenderRequestPlanner_Test, BuildsRequestsAndDropsZeroSizedViewportsWithoutBreakingClearFlow) { Scene scene("SceneRenderRequestPlannerBuildRequests"); GameObject* firstBaseObject = scene.CreateGameObject("FirstBase"); auto* firstBase = firstBaseObject->AddComponent(); firstBase->SetStackType(CameraStackType::Base); firstBase->SetDepth(1.0f); GameObject* skippedBaseObject = scene.CreateGameObject("SkippedBase"); auto* skippedBase = skippedBaseObject->AddComponent(); skippedBase->SetStackType(CameraStackType::Base); skippedBase->SetDepth(2.0f); skippedBase->SetViewportRect(XCEngine::Math::Rect(0.5f, 0.5f, 0.0f, 1.0f)); GameObject* overlayObject = scene.CreateGameObject("Overlay"); auto* overlay = overlayObject->AddComponent(); overlay->SetStackType(CameraStackType::Overlay); overlay->SetDepth(3.0f); SceneRenderRequestPlanner planner; const std::vector requests = planner.BuildRequests( scene, nullptr, CreateValidContext(), RenderSurface(640, 360)); ASSERT_EQ(requests.size(), 2u); EXPECT_EQ(requests[0].camera, firstBase); EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::All); EXPECT_EQ(requests[1].camera, overlay); EXPECT_EQ(requests[1].clearFlags, RenderClearFlags::Depth); } TEST(SceneRenderRequestPlanner_Test, BuildsDirectionalShadowPlanForBaseCameraWhenMainDirectionalLightCastsShadows) { Scene scene("SceneRenderRequestPlannerDirectionalShadow"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetStackType(CameraStackType::Base); camera->SetDepth(1.0f); GameObject* shadowLightObject = scene.CreateGameObject("ShadowLight"); auto* shadowLight = shadowLightObject->AddComponent(); shadowLight->SetLightType(LightType::Directional); shadowLight->SetIntensity(2.5f); shadowLight->SetCastsShadows(true); SceneRenderRequestPlanner planner; const std::vector requests = planner.BuildRequests( scene, nullptr, CreateValidContext(), RenderSurface(640, 360)); ASSERT_EQ(requests.size(), 1u); const CameraRenderRequest& request = requests[0]; EXPECT_EQ(request.camera, camera); ASSERT_TRUE(request.directionalShadow.IsValid()); EXPECT_TRUE(request.directionalShadow.enabled); EXPECT_EQ(request.directionalShadow.mapWidth, 1024u); EXPECT_EQ(request.directionalShadow.mapHeight, 1024u); EXPECT_EQ(request.directionalShadow.lightDirection, XCEngine::Math::Vector3::Back()); EXPECT_GT(request.directionalShadow.focusPoint.z, 0.0f); EXPECT_TRUE(request.shadowCaster.hasCameraDataOverride); EXPECT_EQ(request.shadowCaster.clearFlags, RenderClearFlags::Depth); EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportWidth, 1024u); EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportHeight, 1024u); EXPECT_EQ(request.shadowCaster.cameraDataOverride.clearFlags, RenderClearFlags::Depth); } TEST(SceneRenderRequestPlanner_Test, SkipsDirectionalShadowPlanForOverlayCameraWhenBaseCameraExists) { Scene scene("SceneRenderRequestPlannerOverlayShadow"); GameObject* baseCameraObject = scene.CreateGameObject("BaseCamera"); auto* baseCamera = baseCameraObject->AddComponent(); baseCamera->SetStackType(CameraStackType::Base); baseCamera->SetDepth(1.0f); GameObject* overlayCameraObject = scene.CreateGameObject("OverlayCamera"); auto* overlayCamera = overlayCameraObject->AddComponent(); overlayCamera->SetStackType(CameraStackType::Overlay); overlayCamera->SetDepth(2.0f); GameObject* shadowLightObject = scene.CreateGameObject("ShadowLight"); auto* shadowLight = shadowLightObject->AddComponent(); shadowLight->SetLightType(LightType::Directional); shadowLight->SetIntensity(3.0f); shadowLight->SetCastsShadows(true); SceneRenderRequestPlanner planner; const std::vector requests = planner.BuildRequests( scene, nullptr, CreateValidContext(), RenderSurface(640, 360)); ASSERT_EQ(requests.size(), 2u); EXPECT_EQ(requests[0].camera, baseCamera); EXPECT_TRUE(requests[0].directionalShadow.enabled); EXPECT_TRUE(requests[0].shadowCaster.hasCameraDataOverride); EXPECT_EQ(requests[1].camera, overlayCamera); EXPECT_FALSE(requests[1].directionalShadow.enabled); EXPECT_FALSE(requests[1].shadowCaster.hasCameraDataOverride); } TEST(SceneRenderRequestPlanner_Test, AppliesConfiguredDirectionalShadowPlanningSettings) { Scene scene("SceneRenderRequestPlannerCustomDirectionalShadow"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetStackType(CameraStackType::Base); camera->SetDepth(1.0f); camera->SetProjectionType(CameraProjectionType::Perspective); camera->SetNearClipPlane(0.3f); camera->SetFarClipPlane(100.0f); GameObject* shadowLightObject = scene.CreateGameObject("ShadowLight"); auto* shadowLight = shadowLightObject->AddComponent(); shadowLight->SetLightType(LightType::Directional); shadowLight->SetCastsShadows(true); SceneRenderRequestPlanner planner; DirectionalShadowPlanningSettings settings = {}; settings.mapDimension = 2048u; settings.minFocusDistance = 12.0f; settings.maxFocusDistance = 12.0f; settings.perspectiveFocusFactor = 0.01f; settings.minDepthRange = 80.0f; settings.sampling.receiverDepthBias = 0.0025f; settings.sampling.normalBiasScale = 2.0f; settings.sampling.shadowStrength = 0.75f; settings.casterBias.depthBiasFactor = 2.5f; settings.casterBias.depthBiasUnits = 4; planner.SetDirectionalShadowPlanningSettings(settings); const std::vector requests = planner.BuildRequests( scene, nullptr, CreateValidContext(), RenderSurface(640, 360)); ASSERT_EQ(requests.size(), 1u); const CameraRenderRequest& request = requests[0]; ASSERT_TRUE(request.directionalShadow.IsValid()); EXPECT_EQ(request.directionalShadow.mapWidth, 2048u); EXPECT_EQ(request.directionalShadow.mapHeight, 2048u); EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportWidth, 2048u); EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportHeight, 2048u); EXPECT_GE(request.directionalShadow.focusPoint.z, 6.0f); EXPECT_LE(request.directionalShadow.focusPoint.z, 6.3f); EXPECT_FLOAT_EQ(request.directionalShadow.sampling.receiverDepthBias, 0.0025f); EXPECT_FLOAT_EQ(request.directionalShadow.sampling.normalBiasScale, 2.0f); EXPECT_FLOAT_EQ(request.directionalShadow.sampling.shadowStrength, 0.75f); EXPECT_FLOAT_EQ(request.directionalShadow.casterBias.depthBiasFactor, 2.5f); EXPECT_EQ(request.directionalShadow.casterBias.depthBiasUnits, 4); EXPECT_GE( request.directionalShadow.farClipPlane - request.directionalShadow.nearClipPlane, 80.0f); } TEST(SceneRenderRequestPlanner_Test, SanitizesInvalidDirectionalShadowPlanningSettings) { SceneRenderRequestPlanner planner; DirectionalShadowPlanningSettings invalidSettings = {}; invalidSettings.mapDimension = 0u; invalidSettings.minFocusDistance = -1.0f; invalidSettings.maxFocusDistance = 0.0f; invalidSettings.perspectiveFocusFactor = 0.0f; invalidSettings.orthographicFocusFactor = -2.0f; invalidSettings.minDepthRange = 0.0f; invalidSettings.boundsPadding = -1.0f; invalidSettings.minDepthPadding = -3.0f; invalidSettings.sampling.receiverDepthBias = -0.5f; invalidSettings.sampling.normalBiasScale = -2.0f; invalidSettings.sampling.shadowStrength = 3.0f; invalidSettings.casterBias.depthBiasFactor = -1.0f; invalidSettings.casterBias.depthBiasUnits = -6; planner.SetDirectionalShadowPlanningSettings(invalidSettings); const DirectionalShadowPlanningSettings& settings = planner.GetDirectionalShadowPlanningSettings(); EXPECT_EQ(settings.mapDimension, 1024u); EXPECT_FLOAT_EQ(settings.minFocusDistance, 5.0f); EXPECT_FLOAT_EQ(settings.maxFocusDistance, 64.0f); EXPECT_FLOAT_EQ(settings.perspectiveFocusFactor, 1.0f); EXPECT_FLOAT_EQ(settings.orthographicFocusFactor, 2.0f); EXPECT_FLOAT_EQ(settings.minDepthRange, 20.0f); EXPECT_FLOAT_EQ(settings.boundsPadding, 1.0f); EXPECT_FLOAT_EQ(settings.minDepthPadding, 2.0f); EXPECT_FLOAT_EQ(settings.sampling.receiverDepthBias, 0.0010f); EXPECT_FLOAT_EQ(settings.sampling.normalBiasScale, 2.0f); EXPECT_FLOAT_EQ(settings.sampling.shadowStrength, 1.0f); EXPECT_FLOAT_EQ(settings.casterBias.depthBiasFactor, 2.5f); EXPECT_EQ(settings.casterBias.depthBiasUnits, 4); }