// Terrain Engine implementation

#include <d3d8.h>
#include <d3dx8.h>

#include "D3DFuncs.h"
#include "TerrainEngine.h"
#include "resource.h"

// =====================================================================================
// Constructor/Destructor
// =====================================================================================

CTerrainEngine::CTerrainEngine()
{

}

CTerrainEngine::~CTerrainEngine()
{
	if(m_pTextureHeightmap)
		m_pTextureHeightmap->Release();

	if(m_pTextureGround)
		m_pTextureGround->Release();

	if(m_pVertexBuffer)
		m_pVertexBuffer->Release();

	if(m_pIndexBuffer)
		m_pIndexBuffer->Release();
}

// =====================================================================================
// Initialisation
// =====================================================================================

HRESULT CTerrainEngine::Initialise(LPDIRECT3DDEVICE8& pDevice, char* pszHeightmap)
{
	HRESULT rslt;

	// First, we load our source images to texture surfaces. I've decided to show you the resource method, 
	// which means we can distribute just one exe instead of exe + bitmaps. The commented lines are the
	// "original" load-from-file code you already know. The code next to them is their load-from-resource
	// partner.

/*
	rslt=D3DXCreateTextureFromFileEx(pDevice, pszHeightmap, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
									 D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL,
									 &m_pTextureHeightmap);
*/
	rslt=D3DXCreateTextureFromResourceEx(pDevice, GetModuleHandle( NULL ), MAKEINTRESOURCE(IDB_ISLAND), D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
									 D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL,
									 &m_pTextureHeightmap);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not load heightmap"); }
	




/*
	rslt=D3DXCreateTextureFromFileEx(pDevice, "islandmap.bmp", D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
									 D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL,
									 &m_pTextureGround);
*/
	rslt=D3DXCreateTextureFromResourceEx(pDevice, GetModuleHandle( NULL ), MAKEINTRESOURCE(IDB_ISLANDMAP), D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
									 D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL,
									 &m_pTextureGround);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not load texmap"); }



	// ----------------------------------------------------------------------------
	// Extract heightmap information to 256*256 array. We do this the good old
	// fashioned lock-the-texture-and-index-through-it manner.
	// ----------------------------------------------------------------------------

	D3DLOCKED_RECT	lockedrect;
	::ZeroMemory(&lockedrect, sizeof(lockedrect));
	
	rslt=m_pTextureHeightmap->LockRect(0, &lockedrect, NULL, 0);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not lock heightmap surface"); }

	DWORD* pBits=(DWORD*)lockedrect.pBits;
	int index=0;

	// Display offset (bytes)=((Pitch * y) + x) * Num Bytes per pixel
	{
	for(int y=0; y<256;y+=2)
	{
		for(int x=0; x<256; x+=2)
		{	
			// getr is a handy macro I added to D3DFunc.h. It just extracts a 0-255 UINT from a DWORD representing
			// an RGB colour. For example: D3DCOLOUR_XRGB(100, 10, 0); << getr() would extract 100, the Red
			// element. I've provided macros for alpha, blue and green too.
			
			m_nHeightArray[y][x]=(getr(pBits[(y*256)+x]))/5;
		}
	}
	}
	m_pTextureHeightmap->UnlockRect(0);
	m_pTextureHeightmap->Release();
	m_pTextureHeightmap=NULL;


	// ----------------------------------------------------------------------------
	// Create vertices from array
	// ----------------------------------------------------------------------------

	{
	int z=128;				// Our starting Z axis position, 128 units "into" the screen
	float texX, texY;		// Texture coordinates
	int nArrayIndex=0;		// And an index into the array

	// First we index through each row, and create our vertex buffer - one vertice for every pixel
	// on the heightmap

	for(int nIndexY=0; nIndexY<256; nIndexY+=2)
	{
		texY=(float)(((float)nIndexY/(float)2)/(float)128);
		for(int nIndexX=0; nIndexX<256; nIndexX+=2)
		{
			// Vertice config
			//  1_____2
			// 4|\    |
			//  |  \  |
			//  |____\|3
			//  6     5
			//
			
			int x=nIndexX-128;
			texX=(float)(((float)nIndexX/(float)2)/(float)128);

			// Vertice 1
			m_vTerrain[((nIndexY/2) * 128)+(nIndexX/2)].x=(float)x;
			m_vTerrain[((nIndexY/2) * 128)+(nIndexX/2)].y=(float)m_nHeightArray[nIndexY][nIndexX];
			m_vTerrain[((nIndexY/2) * 128)+(nIndexX/2)].z=(float)z;

			m_vTerrain[((nIndexY/2) * 128)+(nIndexX/2)].tu1=texX;
			m_vTerrain[((nIndexY/2) * 128)+(nIndexX/2)].tv1=texY;

			int col=m_nHeightArray[nIndexY][nIndexX]*5;
			m_vTerrain[((nIndexY/2) * 128)+(nIndexX/2)].dwColour=D3DCOLOR_ARGB(255, col, col, col);
		}
		z-=2;
	}
	}

	// Now that we have our vertices, we create an index buffer array for them

	{
	int nArrayIndex=0;
	for(int nIndexY=0; nIndexY<128; nIndexY++)
	{
		for(int nIndexX=0; nIndexX<127; nIndexX++)
		{
			//int nIndexY=0;
			m_sIndices[nArrayIndex]=((nIndexY+1) * 128)+nIndexX;
			m_sIndices[nArrayIndex+1]=(nIndexY * 128)+nIndexX;
			m_sIndices[nArrayIndex+2]=((nIndexY+1) * 128)+nIndexX+1;

			m_sIndices[nArrayIndex+3]=((nIndexY+1) * 128)+nIndexX+1;
			m_sIndices[nArrayIndex+4]=(nIndexY * 128)+nIndexX;
			m_sIndices[nArrayIndex+5]=(nIndexY * 128)+nIndexX+1;
			
			nArrayIndex+=6;
		}
	}
	}
	

	rslt=pDevice->CreateVertexBuffer(sizeof(m_vTerrain), 0, D3DFVF_TERRAIN, D3DPOOL_MANAGED,
 									 &m_pVertexBuffer);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "CreateVertexBuffer() failed."); }

	// Now lock the vertex buffer...
	BYTE* pVerticeLock=0;
	rslt=m_pVertexBuffer->Lock(0, sizeof(m_vTerrain), &pVerticeLock, 0);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to lock Vertex Buffer."); }

	// ...and copy our array of vertices straight into it
	CopyMemory(pVerticeLock, &m_vTerrain, sizeof(m_vTerrain));

	// Remember to unlock the vertex buffer once we're done.
	m_pVertexBuffer->Unlock();


	BYTE* pIndexData=0;

	// Create the index buffer...
	rslt=pDevice->CreateIndexBuffer(sizeof(m_sIndices), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pIndexBuffer);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to lock Index Buffer."); }

	// ...lock it
	rslt=m_pIndexBuffer->Lock(0, sizeof(m_sIndices), &pIndexData, 0);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to lock Index Buffer."); }

	// ...copy the indicies in, and unlock
	CopyMemory(pIndexData, &m_sIndices, sizeof(m_sIndices));
	m_pIndexBuffer->Unlock();


	return S_OK;
}

