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