It is currently Sun Aug 20, 2017 6:06 pm

All times are UTC - 5 hours




 Page 1 of 1 [ 1 post ] 
Author Message
 Post subject: Abstract Base Classes & Pure Virtual Methods - Inheritance
PostPosted: Wed Nov 14, 2012 4:01 pm 

Joined: Sat Aug 16, 2008 7:58 am
Posts: 448
Ok, I am working a little bit in DirectX11. Im not asking about the SDK I am ok on that. The code I am about to show; without a base class and inheritance does compile, build, run and displays a texture with a light map, but when I try to introduce a base class for my shader classes that does all work that is common between all shader classes and have the derived classes do all the independent setup work, I come across the problem of being unable to instantiate the object because the class is abstract.

Base class ShaderClass.h
#ifndef SHADERCLASS_H
#define SHADERCLASS_H

#include <D3D11.h>
#include <D3DX10math.h>
#include <D3DX11async.h>
#include <fstream>
using namespace std;

class LightMapShaderClass;

class ShaderClass {
protected:
   struct MatrixBufferType {
      D3DXMATRIX world;
      D3DXMATRIX view;
      D3DXMATRIX projection;
   };

   // Two Structs Below Future Possible Use
   /*struct ConstantBufferType {
      D3DXMATRIX world;
      D3DXMATRIX view;
      D3DXMATRIX projection;
   };

   struct PixelBufferType {
      D3DXVECTOR4 pixelColor;
   };*/

   ID3D11Device*      m_pDevice;
   ID3D11DeviceContext*   m_pDeviceContext;
   HWND m_hWnd;
   
   ID3D11VertexShader*    m_pVertexShader;
   ID3D11PixelShader*       m_pPixelShader;
   ID3D11InputLayout*      m_pLayout;
   ID3D11Buffer*           m_pMatrixBuffer;
   //ID3D11Buffer*           m_pConstantBuffer; // Future Possible Use
   //ID3D11Buffer*           m_pPixelBuffer;    //    "
   ID3D11SamplerState*     m_pSampleState;

   WCHAR*        m_vsFilename;
   WCHAR*     m_psFilename;
public:
   ShaderClass();
   //ShaderClass( const ShaderClass& other );
   virtual ~ShaderClass();

   bool initialize( ID3D11Device* pDevice, ID3D11DeviceContext* pDeviceContext, HWND hWnd, WCHAR* vsFilename, WCHAR* psFilename );
   void shutdown();
   bool render( int index, D3DXMATRIX view, D3DXMATRIX world, D3DXMATRIX projection, void* pData );

protected:
   virtual bool initializeShader() = 0;
   virtual void shutdownShader() = 0;
   virtual bool setShaderParameters( D3DXMATRIX world, D3DXMATRIX view, D3DXMATRIX projection, void* pData ) = 0;
   virtual void renderShader( int index ) = 0;

   void outputShaderErrorMessage( ID3D10Blob* pErrorMessage, WCHAR* shaderFilename );

}; // ShaderClass

#endif // SHADERCLASS_H


ShaderClass.cpp
#include "ShaderClass.h"

// ----------------------------------------------------------------------------
ShaderClass::ShaderClass() :
m_pVertexShader( nullptr ),
m_pPixelShader( nullptr ),
m_pLayout( nullptr ),
m_pMatrixBuffer( nullptr ),
//m_pConstantBuffer( nullptr ),
//m_pPixelBuffer( nullptr ),
m_pSampleState( nullptr )
{
} // ShaderClass

// ----------------------------------------------------------------------------
//ShaderClass::ShaderClass( const ShaderClass& other ) {
//} // ShaderClass

// ----------------------------------------------------------------------------
ShaderClass::~ShaderClass() {
} // ~ShaderClass

// ----------------------------------------------------------------------------
bool ShaderClass::initialize( ID3D11Device* pDevice, ID3D11DeviceContext* pDeviceContext, HWND hWnd, WCHAR* vsFilename, WCHAR* psFilename ) {
   bool bResult;

   if ( !pDevice ) {
      return false;
   }
   m_pDevice = pDevice;

   if ( !pDeviceContext ) {
      return false;
   }
   m_pDeviceContext = pDeviceContext;

   m_hWnd = hWnd;

   m_vsFilename = vsFilename;
   m_psFilename = psFilename;

   // Initialize The Vertex And Pixel Shaders
   bResult = initializeShader();
   if ( !bResult ) {
      return false;
   }

   return true;
} // initialize

