// ================================================================
// 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;
}
