#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace XCEngine::Components; using namespace XCEngine::Core; using namespace XCEngine::Math; using namespace XCEngine::Rendering; using namespace XCEngine::Resources; namespace { Mesh* CreateTestMesh(const char* path) { auto* mesh = new Mesh(); IResource::ConstructParams params = {}; params.name = "TestMesh"; params.path = path; params.guid = ResourceGUID::Generate(path); mesh->Initialize(params); return mesh; } Mesh* CreateSectionedTestMesh(const char* path, std::initializer_list materialIds) { Mesh* mesh = CreateTestMesh(path); uint32_t startIndex = 0; for (uint32_t materialId : materialIds) { MeshSection section = {}; section.baseVertex = 0; section.vertexCount = 3; section.startIndex = startIndex; section.indexCount = 3; section.materialID = materialId; mesh->AddSection(section); startIndex += 3; } return mesh; } Material* CreateTestMaterial(const char* path, int32_t renderQueue, const char* shaderPass = nullptr, const char* lightMode = nullptr) { auto* material = new Material(); IResource::ConstructParams params = {}; params.name = "TestMaterial"; params.path = path; params.guid = ResourceGUID::Generate(path); material->Initialize(params); material->SetRenderQueue(renderQueue); if (shaderPass != nullptr) { material->SetShaderPass(shaderPass); } if (lightMode != nullptr) { material->SetTag("LightMode", lightMode); } return material; } TEST(RenderSceneExtractor_Test, SelectsHighestDepthPrimaryCameraAndVisibleObjects) { Scene scene("RenderScene"); GameObject* cameraObjectA = scene.CreateGameObject("CameraA"); auto* cameraA = cameraObjectA->AddComponent(); cameraA->SetPrimary(true); cameraA->SetDepth(0.0f); GameObject* cameraObjectB = scene.CreateGameObject("CameraB"); auto* cameraB = cameraObjectB->AddComponent(); cameraB->SetPrimary(true); cameraB->SetDepth(5.0f); cameraObjectB->GetTransform()->SetLocalPosition(Vector3(2.0f, 3.0f, 4.0f)); GameObject* visibleObject = scene.CreateGameObject("VisibleQuad"); visibleObject->GetTransform()->SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f)); auto* meshFilter = visibleObject->AddComponent(); visibleObject->AddComponent(); Mesh* visibleMesh = CreateTestMesh("Meshes/visible.mesh"); meshFilter->SetMesh(visibleMesh); GameObject* hiddenObject = scene.CreateGameObject("HiddenQuad"); hiddenObject->SetActive(false); auto* hiddenMeshFilter = hiddenObject->AddComponent(); hiddenObject->AddComponent(); Mesh* hiddenMesh = CreateTestMesh("Meshes/hidden.mesh"); hiddenMeshFilter->SetMesh(hiddenMesh); RenderSceneExtractor extractor; const RenderSceneData sceneData = extractor.Extract(scene, nullptr, 1280, 720); ASSERT_TRUE(sceneData.HasCamera()); EXPECT_EQ(sceneData.camera, cameraB); EXPECT_EQ(sceneData.cameraData.viewportWidth, 1280u); EXPECT_EQ(sceneData.cameraData.viewportHeight, 720u); EXPECT_EQ(sceneData.cameraData.worldPosition, Vector3(2.0f, 3.0f, 4.0f)); ASSERT_EQ(sceneData.visibleItems.size(), 1u); EXPECT_EQ(sceneData.visibleItems[0].gameObject, visibleObject); EXPECT_EQ(sceneData.visibleItems[0].mesh, visibleMesh); EXPECT_EQ(sceneData.visibleItems[0].localToWorld.GetTranslation(), Vector3(1.0f, 2.0f, 3.0f)); EXPECT_FALSE(sceneData.visibleItems[0].hasSection); EXPECT_EQ(sceneData.visibleItems[0].renderQueue, static_cast(MaterialRenderQueue::Geometry)); meshFilter->ClearMesh(); hiddenMeshFilter->ClearMesh(); delete visibleMesh; delete hiddenMesh; } TEST(RenderSceneExtractor_Test, OverrideCameraTakesPriority) { Scene scene("OverrideScene"); GameObject* primaryObject = scene.CreateGameObject("PrimaryCamera"); auto* primaryCamera = primaryObject->AddComponent(); primaryCamera->SetPrimary(true); primaryCamera->SetDepth(10.0f); GameObject* overrideObject = scene.CreateGameObject("OverrideCamera"); auto* overrideCamera = overrideObject->AddComponent(); overrideCamera->SetPrimary(false); overrideCamera->SetDepth(-1.0f); RenderSceneExtractor extractor; const RenderSceneData sceneData = extractor.Extract(scene, overrideCamera, 640, 480); ASSERT_TRUE(sceneData.HasCamera()); EXPECT_EQ(sceneData.camera, overrideCamera); EXPECT_NE(sceneData.camera, primaryCamera); EXPECT_EQ(sceneData.cameraData.viewportWidth, 640u); EXPECT_EQ(sceneData.cameraData.viewportHeight, 480u); } TEST(RenderSceneExtractor_Test, ExtractsBrightestDirectionalLightAsMainLight) { Scene scene("LightingScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); GameObject* fillLightObject = scene.CreateGameObject("FillLight"); auto* fillLight = fillLightObject->AddComponent(); fillLight->SetLightType(LightType::Directional); fillLight->SetColor(Color(0.2f, 0.4f, 0.8f, 1.0f)); fillLight->SetIntensity(0.5f); GameObject* pointLightObject = scene.CreateGameObject("PointLight"); auto* pointLight = pointLightObject->AddComponent(); pointLight->SetLightType(LightType::Point); pointLight->SetIntensity(10.0f); GameObject* mainLightObject = scene.CreateGameObject("MainLight"); auto* mainLight = mainLightObject->AddComponent(); mainLight->SetLightType(LightType::Directional); mainLight->SetColor(Color(1.0f, 0.8f, 0.6f, 1.0f)); mainLight->SetIntensity(2.5f); mainLight->SetCastsShadows(true); mainLightObject->GetTransform()->SetLocalRotation( Quaternion::LookRotation(Vector3(-0.3f, -1.0f, -0.2f).Normalized())); RenderSceneExtractor extractor; const RenderSceneData sceneData = extractor.Extract(scene, nullptr, 800, 600); ASSERT_TRUE(sceneData.HasCamera()); ASSERT_TRUE(sceneData.lighting.HasMainDirectionalLight()); EXPECT_FLOAT_EQ(sceneData.lighting.mainDirectionalLight.intensity, 2.5f); EXPECT_EQ(sceneData.lighting.mainDirectionalLight.color.r, 1.0f); EXPECT_EQ(sceneData.lighting.mainDirectionalLight.color.g, 0.8f); EXPECT_EQ(sceneData.lighting.mainDirectionalLight.color.b, 0.6f); EXPECT_TRUE(sceneData.lighting.mainDirectionalLight.castsShadows); EXPECT_EQ( sceneData.lighting.mainDirectionalLight.direction, mainLightObject->GetTransform()->GetForward().Normalized() * -1.0f); } TEST(RenderSceneExtractor_Test, FiltersVisibleItemsByCameraCullingMask) { Scene scene("CullingMaskScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetCullingMask(1u << 5); GameObject* visibleObject = scene.CreateGameObject("VisibleLayer5"); visibleObject->SetLayer(5); auto* visibleMeshFilter = visibleObject->AddComponent(); visibleObject->AddComponent(); Mesh* visibleMesh = CreateTestMesh("Meshes/layer5.mesh"); visibleMeshFilter->SetMesh(visibleMesh); GameObject* hiddenObject = scene.CreateGameObject("HiddenLayer0"); hiddenObject->SetLayer(0); auto* hiddenMeshFilter = hiddenObject->AddComponent(); hiddenObject->AddComponent(); Mesh* hiddenMesh = CreateTestMesh("Meshes/layer0.mesh"); hiddenMeshFilter->SetMesh(hiddenMesh); RenderSceneExtractor extractor; const RenderSceneData sceneData = extractor.Extract(scene, nullptr, 800, 600); ASSERT_EQ(sceneData.visibleItems.size(), 1u); EXPECT_EQ(sceneData.visibleItems[0].gameObject, visibleObject); EXPECT_EQ(sceneData.visibleItems[0].mesh, visibleMesh); visibleMeshFilter->ClearMesh(); hiddenMeshFilter->ClearMesh(); delete visibleMesh; delete hiddenMesh; } TEST(RenderSceneExtractor_Test, ExtractsSectionLevelVisibleItemsAndSortsByRenderQueue) { Scene scene("SectionScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); GameObject* renderObject = scene.CreateGameObject("MultiSectionMesh"); renderObject->GetTransform()->SetLocalPosition(Vector3(0.0f, 0.0f, 5.0f)); auto* meshFilter = renderObject->AddComponent(); auto* meshRenderer = renderObject->AddComponent(); Mesh* mesh = CreateSectionedTestMesh("Meshes/sectioned.mesh", { 1u, 0u }); Material* opaqueMaterial = CreateTestMaterial( "Materials/opaque.mat", static_cast(MaterialRenderQueue::Geometry), "ForwardLit", "ForwardBase"); Material* transparentMaterial = CreateTestMaterial( "Materials/transparent.mat", static_cast(MaterialRenderQueue::Transparent), "ForwardLit", "ForwardBase"); meshFilter->SetMesh(mesh); meshRenderer->SetMaterial(0, opaqueMaterial); meshRenderer->SetMaterial(1, transparentMaterial); RenderSceneExtractor extractor; const RenderSceneData sceneData = extractor.Extract(scene, nullptr, 800, 600); ASSERT_TRUE(sceneData.HasCamera()); ASSERT_EQ(sceneData.visibleItems.size(), 2u); EXPECT_TRUE(sceneData.visibleItems[0].hasSection); EXPECT_EQ(sceneData.visibleItems[0].sectionIndex, 1u); EXPECT_EQ(sceneData.visibleItems[0].materialIndex, 0u); EXPECT_EQ(sceneData.visibleItems[0].material, opaqueMaterial); EXPECT_EQ(sceneData.visibleItems[0].renderQueue, static_cast(MaterialRenderQueue::Geometry)); EXPECT_TRUE(sceneData.visibleItems[1].hasSection); EXPECT_EQ(sceneData.visibleItems[1].sectionIndex, 0u); EXPECT_EQ(sceneData.visibleItems[1].materialIndex, 1u); EXPECT_EQ(sceneData.visibleItems[1].material, transparentMaterial); EXPECT_EQ(sceneData.visibleItems[1].renderQueue, static_cast(MaterialRenderQueue::Transparent)); meshRenderer->ClearMaterials(); meshFilter->ClearMesh(); delete opaqueMaterial; delete transparentMaterial; delete mesh; } TEST(RenderSceneExtractor_Test, SortsOpaqueFrontToBackAndTransparentBackToFront) { Scene scene("QueueSortScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); auto createRenderable = [&](const char* name, float z, int32_t renderQueue) -> Mesh* { GameObject* object = scene.CreateGameObject(name); object->GetTransform()->SetLocalPosition(Vector3(0.0f, 0.0f, z)); auto* meshFilter = object->AddComponent(); auto* meshRenderer = object->AddComponent(); Mesh* mesh = CreateTestMesh(name); Material* material = CreateTestMaterial(name, renderQueue, "ForwardLit", "ForwardBase"); meshFilter->SetMesh(mesh); meshRenderer->SetMaterial(0, material); return mesh; }; Mesh* opaqueFarMesh = createRenderable("OpaqueFar", 10.0f, static_cast(MaterialRenderQueue::Geometry)); Mesh* transparentNearMesh = createRenderable("TransparentNear", 2.0f, static_cast(MaterialRenderQueue::Transparent)); Mesh* opaqueNearMesh = createRenderable("OpaqueNear", 2.0f, static_cast(MaterialRenderQueue::Geometry)); Mesh* transparentFarMesh = createRenderable("TransparentFar", 10.0f, static_cast(MaterialRenderQueue::Transparent)); RenderSceneExtractor extractor; const RenderSceneData sceneData = extractor.Extract(scene, nullptr, 800, 600); ASSERT_EQ(sceneData.visibleItems.size(), 4u); EXPECT_STREQ(sceneData.visibleItems[0].gameObject->GetName().c_str(), "OpaqueNear"); EXPECT_STREQ(sceneData.visibleItems[1].gameObject->GetName().c_str(), "OpaqueFar"); EXPECT_STREQ(sceneData.visibleItems[2].gameObject->GetName().c_str(), "TransparentFar"); EXPECT_STREQ(sceneData.visibleItems[3].gameObject->GetName().c_str(), "TransparentNear"); auto cleanupObject = [](GameObject* object) { auto* meshFilter = object->GetComponent(); auto* meshRenderer = object->GetComponent(); Material* material = meshRenderer != nullptr ? meshRenderer->GetMaterial(0) : nullptr; if (meshRenderer != nullptr) { meshRenderer->ClearMaterials(); } if (meshFilter != nullptr) { meshFilter->ClearMesh(); } delete material; }; cleanupObject(scene.Find("OpaqueFar")); cleanupObject(scene.Find("TransparentNear")); cleanupObject(scene.Find("OpaqueNear")); cleanupObject(scene.Find("TransparentFar")); delete opaqueFarMesh; delete transparentNearMesh; delete opaqueNearMesh; delete transparentFarMesh; } TEST(RenderSceneExtractor_Test, FallsBackToEmbeddedMeshMaterialsWhenRendererHasNoExplicitSlots) { Scene scene("EmbeddedMaterialScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); GameObject* renderObject = scene.CreateGameObject("EmbeddedBackpack"); auto* meshFilter = renderObject->AddComponent(); auto* meshRenderer = renderObject->AddComponent(); Mesh* mesh = CreateSectionedTestMesh("Meshes/embedded.mesh", { 0u }); Material* embeddedMaterial = CreateTestMaterial( "Materials/embedded.mat", static_cast(MaterialRenderQueue::Transparent), "ForwardLit", "ForwardBase"); mesh->AddMaterial(embeddedMaterial); meshFilter->SetMesh(mesh); meshRenderer->ClearMaterials(); RenderSceneExtractor extractor; const RenderSceneData sceneData = extractor.Extract(scene, nullptr, 800, 600); ASSERT_EQ(sceneData.visibleItems.size(), 1u); EXPECT_EQ(sceneData.visibleItems[0].material, embeddedMaterial); EXPECT_EQ(sceneData.visibleItems[0].renderQueue, static_cast(MaterialRenderQueue::Transparent)); EXPECT_EQ(ResolveMaterial(sceneData.visibleItems[0]), embeddedMaterial); meshFilter->ClearMesh(); delete mesh; } TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardLitPassMetadata) { Material forwardMaterial; forwardMaterial.SetShaderPass("ForwardLit"); forwardMaterial.SetTag("LightMode", "ForwardBase"); EXPECT_TRUE(MatchesBuiltinPass(&forwardMaterial, BuiltinMaterialPass::ForwardLit)); Material noMetadataMaterial; EXPECT_TRUE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::ForwardLit)); Material shadowMaterial; shadowMaterial.SetShaderPass("ShadowCaster"); EXPECT_FALSE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::ForwardLit)); EXPECT_TRUE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::ShadowCaster)); Material depthOnlyMaterial; depthOnlyMaterial.SetTag("LightMode", "DepthOnly"); EXPECT_FALSE(MatchesBuiltinPass(&depthOnlyMaterial, BuiltinMaterialPass::ForwardLit)); } TEST(RenderMaterialUtility_Test, MatchesBuiltinUnlitDepthAndObjectIdPassMetadata) { Material unlitMaterial; unlitMaterial.SetShaderPass("Unlit"); EXPECT_TRUE(MatchesBuiltinPass(&unlitMaterial, BuiltinMaterialPass::Unlit)); EXPECT_FALSE(MatchesBuiltinPass(&unlitMaterial, BuiltinMaterialPass::ForwardLit)); Material depthMaterial; depthMaterial.SetTag("LightMode", "DepthOnly"); EXPECT_TRUE(MatchesBuiltinPass(&depthMaterial, BuiltinMaterialPass::DepthOnly)); EXPECT_FALSE(MatchesBuiltinPass(&depthMaterial, BuiltinMaterialPass::Unlit)); Material objectIdMaterial; objectIdMaterial.SetShaderPass("ObjectId"); EXPECT_TRUE(MatchesBuiltinPass(&objectIdMaterial, BuiltinMaterialPass::ObjectId)); EXPECT_FALSE(MatchesBuiltinPass(&objectIdMaterial, BuiltinMaterialPass::ForwardLit)); } TEST(RenderMaterialUtility_Test, ShaderPassMetadataCanDriveBuiltinPassMatching) { Material material; auto* shader = new Shader(); ShaderPass forwardPass = {}; forwardPass.name = "ForwardLit"; shader->AddPass(forwardPass); ResourceHandle shaderHandle(shader); material.SetShader(shaderHandle); EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit)); } TEST(RenderMaterialUtility_Test, ExplicitShaderPassMetadataDisablesImplicitForwardFallback) { Material material; auto* shader = new Shader(); ShaderPass unlitPass = {}; unlitPass.name = "Unlit"; shader->AddPass(unlitPass); ResourceHandle shaderHandle(shader); material.SetShader(shaderHandle); EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit)); } TEST(RenderMaterialUtility_Test, ResolvesBuiltinForwardMaterialContractFromCanonicalNamesAndAliases) { Material canonicalMaterial; canonicalMaterial.SetFloat4("baseColor", Vector4(0.2f, 0.4f, 0.6f, 0.8f)); EXPECT_EQ(ResolveBuiltinBaseColorFactor(&canonicalMaterial), Vector4(0.2f, 0.4f, 0.6f, 0.8f)); Material aliasMaterial; aliasMaterial.SetFloat4("_BaseColor", Vector4(0.7f, 0.6f, 0.5f, 0.4f)); Texture* baseColorTexture = new Texture(); IResource::ConstructParams textureParams = {}; textureParams.name = "AliasBaseColor"; textureParams.path = "Textures/alias_base_color.texture"; textureParams.guid = ResourceGUID::Generate(textureParams.path); baseColorTexture->Initialize(textureParams); aliasMaterial.SetTexture("_BaseColorTexture", ResourceHandle(baseColorTexture)); const BuiltinForwardMaterialData materialData = BuildBuiltinForwardMaterialData(&aliasMaterial); EXPECT_EQ(materialData.baseColorFactor, Vector4(0.7f, 0.6f, 0.5f, 0.4f)); EXPECT_EQ(ResolveBuiltinBaseColorTexture(&aliasMaterial), baseColorTexture); } TEST(RenderMaterialUtility_Test, ResolvesBuiltinForwardMaterialContractFromShaderSemanticMetadata) { auto* shader = new Shader(); ShaderPropertyDesc colorProperty = {}; colorProperty.name = "TintColor"; colorProperty.displayName = "Tint"; colorProperty.type = ShaderPropertyType::Color; colorProperty.semantic = "BaseColor"; shader->AddProperty(colorProperty); ShaderPropertyDesc textureProperty = {}; textureProperty.name = "AlbedoMap"; textureProperty.displayName = "Albedo"; textureProperty.type = ShaderPropertyType::Texture2D; textureProperty.semantic = "BaseColorTexture"; shader->AddProperty(textureProperty); Material material; material.SetShader(ResourceHandle(shader)); material.SetFloat4("TintColor", Vector4(0.3f, 0.5f, 0.7f, 0.9f)); Texture* texture = new Texture(); IResource::ConstructParams textureParams = {}; textureParams.name = "SemanticTexture"; textureParams.path = "Textures/semantic_base_color.texture"; textureParams.guid = ResourceGUID::Generate(textureParams.path); texture->Initialize(textureParams); material.SetTexture("AlbedoMap", ResourceHandle(texture)); EXPECT_EQ(ResolveBuiltinBaseColorFactor(&material), Vector4(0.3f, 0.5f, 0.7f, 0.9f)); EXPECT_EQ(ResolveBuiltinBaseColorTexture(&material), texture); } TEST(RenderMaterialUtility_Test, ResolvesBuiltinForwardMaterialContractFromShaderSemanticDefaults) { auto* shader = new Shader(); ShaderPropertyDesc colorProperty = {}; colorProperty.name = "TintColor"; colorProperty.displayName = "Tint"; colorProperty.type = ShaderPropertyType::Color; colorProperty.defaultValue = "(0.11,0.22,0.33,0.44)"; colorProperty.semantic = "BaseColor"; shader->AddProperty(colorProperty); Material material; material.SetShader(ResourceHandle(shader)); EXPECT_EQ(ResolveBuiltinBaseColorFactor(&material), Vector4(0.11f, 0.22f, 0.33f, 0.44f)); EXPECT_EQ(ResolveBuiltinBaseColorTexture(&material), nullptr); } TEST(RenderMaterialUtility_Test, ExposesSchemaDrivenMaterialConstantPayload) { auto* shader = new Shader(); ShaderPropertyDesc colorProperty = {}; colorProperty.name = "_BaseColor"; colorProperty.type = ShaderPropertyType::Color; colorProperty.defaultValue = "(0.25,0.5,0.75,1.0)"; colorProperty.semantic = "BaseColor"; shader->AddProperty(colorProperty); Material material; material.SetShader(ResourceHandle(shader)); const MaterialConstantPayloadView payload = ResolveSchemaMaterialConstantPayload(&material); ASSERT_TRUE(payload.IsValid()); ASSERT_EQ(payload.size, 16u); ASSERT_TRUE(payload.layout.IsValid()); ASSERT_EQ(payload.layout.count, 1u); EXPECT_EQ(payload.layout.size, 16u); EXPECT_EQ(payload.layout.fields[0].name, "_BaseColor"); EXPECT_EQ(payload.layout.fields[0].offset, 0u); EXPECT_EQ(payload.layout.fields[0].size, 16u); EXPECT_EQ(payload.layout.fields[0].alignedSize, 16u); const float* values = static_cast(payload.data); EXPECT_FLOAT_EQ(values[0], 0.25f); EXPECT_FLOAT_EQ(values[1], 0.5f); EXPECT_FLOAT_EQ(values[2], 0.75f); EXPECT_FLOAT_EQ(values[3], 1.0f); } TEST(RenderMaterialUtility_Test, UsesOpacityOnlyWhenBaseColorFactorIsMissing) { Material material; material.SetFloat("opacity", 0.35f); EXPECT_EQ(ResolveBuiltinBaseColorFactor(&material), Vector4(1.0f, 1.0f, 1.0f, 0.35f)); material.SetFloat4("baseColor", Vector4(0.9f, 0.8f, 0.7f, 0.6f)); EXPECT_EQ(ResolveBuiltinBaseColorFactor(&material), Vector4(0.9f, 0.8f, 0.7f, 0.6f)); } TEST(RenderMaterialUtility_Test, MapsMaterialRenderStateToRhiDescriptors) { Material material; MaterialRenderState renderState; renderState.cullMode = MaterialCullMode::Back; renderState.blendEnable = true; renderState.srcBlend = MaterialBlendFactor::SrcAlpha; renderState.dstBlend = MaterialBlendFactor::InvSrcAlpha; renderState.srcBlendAlpha = MaterialBlendFactor::One; renderState.dstBlendAlpha = MaterialBlendFactor::InvSrcAlpha; renderState.blendOp = MaterialBlendOp::Add; renderState.blendOpAlpha = MaterialBlendOp::Subtract; renderState.colorWriteMask = 0x7; renderState.depthTestEnable = true; renderState.depthWriteEnable = false; renderState.depthFunc = MaterialComparisonFunc::LessEqual; material.SetRenderState(renderState); const XCEngine::RHI::RasterizerDesc rasterizerState = BuildRasterizerState(&material); const XCEngine::RHI::BlendDesc blendState = BuildBlendState(&material); const XCEngine::RHI::DepthStencilStateDesc depthStencilState = BuildDepthStencilState(&material); EXPECT_EQ(rasterizerState.cullMode, static_cast(XCEngine::RHI::CullMode::Back)); EXPECT_EQ(rasterizerState.frontFace, static_cast(XCEngine::RHI::FrontFace::CounterClockwise)); EXPECT_TRUE(blendState.blendEnable); EXPECT_EQ(blendState.srcBlend, static_cast(XCEngine::RHI::BlendFactor::SrcAlpha)); EXPECT_EQ(blendState.dstBlend, static_cast(XCEngine::RHI::BlendFactor::InvSrcAlpha)); EXPECT_EQ(blendState.srcBlendAlpha, static_cast(XCEngine::RHI::BlendFactor::One)); EXPECT_EQ(blendState.dstBlendAlpha, static_cast(XCEngine::RHI::BlendFactor::InvSrcAlpha)); EXPECT_EQ(blendState.blendOp, static_cast(XCEngine::RHI::BlendOp::Add)); EXPECT_EQ(blendState.blendOpAlpha, static_cast(XCEngine::RHI::BlendOp::Subtract)); EXPECT_EQ(blendState.colorWriteMask, 0x7); EXPECT_TRUE(depthStencilState.depthTestEnable); EXPECT_FALSE(depthStencilState.depthWriteEnable); EXPECT_EQ(depthStencilState.depthFunc, static_cast(XCEngine::RHI::ComparisonFunc::LessEqual)); } } // namespace