4.16 How to render MultiTexturedCubes in Direct3D12?

This is just a program to use multiple ConstantBufferViews for multiple cubes, and multiple ShaderResourceViews to render a multextured cube.
Note that in this example we create one instance of Direct3D12RootSignature having the number of descriptors for two ConstantBufferViews and having the number of descriptors for twelve ShaderResourceViews, which correspond to two cubes properties, as shown below.
  // Create a rootSignature specified by the arguments 
  // NUMBER_OF_CONSTANT_BUFFER_VIEW and NUMBER_OF_SHADER_RESOURCE_VIEW.
  rootSignature = new Direct3D12RootSignature(*device, 
                                              NUMBER_OF_CONSTANT_BUFFER_VIEW,
                                              NUMBER_OF_SHADER_RESOURCE_VIEW);
Furthermore, corresponding to the rootSignature, we have to create an instance of Direct3D12CommonDescriptorHeap in the following way.
 // Create a commonDescriptorHeap of the size of 
 // NUMBER_OF_CONSTANT_BUFFER_VIEW + NUMBER_OF_SHADER_RESOURCE_VIEW.
 commonDescriptorHeap  = new Direct3D12CommonDescriptorHeap(*device, 
                NUMBER_OF_CONSTANT_BUFFER_VIEW + NUMBER_OF_SHADER_RESOURCE_VIEW);

In the latest library, we have implemented the following C++ classes.
Direct3D12Object
Direct3DX12MultiTexturedShape
Direct3DX12MultiTexturedBox
Direct3DX12MultiTexturedCube
Direct3DX12Shape
Direct3DX12TexturedFace
These classes will hide the complex internal data structures and procedures to create a multitextured shape. For example, you can create a multiTexturedCube in the following way; you really don't have to create explicitly VertexBuffer and IndexBuffer for a cube, and Texture2D, ShaderResourceView objects.
  const wchar_t* pngfiles[NUMBER_OF_CUBE_FACES] = {
    L"1.png",  L"2.png", L"3.png", 
    L"4.png",  L"5.png", L"6.png"};

  size_t cubeIndex = 0;
  //1 Create the first 6-PNG images textured cube.  
  multiTexturedCube[cubeIndex] = new Direct3DX12MultiTexturedCube(*device, width, height,
          pngfiles, _countof(pngfiles));
        
  //2 Upload the PNG-textures of the first cube. 
  multiTexturedCube[cubeIndex]->update(*graphicsCommandList, *commandAllocator, *commandQueue,
          commonDescriptorHeap, CBV_HANDLE + NUMBER_OF_CUBES, synchronizer);
<

The following Direct3D12MultiTexturedCubes is a simple sample program to render two cubes textured by six image files respectively.



/*
 * Direct3D12MultiTexturedCubes.cpp 
 * Copyright (c) 2016 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 */


// 2016/12/20

#define COMMONCONTROLS_V6
#define WIN10

#include <sol/COMInitializer.h>

#include <sol/WCharModuleFileName.h>

#include <sol/direct3d12/DirectX3D12MainView.h>
#include <sol/direct3d12/DirectX3D12View.h>
#include <sol/direct3d12/DirectXMatrix.h>
#include <sol/direct3d12/Direct3D12CommandAllocator.h>
#include <sol/direct3d12/Direct3D12RenderTargetView.h>
#include <sol/direct3d12/Direct3D12DepthStencilView.h>
#include <sol/direct3d12/Direct3D12CommonDescriptorHeap.h>

#include <sol/direct3d12/Direct3D12TransformConstantBufferView.h>

#include <sol/direct3d12/Direct3D12TextureRootSignature.h>
#include <sol/direct3d12/Direct3D12GraphicsCommandList.h>
#include <sol/direct3d12/Direct3D12Fence.h>
#include <sol/direct3d12/Direct3D12PipelineState.h>

