//-------------------------------------------------------------------------------------- // File: NormalMapEffect.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // http://go.microsoft.com/fwlink/?LinkId=248929 //-------------------------------------------------------------------------------------- #include "pch.h" #include "EffectCommon.h" using namespace DirectX; // Constant buffer layout. Must match the shader! struct NormalMapEffectConstants { XMVECTOR diffuseColor; XMVECTOR emissiveColor; XMVECTOR specularColorAndPower; XMVECTOR lightDirection[IEffectLights::MaxDirectionalLights]; XMVECTOR lightDiffuseColor[IEffectLights::MaxDirectionalLights]; XMVECTOR lightSpecularColor[IEffectLights::MaxDirectionalLights]; XMVECTOR eyePosition; XMVECTOR fogColor; XMVECTOR fogVector; XMMATRIX world; XMVECTOR worldInverseTranspose[3]; XMMATRIX worldViewProj; }; static_assert((sizeof(NormalMapEffectConstants) % 16) == 0, "CB size not padded correctly"); // Traits type describes our characteristics to the EffectBase template. struct NormalMapEffectTraits { using ConstantBufferType = NormalMapEffectConstants; static constexpr int VertexShaderCount = 4; static constexpr int PixelShaderCount = 4; static constexpr int ShaderPermutationCount = 16; }; // Internal NormalMapEffect implementation class. class NormalMapEffect::Impl : public EffectBase { public: Impl(_In_ ID3D11Device* device); Microsoft::WRL::ComPtr specularTexture; Microsoft::WRL::ComPtr normalTexture; bool vertexColorEnabled; bool biasedVertexNormals; EffectLights lights; int GetCurrentShaderPermutation() const noexcept; void Apply(_In_ ID3D11DeviceContext* deviceContext); }; // Include the precompiled shader code. namespace { #if defined(_XBOX_ONE) && defined(_TITLE) #include "Shaders/Compiled/XboxOneNormalMapEffect_VSNormalPixelLightingTx.inc" #include "Shaders/Compiled/XboxOneNormalMapEffect_VSNormalPixelLightingTxVc.inc" #include "Shaders/Compiled/XboxOneNormalMapEffect_VSNormalPixelLightingTxBn.inc" #include "Shaders/Compiled/XboxOneNormalMapEffect_VSNormalPixelLightingTxVcBn.inc" #include "Shaders/Compiled/XboxOneNormalMapEffect_PSNormalPixelLightingTx.inc" #include "Shaders/Compiled/XboxOneNormalMapEffect_PSNormalPixelLightingTxNoFog.inc" #include "Shaders/Compiled/XboxOneNormalMapEffect_PSNormalPixelLightingTxNoSpec.inc" #include "Shaders/Compiled/XboxOneNormalMapEffect_PSNormalPixelLightingTxNoFogSpec.inc" #else #include "Shaders/Compiled/NormalMapEffect_VSNormalPixelLightingTx.inc" #include "Shaders/Compiled/NormalMapEffect_VSNormalPixelLightingTxVc.inc" #include "Shaders/Compiled/NormalMapEffect_VSNormalPixelLightingTxBn.inc" #include "Shaders/Compiled/NormalMapEffect_VSNormalPixelLightingTxVcBn.inc" #include "Shaders/Compiled/NormalMapEffect_PSNormalPixelLightingTx.inc" #include "Shaders/Compiled/NormalMapEffect_PSNormalPixelLightingTxNoFog.inc" #include "Shaders/Compiled/NormalMapEffect_PSNormalPixelLightingTxNoSpec.inc" #include "Shaders/Compiled/NormalMapEffect_PSNormalPixelLightingTxNoFogSpec.inc" #endif } template<> const ShaderBytecode EffectBase::VertexShaderBytecode[] = { { NormalMapEffect_VSNormalPixelLightingTx, sizeof(NormalMapEffect_VSNormalPixelLightingTx) }, { NormalMapEffect_VSNormalPixelLightingTxVc, sizeof(NormalMapEffect_VSNormalPixelLightingTxVc) }, { NormalMapEffect_VSNormalPixelLightingTxBn, sizeof(NormalMapEffect_VSNormalPixelLightingTxBn) }, { NormalMapEffect_VSNormalPixelLightingTxVcBn, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcBn) }, }; template<> const int EffectBase::VertexShaderIndices[] = { 0, // pixel lighting + texture 0, // pixel lighting + texture, no fog 1, // pixel lighting + texture + vertex color 1, // pixel lighting + texture + vertex color, no fog 0, // pixel lighting + texture, no specular 0, // pixel lighting + texture, no fog or specular 1, // pixel lighting + texture + vertex color, no specular 1, // pixel lighting + texture + vertex color, no fog or specular 2, // pixel lighting (biased vertex normal) + texture 2, // pixel lighting (biased vertex normal) + texture, no fog 3, // pixel lighting (biased vertex normal) + texture + vertex color 3, // pixel lighting (biased vertex normal) + texture + vertex color, no fog 2, // pixel lighting (biased vertex normal) + texture, no specular 2, // pixel lighting (biased vertex normal) + texture, no fog or specular 3, // pixel lighting (biased vertex normal) + texture + vertex color, no specular 3, // pixel lighting (biased vertex normal) + texture + vertex color, no fog or specular }; template<> const ShaderBytecode EffectBase::PixelShaderBytecode[] = { { NormalMapEffect_PSNormalPixelLightingTx, sizeof(NormalMapEffect_PSNormalPixelLightingTx) }, { NormalMapEffect_PSNormalPixelLightingTxNoFog, sizeof(NormalMapEffect_PSNormalPixelLightingTxNoFog) }, { NormalMapEffect_PSNormalPixelLightingTxNoSpec, sizeof(NormalMapEffect_PSNormalPixelLightingTxNoSpec) }, { NormalMapEffect_PSNormalPixelLightingTxNoFogSpec, sizeof(NormalMapEffect_PSNormalPixelLightingTxNoFogSpec) }, }; template<> const int EffectBase::PixelShaderIndices[] = { 0, // pixel lighting + texture 1, // pixel lighting + texture, no fog 0, // pixel lighting + texture + vertex color 1, // pixel lighting + texture + vertex color, no fog 2, // pixel lighting + texture, no specular 3, // pixel lighting + texture, no fog or specular 2, // pixel lighting + texture + vertex color, no specular 3, // pixel lighting + texture + vertex color, no fog or specular 0, // pixel lighting (biased vertex normal) + texture 1, // pixel lighting (biased vertex normal) + texture, no fog 0, // pixel lighting (biased vertex normal) + texture + vertex color 1, // pixel lighting (biased vertex normal) + texture + vertex color, no fog 2, // pixel lighting (biased vertex normal) + texture, no specular 3, // pixel lighting (biased vertex normal) + texture, no fog or specular 2, // pixel lighting (biased vertex normal) + texture + vertex color, no specular 3, // pixel lighting (biased vertex normal) + texture + vertex color, no fog or specular }; // Global pool of per-device NormalMapEffect resources. template<> SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; // Constructor. NormalMapEffect::Impl::Impl(_In_ ID3D11Device* device) : EffectBase(device), vertexColorEnabled(false), biasedVertexNormals(false) { if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) { throw std::exception("NormalMapEffect requires Feature Level 10.0 or later"); } static_assert(_countof(EffectBase::VertexShaderIndices) == NormalMapEffectTraits::ShaderPermutationCount, "array/max mismatch"); static_assert(_countof(EffectBase::VertexShaderBytecode) == NormalMapEffectTraits::VertexShaderCount, "array/max mismatch"); static_assert(_countof(EffectBase::PixelShaderBytecode) == NormalMapEffectTraits::PixelShaderCount, "array/max mismatch"); static_assert(_countof(EffectBase::PixelShaderIndices) == NormalMapEffectTraits::ShaderPermutationCount, "array/max mismatch"); lights.InitializeConstants(constants.specularColorAndPower, constants.lightDirection, constants.lightDiffuseColor, constants.lightSpecularColor); } int NormalMapEffect::Impl::GetCurrentShaderPermutation() const noexcept { int permutation = 0; // Use optimized shaders if fog is disabled. if (!fog.enabled) { permutation += 1; } // Support vertex coloring? if (vertexColorEnabled) { permutation += 2; } // Specular map? if (!specularTexture) { permutation += 4; } if (biasedVertexNormals) { // Compressed normals need to be scaled and biased in the vertex shader. permutation += 8; } return permutation; } // Sets our state onto the D3D device. void NormalMapEffect::Impl::Apply(_In_ ID3D11DeviceContext* deviceContext) { // Compute derived parameter values. matrices.SetConstants(dirtyFlags, constants.worldViewProj); fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector); lights.SetConstants(dirtyFlags, matrices, constants.world, constants.worldInverseTranspose, constants.eyePosition, constants.diffuseColor, constants.emissiveColor, true); // Set the textures ID3D11ShaderResourceView* textures[] = { texture.Get(), specularTexture.Get(), normalTexture.Get()}; deviceContext->PSSetShaderResources(0, _countof(textures), textures); // Set shaders and constant buffers. ApplyShaders(deviceContext, GetCurrentShaderPermutation()); } // Public constructor. NormalMapEffect::NormalMapEffect(_In_ ID3D11Device* device) : pImpl(std::make_unique(device)) { } // Move constructor. NormalMapEffect::NormalMapEffect(NormalMapEffect&& moveFrom) noexcept : pImpl(std::move(moveFrom.pImpl)) { } // Move assignment. NormalMapEffect& NormalMapEffect::operator= (NormalMapEffect&& moveFrom) noexcept { pImpl = std::move(moveFrom.pImpl); return *this; } // Public destructor. NormalMapEffect::~NormalMapEffect() { } // IEffect methods. void NormalMapEffect::Apply(_In_ ID3D11DeviceContext* deviceContext) { pImpl->Apply(deviceContext); } void NormalMapEffect::GetVertexShaderBytecode(_Out_ void const** pShaderByteCode, _Out_ size_t* pByteCodeLength) { pImpl->GetVertexShaderBytecode(pImpl->GetCurrentShaderPermutation(), pShaderByteCode, pByteCodeLength); } // Camera settings. void XM_CALLCONV NormalMapEffect::SetWorld(FXMMATRIX value) { pImpl->matrices.world = value; pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector; } void XM_CALLCONV NormalMapEffect::SetView(FXMMATRIX value) { pImpl->matrices.view = value; pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; } void XM_CALLCONV NormalMapEffect::SetProjection(FXMMATRIX value) { pImpl->matrices.projection = value; pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; } void XM_CALLCONV NormalMapEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) { pImpl->matrices.world = world; pImpl->matrices.view = view; pImpl->matrices.projection = projection; pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; } // Material settings. void XM_CALLCONV NormalMapEffect::SetDiffuseColor(FXMVECTOR value) { pImpl->lights.diffuseColor = value; pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; } void XM_CALLCONV NormalMapEffect::SetEmissiveColor(FXMVECTOR value) { pImpl->lights.emissiveColor = value; pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; } void XM_CALLCONV NormalMapEffect::SetSpecularColor(FXMVECTOR value) { // Set xyz to new value, but preserve existing w (specular power). pImpl->constants.specularColorAndPower = XMVectorSelect(pImpl->constants.specularColorAndPower, value, g_XMSelect1110); pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } void NormalMapEffect::SetSpecularPower(float value) { // Set w to new value, but preserve existing xyz (specular color). pImpl->constants.specularColorAndPower = XMVectorSetW(pImpl->constants.specularColorAndPower, value); pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } void NormalMapEffect::DisableSpecular() { // Set specular color to black, power to 1 // Note: Don't use a power of 0 or the shader will generate strange highlights on non-specular materials pImpl->constants.specularColorAndPower = g_XMIdentityR3; pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } void NormalMapEffect::SetAlpha(float value) { pImpl->lights.alpha = value; pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; } void XM_CALLCONV NormalMapEffect::SetColorAndAlpha(FXMVECTOR value) { pImpl->lights.diffuseColor = value; pImpl->lights.alpha = XMVectorGetW(value); pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; } // Light settings. void NormalMapEffect::SetLightingEnabled(bool value) { if (!value) { throw std::exception("NormalMapEffect does not support turning off lighting"); } } void NormalMapEffect::SetPerPixelLighting(bool) { // Unsupported interface method. } void XM_CALLCONV NormalMapEffect::SetAmbientLightColor(FXMVECTOR value) { pImpl->lights.ambientLightColor = value; pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; } void NormalMapEffect::SetLightEnabled(int whichLight, bool value) { pImpl->dirtyFlags |= pImpl->lights.SetLightEnabled(whichLight, value, pImpl->constants.lightDiffuseColor, pImpl->constants.lightSpecularColor); } void XM_CALLCONV NormalMapEffect::SetLightDirection(int whichLight, FXMVECTOR value) { EffectLights::ValidateLightIndex(whichLight); pImpl->constants.lightDirection[whichLight] = value; pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } void XM_CALLCONV NormalMapEffect::SetLightDiffuseColor(int whichLight, FXMVECTOR value) { pImpl->dirtyFlags |= pImpl->lights.SetLightDiffuseColor(whichLight, value, pImpl->constants.lightDiffuseColor); } void XM_CALLCONV NormalMapEffect::SetLightSpecularColor(int whichLight, FXMVECTOR value) { pImpl->dirtyFlags |= pImpl->lights.SetLightSpecularColor(whichLight, value, pImpl->constants.lightSpecularColor); } void NormalMapEffect::EnableDefaultLighting() { EffectLights::EnableDefaultLighting(this); } // Fog settings. void NormalMapEffect::SetFogEnabled(bool value) { pImpl->fog.enabled = value; pImpl->dirtyFlags |= EffectDirtyFlags::FogEnable; } void NormalMapEffect::SetFogStart(float value) { pImpl->fog.start = value; pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; } void NormalMapEffect::SetFogEnd(float value) { pImpl->fog.end = value; pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; } void XM_CALLCONV NormalMapEffect::SetFogColor(FXMVECTOR value) { pImpl->constants.fogColor = value; pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } // Vertex color setting. void NormalMapEffect::SetVertexColorEnabled(bool value) { pImpl->vertexColorEnabled = value; } // Texture settings. void NormalMapEffect::SetTexture(_In_opt_ ID3D11ShaderResourceView* value) { pImpl->texture = value; } void NormalMapEffect::SetNormalTexture(_In_opt_ ID3D11ShaderResourceView* value) { pImpl->normalTexture = value; } void NormalMapEffect::SetSpecularTexture(_In_opt_ ID3D11ShaderResourceView* value) { pImpl->specularTexture = value; } // Normal compression settings. void NormalMapEffect::SetBiasedVertexNormals(bool value) { pImpl->biasedVertexNormals = value; }