// ----------------------------------------------------------------------------
void ShaderClass::shutdown() {
   // Shutdown Individual Shader Contents
   shutdownShader();

} // shutdown

// -----------------------------------------------------------------------------
bool ShaderClass::render( int indexCount, D3DXMATRIX world, D3DXMATRIX view, D3DXMATRIX projection, void* pData  ) {
   bool bResult;

   bResult = setShaderParameters( world, view, projection, pData );
   if ( !bResult ) {
      return false;
   }

   renderShader( indexCount );

   return true;
} // render

// ----------------------------------------------------------------------------
void ShaderClass::outputShaderErrorMessage( ID3D10Blob* pErrorMessage, WCHAR* shaderFilename ) {
   char* compileErrors;
   unsigned long bufferSize, i;
   ofstream fout;

   // Get A Pointer To The Error Message Text Buffer
   compileErrors = (char*)(pErrorMessage->GetBufferPointer());

   // Get The Length Of The Message
   bufferSize = pErrorMessage->GetBufferSize();

   // Open A File To Write The Error Message
   fout.open( "shader-error.txt" );

   // Write Out The Error Message
   for ( i = 0; i < bufferSize; i++ ) {
      fout << compileErrors[i];
   }

   // Close The File
   fout.close();

   // Release The Error Message
   pErrorMessage->Release();
   pErrorMessage = nullptr;

   // Pop A Message To Notify The User
   MessageBox( m_hWnd, L"Error compiling shader. Check shader-error.txt for message", shaderFilename, MB_OK );

   return;

} // outputShaderErrorMessage


Derived Class LightMapShaderClass.h
#ifndef LIGHTMAPSHADERCLASS_H
#define LIGHTMAPSHADERCLASS_H

#include "ShaderClass.h"

using namespace std;

class LightMapShaderClass : public ShaderClass {   
public:
   LightMapShaderClass();
   //LightMapShaderClass( const LightMapShaderClass& other );
   ~LightMapShaderClass();

protected:
   bool   initializeShader();
   void   shutdownShader();
   bool   setShaderParameters( D3DXMATRIX world, D3DXMATRIX view, D3DXMATRIX projection, ID3D11ShaderResourceView** pTextures );
   void   renderShader( int index );

}; // LightMapShaderCLASS

#endif // LIGHTMAPSHADERCLASS_H


LightMapShaderClass.cpp
#include "LightMapShaderClass.h"

// ----------------------------------------------------------------------------
LightMapShaderClass::LightMapShaderClass() : ShaderClass() {    
} // LightMapShaderClass

// ----------------------------------------------------------------------------
//LightMapShaderClass::LightMapShaderClass( const LightMapShaderClass& other ) : ShaderClass( other ) {
//} // LightMapShaderClass

// ----------------------------------------------------------------------------
LightMapShaderClass::~LightMapShaderClass() {
} // ~LightMapShaderClass