#include <sol/direct3d12/D3D12RasterizerDesc.h>
#include <sol/direct3d12/D3D12BlendDesc.h>
#include <sol/direct3d12/D3D12GraphicsPipelineStateDesc.h>
#include <sol/direct3d12/D3D12ResourceBarrier.h>
#include <sol/direct3d12/D3D12Transform.h>
#include <sol/direct3d12/Direct3D12GraphicsCommandList.h>
#include <sol/direct3d12/Direct3D12Synchronizer.h>
#include <sol/direct3d12/Direct3D12Texture2D.h>
#include <sol/direct3d12/Direct3DX12TexturedRectangle.h>
#include <sol/direct3d12/Direct3D12ShaderResourceView.h>
#include <sol/direct3d12/Direct3D12Sampler.h>

#include <sol/direct3d12/Direct3DX12ColoredRectangle.h>
#include <sol/direct3d12/DirectXTransform.h>
#include <sol/direct3d12/Direct3D12Subresources.h>

#include <sol/wic/WICBitmapFileReader.h>

#include <sol/direct3d12/D3D12SubresourceData.h>
#include <sol/direct3d12/D3D12Rectangle.h>
#include <sol/direct3d12/Direct3DX12MultiTexturedCube.h>

#include "resource.h"

namespace SOL {
  
class MainView :public DirectX3D12MainView {
private:
  static const UINT  NUMBER_OF_CUBES                = 2;
  static const UINT  NUMBER_OF_CONSTANT_BUFFER_VIEW = NUMBER_OF_CUBES;
  static const UINT  NUMBER_OF_CUBE_FACES           = 6;
  static const UINT  NUMBER_OF_SHADER_RESOURCE_VIEW = NUMBER_OF_CUBES*NUMBER_OF_CUBE_FACES; //The number of faces of two cubes;
  //Inner class starts.
  class SimpleView :public DirectX3D12View {
  private:
    SmartPtr<Direct3D12CommandAllocator>     commandAllocator;
    SmartPtr<Direct3D12RenderTargetView>     renderTargetView;
    SmartPtr<Direct3D12DepthStencilView>     depthStencilView;
    SmartPtr<Direct3D12RootSignature>        rootSignature;
    SmartPtr<Direct3D12CommonDescriptorHeap> commonDescriptorHeap;
    SmartPtr<Direct3D12TransformConstantBufferView>   constantBufferView[NUMBER_OF_CUBES];
    SmartPtr<Direct3D12GraphicsCommandList>  graphicsCommandList;
    SmartPtr<Direct3D12PipelineState>        pipelineState;
    SmartPtr<Direct3D12Synchronizer>         synchronizer;
    
    SmartPtr<Direct3DX12MultiTexturedCube>   multiTexturedCube[NUMBER_OF_CUBES];
    
    UINT                            frameIndex;
        
    DirectXTransform                worldViewProjection;
    float                           angle;
    StringT<TCHAR>                  directory;

private:
    virtual void createPipelineState(ID3D12Device* device)
    {
      //Create a graphicPipelineStateDes.
      D3D12GraphicsPipelineStateDesc graphicsPipelineStateDesc(*rootSignature);

      UINT count = 0;
      const D3D12_INPUT_ELEMENT_DESC* inputElements = multiTexturedCube[0]->getInputElementDesc(count);
      
      D3D12RasterizerDesc  rasterDesc(
        D3D12_FILL_MODE_SOLID, 
        D3D12_CULL_MODE_NONE, 
        false);
      
      D3D12BlendDesc       blendDesc;
            
      graphicsPipelineStateDesc.setInputLayput(inputElements, count);
      
      graphicsPipelineStateDesc.setRasterizerState(rasterDesc);
      graphicsPipelineStateDesc.setBlendState(blendDesc);
      
      StringT<TCHAR> vsshaderFile = getShaderFilePath(directory, _T("VertexShader.cso"));
      StringT<TCHAR> psshaderFile = getShaderFilePath(directory, _T("PixelShader.cso"));

      graphicsPipelineStateDesc.setVertexShader(vsshaderFile);
      graphicsPipelineStateDesc.setPixelShader(psshaderFile);

      pipelineState = new Direct3D12PipelineState(device, graphicsPipelineStateDesc);
    }
    
