4.17 How to render a star system model in Direct3D12?

This is just a program to use multiple ConstantBufferViews for multiple spheres.
Note that in this example we create one instance of Direct3D12RootSignature having the number of descriptors for five ConstantBufferViews which describe WordViewProjection system properties for a star sphere and four planet spheres. ,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_SPHERES
 commonDescriptorHeap  = new Direct3D12CommonDescriptorHeap(*device,  NUMBER_OF_SPHERES);
Please note that in setPlanetDirectXTransform method, each planet sphere has been rendered with a color and light on a positioin got from the getPlanetOrbitPosition method, by using planetWorldViewProjection and planetConstantBufferView. However, for simplicity, we do not handle properly the reflection light from the sun for the planet.
The following Direct3D12ScintillaStarSystem is a simple sample program to render a simple star system model. This is a really very elementary program, but it may become a slightly interesting Direct3D12 stress test by increasing the number of planets in this example.






/*
 * Direct3D12ScintillaStarSystem.cpp 
 * Copyright (c) 2017 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 */


#define COMMONCONTROLS_V6
#define WIN10

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

#include <sol/direct3d12/Direct3D12TransformLightConstantBufferView.h>

#include <sol/direct3d12/Direct3D12RootSignature.h>
#include <sol/direct3d12/Direct3D12GraphicsCommandList.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/D3D12GraphicsPipelineStateDesc.h>

#include <sol/direct3d12/Direct3D12Synchronizer.h>

#include <sol/direct3d12/Direct3DX12Sphere.h>
#include <sol/direct3d12/Direct3DX12Circle.h>

#include <sol/direct3d12/DirectXTransformLight.h>
#include <sol/direct3d12/DirectX3D12TimerThread.h>

#include "resource.h"

namespace SOL {
  
class MainView :public DirectX3D12MainView {
private:
  static const int NUMBER_OF_SPHERES = 5;
  static const int NUMBER_OF_PLANETS = 4;
  
  static const int NUMBER_OF_CONSTANT_BUFFER_VIEWS = NUMBER_OF_SPHERES;
  static const int NUMBER_OF_SHADER_RESOURCE_VIEWS = 1;
  
  //Inner class starts.
  class SimpleView :public DirectX3D12View {
  private:
    SmartPtr<Direct3D12CommandAllocator>     commandAllocator;
    SmartPtr<Direct3D12RenderTargetView>     renderTargetView;
    SmartPtr<Direct3D12DepthStencilView>     depthStencilView;
    SmartPtr<Direct3D12RootSignature>        rootSignature;
    SmartPtr<Direct3D12CommonDescriptorHeap>  commonDescriptorHeap;
    SmartPtr<Direct3D12TransformLightConstantBufferView>   sunConstantBufferView;
    SmartPtr<Direct3D12TransformLightConstantBufferView>   planetConstantBufferViews[NUMBER_OF_PLANETS];
    SmartPtr<Direct3D12GraphicsCommandList>  graphicsCommandList;
    SmartPtr<Direct3D12PipelineState>        pipelineState;
    
    SmartPtr<Direct3D12Synchronizer>         synchronizer;
    
    SmartPtr<Direct3DX12Sphere>              sun;
    SmartPtr<Direct3DX12Sphere>              planets[NUMBER_OF_PLANETS];
    SmartPtr<Direct3DX12Circle>              orbits [NUMBER_OF_PLANETS];

    UINT                              frameIndex;
    
    SmartPtr<DirectX3D12TimerThread>  timerThread;
    int                               renderingInterval;
    
    DirectXTransformLight             sunWorldViewProjection;
    
    DirectXTransformLight             planetWorldViewProjections[NUMBER_OF_PLANETS];
 
    float                             angles[NUMBER_OF_PLANETS];
    
    StringT<TCHAR>                    directory;

    static const int                  CIRCLE_ANGLE = 360;
    
  private:
    void createPipelineStates(ID3D12Device* device)
    {
      D3D12RasterizerDesc  solid( D3D12_FILL_MODE_SOLID,  D3D12_CULL_MODE_NONE, true);
      D3D12BlendDesc       blendDesc;
      UINT count = 0;
      const D3D12_INPUT_ELEMENT_DESC* inputElements = sun->getInputElementDesc(count);
        
      StringT<TCHAR> vsshaderFile = getShaderFilePath(directory, _T("VertexShader.cso"));
        
      StringT<TCHAR> psshaderFile = getShaderFilePath(directory, _T("PixelShader.cso"));
      //Create a pipelineState by using a new constructor(2017/01/10).
      pipelineState = new Direct3D12PipelineState(
              device,
              *rootSignature,
              inputElements,
              count,
              solid,
              blendDesc,
              vsshaderFile,
              psshaderFile);
    }
      
