text: //--------------------------------------------------------------------------------------
  // File: Game.cpp
  //
  // Developer unit test for DirectXTK Effects
  //
  // Copyright (c) Microsoft Corporation.
  // Licensed under the MIT License.
  //
  // http://go.microsoft.com/fwlink/?LinkID=615561
  //--------------------------------------------------------------------------------------
  
  #include "pch.h"
  #include "Game.h"
  #include "Bezier.h"
  
  #define GAMMA_CORRECT_RENDERING
  
  // Build for LH vs. RH coords
  //#define LH_COORDS
  
  extern void ExitGame() noexcept;
  
  using namespace DirectX;
  using namespace DirectX::PackedVector;
  using namespace DirectX::SimpleMath;
  
  using Microsoft::WRL::ComPtr;
  
  namespace
  {
      constexpr float rowA = -2.f;
      constexpr float row0 = 2.5f;
      constexpr float row1 = 1.5f;
      constexpr float row2 = 0.5f;
      constexpr float row3 = 0.f;
      constexpr float row4 = -0.5f;
      constexpr float row5 = -1.5f;
      constexpr float row6 = -2.5f;
  
      constexpr float colA = -5.f;
      constexpr float col0 = -4.f;
      constexpr float col1 = -3.f;
      constexpr float col2 = -2.f;
      constexpr float col3 = -1.f;
      constexpr float col4 = 0.f;
      constexpr float col5 = 1.f;
      constexpr float col6 = 2.f;
      constexpr float col7 = 3.f;
      constexpr float col8 = 4.f;
      constexpr float col9 = 5.f;
  
      struct TestVertex
      {
          TestVertex(FXMVECTOR position, FXMVECTOR normal, FXMVECTOR textureCoordinate)
          {
              XMStoreFloat3(&this->position, position);
              XMStoreFloat3(&this->normal, normal);
              XMStoreFloat2(&this->textureCoordinate, textureCoordinate);
              XMStoreFloat2(&this->textureCoordinate2, XMVectorScale(textureCoordinate, 3.f));
              XMStoreUByte4(&this->blendIndices, XMVectorSet(0, 1, 2, 3));
  
              float u = XMVectorGetX(textureCoordinate) - 0.5f;
              float v = XMVectorGetY(textureCoordinate) - 0.5f;
  
              float d = 1 - sqrt(u * u + v * v) * 2;
  
              if (d < 0)
                  d = 0;
  
              XMStoreFloat4(&this->blendWeight, XMVectorSet(d, 1 - d, u, v));
  
              color = 0xFFFF00FF;
          }
  
          XMFLOAT3 position;
          XMFLOAT3 normal;
          XMFLOAT2 textureCoordinate;
          XMFLOAT2 textureCoordinate2;
          XMUBYTE4 blendIndices;
          XMFLOAT4 blendWeight;
          XMUBYTE4 color;
  
          static const D3D12_INPUT_LAYOUT_DESC InputLayout;
  
      private:
          static constexpr unsigned int InputElementCount = 7;
          static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount];
      };
  
      const D3D12_INPUT_ELEMENT_DESC TestVertex::InputElements[] =
      {
          { "SV_Position",  0, DXGI_FORMAT_R32G32B32_FLOAT,    0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
          { "NORMAL",       0, DXGI_FORMAT_R32G32B32_FLOAT,    0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
          { "TEXCOORD",     0, DXGI_FORMAT_R32G32_FLOAT,       0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
          { "TEXCOORD",     1, DXGI_FORMAT_R32G32_FLOAT,       0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
          { "BLENDINDICES", 0, DXGI_FORMAT_R8G8B8A8_UINT,      0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
          { "BLENDWEIGHT",  0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
          { "COLOR",        0, DXGI_FORMAT_R8G8B8A8_UNORM,     0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
      };
  
      const D3D12_INPUT_LAYOUT_DESC TestVertex::InputLayout =
      {
          TestVertex::InputElements,
          TestVertex::InputElementCount
      };
  
      using VertexCollection = std::vector<TestVertex>;
      using IndexCollection = std::vector<uint16_t>;
  
      #include "../../Src/TeapotData.inc"
  
      // Tessellates the specified bezier patch.
      void TessellatePatch(VertexCollection& vertices, IndexCollection& indices, TeapotPatch const& patch, FXMVECTOR scale, bool isMirrored)
      {
          constexpr int tessellation = 16;
  
          // Look up the 16 control points for this patch.
          XMVECTOR controlPoints[16];
  
          for (int i = 0; i < 16; i++)
          {
              controlPoints[i] = XMVectorMultiply(TeapotControlPoints[patch.indices[i]], scale);
          }
  
          // Create the index data.
          Bezier::CreatePatchIndices(tessellation, isMirrored, [&](size_t index)
          {
              indices.push_back((uint16_t)(vertices.size() + index));
          });
  
          // Create the vertex data.
          Bezier::CreatePatchVertices(controlPoints, tessellation, isMirrored, [&](FXMVECTOR position, FXMVECTOR normal, FXMVECTOR textureCoordinate)
          {
              vertices.push_back(TestVertex(position, normal, textureCoordinate));
          });
      }
  } // anonymous namespace
  
  
  //--------------------------------------------------------------------------------------
  
  static_assert(std::is_nothrow_move_constructible<RenderTargetState>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<RenderTargetState>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<EffectPipelineStateDescription>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<EffectPipelineStateDescription>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<BasicEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<BasicEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<AlphaTestEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<AlphaTestEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<DualTextureEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<DualTextureEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<EnvironmentMapEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<EnvironmentMapEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<SkinnedEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<SkinnedEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<NormalMapEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<NormalMapEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<SkinnedNormalMapEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<SkinnedNormalMapEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<PBREffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<PBREffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<SkinnedPBREffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<SkinnedPBREffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<DebugEffect>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<DebugEffect>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<EffectTextureFactory>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<EffectTextureFactory>::value, "Move Assign.");
  
  // VS 2017 and the XDK isn't noexcept correct here
  static_assert(std::is_nothrow_move_constructible<IEffectFactory::EffectInfo>::value, "Move Ctor.");
  static_assert(std::is_move_assignable<IEffectFactory::EffectInfo>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<EffectFactory>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<EffectFactory>::value, "Move Assign.");
  
  static_assert(std::is_nothrow_move_constructible<PBREffectFactory>::value, "Move Ctor.");
  static_assert(std::is_nothrow_move_assignable<PBREffectFactory>::value, "Move Assign.");
  
  //--------------------------------------------------------------------------------------
  
  Game::Game() noexcept(false) :
      m_indexCount(0),
      m_vertexBufferView{},
      m_indexBufferView{}
  {
  #ifdef GAMMA_CORRECT_RENDERING
      const DXGI_FORMAT c_RenderFormat = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
  #else
      const DXGI_FORMAT c_RenderFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
  #endif
  
  #ifdef XBOX
      m_deviceResources = std::make_unique<DX::DeviceResources>(
          c_RenderFormat, DXGI_FORMAT_D32_FLOAT, 2,
          DX::DeviceResources::c_Enable4K_UHD
  #ifdef _GAMING_XBOX
          | DX::DeviceResources::c_EnableQHD
  #endif
          );
  #elif defined(UWP)
      m_deviceResources = std::make_unique<DX::DeviceResources>(
          c_RenderFormat, DXGI_FORMAT_D32_FLOAT, 2, D3D_FEATURE_LEVEL_11_0,
          DX::DeviceResources::c_Enable4K_Xbox
          );
  #else
      m_deviceResources = std::make_unique<DX::DeviceResources>(c_RenderFormat);
  #endif
  
  #ifdef LOSTDEVICE
      m_deviceResources->RegisterDeviceNotify(this);
  #endif
  }
  
  Game::~Game()
  {
      if (m_deviceResources)
      {
          m_deviceResources->WaitForGpu();
      }
  }
  
  // Initialize the Direct3D resources required to run.
  void Game::Initialize(
  #ifdef COREWINDOW
      IUnknown* window,
  #else
      HWND window,
  #endif
      int width, int height, DXGI_MODE_ROTATION rotation)
  {
      m_gamePad = std::make_unique<GamePad>();
      m_keyboard = std::make_unique<Keyboard>();
  
  #ifdef XBOX
      UNREFERENCED_PARAMETER(rotation);
      UNREFERENCED_PARAMETER(width);
      UNREFERENCED_PARAMETER(height);
      m_deviceResources->SetWindow(window);
  #ifdef COREWINDOW
      m_keyboard->SetWindow(reinterpret_cast<ABI::Windows::UI::Core::ICoreWindow*>(window));
  #endif
  #elif defined(UWP)
      m_deviceResources->SetWindow(window, width, height, rotation);
      m_keyboard->SetWindow(reinterpret_cast<ABI::Windows::UI::Core::ICoreWindow*>(window));
  #else
      UNREFERENCED_PARAMETER(rotation);
      m_deviceResources->SetWindow(window, width, height);
  #endif
  
      m_deviceResources->CreateDeviceResources();
      CreateDeviceDependentResources();
  
      m_deviceResources->CreateWindowSizeDependentResources();
      CreateWindowSizeDependentResources();
  }
  
  #pragma region Frame Update
  // Executes the basic game loop.
  void Game::Tick()
  {
      m_timer.Tick([&]()
      {
          Update(m_timer);
      });
  
      Render();
  }
  
  // Updates the world.
  void Game::Update(DX::StepTimer const& /*timer*/)
  {
      PIXBeginEvent(PIX_COLOR_DEFAULT, L"Update");
  
      auto pad = m_gamePad->GetState(0);
      auto kb = m_keyboard->GetState();
      if (kb.Escape || (pad.IsConnected() && pad.IsViewPressed()))
      {
          ExitGame();
      }
  
      PIXEndEvent();
  }
  #pragma endregion
  
  #pragma region Frame Render
  // Draws the scene.
  void Game::Render()
  {
      // Don't try to render anything before the first Update.
      if (m_timer.GetFrameCount() == 0)
      {
          return;
      }
  
      // Prepare the command list to render a new frame.
      m_deviceResources->Prepare();
      Clear();
  
      auto commandList = m_deviceResources->GetCommandList();
      PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render");
  
      // Set the descriptor heaps
      ID3D12DescriptorHeap* heaps[] = { m_resourceDescriptors->Heap(), m_states->Heap() };
      commandList->SetDescriptorHeaps(static_cast<UINT>(std::size(heaps)), heaps);
  
      // Time-based animation
      float time = static_cast<float>(m_timer.GetTotalSeconds());
  
      float alphaFade = (sin(time * 2) + 1) / 2;
  
      if (alphaFade >= 1)
          alphaFade = 1 - FLT_EPSILON;
  
      float yaw = time * 0.4f;
      float pitch = time * 0.7f;
      float roll = time * 1.1f;
  
      SimpleMath:: Vector4 blue;
  #ifdef GAMMA_CORRECT_RENDERING
      blue = XMColorSRGBToRGB(Colors::Blue);
  #else
      blue = Colors::Blue;
  #endif
  
      XMMATRIX world = XMMatrixRotationRollPitchYaw(pitch, yaw, roll);
  
      // Setup for teapot drawing.
      commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
      commandList->IASetIndexBuffer(&m_indexBufferView);
      commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  
      m_abstractEffect->Apply(commandList);
  
      //--- BasicEFfect ----------------------------------------------------------------------
      // Simple unlit teapot.
      m_basicEffectUnlit->SetAlpha(1.f);
      m_basicEffectUnlit->SetWorld(world * XMMatrixTranslation(col0, row0, 0));
      m_basicEffectUnlit->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Unlit with alpha fading.
      m_basicEffectUnlit->SetAlpha(alphaFade);
      m_basicEffectUnlit->SetWorld(world * XMMatrixTranslation(col0, row1, 0));
      m_basicEffectUnlit->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Unlit with fog.
      m_basicEffectUnlitFog->SetWorld(world * XMMatrixTranslation(col0, row2, 2 - alphaFade * 6));
      m_basicEffectUnlitFog->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Simple unlit teapot with vertex colors.
      m_basicEffectUnlitVc->SetAlpha(1.f);
      m_basicEffectUnlitVc->SetWorld(world * XMMatrixTranslation(colA, row0, 0));
      m_basicEffectUnlitVc->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Simple unlit teapot with alpha fading with vertex colors.
      m_basicEffectUnlitVc->SetAlpha(alphaFade);
      m_basicEffectUnlitVc->SetWorld(world * XMMatrixTranslation(colA, row1, 0));
      m_basicEffectUnlitVc->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Unlit with fog with vertex colors.
      m_basicEffectUnlitFogVc->SetWorld(world * XMMatrixTranslation(colA, row2, 2 - alphaFade * 6));
      m_basicEffectUnlitFogVc->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Simple lit teapot.
      m_basicEffect->SetAlpha(1.f);
      m_basicEffect->SetWorld(world * XMMatrixTranslation(col1, row0, 0));
      m_basicEffect->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Simple lit teapot, no specular.
      m_basicEffectNoSpecular->SetAlpha(1.f);
      m_basicEffectNoSpecular->SetWorld(world * XMMatrixTranslation(col2, row3, 0));
      m_basicEffectNoSpecular->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Simple lit with alpha fading.
      m_basicEffect->SetAlpha(alphaFade);
      m_basicEffect->SetWorld(world * XMMatrixTranslation(col1, row1, 0));
      m_basicEffect->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Simple lit alpha fading, no specular.
      m_basicEffectNoSpecular->SetAlpha(alphaFade);
      m_basicEffectNoSpecular->SetWorld(world * XMMatrixTranslation(col3, row3, 0));
      m_basicEffectNoSpecular->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Simple lit with fog.
      m_basicEffectFog->SetAlpha(1.f);
      m_basicEffectFog->SetWorld(world * XMMatrixTranslation(col1, row2, 2 - alphaFade * 6));
      m_basicEffectFog->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      {
          // Light only from above.
          m_basicEffect->SetAlpha(1.f);
          m_basicEffect->SetLightDirection(0, XMVectorSet(0, -1, 0, 0));
          m_basicEffect->SetLightEnabled(1, false);
          m_basicEffect->SetLightEnabled(2, false);
          m_basicEffect->SetWorld(world *  XMMatrixTranslation(col2, row0, 0));
          m_basicEffect->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Light only from the left.
          m_basicEffect->SetLightDirection(0, XMVectorSet(1, 0, 0, 0));
          m_basicEffect->SetWorld(world * XMMatrixTranslation(col3, row0, 0));
          m_basicEffect->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Light only from straight in front.
          m_basicEffect->SetLightDirection(0, XMVectorSet(0, 0, -1, 0));
          m_basicEffect->SetWorld(world * XMMatrixTranslation(col4, row0, 0));
          m_basicEffect->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
      }
  
      m_basicEffect->EnableDefaultLighting();
  
      // Non uniform scaling.
      m_basicEffect->SetWorld(XMMatrixScaling(1, 2, 0.25f) * world * XMMatrixTranslation(col5, row0, 0));
      m_basicEffect->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      {
          // Light only from above + per pixel lighting.
          m_basicEffectPPL->SetLightDirection(0, XMVectorSet(0, -1, 0, 0));
          m_basicEffectPPL->SetLightEnabled(1, false);
          m_basicEffectPPL->SetLightEnabled(2, false);
          m_basicEffectPPL->SetWorld(world *  XMMatrixTranslation(col2, row1, 0));
          m_basicEffectPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Light only from the left + per pixel lighting.
          m_basicEffectPPL->SetLightDirection(0, XMVectorSet(1, 0, 0, 0));
          m_basicEffectPPL->SetWorld(world * XMMatrixTranslation(col3, row1, 0));
          m_basicEffectPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Light only from straight in front + per pixel lighting.
          m_basicEffectPPL->SetLightDirection(0, XMVectorSet(0, 0, -1, 0));
          m_basicEffectPPL->SetWorld(world * XMMatrixTranslation(col4, row1, 0));
          m_basicEffectPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          m_basicEffectPPL->EnableDefaultLighting();
  
          // Non uniform scaling + per pixel lighting.
          m_basicEffectPPL->SetWorld(XMMatrixScaling(1, 2, 0.25f) * world * XMMatrixTranslation(col5, row1, 0));
          m_basicEffectPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
      }
  
      //--- SkinnedEFfect --------------------------------------------------------------------
      XMMATRIX bones[4] =
      {
          XMMatrixIdentity(),
          XMMatrixIdentity(),
          XMMatrixScaling(0, 0, 0),
          XMMatrixScaling(0, 0, 0),
      };
  
      m_skinnedEffect->SetBoneTransforms(bones, 4);
      m_skinnedEffect->SetWorld(world);
      m_skinnedEffect->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      m_skinnedEffectNoSpecular->SetBoneTransforms(bones, 4);
      m_skinnedEffectNoSpecular->SetWorld(world * XMMatrixTranslation(col5, row3, 0));
      m_skinnedEffectNoSpecular->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Skinned effect with fog.
      m_skinnedEffectFog->SetBoneTransforms(bones, 4);
      m_skinnedEffectFog->SetWorld(world * XMMatrixTranslation(colA, rowA, 2 - alphaFade * 6));
      m_skinnedEffectFog->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      m_skinnedEffectFogPPL->SetBoneTransforms(bones, 4);
      m_skinnedEffectFogPPL->SetWorld(world * XMMatrixTranslation(col0, rowA, 2 - alphaFade * 6));
      m_skinnedEffectFogPPL->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Skinned effect, variable scaling transforms.
      float scales[4] =
      {
          1 + sin(time * 1.7f) * 0.5f,
          1 + sin(time * 2.3f) * 0.5f,
          0,
          0,
      };
  
      for (int i = 0; i < 4; i++)
      {
          bones[i] = XMMatrixScaling(scales[i], scales[i], scales[i]);
      }
  
      m_skinnedEffect->SetBoneTransforms(bones, 4);
      m_skinnedEffect->SetWorld(world * XMMatrixTranslation(col3, rowA, 0));
      m_skinnedEffect->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Skinned effect, different variable scaling transforms.
      float scales2[4] =
      {
          1,
          1,
          sin(time * 2.3f) * 0.5f,
          sin(time * 3.1f) * 0.5f,
      };
  
      for (int i = 0; i < 4; i++)
      {
          bones[i] = XMMatrixScaling(scales2[i], scales2[i], scales2[i]);
      }
  
      m_skinnedEffectPPL->SetBoneTransforms(bones, 4);
      m_skinnedEffectPPL->SetWorld(world * XMMatrixTranslation(col1, rowA, 0));
      m_skinnedEffectPPL->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      //--- EnvironmentMapEffect -------------------------------------------------------------
      m_envmap->SetEnvironmentMapAmount(1.f);
      m_envmap->SetFresnelFactor(1.f);
      m_envmap->SetWorld(world * XMMatrixTranslation(col6, row0, 0));
      m_envmap->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Environment map with alpha fading.
      m_envmap->SetAlpha(alphaFade);
      m_envmap->SetWorld(world * XMMatrixTranslation(col6, row1, 0));
      m_envmap->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
      m_envmap->SetAlpha(1.f);
   
      // Environment map with fog.
      m_envmapFog->SetWorld(world * XMMatrixTranslation(col6, row2, 2 - alphaFade * 6));
      m_envmapFog->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Environment map, animating the fresnel factor.
      m_envmap->SetFresnelFactor(alphaFade * 3);
      m_envmap->SetWorld(world * XMMatrixTranslation(col6, row4, 0));
      m_envmap->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Environment map, animating the amount, with no fresnel.
      m_envmapNoFresnel->SetEnvironmentMapAmount(alphaFade);
      m_envmapNoFresnel->SetWorld(world * XMMatrixTranslation(col6, row5, 0));
      m_envmapNoFresnel->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Environment map, animating the amount.
      m_envmap->SetEnvironmentMapAmount(alphaFade);
      m_envmap->SetWorld(world * XMMatrixTranslation(col6, row6, 0));
      m_envmap->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Environment map, with animating specular
      m_envmapSpec->SetEnvironmentMapSpecular(blue * alphaFade);
      m_envmapSpec->SetWorld(world * XMMatrixTranslation(col5, row5, 0));
      m_envmapSpec->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      {
          // Light only from above + per pixel lighting, animating the fresnel factor.
          m_envmapPPL->SetLightDirection(0, XMVectorSet(0, -1, 0, 0));
          m_envmapPPL->SetLightEnabled(1, false);
          m_envmapPPL->SetLightEnabled(2, false);
          m_envmapPPL->SetFresnelFactor(alphaFade * 3);
          m_envmapPPL->SetWorld(world *  XMMatrixTranslation(col7, row4, 0));
          m_envmapPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Light only from the left + per pixel lighting, animating the amount, with no fresnel.
          m_envmapNoFresnelPPL->SetEnvironmentMapAmount(alphaFade);
          m_envmapNoFresnelPPL->SetFresnelFactor(0);
          m_envmapNoFresnelPPL->SetLightDirection(0, XMVectorSet(1, 0, 0, 0));
          m_envmapNoFresnelPPL->SetLightEnabled(1, false);
          m_envmapNoFresnelPPL->SetLightEnabled(2, false);
          m_envmapNoFresnelPPL->SetWorld(world * XMMatrixTranslation(col7, row5, 0));
          m_envmapNoFresnelPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Light only from straight in front + per pixel lighting with fog.
          m_envmapFogPPL->SetLightDirection(0, XMVectorSet(0, 0, -1, 0));
          m_envmapFogPPL->SetLightEnabled(1, false);
          m_envmapFogPPL->SetLightEnabled(2, false);
          m_envmapFogPPL->SetWorld(world * XMMatrixTranslation(col7, row6, 2 - alphaFade * 6));
          m_envmapFogPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Light only from the left + per pixel lighting, with animating specular 
          m_envmapSpecPPL->SetLightDirection(0, XMVectorSet(1, 0, 0, 0));
          m_envmapSpecPPL->SetLightEnabled(1, false);
          m_envmapSpecPPL->SetLightEnabled(2, false);
          m_envmapSpecPPL->SetEnvironmentMapSpecular(blue * alphaFade);
          m_envmapSpecPPL->SetWorld(world * XMMatrixTranslation(col5, row6, 0));
          m_envmapSpecPPL->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
      }
  
      m_spheremap->SetWorld(world * XMMatrixTranslation(col4, row5, 0));
      m_spheremap->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      m_dparabolamap->SetWorld(world * XMMatrixTranslation(col4, row6, 0));
      m_dparabolamap->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      //--- DualTextureEFfect ----------------------------------------------------------------
      m_dualTexture->SetAlpha(1.f);
      m_dualTexture->SetWorld(world *  XMMatrixTranslation(col7, row0, 0));
      m_dualTexture->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Dual texture with alpha fading.
      m_dualTexture->SetAlpha(alphaFade);
      m_dualTexture->SetWorld(world *  XMMatrixTranslation(col7, row1, 0));
      m_dualTexture->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // Dual texture with fog.
      m_dualTextureFog->SetWorld(world *  XMMatrixTranslation(col7, row2, 2 - alphaFade * 6));
      m_dualTextureFog->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      //--- AlphaTestEFfect ------------------------------------------------------------------
  
      {
          // Alpha test, > 0.
          m_alphaTest->SetReferenceAlpha(0);
          m_alphaTest->SetWorld(world * XMMatrixTranslation(col8, row0, 0));
          m_alphaTest->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Alpha test, > 128.
          m_alphaTest->SetReferenceAlpha(128);
          m_alphaTest->SetWorld(world * XMMatrixTranslation(col8, row1, 0));
          m_alphaTest->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Alpha test with fog.
          m_alphaTestFog->SetReferenceAlpha(128);
          m_alphaTestFog->SetWorld(world * XMMatrixTranslation(col8, row2, 2 - alphaFade * 6));
          m_alphaTestFog->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Alpha test, < animating value.
          m_alphaTestLess->SetReferenceAlpha(1 + (int)(alphaFade * 254));
          m_alphaTestLess->SetWorld(world * XMMatrixTranslation(col8, row4, 0));
          m_alphaTestLess->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Alpha test, = 255.
          m_alphaTestEqual->SetReferenceAlpha(255);
          m_alphaTestEqual->SetWorld(world * XMMatrixTranslation(col8, row5, 0));
          m_alphaTestEqual->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
          // Alpha test, != 0.
          m_alphaTestNotEqual->SetReferenceAlpha(0);
          m_alphaTestNotEqual->SetWorld(world * XMMatrixTranslation(col8, row6, 0));
          m_alphaTestNotEqual->Apply(commandList);
          commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
      }
  
      //--- NormalMapEffect ------------------------------------------------------------------
      m_normalMapEffect->SetAlpha(1.f);
      m_normalMapEffect->SetWorld(world *  XMMatrixTranslation(col9, row0, 0));
      m_normalMapEffect->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // NormalMapEffect no spec
      m_normalMapEffectNoSpecular->SetWorld(world *  XMMatrixTranslation(col9, row1, 0));
      m_normalMapEffectNoSpecular->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // NormalMap with fog.
      m_normalMapEffectFog->SetWorld(world *  XMMatrixTranslation(col9, row2, 2 - alphaFade * 6));
      m_normalMapEffectFog->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // NormalMap with default diffuse
      m_normalMapEffectNoDiffuse->SetWorld(world *  XMMatrixTranslation(col9, row4, 0));
      m_normalMapEffectNoDiffuse->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      // NormalMap with default diffuse no spec
      m_normalMapEffectNormalsOnly->SetWorld(world *  XMMatrixTranslation(col9, row5, 0));
      m_normalMapEffectNormalsOnly->Apply(commandList);
      commandList->DrawIndexedInstanced(m_indexCount, 1, 0, 0, 0);
  
      PIXEndEvent(commandList);
  
      // Show the new frame.
      PIXBeginEvent(m_deviceResources->GetCommandQueue(), PIX_COLOR_DEFAULT, L"Present");
      m_deviceResources->Present();
      m_graphicsMemory->Commit(m_deviceResources->GetCommandQueue());
  
      // Sample stats to update peak values
      std::ignore = m_graphicsMemory->GetStatistics();
  
      PIXEndEvent(m_deviceResources->GetCommandQueue());
  }
  
  // Helper method to clear the back buffers.
  void Game::Clear()
  {
      auto commandList = m_deviceResources->GetCommandList();
      PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear");
  
      // Clear the views.
      auto const rtvDescriptor = m_deviceResources->GetRenderTargetView();
      auto const dsvDescriptor = m_deviceResources->GetDepthStencilView();
  
      XMVECTORF32 color;
  #ifdef GAMMA_CORRECT_RENDERING
      color.v = XMColorSRGBToRGB(Colors::CornflowerBlue);
  #else
      color.v = Colors::CornflowerBlue;
  #endif
      commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor);
      commandList->ClearRenderTargetView(rtvDescriptor, color, 0, nullptr);
      commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
  
      // Set the viewport and scissor rect.
      auto const viewport = m_deviceResources->GetScreenViewport();
      auto const scissorRect = m_deviceResources->GetScissorRect();
      commandList->RSSetViewports(1, &viewport);
      commandList->RSSetScissorRects(1, &scissorRect);
  
      PIXEndEvent(commandList);
  }
  #pragma endregion
  
  #pragma region Message Handlers
  // Message handlers
  void Game::OnActivated()
  {
  }
  
  void Game::OnDeactivated()
  {
  }
  
  void Game::OnSuspending()
  {
      m_deviceResources->Suspend();
  }
  
  void Game::OnResuming()
  {
      m_deviceResources->Resume();
  
      m_timer.ResetElapsedTime();
  }
  
  #ifdef PC
  void Game::OnWindowMoved()
  {
      auto const r = m_deviceResources->GetOutputSize();
      m_deviceResources->WindowSizeChanged(r.right, r.bottom);
  }
  #endif
  
  #if defined(PC) || defined(UWP)
  void Game::OnDisplayChange()
  {
      m_deviceResources->UpdateColorSpace();
  }
  #endif
  
  #ifndef XBOX
  void Game::OnWindowSizeChanged(int width, int height, DXGI_MODE_ROTATION rotation)
  {
  #ifdef UWP
      if (!m_deviceResources->WindowSizeChanged(width, height, rotation))
          return;
  #else
      UNREFERENCED_PARAMETER(rotation);
      if (!m_deviceResources->WindowSizeChanged(width, height))
          return;
  #endif
  
      CreateWindowSizeDependentResources();
  }
  #endif
  
  #ifdef UWP
  void Game::ValidateDevice()
  {
      m_deviceResources->ValidateDevice();
  }
  #endif
  
  // Properties
  void Game::GetDefaultSize(int& width, int& height) const
  {
      width = 1280;
      height = 720;
  }
  #pragma endregion
  
  #pragma region Direct3D Resources
  // These are the resources that depend on the device.
  void Game::CreateDeviceDependentResources()
  {
      auto device = m_deviceResources->GetD3DDevice();
  
      m_graphicsMemory = std::make_unique<GraphicsMemory>(device);
  
      m_states = std::make_unique<CommonStates>(device);
  
      CreateTeapot();
  
      // Create test effects
      RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat());
  
  #ifdef LH_COORDS
      const float fogstart = -6;
      const float fogend = -8;
  #else
      const float fogstart = 6;
      const float fogend = 8;
  #endif
  
      XMVECTORF32 red, blue, gray;
  #ifdef GAMMA_CORRECT_RENDERING
      red.v = XMColorSRGBToRGB(Colors::Red);
      blue.v = XMColorSRGBToRGB(Colors::Blue);
      gray.v = XMColorSRGBToRGB(Colors::Gray);
  #else
      red.v = Colors::Red;
      blue.v = Colors::Blue;
      gray.v = Colors::Gray;
  #endif
  
      {
          EffectPipelineStateDescription pdAlpha(
              &TestVertex::InputLayout,
              CommonStates::AlphaBlend,
              CommonStates::DepthDefault,
  #ifdef LH_COORDS
              CommonStates::CullClockwise,
  #else
              CommonStates::CullCounterClockwise,
  #endif
              rtState);
  
          EffectPipelineStateDescription pdOpaque(
              &TestVertex::InputLayout,
              CommonStates::Opaque,
              CommonStates::DepthDefault,
  #ifdef LH_COORDS
              CommonStates::CullClockwise,
  #else
              CommonStates::CullCounterClockwise,
  #endif
              rtState);
  
          m_abstractEffect = std::make_unique<BasicEffect>(device, EffectFlags::None, pdAlpha);
  
          //--- BasicEffect ------------------------------------------------------------------
          m_basicEffectUnlit = std::make_unique<BasicEffect>(device, EffectFlags::None, pdAlpha);
          m_basicEffectUnlit->SetDiffuseColor(blue);
  
          m_basicEffectUnlitFog = std::make_unique<BasicEffect>(device, EffectFlags::Fog, pdAlpha);
          m_basicEffectUnlitFog->SetDiffuseColor(blue);
          m_basicEffectUnlitFog->SetFogColor(gray);
          m_basicEffectUnlitFog->SetFogStart(fogstart);
          m_basicEffectUnlitFog->SetFogEnd(fogend);
  
          m_basicEffectUnlitVc = std::make_unique<BasicEffect>(device, EffectFlags::VertexColor, pdAlpha);
  
          m_basicEffectUnlitFogVc = std::make_unique<BasicEffect>(device, EffectFlags::VertexColor | EffectFlags::Fog, pdAlpha);
          m_basicEffectUnlitFogVc->SetFogColor(gray);
          m_basicEffectUnlitFogVc->SetFogStart(fogstart);
          m_basicEffectUnlitFogVc->SetFogEnd(fogend);
  
          m_basicEffect = std::make_unique<BasicEffect>(device, EffectFlags::Lighting, pdAlpha);
          m_basicEffect->EnableDefaultLighting();
          m_basicEffect->SetDiffuseColor(red);
  
          m_basicEffectFog = std::make_unique<BasicEffect>(device, EffectFlags::Lighting | EffectFlags::Fog, pdAlpha);
          m_basicEffectFog->EnableDefaultLighting();
          m_basicEffectFog->SetDiffuseColor(red);
          m_basicEffectFog->SetFogColor(gray);
          m_basicEffectFog->SetFogStart(fogstart);
          m_basicEffectFog->SetFogEnd(fogend);
  
          m_basicEffectNoSpecular = std::make_unique<BasicEffect>(device, EffectFlags::Lighting, pdAlpha);
          m_basicEffectNoSpecular->EnableDefaultLighting();
          m_basicEffectNoSpecular->SetDiffuseColor(red);
          m_basicEffectNoSpecular->DisableSpecular();
  
          m_basicEffectPPL = std::make_unique<BasicEffect>(device, EffectFlags::PerPixelLighting, pdAlpha);
          m_basicEffectPPL->EnableDefaultLighting();
          m_basicEffectPPL->SetDiffuseColor(red);
  
          //--- SkinnedEFfect ----------------------------------------------------------------
          m_skinnedEffect = std::make_unique<SkinnedEffect>(device, EffectFlags::Lighting | EffectFlags::Texture, pdAlpha);
          m_skinnedEffect->EnableDefaultLighting();
  
          m_skinnedEffectFog = std::make_unique<SkinnedEffect>(device, EffectFlags::Lighting | EffectFlags::Texture | EffectFlags::Fog, pdAlpha);
          m_skinnedEffectFog->EnableDefaultLighting();
          m_skinnedEffectFog->SetFogColor(gray);
          m_skinnedEffectFog->SetFogStart(fogstart);
          m_skinnedEffectFog->SetFogEnd(fogend);
  
          m_skinnedEffectNoSpecular = std::make_unique<SkinnedEffect>(device, EffectFlags::Lighting, pdAlpha);
          m_skinnedEffectNoSpecular->EnableDefaultLighting();
          m_skinnedEffectNoSpecular->DisableSpecular();
  
          m_skinnedEffectPPL = std::make_unique<SkinnedEffect>(device, EffectFlags::Lighting | EffectFlags::Texture | EffectFlags::PerPixelLighting, pdAlpha);
          m_skinnedEffectPPL->EnableDefaultLighting();
  
          m_skinnedEffectFogPPL = std::make_unique<SkinnedEffect>(device, EffectFlags::Lighting | EffectFlags::Texture | EffectFlags::PerPixelLighting | EffectFlags::Fog, pdAlpha);
          m_skinnedEffectFogPPL->EnableDefaultLighting();
          m_skinnedEffectFogPPL->SetFogColor(gray);
          m_skinnedEffectFogPPL->SetFogStart(fogstart);
          m_skinnedEffectFogPPL->SetFogEnd(fogend);
  
          //--- EnvironmentMapEffect ---------------------------------------------------------
          m_envmap = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::Fresnel, pdAlpha);
          m_envmap->EnableDefaultLighting();
  
          m_envmapSpec = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::Fresnel | EffectFlags::Specular, pdAlpha);
          m_envmapSpec->EnableDefaultLighting();
  
          m_envmapFog = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::Fresnel | EffectFlags::Fog, pdAlpha);
          m_envmapFog->EnableDefaultLighting();
          m_envmapFog->SetFogColor(gray);
          m_envmapFog->SetFogStart(fogstart);
          m_envmapFog->SetFogEnd(fogend);
  
          m_envmapNoFresnel = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::None, pdAlpha);
          m_envmapNoFresnel->EnableDefaultLighting();
  
          m_envmapPPL = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::PerPixelLighting | EffectFlags::Fresnel, pdAlpha);
          m_envmapPPL->EnableDefaultLighting();
  
          m_envmapSpecPPL = std::make_unique<EnvironmentMapEffect>(device,
              EffectFlags::PerPixelLighting | EffectFlags::Fresnel | EffectFlags::Specular, pdAlpha);
          m_envmapSpecPPL->EnableDefaultLighting();
  
          m_envmapFogPPL = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::PerPixelLighting | EffectFlags::Fresnel | EffectFlags::Fog, pdAlpha);
          m_envmapFogPPL->EnableDefaultLighting();
          m_envmapFogPPL->SetFogColor(gray);
          m_envmapFogPPL->SetFogStart(fogstart);
          m_envmapFogPPL->SetFogEnd(fogend);
  
          m_envmapNoFresnelPPL = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::PerPixelLighting, pdAlpha);
          m_envmapNoFresnelPPL->EnableDefaultLighting();
  
          m_spheremap = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::None, pdAlpha, EnvironmentMapEffect::Mapping_Sphere);
          m_spheremap->EnableDefaultLighting();
  
          m_dparabolamap = std::make_unique<EnvironmentMapEffect>(device, EffectFlags::None, pdAlpha, EnvironmentMapEffect::Mapping_DualParabola);
          m_dparabolamap->EnableDefaultLighting();
  
          //--- DualTextureEFfect ------------------------------------------------------------
          m_dualTexture = std::make_unique<DualTextureEffect>(device, EffectFlags::None, pdAlpha);
  
          m_dualTextureFog = std::make_unique<DualTextureEffect>(device, EffectFlags::Fog, pdAlpha);
          m_dualTextureFog->SetFogColor(gray);
          m_dualTextureFog->SetFogStart(fogstart);
          m_dualTextureFog->SetFogEnd(fogend);
  
          //--- AlphaTestEFfect --------------------------------------------------------------
          m_alphaTest = std::make_unique<AlphaTestEffect>(device, EffectFlags::None, pdOpaque);
  
          m_alphaTestFog = std::make_unique<AlphaTestEffect>(device, EffectFlags::Fog, pdOpaque);
          m_alphaTestFog->SetFogColor(red);
          m_alphaTestFog->SetFogStart(fogstart);
          m_alphaTestFog->SetFogEnd(fogend);
  
          m_alphaTestLess = std::make_unique<AlphaTestEffect>(device, EffectFlags::None, pdOpaque, D3D12_COMPARISON_FUNC_LESS);
  
          m_alphaTestEqual = std::make_unique<AlphaTestEffect>(device, EffectFlags::None, pdOpaque, D3D12_COMPARISON_FUNC_EQUAL);
  
          m_alphaTestNotEqual = std::make_unique<AlphaTestEffect>(device, EffectFlags::None, pdOpaque, D3D12_COMPARISON_FUNC_NOT_EQUAL);
  
          //--- NormalMapEffect --------------------------------------------------------------
          m_normalMapEffect = std::make_unique<NormalMapEffect>(device, EffectFlags::Specular, pdOpaque);
          m_normalMapEffect->EnableDefaultLighting();
          m_normalMapEffect->SetDiffuseColor(Colors::White);
  
          m_normalMapEffectFog = std::make_unique<NormalMapEffect>(device, EffectFlags::Specular | EffectFlags::Fog, pdOpaque);
          m_normalMapEffectFog->SetFogColor(gray);
          m_normalMapEffectFog->SetFogStart(fogstart);
          m_normalMapEffectFog->SetFogEnd(fogend);
          m_normalMapEffectFog->SetDiffuseColor(Colors::White);
          m_normalMapEffectFog->EnableDefaultLighting();
  
          m_normalMapEffectNoDiffuse = std::make_unique<NormalMapEffect>(device, EffectFlags::Specular, pdOpaque);
          m_normalMapEffectNoDiffuse->SetDiffuseColor(Colors::White);
          m_normalMapEffectNoDiffuse->EnableDefaultLighting();
  
          m_normalMapEffectNormalsOnly = std::make_unique<NormalMapEffect>(device, EffectFlags::None, pdOpaque);
          m_normalMapEffectNormalsOnly->SetDiffuseColor(Colors::White);
          m_normalMapEffectNormalsOnly->EnableDefaultLighting();
          m_normalMapEffectNormalsOnly->DisableSpecular();
  
          m_normalMapEffectNoSpecular = std::make_unique<NormalMapEffect>(device, EffectFlags::None, pdOpaque);
          m_normalMapEffectNoSpecular->SetDiffuseColor(Colors::White);
          m_normalMapEffectNoSpecular->EnableDefaultLighting();
          m_normalMapEffectNoSpecular->DisableSpecular();
      }
  
      // Load textures.
      m_resourceDescriptors = std::make_unique<DescriptorHeap>(device, Descriptors::Count);
  
      ResourceUploadBatch resourceUpload(device);
  
      resourceUpload.Begin();
  
  #ifdef GAMMA_CORRECT_RENDERING
      constexpr DDS_LOADER_FLAGS loadFlags = DDS_LOADER_FORCE_SRGB;
      constexpr WIC_LOADER_FLAGS wicLoadFlags = WIC_LOADER_FORCE_SRGB;
  #else
      constexpr DDS_LOADER_FLAGS loadFlags = DDS_LOADER_DEFAULT;
      constexpr WIC_LOADER_FLAGS wicLoadFlags = WIC_LOADER_DEFAULT;
  #endif
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFileEx(device, resourceUpload, L"cat.dds",
              0, D3D12_RESOURCE_FLAG_NONE, loadFlags, 
              m_cat.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_cat.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::Cat));
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFileEx(device, resourceUpload, L"opaqueCat.dds",
              0, D3D12_RESOURCE_FLAG_NONE, loadFlags,
              m_opaqueCat.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_opaqueCat.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::OpaqueCat));
  
      bool iscubemap;
      DX::ThrowIfFailed(
          CreateDDSTextureFromFileEx(device, resourceUpload, L"cubemap.dds",
              0, D3D12_RESOURCE_FLAG_NONE, loadFlags,
              m_cubemap.ReleaseAndGetAddressOf(), nullptr, &iscubemap));
  
      CreateShaderResourceView(device, m_cubemap.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::Cubemap), iscubemap);
  
      DX::ThrowIfFailed(
          CreateWICTextureFromFileEx(device, resourceUpload, L"spheremap.bmp",
              0, D3D12_RESOURCE_FLAG_NONE, wicLoadFlags,
              m_envball.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_envball.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::SphereMap));
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFileEx(device, resourceUpload, L"dualparabola.dds",
              0, D3D12_RESOURCE_FLAG_NONE, loadFlags,
              m_envdual.ReleaseAndGetAddressOf(), nullptr));
  
      CreateShaderResourceView(device, m_envdual.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::DualParabolaMap));
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFileEx(device, resourceUpload, L"overlay.dds",
              0, D3D12_RESOURCE_FLAG_NONE, loadFlags, 
              m_overlay.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_overlay.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::Overlay));
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFileEx(device, resourceUpload, L"default.dds",
              0, D3D12_RESOURCE_FLAG_NONE, loadFlags, 
              m_defaultTex.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_defaultTex.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::DefaultTex));
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFileEx(device, resourceUpload, L"spnza_bricks_a.DDS",
              0, D3D12_RESOURCE_FLAG_NONE, loadFlags, 
              m_brickDiffuse.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_brickDiffuse.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::BrickDiffuse));
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFile(device, resourceUpload, L"spnza_bricks_a_normal.DDS",
              m_brickNormal.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_brickNormal.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::BrickNormal));
  
      DX::ThrowIfFailed(
          CreateDDSTextureFromFile(device, resourceUpload, L"spnza_bricks_a_specular.DDS",
              m_brickSpecular.ReleaseAndGetAddressOf()));
  
      CreateShaderResourceView(device, m_brickSpecular.Get(), m_resourceDescriptors->GetCpuHandle(Descriptors::BrickSpecular));
  
      auto uploadResourcesFinished = resourceUpload.End(m_deviceResources->GetCommandQueue());
  
      uploadResourcesFinished.wait();
  
      // Set textures.
      auto opaqueCat = m_resourceDescriptors->GetGpuHandle(Descriptors::OpaqueCat);
  
      auto sampler = m_states->LinearWrap();
  
      m_skinnedEffect->SetTexture(opaqueCat, sampler);
      m_skinnedEffectFog->SetTexture(opaqueCat, sampler);
      m_skinnedEffectNoSpecular->SetTexture(opaqueCat, sampler);
      m_skinnedEffectPPL->SetTexture(opaqueCat, sampler);
      m_skinnedEffectFogPPL->SetTexture(opaqueCat, sampler);
  
      auto cubemap = m_resourceDescriptors->GetGpuHandle(Descriptors::Cubemap);
  
      auto ansitropic = m_states->AnisotropicWrap();
  
      m_envmap->SetTexture(opaqueCat, sampler);
      m_envmap->SetEnvironmentMap(cubemap, ansitropic);
      m_envmapSpec->SetTexture(opaqueCat, sampler);
      m_envmapSpec->SetEnvironmentMap(cubemap, ansitropic);
      m_envmapFog->SetTexture(opaqueCat, sampler);
      m_envmapFog->SetEnvironmentMap(cubemap, ansitropic);
      m_envmapNoFresnel->SetTexture(opaqueCat, sampler);
      m_envmapNoFresnel->SetEnvironmentMap(cubemap, ansitropic);
      m_envmapPPL->SetTexture(opaqueCat, sampler);
      m_envmapPPL->SetEnvironmentMap(cubemap, ansitropic);
      m_envmapSpecPPL->SetTexture(opaqueCat, sampler);
      m_envmapSpecPPL->SetEnvironmentMap(cubemap, ansitropic);
      m_envmapFogPPL->SetTexture(opaqueCat, sampler);
      m_envmapFogPPL->SetEnvironmentMap(cubemap, ansitropic);
      m_envmapNoFresnelPPL->SetTexture(opaqueCat, sampler);
      m_envmapNoFresnelPPL->SetEnvironmentMap(cubemap, ansitropic);
  
      auto defaultTex = m_resourceDescriptors->GetGpuHandle(Descriptors::DefaultTex);
      m_spheremap->SetTexture(defaultTex, sampler);
      m_dparabolamap->SetTexture(defaultTex, sampler);
  
      auto spheremap = m_resourceDescriptors->GetGpuHandle(Descriptors::SphereMap);
      m_spheremap->SetEnvironmentMap(spheremap, ansitropic);
  
      auto dualmap = m_resourceDescriptors->GetGpuHandle(Descriptors::DualParabolaMap);
      m_dparabolamap->SetEnvironmentMap(dualmap, ansitropic);
  
      auto overlay = m_resourceDescriptors->GetGpuHandle(Descriptors::Overlay);
  
      m_dualTexture->SetTexture(opaqueCat, sampler);
      m_dualTexture->SetTexture2(overlay, sampler);
      m_dualTextureFog->SetTexture(opaqueCat, sampler);
      m_dualTextureFog->SetTexture2(overlay, sampler);
  
      auto cat = m_resourceDescriptors->GetGpuHandle(Descriptors::Cat);
  
      m_alphaTest->SetTexture(cat, sampler);
      m_alphaTestFog->SetTexture(cat, sampler);
      m_alphaTestLess->SetTexture(cat, sampler);
      m_alphaTestEqual->SetTexture(cat, sampler);
      m_alphaTestNotEqual->SetTexture(cat, sampler);
  
      {
          auto albedo = m_resourceDescriptors->GetGpuHandle(Descriptors::BrickDiffuse);
          auto normal = m_resourceDescriptors->GetGpuHandle(Descriptors::BrickNormal);
          auto specular = m_resourceDescriptors->GetGpuHandle(Descriptors::BrickSpecular);
  
          m_normalMapEffect->SetTexture(albedo, sampler);
          m_normalMapEffect->SetNormalTexture(normal);
          m_normalMapEffect->SetSpecularTexture(specular);
  
          m_normalMapEffectFog->SetTexture(albedo, sampler);
          m_normalMapEffectFog->SetNormalTexture(normal);
          m_normalMapEffectFog->SetSpecularTexture(specular);
  
          m_normalMapEffectNoDiffuse->SetTexture(defaultTex, sampler);
          m_normalMapEffectNoDiffuse->SetNormalTexture(normal);
          m_normalMapEffectNoDiffuse->SetSpecularTexture(specular);
  
          m_normalMapEffectNormalsOnly->SetTexture(defaultTex, sampler);
          m_normalMapEffectNormalsOnly->SetNormalTexture(normal);
  
          m_normalMapEffectNoSpecular->SetTexture(albedo, sampler);
          m_normalMapEffectNoSpecular->SetNormalTexture(normal);
      }
  }
  
  // Allocate all memory resources that change on a window SizeChanged event.
  void Game::CreateWindowSizeDependentResources()
  {
      static const XMVECTORF32 cameraPosition = { { { 0.f, 0.f, 6.f, 0.f } } };
  
      auto size = m_deviceResources->GetOutputSize();
      float aspect = (float)size.right / (float)size.bottom;
  
  #ifdef LH_COORDS
      XMMATRIX view = XMMatrixLookAtLH(cameraPosition, g_XMZero, XMVectorSet(0, 1, 0, 0));
      XMMATRIX projection = XMMatrixPerspectiveFovLH(1, aspect, 1, 15);
  #else
      XMMATRIX view = XMMatrixLookAtRH(cameraPosition, g_XMZero, XMVectorSet(0, 1, 0, 0));
      XMMATRIX projection = XMMatrixPerspectiveFovRH(1, aspect, 1, 15);
  #endif
  
  #ifdef UWP
      {
          auto orient3d = m_deviceResources->GetOrientationTransform3D();
          XMMATRIX orient = XMLoadFloat4x4(&orient3d);
          projection *= orient;
      }
  #endif
  
      m_basicEffectUnlit->SetView(view);
      m_basicEffectUnlitFog->SetView(view);
      m_basicEffectUnlitVc->SetView(view);
      m_basicEffectUnlitFogVc->SetView(view);
      m_basicEffect->SetView(view);
      m_basicEffectFog->SetView(view);
      m_basicEffectNoSpecular->SetView(view);
      m_basicEffectPPL->SetView(view);
  
      m_skinnedEffect->SetView(view);
      m_skinnedEffectFog->SetView(view);
      m_skinnedEffectNoSpecular->SetView(view);
      m_skinnedEffectPPL->SetView(view);
      m_skinnedEffectFogPPL->SetView(view);
  
      m_envmap->SetView(view);
      m_envmapSpec->SetView(view);
      m_envmapFog->SetView(view);
      m_envmapNoFresnel->SetView(view);
      m_envmapPPL->SetView(view);
      m_envmapSpecPPL->SetView(view);
      m_envmapFogPPL->SetView(view);
      m_envmapNoFresnelPPL->SetView(view);
  
      m_spheremap->SetView(view);
      m_dparabolamap->SetView(view);
  
      m_dualTexture->SetView(view);
      m_dualTextureFog->SetView(view);
  
      m_alphaTest->SetView(view);
      m_alphaTestFog->SetView(view);
      m_alphaTestLess->SetView(view);
      m_alphaTestEqual->SetView(view);
      m_alphaTestNotEqual->SetView(view);
  
      m_normalMapEffect->SetView(view);
      m_normalMapEffectFog->SetView(view);
      m_normalMapEffectNoDiffuse->SetView(view);
      m_normalMapEffectNormalsOnly->SetView(view);
      m_normalMapEffectNoSpecular->SetView(view);
  
      m_basicEffectUnlit->SetProjection(projection);
      m_basicEffectUnlitFog->SetProjection(projection);
      m_basicEffectUnlitVc->SetProjection(projection);
      m_basicEffectUnlitFogVc->SetProjection(projection);
      m_basicEffect->SetProjection(projection);
      m_basicEffectFog->SetProjection(projection);
      m_basicEffectNoSpecular->SetProjection(projection);
      m_basicEffectPPL->SetProjection(projection);
  
      m_skinnedEffect->SetProjection(projection);
      m_skinnedEffectFog->SetProjection(projection);
      m_skinnedEffectNoSpecular->SetProjection(projection);
      m_skinnedEffectPPL->SetProjection(projection);
      m_skinnedEffectFogPPL->SetProjection(projection);
  
      m_envmap->SetProjection(projection);
      m_envmapSpec->SetProjection(projection);
      m_envmapFog->SetProjection(projection);
      m_envmapNoFresnel->SetProjection(projection);
      m_envmapPPL->SetProjection(projection);
      m_envmapSpecPPL->SetProjection(projection);
      m_envmapFogPPL->SetProjection(projection);
      m_envmapNoFresnelPPL->SetProjection(projection);
  
      m_spheremap->SetProjection(projection);
      m_dparabolamap->SetProjection(projection);
  
      m_dualTexture->SetProjection(projection);
      m_dualTextureFog->SetProjection(projection);
  
      m_alphaTest->SetProjection(projection);
      m_alphaTestFog->SetProjection(projection);
      m_alphaTestLess->SetProjection(projection);
      m_alphaTestEqual->SetProjection(projection);
      m_alphaTestNotEqual->SetProjection(projection);
  
      m_normalMapEffect->SetProjection(projection);
      m_normalMapEffectFog->SetProjection(projection);
      m_normalMapEffectNoDiffuse->SetProjection(projection);
      m_normalMapEffectNormalsOnly->SetProjection(projection);
      m_normalMapEffectNoSpecular->SetProjection(projection);
  }
  
  #ifdef LOSTDEVICE
  void Game::OnDeviceLost()
  {
      m_abstractEffect.reset();
  
      m_basicEffectUnlit.reset();
      m_basicEffectUnlitFog.reset();
      m_basicEffectUnlitVc.reset();
      m_basicEffectUnlitFogVc.reset();
      m_basicEffect.reset();
      m_basicEffectFog.reset();
      m_basicEffectNoSpecular.reset();
      m_basicEffectPPL.reset();
  
      m_skinnedEffect.reset();
      m_skinnedEffectFog.reset();
      m_skinnedEffectNoSpecular.reset();
      m_skinnedEffectPPL.reset();
      m_skinnedEffectFogPPL.reset();
  
      m_envmap.reset();
      m_envmapSpec.reset();
      m_envmapFog.reset();
      m_envmapNoFresnel.reset();
      m_envmapPPL.reset();
      m_envmapSpecPPL.reset();
      m_envmapFogPPL.reset();
      m_envmapNoFresnelPPL.reset();
      m_spheremap.reset();
      m_dparabolamap.reset();
  
      m_dualTexture.reset();
      m_dualTextureFog.reset();
  
      m_alphaTest.reset();
      m_alphaTestFog.reset();
      m_alphaTestLess.reset();
      m_alphaTestEqual.reset();
      m_alphaTestNotEqual.reset();
  
      m_normalMapEffect.reset();
      m_normalMapEffectFog.reset();
      m_normalMapEffectNoDiffuse.reset();
      m_normalMapEffectNormalsOnly.reset();
      m_normalMapEffectNoSpecular.reset();
  
      m_cat.Reset();
      m_opaqueCat.Reset();
      m_cubemap.Reset();
      m_envball.Reset();
      m_envdual.Reset();
      m_overlay.Reset();
      m_defaultTex.Reset();
      m_brickDiffuse.Reset();
      m_brickNormal.Reset();
      m_brickSpecular.Reset();
  
      m_indexBuffer.Reset();
      m_vertexBuffer.Reset();
  
      m_resourceDescriptors.reset();
      m_states.reset();
      m_graphicsMemory.reset();
  }
  
  void Game::OnDeviceRestored()
  {
      CreateDeviceDependentResources();
  
      CreateWindowSizeDependentResources();
  }
  #endif
  #pragma endregion
  
  // Creates a teapot primitive with test input layout.
  void Game::CreateTeapot()
  {
      VertexCollection vertices;
      IndexCollection indices;
  
      XMVECTOR negateXZ = XMVectorMultiply(g_XMNegateX, g_XMNegateZ);
  
      for (size_t i = 0; i < sizeof(TeapotPatches) / sizeof(TeapotPatches[0]); i++)
      {
          TeapotPatch const& patch = TeapotPatches[i];
  
          // Because the teapot is symmetrical from left to right, we only store
          // data for one side, then tessellate each patch twice, mirroring in X.
          TessellatePatch(vertices, indices, patch, g_XMOne, false);
          TessellatePatch(vertices, indices, patch, g_XMNegateX, true);
  
          if (patch.mirrorZ)
          {
              // Some parts of the teapot (the body, lid, and rim, but not the
              // handle or spout) are also symmetrical from front to back, so
              // we tessellate them four times, mirroring in Z as well as X.
              TessellatePatch(vertices, indices, patch, g_XMNegateZ, true);
              TessellatePatch(vertices, indices, patch, negateXZ, false);
          }
      }
  
      // Create the D3D buffers.
      if (vertices.size() >= USHRT_MAX)
          throw std::runtime_error("Too many vertices for 16-bit index buffer");
  
      // Vertex data
      auto verts = reinterpret_cast<const uint8_t*>(vertices.data());
      size_t vertSizeBytes = vertices.size() * sizeof(TestVertex);
  
      m_vertexBuffer = GraphicsMemory::Get().Allocate(vertSizeBytes);
      memcpy(m_vertexBuffer.Memory(), verts, vertSizeBytes);
  
      // Index data
      auto ind = reinterpret_cast<const uint8_t*>(indices.data());
      size_t indSizeBytes = indices.size() * sizeof(uint16_t);
  
      m_indexBuffer = GraphicsMemory::Get().Allocate(indSizeBytes);
      memcpy(m_indexBuffer.Memory(), ind, indSizeBytes);
  
      // Record index count for draw
      m_indexCount = static_cast<UINT>(indices.size());
  
      // Create views
      m_vertexBufferView.BufferLocation = m_vertexBuffer.GpuAddress();
      m_vertexBufferView.StrideInBytes = static_cast<UINT>(sizeof(VertexCollection::value_type));
      m_vertexBufferView.SizeInBytes = static_cast<UINT>(m_vertexBuffer.Size());
  
      m_indexBufferView.BufferLocation = m_indexBuffer.GpuAddress();
      m_indexBufferView.SizeInBytes = static_cast<UINT>(m_indexBuffer.Size());
      m_indexBufferView.Format = DXGI_FORMAT_R16_UINT;
  }
  