text: //--------------------------------------------------------------------------------------
  // File: AlphaTestEffect.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 AlphaTestEffectConstants
  {
      XMVECTOR diffuseColor;
      XMVECTOR alphaTest;
      XMVECTOR fogColor;
      XMVECTOR fogVector;
      XMMATRIX worldViewProj;
  };
  
  static_assert((sizeof(AlphaTestEffectConstants) % 16) == 0, "CB size not padded correctly");
  
  
  // Traits type describes our characteristics to the EffectBase template.
  struct AlphaTestEffectTraits
  {
      using ConstantBufferType = AlphaTestEffectConstants;
  
      static const int VertexShaderCount = 4;
      static const int PixelShaderCount = 4;
      static const int ShaderPermutationCount = 8;
  };
  
  
  // Internal AlphaTestEffect implementation class.
  class AlphaTestEffect::Impl : public EffectBase<AlphaTestEffectTraits>
  {
  public:
      Impl(_In_ ID3D11Device* device);
  
      D3D11_COMPARISON_FUNC alphaFunction;
      int referenceAlpha;
  
      bool vertexColorEnabled;
  
      EffectColor color;
  
      int GetCurrentShaderPermutation() const;
  
      void Apply(_In_ ID3D11DeviceContext* deviceContext);
  };
  
  
  // Include the precompiled shader code.
  namespace
  {
  #if defined(_XBOX_ONE) && defined(_TITLE)
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_VSAlphaTest.inc"
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_VSAlphaTestNoFog.inc"
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_VSAlphaTestVc.inc"
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_VSAlphaTestVcNoFog.inc"
  
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_PSAlphaTestLtGt.inc"
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_PSAlphaTestLtGtNoFog.inc"
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_PSAlphaTestEqNe.inc"
      #include "Shaders/Compiled/XboxOneAlphaTestEffect_PSAlphaTestEqNeNoFog.inc"
  #else
      #include "Shaders/Compiled/AlphaTestEffect_VSAlphaTest.inc"
      #include "Shaders/Compiled/AlphaTestEffect_VSAlphaTestNoFog.inc"
      #include "Shaders/Compiled/AlphaTestEffect_VSAlphaTestVc.inc"
      #include "Shaders/Compiled/AlphaTestEffect_VSAlphaTestVcNoFog.inc"
  
      #include "Shaders/Compiled/AlphaTestEffect_PSAlphaTestLtGt.inc"
      #include "Shaders/Compiled/AlphaTestEffect_PSAlphaTestLtGtNoFog.inc"
      #include "Shaders/Compiled/AlphaTestEffect_PSAlphaTestEqNe.inc"
      #include "Shaders/Compiled/AlphaTestEffect_PSAlphaTestEqNeNoFog.inc"
  #endif
  }
  
  
  template<>
  const ShaderBytecode EffectBase<AlphaTestEffectTraits>::VertexShaderBytecode[] =
  {
      { AlphaTestEffect_VSAlphaTest,        sizeof(AlphaTestEffect_VSAlphaTest)        },
      { AlphaTestEffect_VSAlphaTestNoFog,   sizeof(AlphaTestEffect_VSAlphaTestNoFog)   },
      { AlphaTestEffect_VSAlphaTestVc,      sizeof(AlphaTestEffect_VSAlphaTestVc)      },
      { AlphaTestEffect_VSAlphaTestVcNoFog, sizeof(AlphaTestEffect_VSAlphaTestVcNoFog) },
  };
  
  
  template<>
  const int EffectBase<AlphaTestEffectTraits>::VertexShaderIndices[] =
  {
      0,      // lt/gt
      1,      // lt/gt, no fog
      2,      // lt/gt, vertex color
      3,      // lt/gt, vertex color, no fog
      
      0,      // eq/ne
      1,      // eq/ne, no fog
      2,      // eq/ne, vertex color
      3,      // eq/ne, vertex color, no fog
  };
  
  
  template<>
  const ShaderBytecode EffectBase<AlphaTestEffectTraits>::PixelShaderBytecode[] =
  {
      { AlphaTestEffect_PSAlphaTestLtGt,      sizeof(AlphaTestEffect_PSAlphaTestLtGt)      },
      { AlphaTestEffect_PSAlphaTestLtGtNoFog, sizeof(AlphaTestEffect_PSAlphaTestLtGtNoFog) },
      { AlphaTestEffect_PSAlphaTestEqNe,      sizeof(AlphaTestEffect_PSAlphaTestEqNe)      },
      { AlphaTestEffect_PSAlphaTestEqNeNoFog, sizeof(AlphaTestEffect_PSAlphaTestEqNeNoFog) },
  };
  
  
  template<>
  const int EffectBase<AlphaTestEffectTraits>::PixelShaderIndices[] =
  {
      0,      // lt/gt
      1,      // lt/gt, no fog
      0,      // lt/gt, vertex color
      1,      // lt/gt, vertex color, no fog
      
      2,      // eq/ne
      3,      // eq/ne, no fog
      2,      // eq/ne, vertex color
      3,      // eq/ne, vertex color, no fog
  };
  
  
  // Global pool of per-device AlphaTestEffect resources.
  template<>
  SharedResourcePool<ID3D11Device*, EffectBase<AlphaTestEffectTraits>::DeviceResources> EffectBase<AlphaTestEffectTraits>::deviceResourcesPool = {};
  
  
  // Constructor.
  AlphaTestEffect::Impl::Impl(_In_ ID3D11Device* device)
    : EffectBase(device),
      alphaFunction(D3D11_COMPARISON_GREATER),
      referenceAlpha(0),
      vertexColorEnabled(false)
  {
      static_assert(_countof(EffectBase<AlphaTestEffectTraits>::VertexShaderIndices) == AlphaTestEffectTraits::ShaderPermutationCount, "array/max mismatch");
      static_assert(_countof(EffectBase<AlphaTestEffectTraits>::VertexShaderBytecode) == AlphaTestEffectTraits::VertexShaderCount, "array/max mismatch");
      static_assert(_countof(EffectBase<AlphaTestEffectTraits>::PixelShaderBytecode) == AlphaTestEffectTraits::PixelShaderCount, "array/max mismatch");
      static_assert(_countof(EffectBase<AlphaTestEffectTraits>::PixelShaderIndices) == AlphaTestEffectTraits::ShaderPermutationCount, "array/max mismatch");
  }
  
  
  int AlphaTestEffect::Impl::GetCurrentShaderPermutation() const
  {
      int permutation = 0;
  
      // Use optimized shaders if fog is disabled.
      if (!fog.enabled)
      {
          permutation += 1;
      }
  
      // Support vertex coloring?
      if (vertexColorEnabled)
      {
          permutation += 2;
      }
  
      // Which alpha compare mode?
      if (alphaFunction == D3D11_COMPARISON_EQUAL ||
          alphaFunction == D3D11_COMPARISON_NOT_EQUAL)
      {
          permutation += 4;
      }
  
      return permutation;
  }
  
  
  // Sets our state onto the D3D device.
  void AlphaTestEffect::Impl::Apply(_In_ ID3D11DeviceContext* deviceContext)
  {
      // Compute derived parameter values.
      matrices.SetConstants(dirtyFlags, constants.worldViewProj);
  
      fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector);
              
      color.SetConstants(dirtyFlags, constants.diffuseColor);
  
      // Recompute the alpha test settings?
      if (dirtyFlags & EffectDirtyFlags::AlphaTest)
      {
          // Convert reference alpha from 8 bit integer to 0-1 float format.
          auto reference = static_cast<float>(referenceAlpha) / 255.0f;
                  
          // Comparison tolerance of half the 8 bit integer precision.
          const float threshold = 0.5f / 255.0f;
  
          // What to do if the alpha comparison passes or fails. Positive accepts the pixel, negative clips it.
          static const XMVECTORF32 selectIfTrue  = { { {  1, -1 } } };
          static const XMVECTORF32 selectIfFalse = { { { -1,  1 } } };
          static const XMVECTORF32 selectNever   = { { { -1, -1 } } };
          static const XMVECTORF32 selectAlways  = { { {  1,  1 } } };
  
          float compareTo;
          XMVECTOR resultSelector;
  
          switch (alphaFunction)
          {
              case D3D11_COMPARISON_LESS:
                  // Shader will evaluate: clip((a < x) ? z : w)
                  compareTo = reference - threshold;
                  resultSelector = selectIfTrue;
                  break;
  
              case D3D11_COMPARISON_LESS_EQUAL:
                  // Shader will evaluate: clip((a < x) ? z : w)
                  compareTo = reference + threshold;
                  resultSelector = selectIfTrue;
                  break;
  
              case D3D11_COMPARISON_GREATER_EQUAL:
                  // Shader will evaluate: clip((a < x) ? z : w)
                  compareTo = reference - threshold;
                  resultSelector = selectIfFalse;
                  break;
  
              case D3D11_COMPARISON_GREATER:
                  // Shader will evaluate: clip((a < x) ? z : w)
                  compareTo = reference + threshold;
                  resultSelector = selectIfFalse;
                  break;
  
              case D3D11_COMPARISON_EQUAL:
                  // Shader will evaluate: clip((abs(a - x) < y) ? z : w)
                  compareTo = reference;
                  resultSelector = selectIfTrue;
                  break;
  
              case D3D11_COMPARISON_NOT_EQUAL:
                  // Shader will evaluate: clip((abs(a - x) < y) ? z : w)
                  compareTo = reference;
                  resultSelector = selectIfFalse;
                  break;
  
              case D3D11_COMPARISON_NEVER:
                  // Shader will evaluate: clip((a < x) ? z : w)
                  compareTo = 0;
                  resultSelector = selectNever;
                  break;
  
              case D3D11_COMPARISON_ALWAYS:
                  // Shader will evaluate: clip((a < x) ? z : w)
                  compareTo = 0;
                  resultSelector = selectAlways;
                  break;
  
              default:
                  throw std::exception("Unknown alpha test function");
          }
  
          // x = compareTo, y = threshold, zw = resultSelector.
          constants.alphaTest = XMVectorPermute<0, 1, 4, 5>(XMVectorSet(compareTo, threshold, 0, 0), resultSelector);
                  
          dirtyFlags &= ~EffectDirtyFlags::AlphaTest;
          dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
      }
  
      // Set the texture.
      ID3D11ShaderResourceView* textures[1] = { texture.Get() };
  
      deviceContext->PSSetShaderResources(0, 1, textures);
      
      // Set shaders and constant buffers.
      ApplyShaders(deviceContext, GetCurrentShaderPermutation());
  }
  
  
  // Public constructor.
  AlphaTestEffect::AlphaTestEffect(_In_ ID3D11Device* device)
    : pImpl(std::make_unique<Impl>(device))
  {
  }
  
  
  // Move constructor.
  AlphaTestEffect::AlphaTestEffect(AlphaTestEffect&& moveFrom) noexcept
    : pImpl(std::move(moveFrom.pImpl))
  {
  }
  
  
  // Move assignment.
  AlphaTestEffect& AlphaTestEffect::operator= (AlphaTestEffect&& moveFrom) noexcept
  {
      pImpl = std::move(moveFrom.pImpl);
      return *this;
  }
  
  
  // Public destructor.
  AlphaTestEffect::~AlphaTestEffect()
  {
  }
  
  
  // IEffect methods.
  void AlphaTestEffect::Apply(_In_ ID3D11DeviceContext* deviceContext)
  {
      pImpl->Apply(deviceContext);
  }
  
  
  void AlphaTestEffect::GetVertexShaderBytecode(_Out_ void const** pShaderByteCode, _Out_ size_t* pByteCodeLength)
  {
      pImpl->GetVertexShaderBytecode(pImpl->GetCurrentShaderPermutation(), pShaderByteCode, pByteCodeLength);
  }
  
  
  // Camera settings.
  void XM_CALLCONV AlphaTestEffect::SetWorld(FXMMATRIX value)
  {
      pImpl->matrices.world = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector;
  }
  
  
  void XM_CALLCONV AlphaTestEffect::SetView(FXMMATRIX value)
  {
      pImpl->matrices.view = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector;
  }
  
  
  void XM_CALLCONV AlphaTestEffect::SetProjection(FXMMATRIX value)
  {
      pImpl->matrices.projection = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj;
  }
  
  
  void XM_CALLCONV AlphaTestEffect::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 AlphaTestEffect::SetDiffuseColor(FXMVECTOR value)
  {
      pImpl->color.diffuseColor = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor;
  }
  
  
  void AlphaTestEffect::SetAlpha(float value)
  {
      pImpl->color.alpha = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor;
  }
  
  
  void XM_CALLCONV AlphaTestEffect::SetColorAndAlpha(FXMVECTOR value)
  {
      pImpl->color.diffuseColor = value;
      pImpl->color.alpha = XMVectorGetW(value);
  
      pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor;
  }
  
  
  // Fog settings.
  void AlphaTestEffect::SetFogEnabled(bool value)
  {
      pImpl->fog.enabled = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::FogEnable;
  }
  
  
  void AlphaTestEffect::SetFogStart(float value)
  {
      pImpl->fog.start = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::FogVector;
  }
  
  
  void AlphaTestEffect::SetFogEnd(float value)
  {
      pImpl->fog.end = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::FogVector;
  }
  
  
  void XM_CALLCONV AlphaTestEffect::SetFogColor(FXMVECTOR value)
  {
      pImpl->constants.fogColor = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
  }
  
  
  // Vertex color setting.
  void AlphaTestEffect::SetVertexColorEnabled(bool value)
  {
      pImpl->vertexColorEnabled = value;
  }
  
  
  // Texture settings.
  void AlphaTestEffect::SetTexture(_In_opt_ ID3D11ShaderResourceView* value)
  {
      pImpl->texture = value;
  }
  
  
  void AlphaTestEffect::SetAlphaFunction(D3D11_COMPARISON_FUNC value)
  {
      pImpl->alphaFunction = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::AlphaTest;
  }
  
  
  void AlphaTestEffect::SetReferenceAlpha(int value)
  {
      pImpl->referenceAlpha = value;
  
      pImpl->dirtyFlags |= EffectDirtyFlags::AlphaTest;
  }
  