    XMFLOAT3 getPlanetOrbitPosition(int pindex, float angle)
    {
      if (pindex >= 0 && pindex <NUMBER_OF_PLANETS) {
        return orbits[pindex]->getOrbitPosition(angle);
      } else {
        throw IException("Invalid planet index.");
      }
    }

    //Set the constantBufferView to the sun of sphere[0].
    void setSunDirectXTransform()
    {
      int width  = 0;
      int height = 0;
      getClientSize(width, height);
      int index = 0;
      
      try {
        XMVECTOR eye = XMVectorSet(0.0f, 2.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);

        sunWorldViewProjection.world = DirectXMatrix::rotationY((float)0.0f);
        sunWorldViewProjection.view = DirectXMatrix::lookAtLH(eye, at, up);
        sunWorldViewProjection.projection = DirectXMatrix::perspectiveFovLH( XM_PIDIV2*0.3f, 
            width / (FLOAT)height, 0.01f, 100.0f );
        sunWorldViewProjection.lightDirection = XMFLOAT4( 0.0f, 1.0f, 0.0f, 1.0f );
        sunWorldViewProjection.lightColor = XMFLOAT4(1.0f, 0.2f, 0.0f, 1.0f);

        if (sunConstantBufferView) {
          sunConstantBufferView->update(sunWorldViewProjection);
        }
      } catch (Exception& ex) {
        caught(ex);      
      }
    }
    
    //Set a constantBufferView to each planet (index-th sphere). 
    void setPlanetDirectXTransform(int index, float angle)
    {
      int width  = 0;
      int height = 0;
      getClientSize(width, height);
      if (index >=0 && index < NUMBER_OF_PLANETS) {
        try {
          // Get a position on an orbit.
          XMFLOAT3 position = getPlanetOrbitPosition(index, angle);
          //Translate the position of the planet.      
          DirectXMatrix world = DirectXMatrix::scaling( 1.0f, 1.0f, 1.0f);
          DirectXMatrix m = DirectXMatrix::translation(position.x, position.y, position.z);
          world += m;
 
          planetWorldViewProjections[index].world = world;
    
          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, 1.0f, 0.0f);

          planetWorldViewProjections[index].view = DirectXMatrix::lookAtLH(eye, at, up);
          planetWorldViewProjections[index].projection = DirectXMatrix::perspectiveFovLH( XM_PIDIV2*0.3f, 
            width / (FLOAT)height, 0.01f, 100.0f );
        
          planetWorldViewProjections[index].lightDirection = XMFLOAT4( 0.0f, -0.0f, 1.0f, 1.0f );
          XMFLOAT4 planetColors[NUMBER_OF_PLANETS] = {
            XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f),
            XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f),
            XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f),
            XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f)
          };
          planetWorldViewProjections[index].lightColor = planetColors[index];