// ----------------------------------------------------------------------------
bool LightMapShaderClass::initializeShader() {
   HRESULT hr;
   ID3D10Blob* pErrorMessage;
   ID3D10Blob* pVertexShaderBuffer;
   ID3D10Blob* pPixelShaderBuffer;
   D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
   unsigned int uiNumElements;
   D3D11_BUFFER_DESC matrixBufferDesc;
   D3D11_SAMPLER_DESC samplerDesc;

   // Initialize The Pointers
   pErrorMessage = nullptr;
   pVertexShaderBuffer = nullptr;
   pPixelShaderBuffer = nullptr;

   // Compile The Vertex Shader Code
   hr = D3DX11CompileFromFile( m_vsFilename, NULL, NULL, "LightMapVertexShader", "vs_4_1", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &pVertexShaderBuffer, &pErrorMessage, NULL );
   if ( FAILED( hr ) ) {
      // If The Shader Failed To Compile It Should Have Written Something To The Error Message
      if ( pErrorMessage ) {
         outputShaderErrorMessage( pErrorMessage, m_vsFilename );
      }
      // If There Was Nothing In The Error Message It Could Not Find The Shader File
      else {
         MessageBox( m_hWnd, m_vsFilename, L"Missing Shader File", MB_OK );
      }
      return false;
   }

   // Compile The Pixel Shader Code
   hr = D3DX11CompileFromFile( m_psFilename, NULL, NULL, "LightMapPixelShader", "ps_4_1", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &pPixelShaderBuffer, &pErrorMessage, NULL );
   if ( FAILED( hr ) ) {
      // If The Shader Failed To Compile It Should Have Written Something To The Error Message
      if ( pErrorMessage ) {
         outputShaderErrorMessage( pErrorMessage, m_psFilename );
      }
      // If There Was Nothing In The Error Message It Could Not Find The Shader File
      else {
         MessageBox( m_hWnd, m_psFilename, L"Missing Shader File", MB_OK );
      }
      return false;
   }

   // Create The Vertex Shader From The Buffer
   hr = m_pDevice->CreateVertexShader( pVertexShaderBuffer->GetBufferPointer(), pVertexShaderBuffer->GetBufferSize(), NULL, &m_pVertexShader );
   if ( FAILED( hr ) ) {
      return false;
   }

   // Create The Pixel Shader From The Buffer
   hr = m_pDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), NULL, &m_pPixelShader );
   if ( FAILED( hr ) ) {
      return false;
   }

   // Create The Vertex Input Layout Description
   // This Setup Needs To Match The VertexType Structure In The ModelClass And In The Shader Buffer
   polygonLayout[0].SemanticName = "POSITION";
   polygonLayout[0].SemanticIndex = 0;
   polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
   polygonLayout[0].InputSlot = 0;
   polygonLayout[0].AlignedByteOffset = 0;
   polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
   polygonLayout[0].InstanceDataStepRate = 0;

   polygonLayout[1].SemanticName = "TEXCOORD";
   polygonLayout[1].SemanticIndex = 0;
   polygonLayout[1].Format = DXGI_FORMAT_R32G32B32_FLOAT;
   polygonLayout[1].InputSlot = 0;
   polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
   polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
   polygonLayout[1].InstanceDataStepRate = 0;

   // Get A Count Of The Elements In The Layout
   uiNumElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

   // Create The Vertex Input Layout
   hr = m_pDevice->CreateInputLayout( polygonLayout, uiNumElements, pVertexShaderBuffer->GetBufferPointer(), pVertexShaderBuffer->GetBufferSize(), &m_pLayout );
   if ( FAILED( hr ) ) {
      return false;
   }

   // Release The Vertex Shader Buffer And Pixel Shader Buffer Since They Are No Longer Needed
   pVertexShaderBuffer->Release();
   pVertexShaderBuffer = nullptr;

   pPixelShaderBuffer->Release();
   pPixelShaderBuffer = nullptr;

   // Setup The Description Of The Matrix Dynamic Constant Buffer That Is In The Vertex Shader
   matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
   matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
   matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
   matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
   matrixBufferDesc.MiscFlags = 0;
   matrixBufferDesc.StructureByteStride = 0;

   // Create The Matrix Constant Buffer Pointer So We Can Access The Vertex Shader Constant Buffer From Within This Class
   hr = m_pDevice->CreateBuffer( &matrixBufferDesc, NULL, &m_pMatrixBuffer );
   if ( FAILED( hr ) ) {
      return false;
   }

   // Create A Texture Sampler State Description
   samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
   samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
   samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
   samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
   samplerDesc.MipLODBias = 0.0f;
   samplerDesc.MaxAnisotropy = 1;
   samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
   samplerDesc.BorderColor[0] = 0;
   samplerDesc.BorderColor[1] = 0;
   samplerDesc.BorderColor[2] = 0;
   samplerDesc.BorderColor[3] = 0;
   samplerDesc.MinLOD = 0;
   samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

   // Create The Texture Sampler State
   hr = m_pDevice->CreateSamplerState( &samplerDesc, &m_pSampleState );
   if ( FAILED( hr ) ) {
      return false;
   }

   return true;
} // initializeShader