// =====================================================================================
// Render
// =====================================================================================

HRESULT CTerrainEngine::Render(LPDIRECT3DDEVICE8& pDevice, LPDIRECT3DSURFACE8& pBackBuffer)
{
	HRESULT rslt;

	// One of the simplest Render() methods yet!
	
	rslt=pDevice->SetVertexShader(D3DFVF_TERRAIN);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set vertex shader."); }

	// Use our nice ground texture for the entire set of vertices
	rslt=pDevice->SetTexture(0, m_pTextureGround);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set texture."); }

	// Set the texture as the first input for stage 0 
	rslt=pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set texture stage."); }

	// Set the vertex diffuse colour as the second input for stage 0
	rslt=pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set texture stage."); }

	// Set D3DTOP_BLENDDIFFUSEALPHA for the colour operation for stage 0
	rslt=pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADDSIGNED);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set texture stage."); }

	// Do not perform any operations for the alpha operation for stage 0
	rslt=pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); 
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set texture stage."); }

	// Set the vertex stream source
	rslt=pDevice->SetStreamSource(0, m_pVertexBuffer, sizeof(TERRAINVERTEX));
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set stream source."); }

	// Tell D3D we're going to use our index buffer
	rslt=pDevice->SetIndices(m_pIndexBuffer, 0);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set Index buffer."); }

	// Iterate and draw each row at a time
	for(int nCount=0; nCount < 128; nCount++)
	{
		rslt=pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 768, nCount*768, 256);
		if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to draw primitives"); }
	}

	// Unset texture stage modes - these are the "default" D3D stages
	rslt=pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set texture stage."); }

	return S_OK;
}