          if (planetConstantBufferViews[index]) {
            planetConstantBufferViews[index]->update(planetWorldViewProjections[index]);
          }
        } catch (Exception& ex) {
         caught(ex);      
        }
      }
    }
 
  public:
    //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)
      graphicsCommandList->setOMRenderTargets(0, nullptr, FALSE, nullptr);
      renderTargetView = NULL;
      depthStencilView = NULL;
    }
    
    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);

        renderTargetView = new Direct3D12RenderTargetView(*device, *swapChain);

        depthStencilView = new Direct3D12DepthStencilView(*device, width, height);

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

        //1 Create rootSignature from specified parameters:
        // NUMBER_OF_CONSTANT_BUFFER_VIEWS and NUMBER_OF_SHADER_RESOURCE_VIEWS
        rootSignature = new Direct3D12RootSignature(*device,
          NUMBER_OF_CONSTANT_BUFFER_VIEWS, NUMBER_OF_SHADER_RESOURCE_VIEWS);

        //2 Create commonDescriptorHeap of size of NUMBER_OF_SPHERES.
        commonDescriptorHeap = new Direct3D12CommonDescriptorHeap(*device, NUMBER_OF_SPHERES);

        //3 Create constantBufferViews for the spheres of NUMBER_OF_SPHERES.
        sunConstantBufferView = new Direct3D12TransformLightConstantBufferView(*device,
          commonDescriptorHeap->getCPUHandle(CBV_HANDLE));

        for (size_t i = 0; i < NUMBER_OF_PLANETS; i++) {
          planetConstantBufferViews[i] = new Direct3D12TransformLightConstantBufferView(*device,
            commonDescriptorHeap->getCPUHandle(CBV_HANDLE + 1 + i));
        }

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

        //4 Create a renderTargetView and depthStencilView
        createViews(*device, *swapChain, width, height);

        //5 Create orbits for 4 planets.
        int index = 0;
        orbits[index++] = new Direct3DX12Circle(*device, 0.0f, 0.0f, 0.0f, 1.9f);
        orbits[index++] = new Direct3DX12Circle(*device, 0.0f, 0.0f, 0.0f, 2.6f);
        orbits[index++] = new Direct3DX12Circle(*device, 0.0f, 0.0f, 0.0f, 3.4f);
        orbits[index  ] = new Direct3DX12Circle(*device, 0.0f, 0.0f, 0.0f, 3.9f);

        //6 Creat a shpere for sun.
        sun = new Direct3DX12Sphere(*device, 0.38f, 20, 20);

        //7 Create 4 spheres for planets.
        index = 0;
        planets[index++] = new Direct3DX12Sphere(*device, 0.10f, 20, 20);
        planets[index++] = new Direct3DX12Sphere(*device, 0.18f, 20, 20);
        planets[index++] = new Direct3DX12Sphere(*device, 0.18f, 20, 20);
        planets[index  ] = new Direct3DX12Sphere(*device, 0.12f, 20, 20);

        //8 Create a pipelineState from the sphere.
        createPipelineStates(*device);

        //9 Set world, view and projection to the constantBuffer.
        setSunDirectXTransform();
        for (size_t i = 0; i < NUMBER_OF_PLANETS; i++) {
         setPlanetDirectXTransform(i, angles[i]);
        }
        //10 Create a rendering TimerThread.
        timerThread = new DirectX3D12TimerThread(this, renderingInterval, true); // queuing
        timerThread -> start();

      } 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 ||
        commonDescriptorHeap      == nullptr ||
        graphicsCommandList       == nullptr ||
        pipelineState             == nullptr ||
        timerThread               == nullptr ||
        synchronizer              == nullptr ) {
        return false;
      }
      return true;
    }
    
    virtual void display()
    {
      int width  = 0;
      int height = 0;
      validateClientSize(width, height);
      if ( !ready() ) {
        return;
      }
      try {
        DirectXGISwapChain3*    swapChain = getSwapChain3();
        Direct3D12CommandQueue* commandQueue = getCommandQueue();
        static const float INCREMENT[NUMBER_OF_PLANETS] = { 2.0f, 1.5f, 1.0f, 0.5f };
        for (int i = 0; i < NUMBER_OF_PLANETS; i++) {
          if (angles[i] < CIRCLE_ANGLE - INCREMENT[i]) {
            angles[i] += INCREMENT[i];
          }
          else {
            angles[i] = INCREMENT[i];
          }
        }
        for (size_t i = 0; i < NUMBER_OF_PLANETS; i++) {
          setPlanetDirectXTransform(i, angles[i]);
        }
        
        commandAllocator    -> reset();
        
        graphicsCommandList -> reset(*commandAllocator, *pipelineState);
        
        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(0, 0, width, height);

        graphicsCommandList -> setRSScissorRect(0, 0, 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);

        graphicsCommandList->setGraphicsRootDescriptorTable(CBV_HANDLE, 
            commonDescriptorHeap->getGPUHandle(CBV_HANDLE));
        sun -> drawIndexedInstanced(graphicsCommandList);

        //Draw the sphere on the worldViewProjection.
        for (size_t i = 0; i<NUMBER_OF_PLANETS; i++) {
          graphicsCommandList->setGraphicsRootDescriptorTable(CBV_HANDLE, 
              commonDescriptorHeap->getGPUHandle(CBV_HANDLE + 1 + i));
          planets[i] -> 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);
      }
    }
    
  public:
    //Constructor
    SimpleView(DirectX3D12MainView* parent, const TCHAR* name, Args& args)
    :DirectX3D12View(parent, name, args),
    //angle (30),
    frameIndex(0),
    directory(_T("")),
    renderingInterval(100)
    {
      for (size_t i = 0; i < NUMBER_OF_PLANETS; i++) {
        angles[i] = 30.0f * i;
      }
      directory = (const TCHAR*)args.get(XmNapplicationDirectory);
      renderingInterval = (int)args.get(XmNrenderingInterval);

      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);
    int renderingInterval  = (int) args.get(XmNrenderingInterval);

    // 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);
    ar.set(XmNrenderingInterval, renderingInterval); 

    ar.set(XmNstyle, WS_BORDER|WS_CHILD|WS_VISIBLE);
    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 {    
    Application applet(appClass, argc, argv);

    Args args;
    args.set(XmNwidth,  480);
    args.set(XmNheight, 480);
    args.set(XmNapplicationDirectory, directory);
    args.set(XmNrenderingInterval, 30); //30ms

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

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



Last modified: 10 Jan 2017

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