home *** CD-ROM | disk | FTP | other *** search
/ Introduction to 3D Game …ogramming with DirectX 12 / Introduction-to-3D-Game-Programming-with-DirectX-12.ISO / Code.Textures / Chapter 7 Drawing in Direct3D Part II / LandAndWaves / LandAndWavesApp.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2016-03-02  |  25.1 KB  |  756 lines

  1. //***************************************************************************************
  2. // LandAndWavesApp.cpp by Frank Luna (C) 2015 All Rights Reserved.
  3. //
  4. // Hold down '1' key to view scene in wireframe mode.
  5. //***************************************************************************************
  6.  
  7. #include "../../Common/d3dApp.h"
  8. #include "../../Common/MathHelper.h"
  9. #include "../../Common/UploadBuffer.h"
  10. #include "../../Common/GeometryGenerator.h"
  11. #include "FrameResource.h"
  12. #include "Waves.h"
  13.  
  14. using Microsoft::WRL::ComPtr;
  15. using namespace DirectX;
  16. using namespace DirectX::PackedVector;
  17.  
  18. const int gNumFrameResources = 3;
  19.  
  20. // Lightweight structure stores parameters to draw a shape.  This will
  21. // vary from app-to-app.
  22. struct RenderItem
  23. {
  24.     RenderItem() = default;
  25.  
  26.     // World matrix of the shape that describes the object's local space
  27.     // relative to the world space, which defines the position, orientation,
  28.     // and scale of the object in the world.
  29.     XMFLOAT4X4 World = MathHelper::Identity4x4();
  30.  
  31.     // Dirty flag indicating the object data has changed and we need to update the constant buffer.
  32.     // Because we have an object cbuffer for each FrameResource, we have to apply the
  33.     // update to each FrameResource.  Thus, when we modify obect data we should set 
  34.     // NumFramesDirty = gNumFrameResources so that each frame resource gets the update.
  35.     int NumFramesDirty = gNumFrameResources;
  36.  
  37.     // Index into GPU constant buffer corresponding to the ObjectCB for this render item.
  38.     UINT ObjCBIndex = -1;
  39.  
  40.     MeshGeometry* Geo = nullptr;
  41.  
  42.     // Primitive topology.
  43.     D3D12_PRIMITIVE_TOPOLOGY PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  44.  
  45.     // DrawIndexedInstanced parameters.
  46.     UINT IndexCount = 0;
  47.     UINT StartIndexLocation = 0;
  48.     int BaseVertexLocation = 0;
  49. };
  50.  
  51. enum class RenderLayer : int
  52. {
  53.     Opaque = 0,
  54.     Count
  55. };
  56.  
  57. class LandAndWavesApp : public D3DApp
  58. {
  59. public:
  60.     LandAndWavesApp(HINSTANCE hInstance);
  61.     LandAndWavesApp(const LandAndWavesApp& rhs) = delete;
  62.     LandAndWavesApp& operator=(const LandAndWavesApp& rhs) = delete;
  63.     ~LandAndWavesApp();
  64.  
  65.     virtual bool Initialize()override;
  66.  
  67. private:
  68.     virtual void OnResize()override;
  69.     virtual void Update(const GameTimer& gt)override;
  70.     virtual void Draw(const GameTimer& gt)override;
  71.  
  72.     virtual void OnMouseDown(WPARAM btnState, int x, int y)override;
  73.     virtual void OnMouseUp(WPARAM btnState, int x, int y)override;
  74.     virtual void OnMouseMove(WPARAM btnState, int x, int y)override;
  75.  
  76.     void OnKeyboardInput(const GameTimer& gt);
  77.     void UpdateCamera(const GameTimer& gt);
  78.     void UpdateObjectCBs(const GameTimer& gt);
  79.     void UpdateMainPassCB(const GameTimer& gt);
  80.     void UpdateWaves(const GameTimer& gt);
  81.  
  82.     void BuildRootSignature();
  83.     void BuildShadersAndInputLayout();
  84.     void BuildLandGeometry();
  85.     void BuildWavesGeometryBuffers();
  86.     void BuildPSOs();
  87.     void BuildFrameResources();
  88.     void BuildRenderItems();
  89.     void DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems);
  90.  
  91.     float GetHillsHeight(float x, float z)const;
  92.     XMFLOAT3 GetHillsNormal(float x, float z)const;
  93.  
  94. private:
  95.  
  96.     std::vector<std::unique_ptr<FrameResource>> mFrameResources;
  97.     FrameResource* mCurrFrameResource = nullptr;
  98.     int mCurrFrameResourceIndex = 0;
  99.  
  100.     UINT mCbvSrvDescriptorSize = 0;
  101.  
  102.     ComPtr<ID3D12RootSignature> mRootSignature = nullptr;
  103.  
  104.     std::unordered_map<std::string, std::unique_ptr<MeshGeometry>> mGeometries;
  105.     std::unordered_map<std::string, ComPtr<ID3DBlob>> mShaders;
  106.     std::unordered_map<std::string, ComPtr<ID3D12PipelineState>> mPSOs;
  107.  
  108.     std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;
  109.  
  110.     RenderItem* mWavesRitem = nullptr;
  111.  
  112.     // List of all the render items.
  113.     std::vector<std::unique_ptr<RenderItem>> mAllRitems;
  114.  
  115.     // Render items divided by PSO.
  116.     std::vector<RenderItem*> mRitemLayer[(int)RenderLayer::Count];
  117.  
  118.     std::unique_ptr<Waves> mWaves;
  119.  
  120.     PassConstants mMainPassCB;
  121.  
  122.     bool mIsWireframe = false;
  123.  
  124.     XMFLOAT3 mEyePos = { 0.0f, 0.0f, 0.0f };
  125.     XMFLOAT4X4 mView = MathHelper::Identity4x4();
  126.     XMFLOAT4X4 mProj = MathHelper::Identity4x4();
  127.  
  128.     float mTheta = 1.5f*XM_PI;
  129.     float mPhi = XM_PIDIV2 - 0.1f;
  130.     float mRadius = 50.0f;
  131.  
  132.     float mSunTheta = 1.25f*XM_PI;
  133.     float mSunPhi = XM_PIDIV4;
  134.  
  135.     POINT mLastMousePos;
  136. };
  137.  
  138. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
  139.     PSTR cmdLine, int showCmd)
  140. {
  141.     // Enable run-time memory check for debug builds.
  142. #if defined(DEBUG) | defined(_DEBUG)
  143.     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  144. #endif
  145.  
  146.     try
  147.     {
  148.         LandAndWavesApp theApp(hInstance);
  149.         if(!theApp.Initialize())
  150.             return 0;
  151.  
  152.         return theApp.Run();
  153.     }
  154.     catch(DxException& e)
  155.     {
  156.         MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
  157.         return 0;
  158.     }
  159. }
  160.  
  161. LandAndWavesApp::LandAndWavesApp(HINSTANCE hInstance)
  162.     : D3DApp(hInstance)
  163. {
  164. }
  165.  
  166. LandAndWavesApp::~LandAndWavesApp()
  167. {
  168.     if(md3dDevice != nullptr)
  169.         FlushCommandQueue();
  170. }
  171.  
  172. bool LandAndWavesApp::Initialize()
  173. {
  174.     if(!D3DApp::Initialize())
  175.         return false;
  176.  
  177.     // Reset the command list to prep for initialization commands.
  178.     ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
  179.  
  180.     mWaves = std::make_unique<Waves>(128, 128, 1.0f, 0.03f, 4.0f, 0.2f);
  181.  
  182.     BuildRootSignature();
  183.     BuildShadersAndInputLayout();
  184.     BuildLandGeometry();
  185.     BuildWavesGeometryBuffers();
  186.     BuildRenderItems();
  187.     BuildRenderItems();
  188.     BuildFrameResources();
  189.     BuildPSOs();
  190.  
  191.     // Execute the initialization commands.
  192.     ThrowIfFailed(mCommandList->Close());
  193.     ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
  194.     mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
  195.  
  196.     // Wait until initialization is complete.
  197.     FlushCommandQueue();
  198.  
  199.     return true;
  200. }
  201.  
  202. void LandAndWavesApp::OnResize()
  203. {
  204.     D3DApp::OnResize();
  205.  
  206.     // The window resized, so update the aspect ratio and recompute the projection matrix.
  207.     XMMATRIX P = XMMatrixPerspectiveFovLH(0.25f*MathHelper::Pi, AspectRatio(), 1.0f, 1000.0f);
  208.     XMStoreFloat4x4(&mProj, P);
  209. }
  210.  
  211. void LandAndWavesApp::Update(const GameTimer& gt)
  212. {
  213.     OnKeyboardInput(gt);
  214.     UpdateCamera(gt);
  215.  
  216.     // Cycle through the circular frame resource array.
  217.     mCurrFrameResourceIndex = (mCurrFrameResourceIndex + 1) % gNumFrameResources;
  218.     mCurrFrameResource = mFrameResources[mCurrFrameResourceIndex].get();
  219.  
  220.     // Has the GPU finished processing the commands of the current frame resource?
  221.     // If not, wait until the GPU has completed commands up to this fence point.
  222.     if(mCurrFrameResource->Fence != 0 && mFence->GetCompletedValue() < mCurrFrameResource->Fence)
  223.     {
  224.         HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
  225.         ThrowIfFailed(mFence->SetEventOnCompletion(mCurrFrameResource->Fence, eventHandle));
  226.         WaitForSingleObject(eventHandle, INFINITE);
  227.         CloseHandle(eventHandle);
  228.     }
  229.  
  230.     UpdateObjectCBs(gt);
  231.     UpdateMainPassCB(gt);
  232.     UpdateWaves(gt);
  233. }
  234.  
  235. void LandAndWavesApp::Draw(const GameTimer& gt)
  236. {
  237.     auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;
  238.  
  239.     // Reuse the memory associated with command recording.
  240.     // We can only reset when the associated command lists have finished execution on the GPU.
  241.     ThrowIfFailed(cmdListAlloc->Reset());
  242.  
  243.     // A command list can be reset after it has been added to the command queue via ExecuteCommandList.
  244.     // Reusing the command list reuses memory.
  245.     if(mIsWireframe)
  246.     {
  247.         ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque_wireframe"].Get()));
  248.     }
  249.     else
  250.     {
  251.         ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));
  252.     }
  253.  
  254.     mCommandList->RSSetViewports(1, &mScreenViewport);
  255.     mCommandList->RSSetScissorRects(1, &mScissorRect);
  256.  
  257.     // Indicate a state transition on the resource usage.
  258.     mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
  259.         D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
  260.  
  261.     // Clear the back buffer and depth buffer.
  262.     mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
  263.     mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
  264.  
  265.     // Specify the buffers we are going to render to.
  266.     mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());
  267.  
  268.     mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
  269.  
  270.     // Bind per-pass constant buffer.  We only need to do this once per-pass.
  271.     auto passCB = mCurrFrameResource->PassCB->Resource();
  272.     mCommandList->SetGraphicsRootConstantBufferView(1, passCB->GetGPUVirtualAddress());
  273.  
  274.     DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
  275.  
  276.     // Indicate a state transition on the resource usage.
  277.     mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
  278.         D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
  279.  
  280.     // Done recording commands.
  281.     ThrowIfFailed(mCommandList->Close());
  282.  
  283.     // Add the command list to the queue for execution.
  284.     ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
  285.     mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
  286.  
  287.     // Swap the back and front buffers
  288.     ThrowIfFailed(mSwapChain->Present(0, 0));
  289.     mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;
  290.  
  291.     // Advance the fence value to mark commands up to this fence point.
  292.     mCurrFrameResource->Fence = ++mCurrentFence;
  293.  
  294.     // Add an instruction to the command queue to set a new fence point. 
  295.     // Because we are on the GPU timeline, the new fence point won't be 
  296.     // set until the GPU finishes processing all the commands prior to this Signal().
  297.     mCommandQueue->Signal(mFence.Get(), mCurrentFence);
  298. }
  299.  
  300. void LandAndWavesApp::OnMouseDown(WPARAM btnState, int x, int y)
  301. {
  302.     mLastMousePos.x = x;
  303.     mLastMousePos.y = y;
  304.  
  305.     SetCapture(mhMainWnd);
  306. }
  307.  
  308. void LandAndWavesApp::OnMouseUp(WPARAM btnState, int x, int y)
  309. {
  310.     ReleaseCapture();
  311. }
  312.  
  313. void LandAndWavesApp::OnMouseMove(WPARAM btnState, int x, int y)
  314. {
  315.     if((btnState & MK_LBUTTON) != 0)
  316.     {
  317.         // Make each pixel correspond to a quarter of a degree.
  318.         float dx = XMConvertToRadians(0.25f*static_cast<float>(x - mLastMousePos.x));
  319.         float dy = XMConvertToRadians(0.25f*static_cast<float>(y - mLastMousePos.y));
  320.  
  321.         // Update angles based on input to orbit camera around box.
  322.         mTheta += dx;
  323.         mPhi += dy;
  324.  
  325.         // Restrict the angle mPhi.
  326.         mPhi = MathHelper::Clamp(mPhi, 0.1f, MathHelper::Pi - 0.1f);
  327.     }
  328.     else if((btnState & MK_RBUTTON) != 0)
  329.     {
  330.         // Make each pixel correspond to 0.2 unit in the scene.
  331.         float dx = 0.2f*static_cast<float>(x - mLastMousePos.x);
  332.         float dy = 0.2f*static_cast<float>(y - mLastMousePos.y);
  333.  
  334.         // Update the camera radius based on input.
  335.         mRadius += dx - dy;
  336.  
  337.         // Restrict the radius.
  338.         mRadius = MathHelper::Clamp(mRadius, 5.0f, 150.0f);
  339.     }
  340.  
  341.     mLastMousePos.x = x;
  342.     mLastMousePos.y = y;
  343. }
  344.  
  345. void LandAndWavesApp::OnKeyboardInput(const GameTimer& gt)
  346. {
  347.     if(GetAsyncKeyState('1') & 0x8000)
  348.         mIsWireframe = true;
  349.     else
  350.         mIsWireframe = false;
  351. }
  352.  
  353. void LandAndWavesApp::UpdateCamera(const GameTimer& gt)
  354. {
  355.     // Convert Spherical to Cartesian coordinates.
  356.     mEyePos.x = mRadius*sinf(mPhi)*cosf(mTheta);
  357.     mEyePos.z = mRadius*sinf(mPhi)*sinf(mTheta);
  358.     mEyePos.y = mRadius*cosf(mPhi);
  359.  
  360.     // Build the view matrix.
  361.     XMVECTOR pos = XMVectorSet(mEyePos.x, mEyePos.y, mEyePos.z, 1.0f);
  362.     XMVECTOR target = XMVectorZero();
  363.     XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
  364.  
  365.     XMMATRIX view = XMMatrixLookAtLH(pos, target, up);
  366.     XMStoreFloat4x4(&mView, view);
  367. }
  368.  
  369. void LandAndWavesApp::UpdateObjectCBs(const GameTimer& gt)
  370. {
  371.     auto currObjectCB = mCurrFrameResource->ObjectCB.get();
  372.     for(auto& e : mAllRitems)
  373.     {
  374.         // Only update the cbuffer data if the constants have changed.  
  375.         // This needs to be tracked per frame resource.
  376.         if(e->NumFramesDirty > 0)
  377.         {
  378.             XMMATRIX world = XMLoadFloat4x4(&e->World);
  379.  
  380.             ObjectConstants objConstants;
  381.             XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
  382.  
  383.             currObjectCB->CopyData(e->ObjCBIndex, objConstants);
  384.  
  385.             // Next FrameResource need to be updated too.
  386.             e->NumFramesDirty--;
  387.         }
  388.     }
  389. }
  390.  
  391. void LandAndWavesApp::UpdateMainPassCB(const GameTimer& gt)
  392. {
  393.     XMMATRIX view = XMLoadFloat4x4(&mView);
  394.     XMMATRIX proj = XMLoadFloat4x4(&mProj);
  395.  
  396.     XMMATRIX viewProj = XMMatrixMultiply(view, proj);
  397.     XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
  398.     XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
  399.     XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
  400.  
  401.     XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
  402.     XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));
  403.     XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
  404.     XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
  405.     XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
  406.     XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
  407.     mMainPassCB.EyePosW = mEyePos;
  408.     mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);
  409.     mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
  410.     mMainPassCB.NearZ = 1.0f;
  411.     mMainPassCB.FarZ = 1000.0f;
  412.     mMainPassCB.TotalTime = gt.TotalTime();
  413.     mMainPassCB.DeltaTime = gt.DeltaTime();
  414.  
  415.     auto currPassCB = mCurrFrameResource->PassCB.get();
  416.     currPassCB->CopyData(0, mMainPassCB);
  417. }
  418.  
  419. void LandAndWavesApp::UpdateWaves(const GameTimer& gt)
  420. {
  421.     // Every quarter second, generate a random wave.
  422.     static float t_base = 0.0f;
  423.     if((mTimer.TotalTime() - t_base) >= 0.25f)
  424.     {
  425.         t_base += 0.25f;
  426.  
  427.         int i = MathHelper::Rand(4, mWaves->RowCount() - 5);
  428.         int j = MathHelper::Rand(4, mWaves->ColumnCount() - 5);
  429.  
  430.         float r = MathHelper::RandF(0.2f, 0.5f);
  431.  
  432.         mWaves->Disturb(i, j, r);
  433.     }
  434.  
  435.     // Update the wave simulation.
  436.     mWaves->Update(gt.DeltaTime());
  437.  
  438.     // Update the wave vertex buffer with the new solution.
  439.     auto currWavesVB = mCurrFrameResource->WavesVB.get();
  440.     for(int i = 0; i < mWaves->VertexCount(); ++i)
  441.     {
  442.         Vertex v;
  443.  
  444.         v.Pos = mWaves->Position(i);
  445.         v.Color = XMFLOAT4(DirectX::Colors::Blue);
  446.  
  447.         currWavesVB->CopyData(i, v);
  448.     }
  449.  
  450.     // Set the dynamic VB of the wave renderitem to the current frame VB.
  451.     mWavesRitem->Geo->VertexBufferGPU = currWavesVB->Resource();
  452. }
  453.  
  454. void LandAndWavesApp::BuildRootSignature()
  455. {
  456.     // Root parameter can be a table, root descriptor or root constants.
  457.     CD3DX12_ROOT_PARAMETER slotRootParameter[2];
  458.  
  459.     // Create root CBV.
  460.     slotRootParameter[0].InitAsConstantBufferView(0);
  461.     slotRootParameter[1].InitAsConstantBufferView(1);
  462.  
  463.     // A root signature is an array of root parameters.
  464.     CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(2, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
  465.  
  466.     // create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
  467.     ComPtr<ID3DBlob> serializedRootSig = nullptr;
  468.     ComPtr<ID3DBlob> errorBlob = nullptr;
  469.     HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
  470.         serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
  471.  
  472.     if(errorBlob != nullptr)
  473.     {
  474.         ::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
  475.     }
  476.     ThrowIfFailed(hr);
  477.  
  478.     ThrowIfFailed(md3dDevice->CreateRootSignature(
  479.         0,
  480.         serializedRootSig->GetBufferPointer(),
  481.         serializedRootSig->GetBufferSize(),
  482.         IID_PPV_ARGS(mRootSignature.GetAddressOf())));
  483. }
  484.  
  485. void LandAndWavesApp::BuildShadersAndInputLayout()
  486. {
  487.     mShaders["standardVS"] = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "VS", "vs_5_0");
  488.     mShaders["opaquePS"] = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "PS", "ps_5_0");
  489.  
  490.     mInputLayout =
  491.     {
  492.         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
  493.         { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
  494.     };
  495. }
  496.  
  497. void LandAndWavesApp::BuildLandGeometry()
  498. {
  499.     GeometryGenerator geoGen;
  500.     GeometryGenerator::MeshData grid = geoGen.CreateGrid(160.0f, 160.0f, 50, 50);
  501.  
  502.     //
  503.     // Extract the vertex elements we are interested and apply the height function to
  504.     // each vertex.  In addition, color the vertices based on their height so we have
  505.     // sandy looking beaches, grassy low hills, and snow mountain peaks.
  506.     //
  507.  
  508.     std::vector<Vertex> vertices(grid.Vertices.size());
  509.     for(size_t i = 0; i < grid.Vertices.size(); ++i)
  510.     {
  511.         auto& p = grid.Vertices[i].Position;
  512.         vertices[i].Pos = p;
  513.         vertices[i].Pos.y = GetHillsHeight(p.x, p.z);
  514.  
  515.         // Color the vertex based on its height.
  516.         if(vertices[i].Pos.y < -10.0f)
  517.         {
  518.             // Sandy beach color.
  519.             vertices[i].Color = XMFLOAT4(1.0f, 0.96f, 0.62f, 1.0f);
  520.         }
  521.         else if(vertices[i].Pos.y < 5.0f)
  522.         {
  523.             // Light yellow-green.
  524.             vertices[i].Color = XMFLOAT4(0.48f, 0.77f, 0.46f, 1.0f);
  525.         }
  526.         else if(vertices[i].Pos.y < 12.0f)
  527.         {
  528.             // Dark yellow-green.
  529.             vertices[i].Color = XMFLOAT4(0.1f, 0.48f, 0.19f, 1.0f);
  530.         }
  531.         else if(vertices[i].Pos.y < 20.0f)
  532.         {
  533.             // Dark brown.
  534.             vertices[i].Color = XMFLOAT4(0.45f, 0.39f, 0.34f, 1.0f);
  535.         }
  536.         else
  537.         {
  538.             // White snow.
  539.             vertices[i].Color = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  540.         }
  541.     }
  542.     
  543.     const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
  544.  
  545.     std::vector<std::uint16_t> indices = grid.GetIndices16();
  546.     const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
  547.  
  548.     auto geo = std::make_unique<MeshGeometry>();
  549.     geo->Name = "landGeo";
  550.  
  551.     ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  552.     CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);
  553.  
  554.     ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  555.     CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
  556.  
  557.     geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
  558.         mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);
  559.  
  560.     geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
  561.         mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
  562.  
  563.     geo->VertexByteStride = sizeof(Vertex);
  564.     geo->VertexBufferByteSize = vbByteSize;
  565.     geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  566.     geo->IndexBufferByteSize = ibByteSize;
  567.  
  568.     SubmeshGeometry submesh;
  569.     submesh.IndexCount = (UINT)indices.size();
  570.     submesh.StartIndexLocation = 0;
  571.     submesh.BaseVertexLocation = 0;
  572.  
  573.     geo->DrawArgs["grid"] = submesh;
  574.  
  575.     mGeometries["landGeo"] = std::move(geo);
  576. }
  577.  
  578. void LandAndWavesApp::BuildWavesGeometryBuffers()
  579. {
  580.     std::vector<std::uint16_t> indices(3 * mWaves->TriangleCount()); // 3 indices per face
  581.     assert(mWaves->VertexCount() < 0x0000ffff);
  582.  
  583.     // Iterate over each quad.
  584.     int m = mWaves->RowCount();
  585.     int n = mWaves->ColumnCount();
  586.     int k = 0;
  587.     for(int i = 0; i < m - 1; ++i)
  588.     {
  589.         for(int j = 0; j < n - 1; ++j)
  590.         {
  591.             indices[k] = i*n + j;
  592.             indices[k + 1] = i*n + j + 1;
  593.             indices[k + 2] = (i + 1)*n + j;
  594.  
  595.             indices[k + 3] = (i + 1)*n + j;
  596.             indices[k + 4] = i*n + j + 1;
  597.             indices[k + 5] = (i + 1)*n + j + 1;
  598.  
  599.             k += 6; // next quad
  600.         }
  601.     }
  602.  
  603.     UINT vbByteSize = mWaves->VertexCount()*sizeof(Vertex);
  604.     UINT ibByteSize = (UINT)indices.size()*sizeof(std::uint16_t);
  605.  
  606.     auto geo = std::make_unique<MeshGeometry>();
  607.     geo->Name = "waterGeo";
  608.  
  609.     // Set dynamically.
  610.     geo->VertexBufferCPU = nullptr;
  611.     geo->VertexBufferGPU = nullptr;
  612.  
  613.     ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  614.     CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
  615.  
  616.     geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
  617.         mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
  618.  
  619.     geo->VertexByteStride = sizeof(Vertex);
  620.     geo->VertexBufferByteSize = vbByteSize;
  621.     geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  622.     geo->IndexBufferByteSize = ibByteSize;
  623.  
  624.     SubmeshGeometry submesh;
  625.     submesh.IndexCount = (UINT)indices.size();
  626.     submesh.StartIndexLocation = 0;
  627.     submesh.BaseVertexLocation = 0;
  628.  
  629.     geo->DrawArgs["grid"] = submesh;
  630.  
  631.     mGeometries["waterGeo"] = std::move(geo);
  632. }
  633.  
  634. void LandAndWavesApp::BuildPSOs()
  635. {
  636.     D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;
  637.  
  638.     //
  639.     // PSO for opaque objects.
  640.     //
  641.     ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
  642.     opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
  643.     opaquePsoDesc.pRootSignature = mRootSignature.Get();
  644.     opaquePsoDesc.VS =
  645.     {
  646.         reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
  647.         mShaders["standardVS"]->GetBufferSize()
  648.     };
  649.     opaquePsoDesc.PS =
  650.     {
  651.         reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
  652.         mShaders["opaquePS"]->GetBufferSize()
  653.     };
  654.     opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  655.     opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  656.     opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  657.     opaquePsoDesc.SampleMask = UINT_MAX;
  658.     opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  659.     opaquePsoDesc.NumRenderTargets = 1;
  660.     opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
  661.     opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
  662.     opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
  663.     opaquePsoDesc.DSVFormat = mDepthStencilFormat;
  664.     ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));
  665.  
  666.     //
  667.     // PSO for opaque wireframe objects.
  668.     //
  669.  
  670.     D3D12_GRAPHICS_PIPELINE_STATE_DESC opaqueWireframePsoDesc = opaquePsoDesc;
  671.     opaqueWireframePsoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
  672.     ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaqueWireframePsoDesc, IID_PPV_ARGS(&mPSOs["opaque_wireframe"])));
  673. }
  674.  
  675. void LandAndWavesApp::BuildFrameResources()
  676. {
  677.     for(int i = 0; i < gNumFrameResources; ++i)
  678.     {
  679.         mFrameResources.push_back(std::make_unique<FrameResource>(md3dDevice.Get(),
  680.             1, (UINT)mAllRitems.size(), mWaves->VertexCount()));
  681.     }
  682. }
  683.  
  684. void LandAndWavesApp::BuildRenderItems()
  685. {
  686.     auto wavesRitem = std::make_unique<RenderItem>();
  687.     wavesRitem->World = MathHelper::Identity4x4();
  688.     wavesRitem->ObjCBIndex = 0;
  689.     wavesRitem->Geo = mGeometries["waterGeo"].get();
  690.     wavesRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  691.     wavesRitem->IndexCount = wavesRitem->Geo->DrawArgs["grid"].IndexCount;
  692.     wavesRitem->StartIndexLocation = wavesRitem->Geo->DrawArgs["grid"].StartIndexLocation;
  693.     wavesRitem->BaseVertexLocation = wavesRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
  694.  
  695.     mWavesRitem = wavesRitem.get();
  696.  
  697.     mRitemLayer[(int)RenderLayer::Opaque].push_back(wavesRitem.get());
  698.  
  699.     auto gridRitem = std::make_unique<RenderItem>();
  700.     gridRitem->World = MathHelper::Identity4x4();
  701.     gridRitem->ObjCBIndex = 1;
  702.     gridRitem->Geo = mGeometries["landGeo"].get();
  703.     gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  704.     gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
  705.     gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
  706.     gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
  707.  
  708.     mRitemLayer[(int)RenderLayer::Opaque].push_back(gridRitem.get());
  709.  
  710.     mAllRitems.push_back(std::move(wavesRitem));
  711.     mAllRitems.push_back(std::move(gridRitem));
  712. }
  713.  
  714. void LandAndWavesApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems)
  715. {
  716.     UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
  717.  
  718.     auto objectCB = mCurrFrameResource->ObjectCB->Resource();
  719.  
  720.     // For each render item...
  721.     for(size_t i = 0; i < ritems.size(); ++i)
  722.     {
  723.         auto ri = ritems[i];
  724.  
  725.         cmdList->IASetVertexBuffers(0, 1, &ri->Geo->VertexBufferView());
  726.         cmdList->IASetIndexBuffer(&ri->Geo->IndexBufferView());
  727.         cmdList->IASetPrimitiveTopology(ri->PrimitiveType);
  728.  
  729.         D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = objectCB->GetGPUVirtualAddress();
  730.         objCBAddress += ri->ObjCBIndex*objCBByteSize;
  731.  
  732.         cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);
  733.  
  734.         cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
  735.     }
  736. }
  737.  
  738. float LandAndWavesApp::GetHillsHeight(float x, float z)const
  739. {
  740.     return 0.3f*(z*sinf(0.1f*x) + x*cosf(0.1f*z));
  741. }
  742.  
  743. XMFLOAT3 LandAndWavesApp::GetHillsNormal(float x, float z)const
  744. {
  745.     // n = (-df/dx, 1, -df/dz)
  746.     XMFLOAT3 n(
  747.         -0.03f*z*cosf(0.1f*x) - 0.3f*cosf(0.1f*z),
  748.         1.0f,
  749.         -0.3f*sinf(0.1f*x) + 0.03f*x*sinf(0.1f*z));
  750.  
  751.     XMVECTOR unitNormal = XMVector3Normalize(XMLoadFloat3(&n));
  752.     XMStoreFloat3(&n, unitNormal);
  753.  
  754.     return n;
  755. }
  756.