    void setDirectXTransform(int cubeIndex)
    {
      int width = 0;
      int height = 0;
      getClientSize(width, height);

      try {    
        XMFLOAT3 pos[NUMBER_OF_CUBES] = {
            XMFLOAT3(-3.0f,  0.0f,   1.0f),
            XMFLOAT3(+3.0f,  0.0f,   1.0f),
        };
        
        DirectXMatrix world =  DirectXMatrix::rotationY( angle );
        DirectXMatrix m = DirectXMatrix::translation(pos[cubeIndex].x, pos[cubeIndex].y, pos[cubeIndex].z);
        world += m;
        
        XMVECTOR eye = XMVectorSet(  2.0f,  4.0f, -7.0f,  0.0f );
        XMVECTOR at  = XMVectorSet(  0.0f,  0.0f,  0.0f,  0.0f );
        XMVECTOR up  = XMVectorSet(  0.0f,  1.0f,  0.0f,  0.0f );

        worldViewProjection.world      = world;
        worldViewProjection.view       = DirectXMatrix::lookAtLH(eye, at, up);
        worldViewProjection.projection = DirectXMatrix::perspectiveFovLH( XM_PIDIV2*0.4f, 
                      width / (FLOAT)height, 0.01f, 100.0f );
       
        constantBufferView[cubeIndex]->update(worldViewProjection);
        
      } catch (Exception& ex) {
        caught(ex);      
      }
    }
    
    //Create a renderTargetView and a depthStencilView.
    void createViews(ID3D12Device* device, IDXGISwapChain3* swapChain,
                  int width, int height)
    {
      renderTargetView = new Direct3D12RenderTargetView(device, swapChain);
      depthStencilView = new Direct3D12DepthStencilView(device,  width, height);
    }
    
    //Delete a renderTargetView and a depthStencilView.
    void deleteViews()
    {
      if (graphicsCommandList == nullptr) {
        throw IException("graphicsCommandList is NULL.")
      }
      graphicsCommandList->setOMRenderTargets(0, nullptr, FALSE, nullptr);
    
      renderTargetView = NULL;
      depthStencilView = NULL;
    }
    
    // Create two PNG or JPG textured cubes.
    void    createMultiTexturedCubes()
    {
      int width = 0;
      int height = 0;
      validateClientSize(width, height);
      Direct3D12Device*    device = getD3D12Device();
      DirectXGISwapChain3* swapChain = getSwapChain3();
      Direct3D12CommandQueue* commandQueue = getCommandQueue();
      try {
        const wchar_t* pngfiles[NUMBER_OF_CUBE_FACES] = {
          L"1.png",  L"2.png", L"3.png", 
          L"4.png",  L"5.png", L"6.png",
        };

        size_t cubeIndex = 0;
        //1 Create the first 6-PNG images textured cube.  
        multiTexturedCube[cubeIndex] = new Direct3DX12MultiTexturedCube(*device, width, height,
          pngfiles, _countof(pngfiles));
        
        //2 Upload the PNG-textures of the first cube. 
        multiTexturedCube[cubeIndex]->update(*graphicsCommandList, *commandAllocator, *commandQueue,
                  commonDescriptorHeap, CBV_HANDLE + NUMBER_OF_CUBES, synchronizer);
        
        const wchar_t* jpgfiles[NUMBER_OF_CUBE_FACES] = {
          L"1.jpg",  L"2.jpg", L"3.jpg", 
          L"4.jpg",  L"5.jpg", L"6.jpg",
        };

        cubeIndex++;
        //3 Create the first 6-JPG images textured cube.  
        multiTexturedCube[cubeIndex] = new Direct3DX12MultiTexturedCube(*device, width, height,
          jpgfiles, _countof(jpgfiles));
          
        //4 Upload the JPG-textures of the second cube.
        multiTexturedCube[cubeIndex]->update(*graphicsCommandList, *commandAllocator, *commandQueue,
                  commonDescriptorHeap, 
                  CBV_HANDLE + NUMBER_OF_CUBES + NUMBER_OF_CUBE_FACES,
                  synchronizer);
      } catch (Exception& ex) {
        caught(ex);
      }
    }
    
