// ================================================================
// Filename: D3DCube.cpp
// Description: 3D Cube class, derived from CD3DObject. Enables
// quick and easy creation of a texturemapped cube
//
// This source corresponds to 32Bits.co.uk DirectX
// Basics Series 3 part 5, DirectX 9 Edition.
// ================================================================
#include <d3d9.h>
#include <d3dx9.h>
#include "CD3DObject.h"
#include "D3DCube.h"
// ================================================================================
// Overloaded Constructor & Destructor
// ================================================================================
CD3DCube::CD3DCube()
{
SetClassDesc("Cube class");
SetRTTI(CUBE);
// Reset our class members
m_pTexture=NULL;
m_pVertexBuffer=NULL;
}
CD3DCube::~CD3DCube()
{
if(m_pTexture)
m_pTexture->Release();
if(m_pVertexBuffer)
m_pVertexBuffer->Release();
}
// ================================================================================
// Set up the cube
// ================================================================================
HRESULT CD3DCube::Initialise(float Width, float Height, float Depth, float x, float y, float z,
LPDIRECT3DDEVICE9& pDevice)
{
/*
Note: When specifying the x,y,z for the location of the cube in object space, remember to
subtract Height/2, Width/2 & Depth/2 from x,y,z passed parameters to center the cube
in object space.
*/
// Here we do a little trickery. Rather than manually specify all 36 vertices of the cube, we're
// going to make our life a little easier. A cube only has 8 unique vertices, these 8 vertices are
// shared between all the faces. So, if we specify 8 vertices up front here, and then use
// m_Vertice[x]=pX, it (a) makes the code more manageable, and (b) allows us to only change 8 lines of
// code at a later date, instead of 36 lines ;)
// So, here's our 8 unique vertices.
D3DVECTOR p0={ x, y+Height, z};
D3DVECTOR p1={ x+Width, y+Height, z};
D3DVECTOR p2={ x, y, z};
D3DVECTOR p3={ x+Width, y, z};
D3DVECTOR p4={ x, y+Height, z+Depth};
D3DVECTOR p5={ x+Width, y+Height, z+Depth};
D3DVECTOR p6={ x, y, z+Depth};
D3DVECTOR p7={ x+Width, y, z+Depth};
// Now we set up our cube's vertices, in clockwise order (for CCW culling). Note how we use the pX
// D3DVECTOR's we specified above to set the vertex position.
// Front of the cube
m_Vertices[0].vPos=p0;
m_Vertices[1].vPos=p1;
m_Vertices[2].vPos=p2;
m_Vertices[3].vPos=p2;
m_Vertices[4].vPos=p1;
m_Vertices[5].vPos=p3;
// Back of the cube
m_Vertices[6].vPos=p5;
m_Vertices[7].vPos=p4;
m_Vertices[8].vPos=p7;
m_Vertices[9].vPos=p7;
m_Vertices[10].vPos=p4;
m_Vertices[11].vPos=p6;
// Top of the cube
m_Vertices[12].vPos=p4;
m_Vertices[13].vPos=p5;
m_Vertices[14].vPos=p0;
m_Vertices[15].vPos=p0;
m_Vertices[16].vPos=p5;
m_Vertices[17].vPos=p1;
// Bottom of the cube
m_Vertices[18].vPos=p2;
m_Vertices[19].vPos=p3;
m_Vertices[20].vPos=p6;
m_Vertices[21].vPos=p6;
m_Vertices[22].vPos=p3;
m_Vertices[23].vPos=p7;
// Left of the cube
m_Vertices[24].vPos=p4;
m_Vertices[25].vPos=p0;
m_Vertices[26].vPos=p6;
m_Vertices[27].vPos=p6;
m_Vertices[28].vPos=p0;
m_Vertices[29].vPos=p2;
// Right of the cube
m_Vertices[30].vPos=p1;
m_Vertices[31].vPos=p5;
m_Vertices[32].vPos=p3;
m_Vertices[33].vPos=p3;
m_Vertices[34].vPos=p5;
m_Vertices[35].vPos=p7;
// Phew, thats our cube vertices specified. Now we need to set up the normals. We haven't dealt with
// normals before, so you need to read the tutorial! In a nutshell, the normals help to allow us to
// produce a smooth lighting as they affect the light intensity across the face of a tri.
// Set up the normals. Normals are just unit vectors (for an explanation, read the 32Bits Cheaters
// Guide to 3D Maths, on the site.
// Front normals
m_Vertices[0].vNormal=D3DXVECTOR3(0.0f, 0.0f,-1.0f);
m_Vertices[1].vNormal=D3DXVECTOR3(0.0f, 0.0f,-1.0f);
m_Vertices[2].vNormal=D3DXVECTOR3(0.0f, 0.0f,-1.0f);
m_Vertices[3].vNormal=D3DXVECTOR3(0.0f, 0.0f,-1.0f);
m_Vertices[4].vNormal=D3DXVECTOR3(0.0f, 0.0f,-1.0f);
m_Vertices[5].vNormal=D3DXVECTOR3(0.0f, 0.0f,-1.0f);
// Rear normals
m_Vertices[6].vNormal=D3DXVECTOR3(0.0f, 0.0f, 1.0f);
m_Vertices[7].vNormal=D3DXVECTOR3(0.0f, 0.0f, 1.0f);
m_Vertices[8].vNormal=D3DXVECTOR3(0.0f, 0.0f, 1.0f);
m_Vertices[9].vNormal=D3DXVECTOR3(0.0f, 0.0f, 1.0f);
m_Vertices[10].vNormal=D3DXVECTOR3(0.0f, 0.0f, 1.0f);
m_Vertices[11].vNormal=D3DXVECTOR3(0.0f, 0.0f, 1.0f);
// Top normals
m_Vertices[12].vNormal=D3DXVECTOR3(0.0f, 1.0f, 0.0f);
m_Vertices[13].vNormal=D3DXVECTOR3(0.0f, 1.0f, 0.0f);
m_Vertices[14].vNormal=D3DXVECTOR3(0.0f, 1.0f, 0.0f);
m_Vertices[15].vNormal=D3DXVECTOR3(0.0f, 1.0f, 0.0f);
m_Vertices[16].vNormal=D3DXVECTOR3(0.0f, 1.0f, 0.0f);
m_Vertices[17].vNormal=D3DXVECTOR3(0.0f, 1.0f, 0.0f);
// Bottom normals
m_Vertices[18].vNormal=D3DXVECTOR3(0.0f,-1.0f, 0.0f);
m_Vertices[19].vNormal=D3DXVECTOR3(0.0f,-1.0f, 0.0f);
m_Vertices[20].vNormal=D3DXVECTOR3(0.0f,-1.0f, 0.0f);
m_Vertices[21].vNormal=D3DXVECTOR3(0.0f,-1.0f, 0.0f);
m_Vertices[22].vNormal=D3DXVECTOR3(0.0f,-1.0f, 0.0f);
m_Vertices[23].vNormal=D3DXVECTOR3(0.0f,-1.0f, 0.0f);
// Left normals
m_Vertices[24].vNormal=D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
m_Vertices[25].vNormal=D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
m_Vertices[26].vNormal=D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
m_Vertices[27].vNormal=D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
m_Vertices[28].vNormal=D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
m_Vertices[29].vNormal=D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
// Right normals
m_Vertices[30].vNormal=D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
m_Vertices[31].vNormal=D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
m_Vertices[32].vNormal=D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
m_Vertices[33].vNormal=D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
m_Vertices[34].vNormal=D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
m_Vertices[35].vNormal=D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
// Now we need to set the texturemapping up. Again, because we only have 8 unique vertices,
// (and of course all the faces of the cube will have their texture coordinates identical)
// we can do some iteration trickery to reduce the amount of code. First up, manually specify
// the first face's texcoords:
m_Vertices[0].tu=0.0f;
m_Vertices[0].tv=0.0f;
m_Vertices[1].tu=1.0f;
m_Vertices[1].tv=0.0f;
m_Vertices[2].tu=0.0f;
m_Vertices[2].tv=1.0f;
m_Vertices[3].tu=0.0f;
m_Vertices[3].tv=1.0f;
m_Vertices[4].tu=1.0f;
m_Vertices[4].tv=0.0f;
m_Vertices[5].tu=1.0f;
m_Vertices[5].tv=1.0f;
// Ok, we now have one set of correct tu/tv coords. Loop through the rest of the vertices and
// copy them
{
for(int cnt=0; cnt < 6; cnt++)
{
// This (evaluation ? true : false) expression is a C++ trick. Read up on your C++ if you're
// not sure about it (it's also discussed on the website)
m_Vertices[cnt+6].tu=(m_Vertices[cnt].tu==1.0f ? 0.0f : 1.0f);
m_Vertices[cnt+6].tv=m_Vertices[cnt].tv;
m_Vertices[cnt+12].tu=m_Vertices[cnt].tu;
m_Vertices[cnt+12].tv=m_Vertices[cnt].tv;
m_Vertices[cnt+18].tu=m_Vertices[cnt].tu;
m_Vertices[cnt+18].tv=m_Vertices[cnt].tv;
m_Vertices[cnt+24].tu=m_Vertices[cnt].tu;
m_Vertices[cnt+24].tv=m_Vertices[cnt].tv;
m_Vertices[cnt+30].tu=m_Vertices[cnt].tu;
m_Vertices[cnt+30].tv=m_Vertices[cnt].tv;
}
}
// Now we've finished with specifying our vertices, we can stick them in the vertex buffer
// using the same old code:
HRESULT rslt;
// Create the vertex buffer. Note I've switched to using Managed vertex buffers here, to save trouble
// with lost devices.
rslt=pDevice->CreateVertexBuffer(sizeof(m_Vertices), 0, D3DFVF_CUBE, D3DPOOL_MANAGED,
&m_pVertexBuffer, NULL);
if(FAILED(rslt)) { return Error(rslt, __LINE__, __FILE__, "CreateVertexBuffer() failed."); }
// Now lock the vertex buffer...
BYTE* pVerticeLock=0;
rslt=m_pVertexBuffer->Lock(0, sizeof(m_Vertices), (void**)&pVerticeLock, 0);
if(FAILED(rslt)) { return Error(rslt, __LINE__, __FILE__, "Failed to lock Vertex Buffer."); }
// ...and copy our array of vertices straight into it
CopyMemory(pVerticeLock, &m_Vertices, sizeof(m_Vertices));
// Remember to unlock the vertex buffer once we're done.
m_pVertexBuffer->Unlock();
// and that's our cube set up. Not particularly complicated, just a lot of code.
return S_OK;
}
// ================================================================================
// Overloaded function to load a texture for the cube
// ================================================================================
HRESULT CD3DCube::SetTexture(char* strTexturePath, LPDIRECT3DDEVICE9& pDevice)
{
if(m_pTexture)
m_pTexture->Release();
HRESULT rslt;
// Again, I'm using the managed pool for the texture.
rslt=D3DXCreateTextureFromFileEx(pDevice, strTexturePath, 0, 0, D3DX_DEFAULT, 0,
D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT,
D3DX_DEFAULT, 0, NULL, NULL, &m_pTexture);
if(FAILED(rslt)) { return Error(rslt, __LINE__, __FILE__, "Could not create texture from file."); }
return S_OK;
}
HRESULT CD3DCube::SetTexture(LPDIRECT3DTEXTURE9 pTexture)
{
if(!pTexture)
return E_FAIL;
// Remember to increment the reference count to the texture surface we're using!
pTexture->AddRef();
m_pTexture=pTexture;
return S_OK;
}
// ================================================================================
// Render the Cube
// ================================================================================
HRESULT CD3DCube::Render(LPDIRECT3DDEVICE9& pDevice, LPDIRECT3DSURFACE9& pRenderSurface)
{
HRESULT rslt;
// The standard Render() method. Set the FVF vertex shader, set the stream source.
rslt=pDevice->SetFVF(D3DFVF_CUBE);
if(FAILED(rslt)) { return Error(rslt, __LINE__, __FILE__, "Could not set vertex shader."); }
rslt=pDevice->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(CUBEVERTEX));
if(FAILED(rslt)) { return Error(rslt, __LINE__, __FILE__, "Could not set stream source."); }
// Set the texture
rslt=pDevice->SetTexture(0, m_pTexture);
if(FAILED(rslt)) { return Error(rslt, __LINE__, __FILE__, "Could not set the texture."); }
rslt=pDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 12);
if(FAILED(rslt)) { return Error(rslt, __LINE__, __FILE__, "Call to DrawPrimitive() failed."); }
// Be nice and set the texture to NULL, ie: No texturing. If we are doing any other rendering after
// this call, our SetTexture() call here might affect it. Not nice!
rslt=pDevice->SetTexture(0, NULL);
return S_OK;
}