4.11 How to render a textured cube in Direct3D12?

  In the lastest SOL9.2.0 library, we have added new classes to support a texture in Direct3D12 on Windows 10 to 'sol/direct3d12' folder.

1. We have updated WICBitmapFileReader to read an image file, and get the pixel data in order to create an intermediate texture resource.
2. We have also implemented Direct3D12Subresoures to upload an intermediate texture resource to a target texture resource.
ComIUnknown
WICBitmapDecoder
WICBitmapFileReader
Direct3D12Object
Direct3D12DeviceChild
Direct3D12Pageable
Direct3D12Resource
Direct3D12Subresources
If you would like to use JPG or PNG APIs to read the image files instead of WICBitmapFileReader, you can use JPGFileReader or PNGFileReader of our SOL9.2.0 library to create an intermediate texture resource.
  The following Direct3D12BMPTexturedCube is a very simple sample program to render a BMP textured cube.



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


// 2016/11/15

#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/Direct3DX12TexturedCube.h>

#include <sol/direct3d12/Direct3D12ShaderResourceView.h>
#include <sol/direct3d12/Direct3D12Sampler.h>

#include <sol/direct3d12/Direct3DX12ColoredCube.h>
#include <sol/direct3d12/DirectXTransform.h>

#include <sol/direct3d12/Direct3D12Subresources.h>

#include <sol/wic/WICBitmapFileReader.h>
#include <sol/direct3d12/D3D12SubresourceData.h>

#include "resource.h"

namespace SOL {
  
class MainView :public DirectX3D12MainView {
  //Inner class 
  class SimpleView :public DirectX3D12View {
  private:
    SmartPtr<Direct3D12CommandAllocator>     commandAllocator;
    SmartPtr<Direct3D12RenderTargetView>     renderTargetView;
    SmartPtr<Direct3D12DepthStencilView>     depthStencilView;
    SmartPtr<Direct3D12TextureRootSignature> rootSignature;
    SmartPtr<Direct3D12CommonDescriptorHeap>   commonDescriptorHeap;
    SmartPtr<Direct3D12TransformConstantBufferView>   constantBufferView;
    SmartPtr<Direct3D12ShaderResourceView>   shaderResourceView;
    SmartPtr<Direct3D12GraphicsCommandList>  graphicsCommandList;
    SmartPtr<Direct3D12PipelineState>        pipelineState;
    SmartPtr<Direct3D12Synchronizer>         synchronizer;
    SmartPtr<Direct3DX12TexturedCube>        texturedCube;
    SmartPtr<Direct3D12Subresources>         subresources;
    SmartPtr<Direct3D12Texture2D>            texture2D;
  
    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 = texturedCube->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 width = 0;
      int height = 0;
      getClientSize(width, height);

      try {        
        XMVECTOR eye = XMVectorSet(  2.0f,  4.0f, -8.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      = DirectXMatrix::rotationY( angle );
        worldViewProjection.view       = DirectXMatrix::lookAtLH(eye, at, up);
        worldViewProjection.projection = DirectXMatrix::perspectiveFovLH( XM_PIDIV2*0.3f, width / (FLOAT)height, 0.01f, 100.0f );
       
        if (constantBufferView != nullptr) {
          constantBufferView->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;
    }
    
    void readImageFile(int width, int height, D3D12SubresourceData& subresource)
    {
      WCharModuleFileName moduleFileName;
      const wchar_t* dir = moduleFileName.getDir();
      wchar_t path[MAX_PATH];
      swprintf_s(path, CountOf(path), L"%s\\..\\image\\%s", dir, L"LightedTorus.bmp");

      WICImagingFactory factory;

      WICBitmapFileReader reader(factory, (const wchar_t*)path);
      
      reader.read(width, height, subresource);
    }
    
  public:
    
    virtual void initialize()
    {
      int width  = 0;
      int height = 0;
      validateClientSize(width, height);
      Direct3D12Device*    device    = getD3D12Device();
      DirectXGISwapChain3* swapChain = getSwapChain3();
      Direct3D12CommandQueue* commandQueue = getCommandQueue();
      
      try {

        commandAllocator         = new Direct3D12CommandAllocator(*device);

        graphicsCommandList      = new Direct3D12GraphicsCommandList(*device, *commandAllocator);

        rootSignature            = new Direct3D12TextureRootSignature(*device);
    
        synchronizer             = new Direct3D12Synchronizer(*device, *commandQueue);

        createViews(*device, *swapChain, width, height);

        texturedCube             = new Direct3DX12TexturedCube(*device); 
        
        D3D12SubresourceData imageResource;
        
        readImageFile((UINT&)width, (UINT&)height, imageResource);
        
        texture2D                = new Direct3D12Texture2D(*device, width, height);
        
        subresources             = new Direct3D12Subresources(*device, *texture2D);
        
        commonDescriptorHeap     = new Direct3D12CommonDescriptorHeap(*device);
 
        constantBufferView       = new Direct3D12TransformConstantBufferView(*device, 
                                    commonDescriptorHeap->getCPUHandle(CBV_HANDLE));
             
        subresources -> update(*graphicsCommandList,
            *commandAllocator,
            *commandQueue,
            imageResource,
            synchronizer);
        
        shaderResourceView         = new Direct3D12ShaderResourceView(*device, 
                                      commonDescriptorHeap->getCPUHandle(SRV_HANDLE),
                                      *texture2D);
      
        createPipelineState(*device);
        
        setDirectXTransform();

      } 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 ||
        texturedCube              == nullptr ||
        texture2D                 == nullptr ||

        subresources              == nullptr ||
        shaderResourceView        == 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();

        worldViewProjection.world = DirectXMatrix::rotationY( angle );

        constantBufferView->update(worldViewProjection);

        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->setGraphicsRootDescriptorTable(0, 
                  commonDescriptorHeap->getGPUHandle(CBV_HANDLE));  //ConstantBufferView
        
        graphicsCommandList->setGraphicsRootDescriptorTable(1,
                  commonDescriptorHeap->getGPUHandle(SRV_HANDLE));  //ShaderResourceView
        
        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);
        
        texturedCube -> drawIndexedInstanced(graphicsCommandList);

        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);
      }
    }
    
    void render()
    {
      angle += 0.02f;
      display();
    }
      
    void incrementAngle()
    {
      angle += 0.1f;
      display();
    }
    
    void decrementAngle()
    {
      angle -= 0.1f;
      display();
    }
    
  public:
    //Constructor
    SimpleView(DirectX3D12MainView* parent, const TCHAR* name, Args& args)
    :DirectX3D12View(parent, name, args),
    angle (0.6f),
    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);      
    }
  }  
  virtual void keyDown(Event& event)
  {
    WPARAM wparam = event.getWParam();
    switch(wparam) {
    case VK_LEFT:
      view -> decrementAngle();
      break;
        
    case VK_RIGHT:
      view -> incrementAngle();
      break;
    }  
  }
};
}


//////////////////////////////////////////////
//
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: 20 Nov 2016

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