  public:
    
    virtual void initialize()
    {
      int width  = 0;
      int height = 0;
      validateClientSize(width, height);
      Direct3D12Device*    device    = getD3D12Device();
      DirectXGISwapChain3* swapChain = getSwapChain3();
      Direct3D12CommandQueue* commandQueue = getCommandQueue();
      
      try {
        // 1 Create a commandAllocator
        commandAllocator         = new Direct3D12CommandAllocator(*device);
        
        //2 Create a graphicsCommandList
        graphicsCommandList      = new Direct3D12GraphicsCommandList(*device, *commandAllocator);

        //3 Create a rootSignature specified by the arguments 
        // NUMBER_OF_CONSTANT_BUFFER_VIEW and NUMBER_OF_SHADER_RESOURCE_VIEW.
        rootSignature            = new Direct3D12RootSignature(*device, 
                                                      NUMBER_OF_CONSTANT_BUFFER_VIEW,
                                                      NUMBER_OF_SHADER_RESOURCE_VIEW);
        //4 Create a synchronizer
        synchronizer             = new Direct3D12Synchronizer(*device, *commandQueue);

        //5 Create renderTargetView and depthStencilView
        createViews(*device, *swapChain, width, height);
        
        //6 Create a commonDescriptorHeap of the size of 
        // NUMBER_OF_CONSTANT_BUFFER_VIEW + NUMBER_OF_SHADER_RESOURCE_VIEW.
        commonDescriptorHeap     = new Direct3D12CommonDescriptorHeap(*device, 
                NUMBER_OF_CONSTANT_BUFFER_VIEW + NUMBER_OF_SHADER_RESOURCE_VIEW);
 
        //7 Create constantBufferViews of the number of NUMBER_OF_CUBES
        for (UINT i = 0; i < NUMBER_OF_CUBES; i++) {
          constantBufferView[i]  = new Direct3D12TransformConstantBufferView(*device,
                                                  commonDescriptorHeap->getCPUHandle(CBV_HANDLE + i));
        }
        //8 Create multiTexturedCubes
        createMultiTexturedCubes();

        //9 Create a pipelineState
        createPipelineState(*device);
        

      } catch (Exception& ex) {
        caught(ex);
      }
    }

    bool ready()
    {
      Direct3D12Device*    device          = getD3D12Device();
      DirectXGISwapChain3* swapChain       = getSwapChain3();
      Direct3D12CommandQueue* commandQueue = getCommandQueue();
      
      if (
        device                    == nullptr ||
        commandQueue              == nullptr ||
        swapChain                 == nullptr ||
        
        commandAllocator          == nullptr ||
        renderTargetView          == nullptr ||
        depthStencilView          == nullptr ||

        rootSignature             == nullptr ||

        constantBufferView        == nullptr ||

        graphicsCommandList       == nullptr ||
        pipelineState             == nullptr ||
        synchronizer              == nullptr ) {

        return false;
      }
      return true;
    }
    