// ----------------------------------------------------------------------------
void LightMapShaderClass::shutdownShader() {
   // Release The Sampler State
   if ( m_pSampleState ) {
      m_pSampleState->Release();
      m_pSampleState = nullptr;
   }

   // Release The Matrix Constant Buffer
   if ( m_pMatrixBuffer ) {
      m_pMatrixBuffer->Release();
      m_pMatrixBuffer = nullptr;
   }

   // Release The Layout
   if ( m_pLayout ) {
      m_pLayout->Release();
      m_pLayout = nullptr;
   }

   // Release The Pixel Shader
   if ( m_pPixelShader ) {
      m_pPixelShader->Release();
      m_pPixelShader = nullptr;
   }

   // Release The Vertex Shader
   if ( m_pVertexShader ) {
      m_pVertexShader->Release();
      m_pVertexShader = nullptr;
   }

   return;
} // shutdownShader

// ----------------------------------------------------------------------------
bool LightMapShaderClass::setShaderParameters( D3DXMATRIX world, D3DXMATRIX view, D3DXMATRIX projection, ID3D11ShaderResourceView** pTextures ) {
   HRESULT hr;
   D3D11_MAPPED_SUBRESOURCE mappedResource;
   MatrixBufferType* pData;
   unsigned int uiBufferNumber;

   // Transpose The Matrices To Prepare Them For The Shader
   D3DXMatrixTranspose( &world, &world );
   D3DXMatrixTranspose( &view, &view );
   D3DXMatrixTranspose( &projection, &projection );

   // Lock The Matrix Constant Buffer So It Can Be Written To
   hr = m_pDeviceContext->Map( m_pMatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
   if ( FAILED( hr ) )  {
      return false;
   }

   // Get A Pointer To The Data In The Constant Buffer
   pData = (MatrixBufferType*)mappedResource.pData;

   // Copy The Matrices Into The Constant Buffer
   pData->world = world;
   pData->view  = view;
   pData->projection = projection;

   // Unlock The Matrix Constant Buffer
   m_pDeviceContext->Unmap( m_pMatrixBuffer, 0 );

   // Set The Position Of The Matrix Constant Buffer In The Vertex Shader
   uiBufferNumber = 0;

   // Now Set The Matrix Constant Buffer In The Vertex Shader With The Updated Values
   m_pDeviceContext->VSSetConstantBuffers( uiBufferNumber, 1, &m_pMatrixBuffer );

   // Set Shader Texture Array Resource In The Pixel Shader
   m_pDeviceContext->PSSetShaderResources( 0, 2, pTextures );

   return true;
} // setShaderParameters

// ----------------------------------------------------------------------------
void LightMapShaderClass::renderShader( int indexCount ) {
   // Set The Vertex Input Layout
   m_pDeviceContext->IASetInputLayout( m_pLayout );
   
   // Set The Vertex And Pixel Shaders That Will Be Used To Render This Triangle
   m_pDeviceContext->VSSetShader( m_pVertexShader, NULL, 0 );
   m_pDeviceContext->PSSetShader( m_pPixelShader, NULL, 0 );

   // Set The Sampler State In The Pixel Shader
   m_pDeviceContext->PSSetSamplers( 0, 1, &m_pSampleState );

   // Render The Triangles
   m_pDeviceContext->DrawIndexed( indexCount, 0, 0 );

   return;
} // renderShader


Class::function() which instantiates and initializes our derived shaders
// ----------------------------------------------------------------------------
bool GraphicsClass::initialize( int iScreenWidth, int iScreenHeight, HWND hWnd ) {
   bool bResult;
   D3DXMATRIX baseViewMatrix;

   // Create The Direct3D Object
   _pD3D = new D3DClass;
   if ( !_pD3D ) {
      return false;
   }
   // Initialize The Direct3D Object
   bResult = _pD3D->initialize( iScreenWidth, iScreenHeight, VSYNC_ENABLED, hWnd,
                         FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR );
   if ( !bResult ) {
      MessageBox( hWnd, L"Could not initialize Direct3D", L"Error", MB_OK );
      return false;
   }

   // Create The Camera Object
   _pCamera = new Camera;
   if ( !_pCamera ) {
      return false;
   }
   // Initialize The Base View Matrix With The Camera For 2D User Interface Rendering
   _pCamera->setPosition( 0.0f, 0.0f, -1.0f );
   _pCamera->render();
   _pCamera->getViewMatrix( baseViewMatrix );

   // Create The Model Object
   _pModel = new ModelClass;
   if ( !_pModel ) {
      return false;
   }
   // Initialize The Model Object
   bResult = _pModel->initialize( _pD3D->getDevice(), "../DX11Engine/data/square.txt", L"../DX11Engine/data/stone01.dds", L"../DX11Engine/data/light01.dds" );
   if ( !bResult ) {
       MessageBox( hWnd, L"Could not initialize the model object.", L"Error", MB_OK );
       return false;
   }

   // Create The LightMapShader Object
   _pShader = new LightMapShaderClass;
   if ( !_pShader ) {
      return false;
   }
   // Initialize The LightShader Object
   bResult = _pShader->initialize( _pD3D->getDevice(), _pD3D->getDeviceContext(), hWnd, L"../DX11Engine/lightmap.vs", L"../DX11Engine/lightmap.ps" );
   if ( !bResult ) {
      MessageBox( hWnd, L"Could not initialize the light map shader object.", L"Error", MB_OK );
      return false;
   }

   return true;
} // initialize


Intellisense says that it can not instantiate the derived class because it is abstract, to find out what is causing it to be abstract, both the base and derived classes compile, I have to compile the GraphicsClass and it points to the method setShaderParameters(). Am I not able to use a void* as a placeholder in the base class, and be able to pass in the type of pointer I need? This is where I am kindof getting stumped. A Second thing to note is the way the frame work is laid out here I need for the base class to have the setShaderParameters to be purely virtual, all shader types must implement this function; if I try to remove this out of the base class and just assume that each derived class would manage their own, because the result of this would be that the base class has no visible scope of that function then which is called inside of the ShaderClass::render() call, even if I use a class prototype inside of the base class header and try to scope the function call where it is needed it still doesn't work because it doesn't belong to a specific object. I'll try to make a diagram the best I can to illustrate what I need.

[BaseClass] - Does all common functionallity and initializations with all shader classes' common attributes
and properties.
First three methods are public access functions for invoking the 4 functions below in the
derived class. Two are invoked in the render() call. One function is protected from
outside classes since this handles the error messages for the shaders.
public: initialize(), shutdown(), render()
protected: outputShaderErrorMessage()

[DerivedClass] - Each Derived class must have certain methods or functions to be implemented.
All functions below are protected and are not meant to be called from an outside
class these are all invoked from with in the base class.
initializeShader() - sets up all the buffers, properties and layouts needed to send the information to the
back buffers, shaders - graphics card for rendering
shutdownShader() - releases all the individual components that was needed for this particular shader.
setShaderParameters() - this sets up how the shader scripts will process the information and gets the
graphics pipeline ready to invoke the vertex and pixel shaders.
renderShader() - this function sets the vertex and pixel shaders in the pipeline then goes on to render
the primitives as how they are specified.

I need to point out that the setShaderParameter function for each derived shader class may happen to have a differen't
prototype signature then the base class. The reason for this is with in a basic example. To render a basic texture onto a
square takes only 1 texture so a pointer to a texture would be the last item passed into this function, but if we look at a
LightMap or MultiTextureMap, AlphaMaps, BumpMaps etc, these can take two or more textures and now the last parameter
is no longer a pointer to a texture, but a pointer to a texture array. Also if we have a material or colorShader or lightShader
then we may need to have more parameters as well such as D3DXVECTOR3, D3DXVECTOR4 D3DXMATRIX values for other
calculations that would be needed for light dir, color, etc. So I am not sure how to express this at this point in the code.
Any help would be amazing! Thank You!


Offline
 Profile  
 
Display posts from previous:  Sort by  
 Page 1 of 1 [ 1 post ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  

cron