From dc41d749fef863e4ac65b6471bdce10046ad5639 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 11:58:51 -0700 Subject: [PATCH 1/6] Add SkinnedNPREffect --- Inc/Effects.h | 41 +++++- Src/NPREffect.cpp | 221 +++++++++++++++++++++++++++++---- Src/Shaders/CompileShaders.cmd | 3 + Src/Shaders/NPREffect.fx | 29 +++++ 4 files changed, 266 insertions(+), 28 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index be5eb548..d5d3deea 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -578,13 +578,14 @@ namespace DirectX static constexpr int MaxTextures = 8; protected: - // Private implementation. + // Internal implementation. class Impl; std::unique_ptr pImpl; DIRECTX_TOOLKIT_API DGSLEffect(_In_ ID3D11Device* device, _In_opt_ ID3D11PixelShader* pixelShader, bool skinningEnabled); + private: // Unsupported interface methods. DIRECTX_TOOLKIT_API void __cdecl SetPerPixelLighting(bool value) override; }; @@ -680,13 +681,14 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl SetInstancingEnabled(bool value); protected: - // Private implementation. + // Internal implementation. class Impl; std::unique_ptr pImpl; DIRECTX_TOOLKIT_API NormalMapEffect(_In_ ID3D11Device* device, bool skinningEnabled); + private: // Unsupported interface methods. DIRECTX_TOOLKIT_API void __cdecl SetLightingEnabled(bool value) override; DIRECTX_TOOLKIT_API void __cdecl SetPerPixelLighting(bool value) override; @@ -786,13 +788,14 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl SetRenderTargetSizeInPixels(int width, int height); protected: - // Private implementation. + // Internal implementation. class Impl; std::unique_ptr pImpl; DIRECTX_TOOLKIT_API PBREffect(_In_ ID3D11Device* device, bool skinningEnabled); + private: // Unsupported interface methods. DIRECTX_TOOLKIT_API void __cdecl SetLightingEnabled(bool value) override; DIRECTX_TOOLKIT_API void __cdecl SetPerPixelLighting(bool value) override; @@ -890,7 +893,9 @@ namespace DirectX Mode_MatCap, // Material Capture shading }; - DIRECTX_TOOLKIT_API explicit NPREffect(_In_ ID3D11Device* device); + DIRECTX_TOOLKIT_API inline explicit NPREffect(_In_ ID3D11Device* device) : + NPREffect(device, false) + {} DIRECTX_TOOLKIT_API NPREffect(NPREffect&&) noexcept; DIRECTX_TOOLKIT_API NPREffect& operator= (NPREffect&&) noexcept; @@ -960,12 +965,15 @@ namespace DirectX // Instancing settings. DIRECTX_TOOLKIT_API void __cdecl SetInstancingEnabled(bool value); - private: - // Private implementation. + protected: + // Internal implementation. class Impl; std::unique_ptr pImpl; + DIRECTX_TOOLKIT_API NPREffect(_In_ ID3D11Device* device, bool skinningEnabled); + + private: // Unsupported interface methods. DIRECTX_TOOLKIT_API void __cdecl SetLightingEnabled(bool value) override; DIRECTX_TOOLKIT_API void __cdecl SetPerPixelLighting(bool value) override; @@ -975,6 +983,27 @@ namespace DirectX DIRECTX_TOOLKIT_API void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; }; + class DIRECTX_TOOLKIT_API SkinnedNPREffect : public NPREffect, public IEffectSkinning + { + public: + explicit SkinnedNPREffect(_In_ ID3D11Device* device) : + NPREffect(device, true) + {} + + SkinnedNPREffect(SkinnedNPREffect&&) = default; + SkinnedNPREffect& operator= (SkinnedNPREffect&&) = default; + + SkinnedNPREffect(SkinnedNPREffect const&) = delete; + SkinnedNPREffect& operator= (SkinnedNPREffect const&) = delete; + + ~SkinnedNPREffect() override; + + // Animation settings. + void __cdecl SetWeightsPerVertex(int value) override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; + }; + //------------------------------------------------------------------------------ // Abstract interface to factory for sharing effects and texture resources class DIRECTX_TOOLKIT_API IEffectFactory diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index 10ba87aa..c32fdde8 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -10,6 +10,14 @@ #include "pch.h" #include "EffectCommon.h" +namespace DirectX +{ + namespace EffectDirtyFlags + { + constexpr int ConstantBufferBones = 0x100000; + } +} + using namespace DirectX; namespace @@ -32,15 +40,21 @@ namespace static_assert((sizeof(NPREffectConstants) % 16) == 0, "CB size not padded correctly"); + XM_ALIGNED_STRUCT(16) BoneConstants + { + XMVECTOR Bones[SkinnedNPREffect::MaxBones][3]; + }; + + static_assert((sizeof(BoneConstants) % 16) == 0, "CB size not padded correctly"); // Traits type describes our characteristics to the EffectBase template. struct NPREffectTraits { using ConstantBufferType = NPREffectConstants; - static constexpr int VertexShaderCount = 16; + static constexpr int VertexShaderCount = 18; static constexpr int PixelShaderCount = 6; - static constexpr int ShaderPermutationCount = 48; + static constexpr int ShaderPermutationCount = 54; static constexpr int ModeCount = 3; }; @@ -68,10 +82,14 @@ class NPREffect::Impl : public EffectBase Impl(Impl&&) = default; Impl& operator=(Impl&&) = default; + void Initialize(_In_ ID3D11Device* device, bool enableSkinning); + bool vertexColorEnabled; bool biasedVertexNormals; bool instancing; bool textureEnabled; + int weightsPerVertex; + NPREffect::Mode nprMode; Microsoft::WRL::ComPtr texture; Microsoft::WRL::ComPtr matcap; @@ -79,6 +97,11 @@ class NPREffect::Impl : public EffectBase int GetCurrentShaderPermutation() const noexcept; void Apply(_In_ ID3D11DeviceContext* deviceContext); + + BoneConstants boneConstants; + +private: + ConstantBuffer mBones; }; @@ -111,6 +134,9 @@ namespace #include "XboxOneNPREffect_VSNPREffectVcBnTx.inc" #include "XboxOneNPREffect_VSNPREffectVcBnInstTx.inc" +#include "XboxOneNPREffect_VSSkinnedNPREffectTx.inc" +#include "XboxOneNPREffect_VSSkinnedNPREffectTxBn.inc" + #include "XboxOneNPREffect_PSCelShading.inc" #include "XboxOneNPREffect_PSCelShadingTx.inc" @@ -144,6 +170,9 @@ namespace #include "NPREffect_VSNPREffectVcBnTx.inc" #include "NPREffect_VSNPREffectVcBnInstTx.inc" +#include "NPREffect_VSSkinnedNPREffectTx.inc" +#include "NPREffect_VSSkinnedNPREffectTxBn.inc" + #include "NPREffect_PSCelShading.inc" #include "NPREffect_PSCelShadingTx.inc" @@ -159,22 +188,24 @@ namespace template<> const ShaderBytecode EffectBase::VertexShaderBytecode[] = { - { NPREffect_VSNPREffect, sizeof(NPREffect_VSNPREffect) }, - { NPREffect_VSNPREffectVc, sizeof(NPREffect_VSNPREffectVc) }, - { NPREffect_VSNPREffectBn, sizeof(NPREffect_VSNPREffectBn) }, - { NPREffect_VSNPREffectVcBn, sizeof(NPREffect_VSNPREffectVcBn) }, - { NPREffect_VSNPREffectInst, sizeof(NPREffect_VSNPREffectInst) }, - { NPREffect_VSNPREffectVcInst, sizeof(NPREffect_VSNPREffectVcInst) }, - { NPREffect_VSNPREffectBnInst, sizeof(NPREffect_VSNPREffectBnInst) }, - { NPREffect_VSNPREffectVcBnInst, sizeof(NPREffect_VSNPREffectVcBnInst) }, - { NPREffect_VSNPREffectTx, sizeof(NPREffect_VSNPREffectTx) }, - { NPREffect_VSNPREffectVcTx, sizeof(NPREffect_VSNPREffectVcTx) }, - { NPREffect_VSNPREffectBnTx, sizeof(NPREffect_VSNPREffectBnTx) }, - { NPREffect_VSNPREffectVcBnTx, sizeof(NPREffect_VSNPREffectVcBnTx) }, - { NPREffect_VSNPREffectInstTx, sizeof(NPREffect_VSNPREffectInstTx) }, - { NPREffect_VSNPREffectVcInstTx, sizeof(NPREffect_VSNPREffectVcInstTx) }, - { NPREffect_VSNPREffectBnInstTx, sizeof(NPREffect_VSNPREffectBnInstTx) }, - { NPREffect_VSNPREffectVcBnInstTx, sizeof(NPREffect_VSNPREffectVcBnInstTx) }, + { NPREffect_VSNPREffect, sizeof(NPREffect_VSNPREffect) }, + { NPREffect_VSNPREffectVc, sizeof(NPREffect_VSNPREffectVc) }, + { NPREffect_VSNPREffectBn, sizeof(NPREffect_VSNPREffectBn) }, + { NPREffect_VSNPREffectVcBn, sizeof(NPREffect_VSNPREffectVcBn) }, + { NPREffect_VSNPREffectInst, sizeof(NPREffect_VSNPREffectInst) }, + { NPREffect_VSNPREffectVcInst, sizeof(NPREffect_VSNPREffectVcInst) }, + { NPREffect_VSNPREffectBnInst, sizeof(NPREffect_VSNPREffectBnInst) }, + { NPREffect_VSNPREffectVcBnInst, sizeof(NPREffect_VSNPREffectVcBnInst) }, + { NPREffect_VSNPREffectTx, sizeof(NPREffect_VSNPREffectTx) }, + { NPREffect_VSNPREffectVcTx, sizeof(NPREffect_VSNPREffectVcTx) }, + { NPREffect_VSNPREffectBnTx, sizeof(NPREffect_VSNPREffectBnTx) }, + { NPREffect_VSNPREffectVcBnTx, sizeof(NPREffect_VSNPREffectVcBnTx) }, + { NPREffect_VSNPREffectInstTx, sizeof(NPREffect_VSNPREffectInstTx) }, + { NPREffect_VSNPREffectVcInstTx, sizeof(NPREffect_VSNPREffectVcInstTx) }, + { NPREffect_VSNPREffectBnInstTx, sizeof(NPREffect_VSNPREffectBnInstTx) }, + { NPREffect_VSNPREffectVcBnInstTx, sizeof(NPREffect_VSNPREffectVcBnInstTx) }, + { NPREffect_VSSkinnedNPREffectTx, sizeof(NPREffect_VSSkinnedNPREffectTx) }, + { NPREffect_VSSkinnedNPREffectTxBn, sizeof(NPREffect_VSSkinnedNPREffectTxBn) }, }; @@ -244,6 +275,14 @@ const int EffectBase::VertexShaderIndices[] = 15, // instancing + vertex color (biased vertex normal) + cel shading + texture 15, // instancing + vertex color (biased vertex normal) + gooch shading + texture 15, // instancing + vertex color (biased vertex normal) + matcap shading + texture + + 16, // skinning + cel shading + 16, // skinning + gooch shading + 16, // skinning + matcap shading + + 17, // skinning (biased vertex normal) + cel shading + 17, // skinning (biased vertex normal) + gooch shading + 17, // skinning (biased vertex normal) + matcap shading }; @@ -325,6 +364,14 @@ const int EffectBase::PixelShaderIndices[] = 3, // instancing + vertex color (biased vertex normal) + cel shading + texture 4, // instancing + vertex color (biased vertex normal) + gooch shading + texture 5, // instancing + vertex color (biased vertex normal) + matcap shading + texture + + 0, // skinning + cel shading + 1, // skinning + gooch shading + 2, // skinning + matcap shading + + 3, // skinning (biased vertex normal) + cel shading + 4, // skinning (biased vertex normal) + gooch shading + 5, // skinning (biased vertex normal) + matcap shading }; #pragma endregion @@ -340,13 +387,17 @@ NPREffect::Impl::Impl(_In_ ID3D11Device* device) biasedVertexNormals(false), instancing(false), textureEnabled(false), + weightsPerVertex(0), nprMode(NPREffect::Mode_Cel) { static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == NPREffectTraits::ShaderPermutationCount, "array/max mismatch"); static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == NPREffectTraits::VertexShaderCount, "array/max mismatch"); static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == NPREffectTraits::PixelShaderCount, "array/max mismatch"); static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == NPREffectTraits::ShaderPermutationCount, "array/max mismatch"); +} +void NPREffect::Impl::Initialize(_In_ ID3D11Device* device, bool enableSkinning) +{ constants.lightDirectionAndCelBands = s_defaultLightDir; constants.diffuseColorAndAlpha = s_defaultDiffuse; constants.specularColorAndSpecularThreshold = s_defaultSpecular; @@ -355,13 +406,40 @@ NPREffect::Impl::Impl(_In_ ID3D11Device* device) constants.goochCoolColorAndAlpha = s_defaultCool; constants.goochWarmColorAndBeta = s_defaultWarm; constants.eyePosition = g_XMZero; -} + if (enableSkinning) + { + weightsPerVertex = 4; + + mBones.Create(device); + + for (size_t j = 0; j < SkinnedNPREffect::MaxBones; ++j) + { + boneConstants.Bones[j][0] = g_XMIdentityR0; + boneConstants.Bones[j][1] = g_XMIdentityR1; + boneConstants.Bones[j][2] = g_XMIdentityR2; + } + } +} int NPREffect::Impl::GetCurrentShaderPermutation() const noexcept { int permutation = static_cast(nprMode); + if (weightsPerVertex > 0) + { + if (biasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 3; + } + + // Vertex skinning does not support vertex colors or instancing, always includes a texture. + permutation += 48; + + return permutation; + } + // Support vertex coloring? if (vertexColorEnabled) { @@ -403,6 +481,28 @@ void NPREffect::Impl::Apply(_In_ ID3D11DeviceContext* deviceContext) constants.worldViewProj, constants.eyePosition); + if (weightsPerVertex > 0) + { + #if defined(_XBOX_ONE) && defined(_TITLE) + void* grfxMemoryBone; + mBones.SetData(deviceContext, boneConstants, &grfxMemoryBone); + + ComPtr deviceContextX; + ThrowIfFailed(deviceContext->QueryInterface(IID_GRAPHICS_PPV_ARGS(deviceContextX.GetAddressOf()))); + + deviceContextX->VSSetPlacementConstantBuffer(1, mBones.GetBuffer(), grfxMemoryBone); + #else + if (dirtyFlags & EffectDirtyFlags::ConstantBufferBones) + { + mBones.SetData(deviceContext, boneConstants); + dirtyFlags &= ~EffectDirtyFlags::ConstantBufferBones; + } + + ID3D11Buffer* buffer = mBones.GetBuffer(); + deviceContext->VSSetConstantBuffers(1, 1, &buffer); + #endif + } + // Set texture. switch (nprMode) { @@ -434,10 +534,11 @@ void NPREffect::Impl::Apply(_In_ ID3D11DeviceContext* deviceContext) // Public constructor. -NPREffect::NPREffect(_In_ ID3D11Device* device) +NPREffect::NPREffect(_In_ ID3D11Device* device, bool skinningEnabled) : pImpl(std::make_unique(device)) -{} - +{ + pImpl->Initialize(device, skinningEnabled); +} NPREffect::NPREffect(NPREffect&&) noexcept = default; NPREffect& NPREffect::operator= (NPREffect&&) noexcept = default; @@ -623,6 +724,11 @@ void NPREffect::SetColorAndAlpha(FXMVECTOR value) // Texture settings. void NPREffect::SetTextureEnabled(bool value) { + if (!value && (pImpl->weightsPerVertex > 0)) + { + throw std::invalid_argument("Texturing is alaways enabled for SkinnedNPREffect"); + } + pImpl->textureEnabled = value; } @@ -742,6 +848,11 @@ void NPREffect::DisableRimLighting() // Vertex color setting. void NPREffect::SetVertexColorEnabled(bool value) { + if (value && (pImpl->weightsPerVertex > 0)) + { + throw std::invalid_argument("Per-vertex color is not supported for SkinnedNPREffect"); + } + pImpl->vertexColorEnabled = value; } @@ -756,5 +867,71 @@ void NPREffect::SetBiasedVertexNormals(bool value) // Instancing settings. void NPREffect::SetInstancingEnabled(bool value) { + if (value && (pImpl->weightsPerVertex > 0)) + { + throw std::invalid_argument("Instancing is not supported for SkinnedNPREffect"); + } + pImpl->instancing = value; } + + +//-------------------------------------------------------------------------------------- +// SkinnedNPREffect +//-------------------------------------------------------------------------------------- + +SkinnedNPREffect::~SkinnedNPREffect() +{} + +// Animation settings. +void SkinnedNPREffect::SetWeightsPerVertex(int value) +{ + if ((value != 1) && + (value != 2) && + (value != 4)) + { + throw std::invalid_argument("WeightsPerVertex must be 1, 2, or 4"); + } + + pImpl->weightsPerVertex = value; +} + + +void SkinnedNPREffect::SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) +{ + if (count > MaxBones) + throw std::invalid_argument("count parameter exceeds MaxBones"); + + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < count; i++) + { + #if DIRECTX_MATH_VERSION >= 313 + XMStoreFloat3x4A(reinterpret_cast(&boneConstant[i]), value[i]); + #else + // Xbox One XDK has an older version of DirectXMath + XMMATRIX boneMatrix = XMMatrixTranspose(value[i]); + + boneConstant[i][0] = boneMatrix.r[0]; + boneConstant[i][1] = boneMatrix.r[1]; + boneConstant[i][2] = boneMatrix.r[2]; + #endif + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} + + +void SkinnedNPREffect::ResetBoneTransforms() +{ + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < MaxBones; ++i) + { + boneConstant[i][0] = g_XMIdentityR0; + boneConstant[i][1] = g_XMIdentityR1; + boneConstant[i][2] = g_XMIdentityR2; + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} diff --git a/Src/Shaders/CompileShaders.cmd b/Src/Shaders/CompileShaders.cmd index 4ecfff9f..d996659a 100644 --- a/Src/Shaders/CompileShaders.cmd +++ b/Src/Shaders/CompileShaders.cmd @@ -246,6 +246,9 @@ call :CompileShader%1 NPREffect vs VSNPREffectBnInstTx call :CompileShader%1 NPREffect vs VSNPREffectVcInstTx call :CompileShader%1 NPREffect vs VSNPREffectVcBnInstTx +call :CompileShader%1 NPREffect vs VSSkinnedNPREffectTx +call :CompileShader%1 NPREffect vs VSSkinnedNPREffectTxBn + call :CompileShader%1 NPREffect ps PSCelShading call :CompileShader%1 NPREffect ps PSCelShadingTx diff --git a/Src/Shaders/NPREffect.fx b/Src/Shaders/NPREffect.fx index 3f668f7d..93d001ce 100644 --- a/Src/Shaders/NPREffect.fx +++ b/Src/Shaders/NPREffect.fx @@ -41,9 +41,16 @@ cbuffer Parameters : register(b0) float4x4 WorldViewProj : packoffset(c15); }; +cbuffer SkinningParameters : register(b1) +{ + float4x3 Bones[72]; +} + #include "Structures.fxh" #include "Utilities.fxh" +#include "Skinning.fxh" + VSOutputPixelLighting ComputeCommonVS(float4 position, float3 normal) { @@ -224,6 +231,28 @@ VSOutputPixelLightingTx VSNPREffectVcBnInstTx(VSInputNmTxVcInst vin) } +// Vertex shader: skinning (four bones) + texture. +VSOutputPixelLightingTx VSSkinnedNPREffectTx(VSInputNmTxWeights vin) +{ + float3 normal = Skin(vin, vin.Normal, 4); + + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord); + vout.Diffuse = float4(DiffuseColor, Alpha); + return vout; +} + +VSOutputPixelLightingTx VSSkinnedNPREffectTxBn(VSInputNmTxWeights vin) +{ + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 4); + + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord); + vout.Diffuse = float4(DiffuseColor, Alpha); + return vout; +} + + //--- Cel shading --- float Quantize(float intensity, float bands) { From 2e3f747e066a7c0e61e4b54573cf5bd4738cc348 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 12:12:18 -0700 Subject: [PATCH 2/6] Code review --- Inc/Effects.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index d5d3deea..30bcd737 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -605,10 +605,10 @@ namespace DirectX ~SkinnedDGSLEffect() override; - // Animation setting. - void __cdecl SetWeightsPerVertex(int value) override; - void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - void __cdecl ResetBoneTransforms() override; + // Animation settings. + DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; + DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ @@ -710,9 +710,9 @@ namespace DirectX ~SkinnedNormalMapEffect() override; // Animation settings. - void __cdecl SetWeightsPerVertex(int value) override; - void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - void __cdecl ResetBoneTransforms() override; + DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; + DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ @@ -819,9 +819,9 @@ namespace DirectX ~SkinnedPBREffect() override; // Animation settings. - void __cdecl SetWeightsPerVertex(int value) override; - void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - void __cdecl ResetBoneTransforms() override; + DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; + DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ @@ -999,9 +999,9 @@ namespace DirectX ~SkinnedNPREffect() override; // Animation settings. - void __cdecl SetWeightsPerVertex(int value) override; - void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - void __cdecl ResetBoneTransforms() override; + DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; + DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ From 31ef4007b59fe97587031cca9d7682574a15e1a2 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 12:45:57 -0700 Subject: [PATCH 3/6] Unbreak --- Inc/Effects.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 30bcd737..cd98d4c3 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -606,9 +606,9 @@ namespace DirectX ~SkinnedDGSLEffect() override; // Animation settings. - DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; - DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; + void __cdecl SetWeightsPerVertex(int value) override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ @@ -710,9 +710,9 @@ namespace DirectX ~SkinnedNormalMapEffect() override; // Animation settings. - DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; - DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; + void __cdecl SetWeightsPerVertex(int value) override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ @@ -819,9 +819,9 @@ namespace DirectX ~SkinnedPBREffect() override; // Animation settings. - DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; - DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; + void __cdecl SetWeightsPerVertex(int value) override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ @@ -999,9 +999,9 @@ namespace DirectX ~SkinnedNPREffect() override; // Animation settings. - DIRECTX_TOOLKIT_API void __cdecl SetWeightsPerVertex(int value) override; - DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; + void __cdecl SetWeightsPerVertex(int value) override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; }; //------------------------------------------------------------------------------ From 3f7db2134ebbb26e562e660f24d296a59463c0d3 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Tue, 21 Jul 2026 10:55:46 -0700 Subject: [PATCH 4/6] Fix shader mapping --- Src/NPREffect.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index c32fdde8..d0f35293 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -365,9 +365,9 @@ const int EffectBase::PixelShaderIndices[] = 4, // instancing + vertex color (biased vertex normal) + gooch shading + texture 5, // instancing + vertex color (biased vertex normal) + matcap shading + texture - 0, // skinning + cel shading - 1, // skinning + gooch shading - 2, // skinning + matcap shading + 3, // skinning + cel shading + 4, // skinning + gooch shading + 5, // skinning + matcap shading 3, // skinning (biased vertex normal) + cel shading 4, // skinning (biased vertex normal) + gooch shading @@ -409,6 +409,7 @@ void NPREffect::Impl::Initialize(_In_ ID3D11Device* device, bool enableSkinning) if (enableSkinning) { + textureEnabled = true; weightsPerVertex = 4; mBones.Create(device); From 38181e6e76938b9c5976114a82a0c7bcdaf9b791 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Wed, 22 Jul 2026 10:50:44 -0700 Subject: [PATCH 5/6] Add NPREffectFactory --- CMakeLists.txt | 1 + DirectXTK_Desktop_2022.vcxproj | 1 + DirectXTK_Desktop_2022.vcxproj.filters | 3 + DirectXTK_Desktop_2022_Win10.vcxproj | 1 + DirectXTK_Desktop_2022_Win10.vcxproj.filters | 3 + DirectXTK_Desktop_2026.slnx | 6 +- DirectXTK_Desktop_2026.vcxproj | 1 + DirectXTK_Desktop_2026.vcxproj.filters | 3 + DirectXTK_GDKW_2022.vcxproj | 1 + DirectXTK_GDKW_2022.vcxproj.filters | 3 + DirectXTK_GDK_2022.vcxproj | 1 + DirectXTK_GDK_2022.vcxproj.filters | 3 + DirectXTK_Windows10_2022.vcxproj | 1 + DirectXTK_Windows10_2022.vcxproj.filters | 3 + Inc/Effects.h | 49 ++ Src/DGSLEffectFactory.cpp | 12 +- Src/EffectFactory.cpp | 17 +- Src/NPREffect.cpp | 4 +- Src/NPREffectFactory.cpp | 455 +++++++++++++++++++ Src/PBREffectFactory.cpp | 12 +- 20 files changed, 551 insertions(+), 29 deletions(-) create mode 100644 Src/NPREffectFactory.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1da832dd..fdb4dd48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,7 @@ set(LIBRARY_SOURCES Src/DebugEffect.cpp Src/DGSLEffect.cpp Src/NPREffect.cpp + Src/NPREffectFactory.cpp Src/DGSLEffectFactory.cpp Src/DirectXHelpers.cpp Src/DualPostProcess.cpp diff --git a/DirectXTK_Desktop_2022.vcxproj b/DirectXTK_Desktop_2022.vcxproj index d66aefc4..d6b66316 100644 --- a/DirectXTK_Desktop_2022.vcxproj +++ b/DirectXTK_Desktop_2022.vcxproj @@ -96,6 +96,7 @@ + diff --git a/DirectXTK_Desktop_2022.vcxproj.filters b/DirectXTK_Desktop_2022.vcxproj.filters index 05534581..6c97be34 100644 --- a/DirectXTK_Desktop_2022.vcxproj.filters +++ b/DirectXTK_Desktop_2022.vcxproj.filters @@ -287,6 +287,9 @@ Audio + + Src + diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj b/DirectXTK_Desktop_2022_Win10.vcxproj index 0f6540d6..280e4d7f 100644 --- a/DirectXTK_Desktop_2022_Win10.vcxproj +++ b/DirectXTK_Desktop_2022_Win10.vcxproj @@ -104,6 +104,7 @@ + diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj.filters b/DirectXTK_Desktop_2022_Win10.vcxproj.filters index cfea2c70..84b887f8 100644 --- a/DirectXTK_Desktop_2022_Win10.vcxproj.filters +++ b/DirectXTK_Desktop_2022_Win10.vcxproj.filters @@ -287,6 +287,9 @@ Src + + Src + diff --git a/DirectXTK_Desktop_2026.slnx b/DirectXTK_Desktop_2026.slnx index 18982815..4c6ec620 100644 --- a/DirectXTK_Desktop_2026.slnx +++ b/DirectXTK_Desktop_2026.slnx @@ -8,11 +8,11 @@ - + - - + + diff --git a/DirectXTK_Desktop_2026.vcxproj b/DirectXTK_Desktop_2026.vcxproj index 5418c4d3..afb6b9e8 100644 --- a/DirectXTK_Desktop_2026.vcxproj +++ b/DirectXTK_Desktop_2026.vcxproj @@ -104,6 +104,7 @@ + diff --git a/DirectXTK_Desktop_2026.vcxproj.filters b/DirectXTK_Desktop_2026.vcxproj.filters index cfea2c70..84b887f8 100644 --- a/DirectXTK_Desktop_2026.vcxproj.filters +++ b/DirectXTK_Desktop_2026.vcxproj.filters @@ -287,6 +287,9 @@ Src + + Src + diff --git a/DirectXTK_GDKW_2022.vcxproj b/DirectXTK_GDKW_2022.vcxproj index f309eeb1..8417d997 100644 --- a/DirectXTK_GDKW_2022.vcxproj +++ b/DirectXTK_GDKW_2022.vcxproj @@ -424,6 +424,7 @@ + diff --git a/DirectXTK_GDKW_2022.vcxproj.filters b/DirectXTK_GDKW_2022.vcxproj.filters index 511fb417..29fc5ea8 100644 --- a/DirectXTK_GDKW_2022.vcxproj.filters +++ b/DirectXTK_GDKW_2022.vcxproj.filters @@ -289,6 +289,9 @@ Audio + + Src + diff --git a/DirectXTK_GDK_2022.vcxproj b/DirectXTK_GDK_2022.vcxproj index d4f512a9..a6bfcd16 100644 --- a/DirectXTK_GDK_2022.vcxproj +++ b/DirectXTK_GDK_2022.vcxproj @@ -249,6 +249,7 @@ + diff --git a/DirectXTK_GDK_2022.vcxproj.filters b/DirectXTK_GDK_2022.vcxproj.filters index 511fb417..29fc5ea8 100644 --- a/DirectXTK_GDK_2022.vcxproj.filters +++ b/DirectXTK_GDK_2022.vcxproj.filters @@ -289,6 +289,9 @@ Audio + + Src + diff --git a/DirectXTK_Windows10_2022.vcxproj b/DirectXTK_Windows10_2022.vcxproj index 25ee0c87..d6e1e3fa 100644 --- a/DirectXTK_Windows10_2022.vcxproj +++ b/DirectXTK_Windows10_2022.vcxproj @@ -116,6 +116,7 @@ + diff --git a/DirectXTK_Windows10_2022.vcxproj.filters b/DirectXTK_Windows10_2022.vcxproj.filters index 354afcf7..bb1ed59a 100644 --- a/DirectXTK_Windows10_2022.vcxproj.filters +++ b/DirectXTK_Windows10_2022.vcxproj.filters @@ -365,5 +365,8 @@ Src + + Src + \ No newline at end of file diff --git a/Inc/Effects.h b/Inc/Effects.h index cd98d4c3..8ec87c29 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -1156,6 +1156,55 @@ namespace DirectX }; + // Factory for Non-Photorealistic Rendering (NPR) + class NPREffectFactory : public IEffectFactory + { + public: + DIRECTX_TOOLKIT_API explicit NPREffectFactory(_In_ ID3D11Device* device); + + DIRECTX_TOOLKIT_API NPREffectFactory(NPREffectFactory&&) noexcept; + DIRECTX_TOOLKIT_API NPREffectFactory& operator= (NPREffectFactory&&) noexcept; + + NPREffectFactory(NPREffectFactory const&) = delete; + NPREffectFactory& operator= (NPREffectFactory const&) = delete; + + DIRECTX_TOOLKIT_API ~NPREffectFactory() override; + + // IEffectFactory methods. + DIRECTX_TOOLKIT_API std::shared_ptr __cdecl CreateEffect( + _In_ const EffectInfo& info, + _In_opt_ ID3D11DeviceContext* deviceContext) override; + DIRECTX_TOOLKIT_API void __cdecl CreateTexture( + _In_z_ const wchar_t* name, + _In_opt_ ID3D11DeviceContext* deviceContext, + _Outptr_ ID3D11ShaderResourceView** textureView) override; + + // Settings. + DIRECTX_TOOLKIT_API void __cdecl ReleaseCache(); + + DIRECTX_TOOLKIT_API void __cdecl SetSharing(bool enabled) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl EnableForceSRGB(bool forceSRGB) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl SetDirectory(_In_opt_z_ const wchar_t* path) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl SetMode(NPREffect::Mode mode) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl SetDefaultMatCap(_In_opt_ ID3D11ShaderResourceView* textureView) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl SetEmissiveAsMatCap(bool value) noexcept; + + // Properties. + DIRECTX_TOOLKIT_API ID3D11Device* GetDevice() const noexcept; + + private: + // Private implementation. + class Impl; + + std::shared_ptr pImpl; + }; + + // Factory for sharing Visual Studio Shader Designer (DGSL) shaders and texture resources class DGSLEffectFactory : public IEffectFactory { diff --git a/Src/DGSLEffectFactory.cpp b/Src/DGSLEffectFactory.cpp index 49fa9c8c..ce242a9d 100644 --- a/Src/DGSLEffectFactory.cpp +++ b/Src/DGSLEffectFactory.cpp @@ -51,8 +51,6 @@ class DGSLEffectFactory::Impl void CreatePixelShader(_In_z_ const wchar_t* shader, _Outptr_ ID3D11PixelShader** pixelShader); void ReleaseCache(); - void SetSharing(bool enabled) noexcept { mSharing = enabled; } - void EnableForceSRGB(bool forceSRGB) noexcept { mForceSRGB = forceSRGB; } static SharedResourcePool instancePool; @@ -60,6 +58,9 @@ class DGSLEffectFactory::Impl ComPtr mDevice; + bool mSharing; + bool mForceSRGB; + private: using EffectCache = std::map< std::wstring, std::shared_ptr >; using TextureCache = std::map< std::wstring, ComPtr >; @@ -70,9 +71,6 @@ class DGSLEffectFactory::Impl TextureCache mTextureCache; ShaderCache mShaderCache; - bool mSharing; - bool mForceSRGB; - std::mutex mutex; }; @@ -590,12 +588,12 @@ void DGSLEffectFactory::ReleaseCache() void DGSLEffectFactory::SetSharing(bool enabled) noexcept { - pImpl->SetSharing(enabled); + pImpl->mSharing = enabled; } void DGSLEffectFactory::EnableForceSRGB(bool forceSRGB) noexcept { - pImpl->EnableForceSRGB(forceSRGB); + pImpl->mForceSRGB = forceSRGB; } void DGSLEffectFactory::SetDirectory(_In_opt_z_ const wchar_t* path) noexcept diff --git a/Src/EffectFactory.cpp b/Src/EffectFactory.cpp index 3abef803..1099c53c 100644 --- a/Src/EffectFactory.cpp +++ b/Src/EffectFactory.cpp @@ -87,9 +87,6 @@ class EffectFactory::Impl void CreateTexture(_In_z_ const wchar_t* texture, _In_opt_ ID3D11DeviceContext* deviceContext, _Outptr_ ID3D11ShaderResourceView** textureView); void ReleaseCache(); - void SetSharing(bool enabled) noexcept { mSharing = enabled; } - void EnableNormalMapEffect(bool enabled) noexcept { mUseNormalMapEffect = enabled; } - void EnableForceSRGB(bool forceSRGB) noexcept { mForceSRGB = forceSRGB; } static SharedResourcePool instancePool; @@ -97,6 +94,10 @@ class EffectFactory::Impl ComPtr mDevice; + bool mSharing; + bool mUseNormalMapEffect; + bool mForceSRGB; + private: using EffectCache = std::map< std::wstring, std::shared_ptr >; using TextureCache = std::map< std::wstring, ComPtr >; @@ -108,10 +109,6 @@ class EffectFactory::Impl EffectCache mEffectNormalMapSkinned; TextureCache mTextureCache; - bool mSharing; - bool mUseNormalMapEffect; - bool mForceSRGB; - std::mutex mutex; }; @@ -513,17 +510,17 @@ void EffectFactory::ReleaseCache() void EffectFactory::SetSharing(bool enabled) noexcept { - pImpl->SetSharing(enabled); + pImpl->mSharing = enabled; } void EffectFactory::EnableNormalMapEffect(bool enabled) noexcept { - pImpl->EnableNormalMapEffect(enabled); + pImpl->mUseNormalMapEffect = enabled; } void EffectFactory::EnableForceSRGB(bool forceSRGB) noexcept { - pImpl->EnableForceSRGB(forceSRGB); + pImpl->mForceSRGB = forceSRGB; } void EffectFactory::SetDirectory(_In_opt_z_ const wchar_t* path) noexcept diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index d0f35293..4f0f288d 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -510,7 +510,7 @@ void NPREffect::Impl::Apply(_In_ ID3D11DeviceContext* deviceContext) case Mode_MatCap: if (textureEnabled) { - ID3D11ShaderResourceView* srvs[] = { texture.Get(), matcap.Get() }; + ID3D11ShaderResourceView* srvs[] = { (texture) ? texture.Get() : GetDefaultTexture(), matcap.Get() }; deviceContext->PSSetShaderResources(0, 2, srvs); } else @@ -523,7 +523,7 @@ void NPREffect::Impl::Apply(_In_ ID3D11DeviceContext* deviceContext) default: if (textureEnabled) { - ID3D11ShaderResourceView* srv = texture.Get(); + ID3D11ShaderResourceView* srv = (texture) ? texture.Get() : GetDefaultTexture(); deviceContext->PSSetShaderResources(0, 1, &srv); } break; diff --git a/Src/NPREffectFactory.cpp b/Src/NPREffectFactory.cpp new file mode 100644 index 00000000..95139786 --- /dev/null +++ b/Src/NPREffectFactory.cpp @@ -0,0 +1,455 @@ +//-------------------------------------------------------------------------------------- +// File: NPREffectFactory.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// https://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Effects.h" +#include "DemandCreate.h" +#include "SharedResourcePool.h" + +#include "DDSTextureLoader.h" +#include "WICTextureLoader.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + template + void SetMaterialProperties(_In_ T* effect, const NPREffectFactory::EffectInfo& info, NPREffect::Mode mode) + { + effect->SetMode(mode); + + effect->EnableDefaultLighting(); + + effect->SetAlpha(info.alpha); + + // Most DirectX Tool Kit effects do not have an ambient material color. + + XMVECTOR color = XMLoadFloat3(&info.diffuseColor); + effect->SetDiffuseColor(color); + + if (info.specularColor.x != 0 || info.specularColor.y != 0 || info.specularColor.z != 0) + { + color = XMLoadFloat3(&info.specularColor); + effect->SetSpecularColor(color); + } + else + { + effect->DisableSpecular(); + } + + if (info.biasedVertexNormals) + { + effect->SetBiasedVertexNormals(true); + } + } +} + +// Internal NPREffectFactory implementation class. Only one of these helpers is allocated +// per D3D device, even if there are multiple public facing NPREffectFactory instances. +class NPREffectFactory::Impl +{ +public: + explicit Impl(_In_ ID3D11Device* device) + : mPath{}, + mMode(NPREffect::Mode_Cel), + mDevice(device), + mSharing(true), + mForceSRGB(false), + mUseEmissiveForMatCap(true) + { + if (!device) + throw std::invalid_argument("Direct3D device is null"); + } + + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + Impl(Impl&&) = delete; + Impl& operator=(Impl&&) = delete; + + std::shared_ptr CreateEffect(_In_ NPREffectFactory* factory, _In_ const EffectFactory::EffectInfo& info, _In_opt_ ID3D11DeviceContext* deviceContext); + void CreateTexture(_In_z_ const wchar_t* texture, _In_opt_ ID3D11DeviceContext* deviceContext, _Outptr_ ID3D11ShaderResourceView** textureView); + + void ReleaseCache(); + + static SharedResourcePool instancePool; + + wchar_t mPath[MAX_PATH]; + + NPREffect::Mode mMode; + + ComPtr mMatCap; + ComPtr mDevice; + + bool mSharing; + bool mForceSRGB; + bool mUseEmissiveForMatCap; + +private: + using EffectCache = std::map< std::wstring, std::shared_ptr >; + using TextureCache = std::map< std::wstring, ComPtr >; + + EffectCache mEffectCache; + EffectCache mEffectCacheSkinning; + EffectCache mEffectCacheDualTexture; + TextureCache mTextureCache; + + std::mutex mutex; +}; + + +// Global instance pool. +SharedResourcePool NPREffectFactory::Impl::instancePool; + + +_Use_decl_annotations_ +std::shared_ptr NPREffectFactory::Impl::CreateEffect(NPREffectFactory* factory, const EffectFactory::EffectInfo& info, ID3D11DeviceContext* deviceContext) +{ + if (info.enableSkinning) + { + // SkinnedNPREffect + if (mSharing && info.name && *info.name) + { + auto it = mEffectCacheSkinning.find(info.name); + if (mSharing && it != mEffectCacheSkinning.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get()); + + SetMaterialProperties(effect.get(), info, mMode); + + if (info.diffuseTexture && *info.diffuseTexture) + { + ComPtr srv; + factory->CreateTexture(info.diffuseTexture, deviceContext, srv.GetAddressOf()); + effect->SetTexture(srv.Get()); + } + + if (mMode == NPREffect::Mode_MatCap) + { + if (mUseEmissiveForMatCap && info.emissiveTexture && *info.emissiveTexture) + { + ComPtr srv; + factory->CreateTexture(info.emissiveTexture, deviceContext, srv.GetAddressOf()); + effect->SetMatCap(srv.Get()); + } + else + { + effect->SetMatCap(mMatCap.Get()); + } + } + + if (mSharing && info.name && *info.name) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(info.name, effect); + mEffectCacheSkinning.insert(v); + } + + return std::move(effect); + } + else if (info.enableDualTexture) + { + // DualTextureEffect + if (mSharing && info.name && *info.name) + { + auto it = mEffectCacheDualTexture.find(info.name); + if (mSharing && it != mEffectCacheDualTexture.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get()); + + // Dual texture effect doesn't support lighting (usually it's lightmaps) + + effect->SetAlpha(info.alpha); + + if (info.perVertexColor) + { + effect->SetVertexColorEnabled(true); + } + + const XMVECTOR color = XMLoadFloat3(&info.diffuseColor); + effect->SetDiffuseColor(color); + + if (info.diffuseTexture && *info.diffuseTexture) + { + ComPtr srv; + + factory->CreateTexture(info.diffuseTexture, deviceContext, srv.GetAddressOf()); + + effect->SetTexture(srv.Get()); + } + + if (info.emissiveTexture && *info.emissiveTexture) + { + ComPtr srv; + + factory->CreateTexture(info.emissiveTexture, deviceContext, srv.GetAddressOf()); + + effect->SetTexture2(srv.Get()); + } + else if (info.specularTexture && *info.specularTexture) + { + // If there's no emissive texture specified, use the specular texture as the second texture + ComPtr srv; + + factory->CreateTexture(info.specularTexture, deviceContext, srv.GetAddressOf()); + + effect->SetTexture2(srv.Get()); + } + + if (mSharing && info.name && *info.name) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(info.name, effect); + mEffectCacheDualTexture.insert(v); + } + + return std::move(effect); + } + else + { + // NPREffect + if (mSharing && info.name && *info.name) + { + auto it = mEffectCache.find(info.name); + if (mSharing && it != mEffectCache.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get()); + + SetMaterialProperties(effect.get(), info, mMode); + + if (info.perVertexColor) + { + effect->SetVertexColorEnabled(true); + } + + if (info.diffuseTexture && *info.diffuseTexture) + { + ComPtr srv; + + factory->CreateTexture(info.diffuseTexture, deviceContext, srv.GetAddressOf()); + + effect->SetTexture(srv.Get()); + effect->SetTextureEnabled(true); + } + + if (mMode == NPREffect::Mode_MatCap) + { + // TODO: Use emissiveTexture or specularTexture for matcap? + effect->SetMatCap(mMatCap.Get()); + } + + if (mSharing && info.name && *info.name) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(info.name, effect); + mEffectCache.insert(v); + } + + return std::move(effect); + } +} + +_Use_decl_annotations_ +void NPREffectFactory::Impl::CreateTexture(const wchar_t* name, ID3D11DeviceContext* deviceContext, ID3D11ShaderResourceView** textureView) +{ + if (!name || !textureView) + throw std::invalid_argument("name and textureView parameters can't be null"); + +#if defined(_XBOX_ONE) && defined(_TITLE) + UNREFERENCED_PARAMETER(deviceContext); +#endif + + auto it = mTextureCache.find(name); + + if (mSharing && it != mTextureCache.end()) + { + ID3D11ShaderResourceView* srv = it->second.Get(); + srv->AddRef(); + *textureView = srv; + } + else + { + wchar_t fullName[MAX_PATH] = {}; + wcscpy_s(fullName, mPath); + wcscat_s(fullName, name); + + WIN32_FILE_ATTRIBUTE_DATA fileAttr = {}; + if (!GetFileAttributesExW(fullName, GetFileExInfoStandard, &fileAttr)) + { + // Try Current Working Directory (CWD) + wcscpy_s(fullName, name); + if (!GetFileAttributesExW(fullName, GetFileExInfoStandard, &fileAttr)) + { + DebugTrace("ERROR: NPREffectFactory could not find texture file '%ls'\n", name); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "NPREffectFactory::CreateTexture"); + } + } + + wchar_t ext[_MAX_EXT] = {}; + _wsplitpath_s(name, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT); + const bool isdds = _wcsicmp(ext, L".dds") == 0; + + if (isdds) + { + HRESULT hr = CreateDDSTextureFromFileEx( + mDevice.Get(), fullName, 0, + D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + mForceSRGB ? DDS_LOADER_FORCE_SRGB : DDS_LOADER_DEFAULT, nullptr, textureView); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateDDSTextureFromFile failed (%08X) for '%ls'\n", + static_cast(hr), fullName); + throw std::runtime_error("NPREffectFactory::CreateDDSTextureFromFile"); + } + } + #if !defined(_XBOX_ONE) || !defined(_TITLE) + else if (deviceContext) + { + std::lock_guard lock(mutex); + HRESULT hr = CreateWICTextureFromFileEx( + mDevice.Get(), deviceContext, fullName, 0, + D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + mForceSRGB ? WIC_LOADER_FORCE_SRGB : WIC_LOADER_DEFAULT, nullptr, textureView); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateWICTextureFromFile failed (%08X) for '%ls'\n", + static_cast(hr), fullName); + throw std::runtime_error("NPREffectFactory::CreateWICTextureFromFile"); + } + } + #endif + else + { + HRESULT hr = CreateWICTextureFromFileEx( + mDevice.Get(), fullName, 0, + D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + mForceSRGB ? WIC_LOADER_FORCE_SRGB : WIC_LOADER_DEFAULT, nullptr, textureView); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateWICTextureFromFile failed (%08X) for '%ls'\n", + static_cast(hr), fullName); + throw std::runtime_error("NPREffectFactory::CreateWICTextureFromFile"); + } + } + + if (mSharing && *name && it == mTextureCache.end()) + { + std::lock_guard lock(mutex); + TextureCache::value_type v(name, *textureView); + mTextureCache.insert(v); + } + } +} + +void NPREffectFactory::Impl::ReleaseCache() +{ + std::lock_guard lock(mutex); + mEffectCache.clear(); + mEffectCacheSkinning.clear(); + mEffectCacheDualTexture.clear(); + mTextureCache.clear(); +} + + + +//-------------------------------------------------------------------------------------- +// NPREffectFactory +//-------------------------------------------------------------------------------------- + +NPREffectFactory::NPREffectFactory(_In_ ID3D11Device* device) + : pImpl(Impl::instancePool.DemandCreate(device)) +{} + + +NPREffectFactory::NPREffectFactory(NPREffectFactory&&) noexcept = default; +NPREffectFactory& NPREffectFactory::operator= (NPREffectFactory&&) noexcept = default; +NPREffectFactory::~NPREffectFactory() = default; + + +_Use_decl_annotations_ +std::shared_ptr NPREffectFactory::CreateEffect(const EffectInfo& info, ID3D11DeviceContext* deviceContext) +{ + return pImpl->CreateEffect(this, info, deviceContext); +} + +_Use_decl_annotations_ +void NPREffectFactory::CreateTexture(const wchar_t* name, ID3D11DeviceContext* deviceContext, ID3D11ShaderResourceView** textureView) +{ + return pImpl->CreateTexture(name, deviceContext, textureView); +} + +void NPREffectFactory::ReleaseCache() +{ + pImpl->ReleaseCache(); +} + +void NPREffectFactory::SetSharing(bool enabled) noexcept +{ + pImpl->mSharing = enabled; +} + +void NPREffectFactory::EnableForceSRGB(bool forceSRGB) noexcept +{ + pImpl->mForceSRGB = forceSRGB; +} + +void NPREffectFactory::SetDirectory(_In_opt_z_ const wchar_t* path) noexcept +{ + if (path && *path != 0) + { + wcscpy_s(pImpl->mPath, path); + size_t len = wcsnlen(pImpl->mPath, MAX_PATH); + if (len > 0 && len < (MAX_PATH - 1)) + { + // Ensure it has a trailing slash + if (pImpl->mPath[len - 1] != L'\\') + { + pImpl->mPath[len] = L'\\'; + pImpl->mPath[len + 1] = 0; + } + } + } + else + *pImpl->mPath = 0; +} + +void NPREffectFactory::SetMode(NPREffect::Mode mode) noexcept +{ + pImpl->mMode = mode; +} + +void NPREffectFactory::SetDefaultMatCap(_In_opt_ ID3D11ShaderResourceView* textureView) noexcept +{ + pImpl->mMatCap = textureView; +} + + +void NPREffectFactory::SetEmissiveAsMatCap(bool value) noexcept +{ + pImpl->mUseEmissiveForMatCap = value; +} + + +ID3D11Device* NPREffectFactory::GetDevice() const noexcept +{ + return pImpl->mDevice.Get(); +} diff --git a/Src/PBREffectFactory.cpp b/Src/PBREffectFactory.cpp index 89a1c994..a48bdd1b 100644 --- a/Src/PBREffectFactory.cpp +++ b/Src/PBREffectFactory.cpp @@ -116,8 +116,6 @@ class PBREffectFactory::Impl _Outptr_ ID3D11ShaderResourceView** textureView); void ReleaseCache(); - void SetSharing(bool enabled) noexcept { mSharing = enabled; } - void EnableForceSRGB(bool forceSRGB) noexcept { mForceSRGB = forceSRGB; } static SharedResourcePool instancePool; @@ -125,6 +123,9 @@ class PBREffectFactory::Impl ComPtr mDevice; + bool mSharing; + bool mForceSRGB; + private: using EffectCache = std::map< std::wstring, std::shared_ptr >; using TextureCache = std::map< std::wstring, ComPtr >; @@ -133,9 +134,6 @@ class PBREffectFactory::Impl EffectCache mEffectCacheSkinning; TextureCache mTextureCache; - bool mSharing; - bool mForceSRGB; - std::mutex mutex; }; @@ -341,12 +339,12 @@ void PBREffectFactory::ReleaseCache() void PBREffectFactory::SetSharing(bool enabled) noexcept { - pImpl->SetSharing(enabled); + pImpl->mSharing = enabled; } void PBREffectFactory::EnableForceSRGB(bool forceSRGB) noexcept { - pImpl->EnableForceSRGB(forceSRGB); + pImpl->mForceSRGB = forceSRGB; } void PBREffectFactory::SetDirectory(_In_opt_z_ const wchar_t* path) noexcept From b5b90dd842e9ac73dc937603d9fad021eb631a87 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Wed, 22 Jul 2026 10:56:11 -0700 Subject: [PATCH 6/6] Minor fix for DLL durango Cmake --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fdb4dd48..8f6e2e65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,7 +297,9 @@ if(WIN32 AND BUILD_SHARED_LIBS) target_compile_definitions(${PROJECT_NAME} INTERFACE DIRECTX_TOOLKIT_IMPORT) if(XBOX_CONSOLE_TARGET MATCHES "durango") - target_link_libraries(${PROJECT_NAME} PRIVATE kernelx.lib combase.lib d3d12_x.lib xi.lib) + target_link_libraries(${PROJECT_NAME} PRIVATE kernelx.lib combase.lib d3d11_x.lib xi.lib) + else() + target_link_libraries(${PROJECT_NAME} PRIVATE d3d11.lib dxguid.lib) endif() if(MINGW AND BUILD_XINPUT)