    virtual void display()
    {
      int width  = 0;
      int height = 0;
      validateClientSize(width, height);
      if ( !ready() ) {
        return;
      }

      try {
        Direct3D12Device*       device       = getD3D12Device();
        DirectXGISwapChain3*    swapChain    = getSwapChain3();
        Direct3D12CommandQueue* commandQueue = getCommandQueue();

        commandAllocator    -> reset();
        
        graphicsCommandList->reset(*commandAllocator, nullptr);

        frameIndex = swapChain -> getCurrentBackBufferIndex();

        D3D12ResourceBarrier barrier(renderTargetView->getResource(frameIndex));

        barrier.startRendering();

        graphicsCommandList->resourceBarrier(1, barrier);
                 
        graphicsCommandList->setDescriptorHeap(*commonDescriptorHeap);

        graphicsCommandList->setGraphicsRootSignature(*rootSignature);

        graphicsCommandList->setPipelineState(*pipelineState);
        
        
        graphicsCommandList -> setRSViewport(width, height);

        graphicsCommandList -> setRSScissorRect(width, height);

        D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = renderTargetView->getHandle(frameIndex);
                
        graphicsCommandList->clearRenderTargetView(rtvHandle, XMColor(0.0f, 0.0f, 0.0f, 1.0f));

        D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = *depthStencilView;
        
        graphicsCommandList->clearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH);
        
        graphicsCommandList->setOMRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
        
        for (size_t i = 0; i < NUMBER_OF_CUBES; i++) {
          //Update i-th constantBufferView by using WorldViewProjection system.
          setDirectXTransform(i);
          //Set i-th GPU commonDescriptorHeap address to the graphicsCommandList
          graphicsCommandList->setGraphicsRootDescriptorTable(CBV_HANDLE, 
                  commonDescriptorHeap->getGPUHandle(CBV_HANDLE + i));  //ConstantBufferView
          //Draw i-th multiTexturedCube
          multiTexturedCube[i] -> draw(graphicsCommandList, commonDescriptorHeap);
        }
        
        barrier.endRendering();
        
        graphicsCommandList->resourceBarrier(1, barrier);

        graphicsCommandList->close();

        commandQueue->executeCommandList(*graphicsCommandList);

        swapChain->present(1, 0);
        
        synchronizer->waitForCompletion();

      } catch (Exception& ex) {
        caught(ex);
      }
    }
    
    virtual void resize(int width, int height)
    {
      Direct3D12Device*   device = getD3D12Device();
      DirectXGISwapChain3* swapChain = getSwapChain3();
      if (device           == NULL || 
          swapChain        == NULL) { 
        return ;
      }
      
      try {
        deleteViews();
        
        swapChain->resizeBuffers(width, height);

        createViews(*device, *swapChain, width, height);
        
      } catch (Exception& ex) {
        caught(ex);
      }
    }
        
  public:
    //Constructor
    SimpleView(DirectX3D12MainView* parent, const TCHAR* name, Args& args)
    :DirectX3D12View(parent, name, args),
    angle (1.0f),
    frameIndex(0),
    directory(_T(""))
    {
      directory = (const TCHAR*)args.get(XmNapplicationDirectory);

      postResizeRequest();
    }
    
    ~SimpleView()
    {
    }
  };
  // Inner class ends.
  
private:
  SmartPtr<SimpleView> view;

public:
  /**
   * Constructor
   */
  MainView(Application& applet, const TCHAR* name, Args& args)
  :DirectX3D12MainView(applet, name,
                 args.set(XmNstyle, (ulong)WS_CLIPCHILDREN|WS_CLIPSIBLINGS) ),
  view(NULL)
  {
    const TCHAR* directory = (const TCHAR*)args.get(XmNapplicationDirectory);

    // 1 Restore the replacement of MainView 
    restorePlacement();

    // 2 Create a view of SimpleView.
    Args ar;
    int width  = 0;
    int height = 0;
    getClientSize(width, height);

    ar.set(XmNwidth, width);
    ar.set(XmNheight,height);
    ar.set(XmNapplicationDirectory, (const TCHAR*)directory);
    view = new SimpleView(this, _T(""), ar);

    // 3 Post a resize request to this MainView.
    postResizeRequest();
  }

public:
  ~MainView()
  {
  }

  void resize(int width, int height)
  {
    if (view != nullptr) {
      view -> reshape(0, 0, width, height);      
    }
  }  
};
}


//////////////////////////////////////////////
//
void  Main(int argc, TCHAR** argv)
{
  TCHAR directory[MAX_PATH];
  appDirectory(argv[0], directory, CountOf(directory));

  const TCHAR* appClass = appName(argv[0]); 
  try {
    //For DXGI components.
    COMInitializer initializer( COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    Application applet(appClass, argc, argv);

    Args args;
    args.set(XmNwidth,  480);
    args.set(XmNheight, 480);
    args.set(XmNapplicationDirectory, directory);

    MainView mainView(applet, appClass, args);
    mainView.realize();

    applet.run();
  } catch (Exception& ex) {
    caught(ex);
  }
}


Last modified: 18 Dec 2016

Copyright (c) 2016 Antillia.com ALL RIGHTS RESERVED.