However, I figured that my lack of C++ skills is unacceptable, so I set out to learn C++.
So far, everything seems quite straightforward and obvious, though I obviously had some difficulties in the beginning.
To test and further develop my C++ skills, I decided to port my current engine, which I've written in C#/SlimDX to C++.
I'm not really experiencing any trouble with porting and solving problems...
... however I'm quite at a loss as to how to produce "good / clean code".
Online reading materials do a great job at explaining programming logic and how to solve problems, but they seem to give no indication on what would be a good or clean way to do so.
Due to this, I feel rather insecure about my current way of coding C++.
Here's a random class as an example on how I'm currently approaching things:
GBuffer.h:
[source]#pragma once
#include "stdafx.h"
// Forward Declarations
class CDeferredRenderer;
class CGBuffer
{
public:
CGBuffer(void);
~CGBuffer(void);
ID3D11Texture2D *GDiffuse;
ID3D11Texture2D *GNormal;
ID3D11Texture2D *GDepth;
ID3D11Texture2D *GLight;
ID3D11RenderTargetView *RTDiffuse;
ID3D11RenderTargetView *RTNormal;
ID3D11RenderTargetView *RTDepth;
ID3D11RenderTargetView *RTLight;
ID3D11ShaderResourceView *GDiffuseView;
ID3D11ShaderResourceView *GNormalView;
ID3D11ShaderResourceView *GDepthView;
ID3D11ShaderResourceView *GLightView;
void Init(CDeferredRenderer * Renderer);
void Clear();
void BeginGeometryStage(ID3D11DepthStencilView *DepthBufferView);
void Release();
private:
CDeferredRenderer *Renderer;
ID3D11Device *Device;
ID3D11DeviceContext *Context;
void CreateGBufferTextures();
void CreateGTexture( DXGI_FORMAT Format, ID3D11Texture2D* & Texture, ID3D11RenderTargetView* & RTView, ID3D11ShaderResourceView* & ResView );
};[/source]
GBuffer.cpp:
[source]#include "StdAfx.h"
#include "GBuffer.h"
#include "DeferredRenderer.h"
#include "Engine.h"
#include "Log.h"
#include "StringParser.h"
CGBuffer::CGBuffer(void)
{
}
CGBuffer::~CGBuffer(void)
{
}
void CGBuffer::Init( CDeferredRenderer * Renderer )
{
this->Renderer = Renderer;
this->Device = Renderer->Device;
this->Context = Renderer->Context;
CreateGBufferTextures();
}
void CGBuffer::CreateGBufferTextures()
{
CreateGTexture(DXGI_FORMAT_R8G8B8A8_UNORM, GDiffuse, RTDiffuse, GDiffuseView);
CreateGTexture(DXGI_FORMAT_R16G16_UNORM, GNormal, RTNormal, GNormalView);
CreateGTexture(DXGI_FORMAT_R32_FLOAT, GDepth, RTDepth, GDepthView);
CreateGTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, GLight, RTLight, GLightView);
}
void CGBuffer::CreateGTexture( DXGI_FORMAT Format, ID3D11Texture2D* & Texture, ID3D11RenderTargetView* & RTView, ID3D11ShaderResourceView* & ResView )
{
// Create Texture
D3D11_TEXTURE2D_DESC TextureDesc;
ZeroMemory(&TextureDesc, sizeof(D3D11_TEXTURE2D_DESC));
TextureDesc.Width = SCREEN_WIDTH;
TextureDesc.Height = SCREEN_HEIGHT;
TextureDesc.MipLevels = 1;
TextureDesc.ArraySize = 1;
TextureDesc.Format = Format;
TextureDesc.SampleDesc.Count = 1;
TextureDesc.SampleDesc.Quality = 0;
TextureDesc.Usage = D3D11_USAGE_DEFAULT;
TextureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
TextureDesc.CPUAccessFlags = 0;
TextureDesc.MiscFlags = 0;
HRESULT hr = Device->CreateTexture2D(&TextureDesc, NULL, &Texture);
assert(SUCCEEDED(hr));
// Create RenderView
D3D11_RENDER_TARGET_VIEW_DESC Desc;
ZeroMemory(&Desc, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
Desc.Format = Format;
Desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = Device->CreateRenderTargetView(Texture, &Desc, &RTView);
assert(SUCCEEDED(hr));
// Create Shader Resource View
hr = Device->CreateShaderResourceView(Texture, NULL, &ResView);
assert(SUCCEEDED(hr));
}
void CGBuffer::Release()
{
GDiffuse->Release();
GNormal->Release();
GDepth->Release();
GLight->Release();
RTDiffuse->Release();
RTNormal->Release();
RTDepth->Release();
RTLight->Release();
GDiffuseView->Release();
GNormalView->Release();
GDepthView->Release();
GLightView->Release();
GDiffuse = NULL;
GNormal = NULL;
GDepth = NULL;
GLight = NULL;
RTDiffuse = NULL;
RTNormal = NULL;
RTDepth = NULL;
RTLight = NULL;
GDiffuseView = NULL;
GNormalView = NULL;
GDepthView = NULL;
GLightView = NULL;
}
void CGBuffer::Clear()
{
Context->ClearRenderTargetView(RTDiffuse, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f));
Context->ClearRenderTargetView(RTNormal, D3DXCOLOR(0.5f, 0.5f, 0.0f, 0.0f));
Context->ClearRenderTargetView(RTDepth, D3DXCOLOR(1.0f, 0.0f, 0.0f, 0.0f));
Context->ClearRenderTargetView(RTLight, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f));
}
void CGBuffer::BeginGeometryStage(ID3D11DepthStencilView *DepthBufferView)
{
ID3D11RenderTargetView* RenderTargetViews[3] =
{
RTDiffuse,
RTNormal,
RTDepth
};
Context->OMSetRenderTargets(3, RenderTargetViews, DepthBufferView);
}[/source]
This works fine, but there's a lot of questions popping up in my head:
- I only include stdafx.h (containing rarely changing libs such as DX libs) in my header files. I then add forward declarations for all other classes used in this one.
In my .cpp I include all header files of other classes needed.
It seems like I cannot run into problems this way... is this the correct approach for managing classes in c++? - I keep only pointers to all DX specific stuff (Textures, Rendertargetviews etc) which I can easily pass around.
I do the same thing with my own classes (My Renderer Class keeps only a Pointer to my GBuffer class)
Is this okay to do?
Or should I rather define things not as pointers, and then write get methods to pass references to member variables to other classes?
[source]
class CDeferredRenderer
{
private:
CGBuffer *GBuffer;
....
};
void CDeferredRenderer::Init( CEngine * Engine, HWND hWnd )
{
...
GBuffer = new CGBuffer();
GBuffer->Init(this);
...
}
[/source] - This also means I have to do things like this (although rarely):
[source]void CGBuffer::CreateGTexture( DXGI_FORMAT Format, ID3D11Texture2D* & Texture, ID3D11RenderTargetView* & RTView, ID3D11ShaderResourceView* & ResView )[/source]
Is this okay? - Should I make all members private and write Getters for them?
In C# I'm used to do things like:
[source]public CGBuffer GBuffer { get; private set; }[/source]
I obviously can't reproduce this in C++ without writing a Get method for each variable I want to have read-only access to.
Should I really do this, or is it better to focus on functionality rather than wrapping everything with accessor methods? - Should I prefer using references over pointers if I'm in a situation where I can use both to achieve the same thing?
I noticed the DirectX11 api doesn't make much use of references. For example:
[source]D3DXMatrixIdentity(D3DXMATRIX *pOut);[/source]
instead of
[source]D3DXMatrixIdentity(D3DXMATRIX & Out);[/source] - I've seen some code where all member variables are preceded with "m" and pointers with "p" resulting in code like:
[source]ID3D11Texture2D* mpGDiffuse;[/source]
Is this considered a good practice?
Cheers,
Hyu