

#include "C3DObject.h"
#include "d3dfont.h"
#include "Input.h"

#include "Singleton.h"
#include "FMTypes.h"


#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>

#include	<mmsystem.h>
#include	<stdio.h>

#include	<vector>


HINSTANCE g_hInstance;


#ifdef _DEBUG
bool g_bWindowed=true;
int g_Width=960;
int g_Height=600;
#else
bool g_bWindowed=false;
int g_Width=1280;
int g_Height=800;
#endif

BOOL	g_bActive = false;
HWND	g_hWnd = NULL;



const float Pi=3.14159265f;




namespace Engine
{


LPDIRECT3DTEXTURE9 g_pShadowMap=NULL;
LPDIRECT3DTEXTURE9 g_pNoiseTexture=NULL;

static bool g_bShowShadowMap=true;
static bool g_bShowInfo=true;
static bool g_bShadowCull=true;

const char* NAME = "Shadow Mapping Test";
const char* TITLE = "Shadow Mapping Test";


D3DXMATRIX ViewMatrix;
D3DXMATRIX LightViewMatrix;
D3DXMATRIX ProjectionMatrix;
D3DXMATRIX LightProjectionMatrix;
LPD3DXEFFECT g_pEffect;

int g_ShadowMapWidth = 1024*2;
int  g_ShadowMapHeight = 1024*2;
D3DFORMAT g_ShadowMapFormat=D3DFMT_A16B16G16R16F;


bool g_bJitter=false;
bool g_bPCF=true;
float g_Bias=0.0f;//0.00000001f;
bool g_bClear=true;
bool g_bFlip=false;
float g_ZMaxValue=130944.0f/16;;


CD3DFont* g_pFont;

LPDIRECT3D9						g_pD3D = NULL;
LPDIRECT3DDEVICE9			g_pD3DDevice = NULL;




class YPRCamera
{
public:
	void SetYPR(float y, float p, float r)
	{
		m_Yaw=y;
		m_Pitch=p;
		m_Roll=r;
	}

	void SetPos(float x, float y, float z)
	{
		m_x=x;
		m_y=y;
		m_z=z;
	}

	void GetYPR(float *pY, float *pP, float *pR)
	{
		*pY=m_Yaw;
		*pP=m_Pitch;
		*pR=m_Roll;
	}

	void GetPos(float *pX, float *pY, float *pZ)
	{
		*pX=m_x;
		*pY=m_y;
		*pZ=m_z;
	}

	D3DXMATRIX GetRotationMatrix() const
	{
		D3DXMATRIX Rotation;
		D3DXMatrixRotationYawPitchRoll(&Rotation, m_Yaw, m_Pitch, m_Roll);
		return Rotation;
	}

	D3DXMATRIX GetViewMatrix() const
	{
		D3DXMATRIX Rotation;
		D3DXMatrixRotationYawPitchRoll(&Rotation, m_Yaw, m_Pitch, m_Roll);

		D3DXMATRIX Translation;
		D3DXMatrixTranslation(&Translation, m_x, m_y, m_z);

		D3DXMATRIX RotationTranslation;
		D3DXMatrixMultiply(&RotationTranslation, &Rotation, &Translation);

		D3DXMATRIX RotationTranslationInverse;
		D3DXMatrixInverse(&RotationTranslationInverse, NULL, &RotationTranslation);

		return RotationTranslationInverse;
	}


private:
	float m_Yaw, m_Pitch, m_Roll;
	float m_x, m_y, m_z;
};





class Rectangle;

Rectangle *g_pTexturedRect;
Rectangle *g_pClearRect;


YPRCamera g_Camera;


using namespace std;



class Rectangle
{
public:
	Rectangle(float x, float y, float width, float height);
	virtual ~Rectangle();

	void Render();

private:
	int m_fvf;
	IDirect3DVertexBuffer9* m_pVB;
};



C3DObject *g_pObject;



Rectangle::Rectangle(float x, float y, float width, float height)
{
	struct
	{
		float x, y, z, rhw;
		float u, v;
	}* pData=NULL;


	m_fvf=D3DFVF_XYZRHW|D3DFVF_TEX1|D3DFVF_TEXCOORDSIZE2(0);
	int numverts=4;
	g_pD3DDevice->CreateVertexBuffer(numverts*D3DXGetFVFVertexSize(m_fvf), D3DUSAGE_WRITEONLY, m_fvf, D3DPOOL_MANAGED, &m_pVB, NULL);


	int OffsetToLock=0;
	int SizeToLock=0;

	int flags=0;
	m_pVB->Lock(OffsetToLock, SizeToLock, (void**)&pData, flags);

		pData->x=x;
		pData->y=y;
		pData->z=0.5f;
		pData->rhw=1/pData->z;
		pData->u=0.0f;
		pData->v=0.0f;
		
	pData++;
		pData->x=x+width;
		pData->y=y;
		pData->z=0.5f;
		pData->rhw=1/pData->z;
		pData->u=1.0f;
		pData->v=0.0f;
		
	pData++;
		pData->x=x;
		pData->y=y+height;
		pData->z=0.5f;
		pData->rhw=1/pData->z;
		pData->u=0.0f;
		pData->v=1.0f;
		
	pData++;
		pData->x=x+width;
		pData->y=y+height;
		pData->z=0.5f;
		pData->rhw=1/pData->z;
		pData->u=1.0f;
		pData->v=1.0f;

	m_pVB->Unlock();
}


Rectangle::~Rectangle()
{
	m_pVB->Release();
}


void Rectangle::Render()
{
	g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	g_pD3DDevice->SetVertexShader(0);
	g_pD3DDevice->SetFVF(m_fvf);

//	int d=D3DXGetFVFVertexSize(m_fvf);

	g_pD3DDevice->SetStreamSource(0, m_pVB, 0 /*streamoffset*/, D3DXGetFVFVertexSize(m_fvf) /*stride*/); 
	g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

}





void C3DObject::Render(bool bShadowPass)
{
//	D3DXHANDLE handle=m_pEffect->GetParameterByName(NULL, "");

	int i=0;
	D3DXHANDLE handle;
	while(handle=m_pEffect->GetParameter(NULL, i))
	{
		D3DXPARAMETER_DESC desc;
		m_pEffect->GetParameterDesc(handle, &desc);

		HRESULT hres;
		if(strcmp(desc.Name, "JITTER")==0)
		{
			m_pEffect->SetBool(handle, g_bJitter);
		}
		else if(strcmp(desc.Name, "PCF")==0)
		{
			hres=m_pEffect->SetBool(handle, g_bPCF);
		}
		if(strcmp(desc.Name, "bFlip")==0)
		{
			if(g_bClear)
				m_pEffect->SetBool(handle, false);
			else
				m_pEffect->SetBool(handle, g_bFlip);
		}
		else if(strcmp(desc.Name, "Bias")==0)
		{
			m_pEffect->SetFloat(handle, g_Bias);
		}
		else if(strcmp(desc.Name, "ShadowClearColor")==0)
		{
			float size[4];
			size[0]=g_ZMaxValue;
			size[1]=g_ZMaxValue;
			size[2]=g_ZMaxValue;
			size[3]=g_ZMaxValue;
			m_pEffect->SetFloatArray(handle, &size[0], 4);
		}
		else if(strcmp(desc.Name, "SHADOW_SIZE")==0)
		{
			float size[2];
			size[0]=(float)g_ShadowMapWidth;
			size[1]=(float)g_ShadowMapHeight;
			m_pEffect->SetFloatArray(handle, &size[0], 2);
		}

		if(desc.Semantic)
		{
			if(_stricmp(desc.Semantic, "World")==0)
			{
				m_pEffect->SetMatrix(handle, &m_WorldMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldInverse")==0)
			{
				D3DXMATRIX WorldInverseMatrix;
				D3DXMatrixInverse(&WorldInverseMatrix, NULL, &m_WorldMatrix);
				m_pEffect->SetMatrix(handle, &WorldInverseMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldInverseTranspose")==0)
			{
				D3DXMATRIX WorldInverseMatrix;
				D3DXMatrixInverse(&WorldInverseMatrix, NULL, &m_WorldMatrix);
				D3DXMATRIX WorldInverseTransposeMatrix;
				D3DXMatrixTranspose(&WorldInverseTransposeMatrix, &WorldInverseMatrix);
				m_pEffect->SetMatrix(handle, &WorldInverseTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldView")==0)
			{
				D3DXMATRIX WorldViewMatrix;
				D3DXMatrixMultiply(&WorldViewMatrix, &m_WorldMatrix, &ViewMatrix);
				m_pEffect->SetMatrix(handle, &WorldViewMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldViewInverse")==0)
			{
				D3DXMATRIX WorldViewMatrix;
				D3DXMatrixMultiply(&WorldViewMatrix, &m_WorldMatrix, &ViewMatrix);
				D3DXMATRIX WorldViewInverseMatrix;
				D3DXMatrixInverse(&WorldViewInverseMatrix, NULL, &WorldViewMatrix);
				m_pEffect->SetMatrix(handle, &WorldViewInverseMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldViewInverseTranspose")==0)
			{
				D3DXMATRIX WorldViewMatrix;
				D3DXMatrixMultiply(&WorldViewMatrix, &m_WorldMatrix, &ViewMatrix);
				D3DXMATRIX WorldViewInverseMatrix;
				D3DXMatrixInverse(&WorldViewInverseMatrix, NULL, &WorldViewMatrix);
				D3DXMATRIX WorldViewInverseTransposeMatrix;
				D3DXMatrixTranspose(&WorldViewInverseTransposeMatrix, &WorldViewInverseMatrix);
				m_pEffect->SetMatrix(handle, &WorldViewInverseTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldViewProjection")==0)
			{
				D3DXMATRIX WorldViewMatrix;
				D3DXMatrixMultiply(&WorldViewMatrix, &m_WorldMatrix, &ViewMatrix);
				D3DXMATRIX WorldViewProjectionMatrix;
				D3DXMatrixMultiply(&WorldViewProjectionMatrix, &WorldViewMatrix, &ProjectionMatrix);
				m_pEffect->SetMatrix(handle, &WorldViewProjectionMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldViewProjectionInverse")==0)
			{
				D3DXMATRIX WorldViewMatrix;
				D3DXMatrixMultiply(&WorldViewMatrix, &m_WorldMatrix, &ViewMatrix);
				D3DXMATRIX WorldViewProjectionMatrix;
				D3DXMatrixMultiply(&WorldViewProjectionMatrix, &WorldViewMatrix, &ProjectionMatrix);
				D3DXMATRIX WorldViewProjectionInverseMatrix;
				D3DXMatrixInverse(&WorldViewProjectionInverseMatrix, NULL, &WorldViewProjectionMatrix);
				m_pEffect->SetMatrix(handle, &WorldViewProjectionInverseMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldViewProjectionInverseTranspose")==0)
			{
				D3DXMATRIX WorldViewMatrix;
				D3DXMatrixMultiply(&WorldViewMatrix, &m_WorldMatrix, &ViewMatrix);
				D3DXMATRIX WorldViewProjectionMatrix;
				D3DXMatrixMultiply(&WorldViewProjectionMatrix, &WorldViewMatrix, &ProjectionMatrix);
				D3DXMATRIX WorldViewProjectionInverseMatrix;
				D3DXMatrixInverse(&WorldViewProjectionInverseMatrix, NULL, &WorldViewProjectionMatrix);
				D3DXMATRIX WorldViewProjectionInverseTransposeMatrix;
				D3DXMatrixTranspose(&WorldViewProjectionInverseTransposeMatrix, &WorldViewProjectionInverseMatrix);
				m_pEffect->SetMatrix(handle, &WorldViewProjectionInverseTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "View")==0)
			{
				D3DXHANDLE ahandle=m_pEffect->GetAnnotation(handle, 0);
				D3DXPARAMETER_DESC desc;
				m_pEffect->GetParameterDesc(ahandle, &desc);

				if(_stricmp(desc.Name, "frustum")==0)
				{
					m_pEffect->SetMatrix(handle, &LightViewMatrix);
				}
				else
				{
					m_pEffect->SetMatrix(handle, &ViewMatrix);
				}
			}
			else if(_stricmp(desc.Semantic, "ViewProjection")==0)
			{
				D3DXMATRIX ViewProjectionMatrix;
				D3DXMatrixMultiply(&ViewProjectionMatrix, &ViewMatrix, &ProjectionMatrix);
				m_pEffect->SetMatrix(handle, &ViewMatrix);
			}
			else if(_stricmp(desc.Semantic, "ViewInverse")==0)
			{
				D3DXMATRIX ViewInverseMatrix;
				D3DXMatrixInverse(&ViewInverseMatrix, NULL, &ViewMatrix);
				m_pEffect->SetMatrix(handle, &ViewInverseMatrix);
			}
			else if(_stricmp(desc.Semantic, "ViewInverseTranspose")==0)
			{
				D3DXMATRIX ViewInverseMatrix;
				D3DXMatrixInverse(&ViewInverseMatrix, NULL, &ViewMatrix);
				D3DXMATRIX ViewInverseTransposeMatrix;
				D3DXMatrixTranspose(&ViewInverseTransposeMatrix, &ViewInverseMatrix);
				m_pEffect->SetMatrix(handle, &ViewInverseTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "ViewProjectionInverse")==0)
			{
				D3DXMATRIX ViewProjectionMatrix;
				D3DXMatrixMultiply(&ViewProjectionMatrix, &ViewMatrix, &ProjectionMatrix);
				D3DXMATRIX ViewProjectionInverseMatrix;
				D3DXMatrixInverse(&ViewProjectionInverseMatrix, NULL, &ViewProjectionMatrix);
				m_pEffect->SetMatrix(handle, &ViewProjectionInverseMatrix);
			}
			else if(_stricmp(desc.Semantic, "ViewProjectionInverseTranspose")==0)
			{
				D3DXMATRIX ViewProjectionMatrix;
				D3DXMatrixMultiply(&ViewProjectionMatrix, &ViewMatrix, &ProjectionMatrix);
				D3DXMATRIX ViewProjectionInverseMatrix;
				D3DXMatrixInverse(&ViewProjectionInverseMatrix, NULL, &ViewProjectionMatrix);
				D3DXMATRIX ViewProjectionInverseTransposeMatrix;
				D3DXMatrixTranspose(&ViewProjectionInverseTransposeMatrix, &ViewProjectionInverseMatrix);
				m_pEffect->SetMatrix(handle, &ViewProjectionInverseTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "Projection")==0)
			{
				D3DXHANDLE ahandle=m_pEffect->GetAnnotation(handle, 0);
				D3DXPARAMETER_DESC desc;
				m_pEffect->GetParameterDesc(ahandle, &desc);

				if(_stricmp(desc.Name, "frustum")==0)
				{
					m_pEffect->SetMatrix(handle, &LightProjectionMatrix);
				}
				else
				{
				m_pEffect->SetMatrix(handle, &ProjectionMatrix);
				}

			}
			else if(_stricmp(desc.Semantic, "ProjectionInverse")==0)
			{
				D3DXMATRIX ProjectionInverseMatrix;
				D3DXMatrixInverse(&ProjectionInverseMatrix, NULL, &ProjectionMatrix);
				m_pEffect->SetMatrix(handle, &ProjectionInverseMatrix);
			}
			else if(_stricmp(desc.Semantic, "ProjectionInverseTranspose")==0)
			{
				D3DXMATRIX ProjectionInverseMatrix;
				D3DXMatrixInverse(&ProjectionInverseMatrix, NULL, &ProjectionMatrix);
				D3DXMATRIX ProjectionInverseTransposeMatrix;
				D3DXMatrixTranspose(&ProjectionInverseTransposeMatrix, &ProjectionInverseMatrix);
				m_pEffect->SetMatrix(handle, &ProjectionInverseTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldViewTranspose")==0)
			{
				D3DXMATRIX WorldViewMatrix;
				D3DXMatrixMultiply(&WorldViewMatrix, &m_WorldMatrix, &ViewMatrix);
				D3DXMATRIX WorldViewTransposeMatrix;
				D3DXMatrixTranspose(&WorldViewTransposeMatrix, &WorldViewMatrix);
				m_pEffect->SetMatrix(handle, &WorldViewTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "ViewProjectionTranspose")==0)
			{
				D3DXMATRIX ViewProjectionMatrix;
				D3DXMatrixMultiply(&ViewProjectionMatrix, &ViewMatrix, &ProjectionMatrix);
				D3DXMATRIX ViewProjectionTransposeMatrix;
				D3DXMatrixTranspose(&ViewProjectionTransposeMatrix, &ViewProjectionMatrix);
				m_pEffect->SetMatrix(handle, &ViewProjectionTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "WorldTranspose")==0)
			{
				D3DXMATRIX WorldTransposeMatrix;
				D3DXMatrixTranspose(&WorldTransposeMatrix, &m_WorldMatrix);
				m_pEffect->SetMatrix(handle, &WorldTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "ViewTranspose")==0)
			{
				D3DXMATRIX ViewTransposeMatrix;
				D3DXMatrixTranspose(&ViewTransposeMatrix, &ViewMatrix);
				m_pEffect->SetMatrix(handle, &ViewTransposeMatrix);
			}
			else if(_stricmp(desc.Semantic, "viewportpixelsize")==0)
			{
				D3DVIEWPORT9 viewport;
				g_pD3DDevice->GetViewport(&viewport);
				float ViewportPixelSize[2];
				ViewportPixelSize[0]=(float)viewport.Width;
				ViewportPixelSize[1]=(float)viewport.Height;
				m_pEffect->SetFloatArray(handle, ViewportPixelSize, 2);
			}
			else if(_stricmp(desc.Semantic, "diffuse0")==0)
			{
				m_pEffect->SetTexture(handle, m_pTextures[0]);
			}
			else if(_stricmp(desc.Semantic, "rendercolortarget")==0)
			{
				m_pEffect->SetTexture(handle, g_pShadowMap);
			}

			//non-standard
			else if(_stricmp(desc.Semantic, "noise")==0)
			{
				m_pEffect->SetTexture(handle, g_pNoiseTexture);
			}




		}

		i++;
	}

#if 0
	D3DXHANDLE technique=m_pEffect->GetTechnique(0);
	D3DXHANDLE pass=m_pEffect->GetPass(technique, 0);
	D3DXHANDLE annotation;
	i=0;
	while(annotation=m_pEffect->GetAnnotation(pass, i))
	{
		D3DXPARAMETER_DESC desc;
		m_pEffect->GetParameterDesc(annotation, &desc);


		i++;
	}
#endif


	UINT NumPasses;
	int Flags=0;
	m_pEffect->Begin(&NumPasses, Flags);
/*	for(UINT i=0; i<NumPasses; i++)
	{
		m_pEffect->BeginPass(i);

//		g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, false);
		this->m_pMesh->DrawSubset(0);
		m_pEffect->EndPass();
	}
*/

	if(bShadowPass)
	{
		m_pEffect->BeginPass(1);

		g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
		g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);

		if(g_bFlip)
		{
			g_pD3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX);
		}
		else
		{
			g_pD3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MIN);
		}

		g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
		g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

		g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, false);
		g_pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, false);

		if(g_bShadowCull)
		{
			g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
		}
		else
		{
			g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
		}

		m_pMesh->DrawSubset(0);

		g_pD3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
		g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
		g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

		m_pEffect->EndPass();
	}
	else
	{
		m_pEffect->BeginPass(2);

//		g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
		g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, true);
		g_pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);
		g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
/*
//		for(int i=0; i<8; i++)
		{
			g_pD3DDevice->SetTexture(1, g_pShadowMap);
		}

		g_pD3DDevice->SetTexture(0, this->m_pTextures[0]);
*/

		m_pMesh->DrawSubset(0);

/*		for(int i=0; i<8; i++)
		{
			g_pD3DDevice->SetTexture(i, NULL);
		}
*/
		m_pEffect->EndPass();


	}

	m_pEffect->End();
}


C3DObject::C3DObject(char* pFilename, LPD3DXEFFECT pEffect)
{
	m_pEffect=pEffect;

	DWORD options=D3DXMESH_VB_MANAGED|D3DXMESH_IB_MANAGED ;
//	ID3DXBuffer *pAdjacency;
//	ID3DXBuffer *pMaterials;
//	ID3DXBuffer *pEffectInstances;
	DWORD NumMaterials;
	LPD3DXBUFFER pMaterialsBuffer;

	HRESULT hres=D3DXLoadMeshFromX(pFilename,
		options,
		g_pD3DDevice,
		NULL,		//&pAdjacency,
		&pMaterialsBuffer,		//LPD3DXBUFFER * ppMaterials,
		NULL,		//LPD3DXBUFFER * ppEffectInstances,
		&NumMaterials,
		&m_pMesh);

		D3DXMATERIAL* pMaterial = (D3DXMATERIAL*)pMaterialsBuffer->GetBufferPointer();
		for(u32 i=0; i<NumMaterials; i++)
		{
			LPDIRECT3DTEXTURE9 pTexture;
			HRESULT hres=D3DXCreateTextureFromFile(g_pD3DDevice, pMaterial->pTextureFilename, &pTexture);
			if(!SUCCEEDED(hres))
			{
				pTexture=NULL;
			}

			m_pTextures.push_back(pTexture);

			pMaterial++;
		}
		pMaterialsBuffer->Release();
}



void geReleaseAllObjects()
{

	if(g_pD3DDevice)
    {
		for(int i=0; i<8; i++)
			g_pD3DDevice->SetTexture(i, NULL);

        g_pD3DDevice->Release();
        g_pD3DDevice = NULL;
    }

    if(g_pD3D)
    {
        g_pD3D->Release();
        g_pD3D = NULL;
    }
}



void CleanUp()
{
	delete g_pObject;

	delete g_pClearRect;
	delete g_pTexturedRect;

	delete g_pFont;

	geReleaseAllObjects();
}



long PASCAL WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
		case WM_PAINT:
			break;

		case WM_ACTIVATEAPP:
			if(g_bWindowed)
			{
				g_bActive=true;
			}
			else
			{
				g_bActive = (wParam == WA_ACTIVE) || (wParam == WA_CLICKACTIVE);
			}

			if(g_bActive)
			{
				//TODO: reload lost textures

			}
			else
			{
			}

			return 1;

        case WM_DESTROY:
			CleanUp();
            PostQuitMessage(0);
            return 0;

        case WM_KEYDOWN:
            switch (wParam)
            {
				case VK_ESCAPE:
                    PostMessage(hWnd, WM_CLOSE, 0, 0);
					return 0;
					break;
			}
            break;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}



bool InitEngine(int width, int height, int BitDepth, bool bWindowed)
{
	HRESULT hres;
	HINSTANCE hInstance = GetModuleHandle(NULL);

    WNDCLASS	wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = 0;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = "";
    wc.lpszClassName = NAME;
    RegisterClass(&wc);

    if(bWindowed)
	{
//		g_hWnd = CreateWindowEx(WS_OVERLAPPEDWINDOW|WS_EX_TOPMOST,NAME,TITLE,0,10,20,width,height,NULL,NULL,hInstance,NULL);
	    g_hWnd = CreateWindowEx(0, NAME, TITLE, WS_OVERLAPPEDWINDOW|WS_POPUP|WS_SIZEBOX|WS_CAPTION,  0, 0, width, height, NULL, NULL, hInstance, NULL);

		ShowWindow(g_hWnd, SW_SHOWNORMAL);
	}
	else
	{
		g_hWnd = CreateWindowEx(WS_EX_TOPMOST,NAME,TITLE,0,0,0,0,0,NULL,NULL,hInstance,NULL);
		ShowWindow(g_hWnd, 0);
	}
    SetFocus(g_hWnd);

	if(!(g_pD3D=Direct3DCreate9( D3D_SDK_VERSION)))
	{
		return false;
	}

	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	
	d3dpp.Windowed = bWindowed;
	if(bWindowed)
	{
		D3DDISPLAYMODE d3ddm;
		if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm)))
		{
		    return false;
		}

		d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
		d3dpp.BackBufferFormat = d3ddm.Format;
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	}
	else
	{
		d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
		d3dpp.BackBufferWidth = width;
		d3dpp.BackBufferHeight = height;
		d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
		d3dpp.BackBufferCount = 1;
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
		d3dpp.FullScreen_RefreshRateInHz  = D3DPRESENT_RATE_DEFAULT;
		d3dpp.hDeviceWindow = g_hWnd;
	}

	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;

	if(FAILED(hres=g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                         D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDevice )))
	{
		return false;
	}

	g_pD3DDevice->SetRenderState( D3DRS_SRCBLEND,   D3DBLEND_INVDESTCOLOR );
	g_pD3DDevice->SetRenderState( D3DRS_DESTBLEND,  D3DBLEND_ONE );

    g_pD3DDevice->SetRenderState( D3DRS_ZENABLE, true );
//	g_pD3DDevice->SetRenderState( D3DRS_TEXTUREPERSPECTIVE, true);

    g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );	//nem jo a modulate, ha nincs diffuse !!
//    g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );	//nem jo a modulate, ha nincs diffuse !!
    g_pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE ); //def

//	g_pD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);

	g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);//CCW);

	return true;
}


void	ClearCompletely()
{
	DWORD Count=0;
	CONST D3DRECT * pRects=NULL;
	DWORD Flags=D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER;
	D3DCOLOR Color=0;
	float Z=1.0f;
	DWORD Stencil=0;
	g_pD3DDevice->Clear(Count, pRects, Flags, Color, Z, Stencil);
}


void	ClearColorOnly()
{
	DWORD Count=0;
	CONST D3DRECT * pRects=NULL;
	DWORD Flags=D3DCLEAR_TARGET;
	D3DCOLOR Color=0xff0000ff;
	float Z=1.0f;
	DWORD Stencil=0;
//	g_pD3DDevice->Clear(Count, pRects, Flags, Color, Z, Stencil);

	g_pEffect->BeginPass(0);

	g_pD3DDevice->SetRenderState( D3DRS_ZENABLE, false );

	if(g_bClear)
	{
		g_pClearRect->Render();
	}

	g_pEffect->EndPass();
}


void RenderShadowMap()
{
	IDirect3DSurface9 *pOldRenderTarget, *pOldDepthStencilSurface;
	g_pD3DDevice->GetRenderTarget(0, &pOldRenderTarget);
	g_pD3DDevice->GetDepthStencilSurface(&pOldDepthStencilSurface);

	IDirect3DSurface9* pSurf;
	g_pShadowMap->GetSurfaceLevel(0, &pSurf);
	g_pD3DDevice->SetRenderTarget(0, pSurf);
	pSurf->Release();

	g_pD3DDevice->SetDepthStencilSurface(NULL);
    g_pD3DDevice->SetRenderState( D3DRS_ZENABLE, false );

	g_pD3DDevice->SetTexture(0, NULL);

	ClearColorOnly();


		g_pObject->Render(true);


	g_pD3DDevice->SetRenderTarget(0, pOldRenderTarget);
	pOldRenderTarget->Release();
	g_pD3DDevice->SetDepthStencilSurface(pOldDepthStencilSurface);
	if(pOldDepthStencilSurface)
	{
		pOldDepthStencilSurface->Release();
	}
}




void RenderSomething()
{
//	static float x=0, y=-5.0f;
	DIMOUSESTATE state;
	MouseInput::Instance()->GetDevice()->GetDeviceState(sizeof(state), &state);

	KeyboardInput::Instance()->Update();

	{
		float x, y, z;
		float yaw, pitch, roll;

		g_Camera.GetYPR(&yaw, &pitch, &roll);
		g_Camera.GetPos(&x, &y, &z);

//		if(state.rgbButtons[0])
		{
			yaw+=state.lX*0.01f;
			pitch+=state.lY*0.01f;
			if(pitch>Pi/2)
			{
				pitch=Pi/2;
			}
			else if(pitch<-Pi/2)
			{
				pitch=-Pi/2;
			}
		}


		g_Camera.SetYPR(yaw, pitch, roll);
		D3DMATRIX ViewMatrix=g_Camera.GetViewMatrix();

//		if(state.rgbButtons[0])
		if(KeyboardInput::Instance()->IsPressed(DIK_W))
		{
			x+=ViewMatrix._13*20;
			y+=ViewMatrix._23*20;
			z+=ViewMatrix._33*20;
		}

		if(KeyboardInput::Instance()->IsPressed(DIK_S))
		{
			x-=ViewMatrix._13*20;
			y-=ViewMatrix._23*20;
			z-=ViewMatrix._33*20;
		}

		if(KeyboardInput::Instance()->IsPressed(DIK_D))
		{
			x+=ViewMatrix._11*20;
			y+=ViewMatrix._21*20;
			z+=ViewMatrix._31*20;
		}

		if(KeyboardInput::Instance()->IsPressed(DIK_A))
		{
			x-=ViewMatrix._11*20;
			y-=ViewMatrix._21*20;
			z-=ViewMatrix._31*20;
		}

		if(KeyboardInput::Instance()->IsPressed(DIK_X))
		{
			g_Bias+=0.0000001f;
		}
		if(KeyboardInput::Instance()->IsPressed(DIK_Z))
		{
			g_Bias-=0.0000001f;
		}

		g_Camera.SetPos(x, y, z);

		float fov=Pi*(90.0f/180);
		float aspect=(float)g_Width/g_Height;	//tfh. hogy negyzet alakuak a pixelek
		D3DXMatrixPerspectiveFovLH(&ProjectionMatrix, fov, aspect, 1.0f, 8000.0f);
	}


	{	//feny kamera beallitasa
/*		const D3DXVECTOR3 eye(-2500.0, 2000.1f, 2500);
		const D3DXVECTOR3 at(0, 0, 0);
		const D3DXVECTOR3 up(0, 1.0f, 0);

		D3DXMatrixLookAtLH(&LightViewMatrix, &eye, &at, &up);
*/
		static float yaw, pitch=Pi/4;

//		pitch+=state.lY*0.01f;
		if(KeyboardInput::Instance()->IsPressed(DIK_LEFT))
		{
			yaw+=0.02f;
		}
		if(KeyboardInput::Instance()->IsPressed(DIK_RIGHT))
		{
			yaw-=0.02f;
		}
		if(KeyboardInput::Instance()->IsPressed(DIK_UP))
		{
			pitch+=0.02f;
		}
		if(KeyboardInput::Instance()->IsPressed(DIK_DOWN))
		{
			pitch-=0.02f;
		}

		if(pitch>Pi/2)
		{
			pitch=Pi/2;
		}
		else if(pitch<-Pi/2)
		{
			pitch=-Pi/2;
		}

		YPRCamera LightCamera;
		LightCamera.SetYPR(yaw, pitch, 0);
		D3DXMATRIX RotationMatrix=LightCamera.GetRotationMatrix();

		float x, y, z;
		g_Camera.GetPos(&x, &y, &z);

		x-=RotationMatrix._31*4000;
		y-=RotationMatrix._32*4000;
		z-=RotationMatrix._33*4000;

		LightCamera.SetPos(x, y, z);
		LightViewMatrix=LightCamera.GetViewMatrix();

//		float fov=Pi*(90.0f/180);
//		float aspect=1.0f;
//		D3DXMatrixPerspectiveFovLH(&LightProjectionMatrix, fov, aspect, 1.2f, 8000.0f);

		float w=8000.0f;
		float h=8000.0f;
		float zn=1.0f;
		float zf=14000.0f;
		D3DXMatrixOrthoLH(&LightProjectionMatrix, w, h, zn, zf);

#pragma region Kerekites
		{
			D3DXMATRIX mat;
			D3DXMatrixMultiply(&mat, &LightViewMatrix, &LightProjectionMatrix);

			float xl=x*mat._11+y*mat._21+z*mat._31;//+1*mat._41;
			float yl=x*mat._12+y*mat._22+z*mat._32;//+1*mat._42;
			float zl=x*mat._13+y*mat._23+z*mat._33;//+1*mat._43;

			xl=floorf(xl*(g_ShadowMapWidth/2)) / (g_ShadowMapWidth/2);
			yl=floorf(yl*(g_ShadowMapHeight/2)) / (g_ShadowMapHeight/2);

			D3DXMATRIX InvMat;
			D3DXMatrixInverse(&InvMat, NULL, &mat);

			float x=xl*InvMat._11+yl*InvMat._21+zl*InvMat._31;//+1*InvMat._41;
			float y=xl*InvMat._12+yl*InvMat._22+zl*InvMat._32;//+1*InvMat._42;
			float z=xl*InvMat._13+yl*InvMat._23+zl*InvMat._33;//+1*InvMat._43;

			LightCamera.SetPos(x, y, z);
			LightViewMatrix=LightCamera.GetViewMatrix();
		}
#pragma endregion
	}

	D3DXMATRIX WorldMatrix;
	D3DXMatrixIdentity(&WorldMatrix);
	g_pObject->SetWorldMatrix(WorldMatrix);

	if(!g_bClear)
	{
		g_bFlip=!g_bFlip;
	}
	else
	{
		g_bFlip=false;
	}


	RenderShadowMap();


	ClearCompletely();

	ViewMatrix=g_Camera.GetViewMatrix();
	g_pObject->Render(false);

	if(g_bShowShadowMap)
	{
		g_pD3DDevice->SetPixelShader(0);
		g_pD3DDevice->SetTexture(0, g_pShadowMap);
		g_pTexturedRect->Render();
	}
}


void RenderLoop()
{
	static int frame=0;

	g_pD3DDevice->BeginScene();


	RenderSomething();

	//calculate smoothened frame rate
	const int count=16;
	static int times[count];

	int Time = (int)timeGetTime();
	static int LastTime;
	int TimeDiff=Time-LastTime;

	if(LastTime==0)
	{
		TimeDiff=0;
	}

	LastTime=Time;

	for(int i=count-2; i>=0; i--)
	{
		times[i+1]=times[i];		//shift up times
	}

	times[0]=TimeDiff;

	float AverageTime=0.0f;

	for(int i=0; i<count; i++)
	{
		AverageTime+=times[i];
	}

	AverageTime*=1.0f/count;


			g_pD3DDevice->SetPixelShader(0);
			g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
			g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, false);
			g_pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, false);
			g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);


	if(g_bShowInfo)
	{
		g_pFont->setcolor( 0xff0000ff );

		char text[64];
		g_pFont->locatexy( g_Width-140.0f, 0.0f );

		sprintf_s(text, 64, "FPS: %f", 1000.0f/AverageTime);
		g_pFont->printf(text);

		g_pFont->locatexy( g_Width-260.0f, 20.0f );
		sprintf_s(text, 64, "Shadow Map Size: %d x %d", g_ShadowMapWidth, g_ShadowMapHeight);
		g_pFont->printf(text);

		g_pFont->locatexy( g_Width-260.0f, 40.0f );
		sprintf_s(text, 64, "Shadow Bias: %f", g_Bias);
		g_pFont->printf(text);

		g_pFont->locatexy( g_Width-260.0f, 60.0f );
		if(g_bShadowCull)
		{
			sprintf_s(text, 64, "Culling: On");
		}
		else
		{
			sprintf_s(text, 64, "Culling: Off");
		}

		g_pFont->printf(text);

	}


	g_pD3DDevice->EndScene();

	HRESULT hres;
	hres=g_pD3DDevice->Present(0,0,0,0);

	frame++;
}


void CreateFont()
{
	g_pFont = new CD3DFont( _T("Arial"), 12, D3DFONT_BOLD );
	g_pFont->InitDeviceObjects( g_pD3DDevice );
	g_pFont->RestoreDeviceObjects();
}


IDirect3DTexture9* CreateShadowMap();


void OnKeyDown(int key)
{
	switch(key)
	{
	case DIK_SPACE:
		g_bShowShadowMap=!g_bShowShadowMap;
		break;

	case DIK_I:
		g_bShowInfo=!g_bShowInfo;
		break;

	case DIK_F:
		static int aniso;
		aniso++;
		aniso%=8;
		for(int i=0; i<8; i++)
		{
			g_pD3DDevice->SetSamplerState(i, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
			g_pD3DDevice->SetSamplerState(i, D3DSAMP_MAXANISOTROPY, aniso);
		}
		break;

	case DIK_BACK:
		g_bClear=!g_bClear;
		break;

	case DIK_P:
		g_bPCF=!g_bPCF;
		break;

	case DIK_C:
		g_bShadowCull=!g_bShadowCull;
		break;

	case DIK_J:
		g_bJitter=!g_bJitter;
		break;

	case DIK_PGUP:
		g_ShadowMapWidth*=2;
		g_ShadowMapHeight*=2;

		g_pShadowMap->Release();
		g_pShadowMap=CreateShadowMap();
		delete g_pClearRect;
		g_pClearRect=new Rectangle(0, 0, (float)g_ShadowMapWidth, (float)g_ShadowMapWidth);

		break;

	case DIK_PGDN:
		g_ShadowMapWidth/=2;
		g_ShadowMapHeight/=2;
	
		g_pShadowMap->Release();
		g_pShadowMap=CreateShadowMap();
		delete g_pClearRect;
		g_pClearRect=new Rectangle(0, 0, (float)g_ShadowMapWidth, (float)g_ShadowMapWidth);

		break;


	}

	return;
}



IDirect3DTexture9* CreateShadowMap()
{
	IDirect3DTexture9* pTexture;
	int MipLevels=1;
	HRESULT hres=D3DXCreateTexture(g_pD3DDevice, g_ShadowMapWidth, g_ShadowMapHeight, MipLevels, D3DUSAGE_RENDERTARGET,
		g_ShadowMapFormat, D3DPOOL_DEFAULT, &pTexture);

	return pTexture;
}




void InitTest()
{
	(*KeyboardInput::Instance())+=OnKeyDown;

	timeBeginPeriod(1);

	g_pShadowMap=CreateShadowMap();

	LPD3DXBUFFER pCompilationErrors;

	{
		DWORD Flags=0;
		LPD3DXINCLUDE pInclude=NULL;
		LPD3DXEFFECTPOOL pPool=NULL;
		CONST D3DXMACRO* pDefines=NULL;

		HRESULT hres= D3DXCreateEffectFromFile(
			g_pD3DDevice,
			"ShadowMap.fx",
			pDefines,
			pInclude,
			Flags,
			pPool,
			&g_pEffect,
			&pCompilationErrors);

		if(pCompilationErrors)
		{
			char* pErrors=(char*)pCompilationErrors->GetBufferPointer();
			OutputDebugString(pErrors);
		}
	}

	LPDIRECT3DTEXTURE9 pTexture;
	HRESULT hres=D3DXCreateTextureFromFile(g_pD3DDevice, "noise.png", &pTexture);
	g_pNoiseTexture=pTexture;


	g_pTexturedRect=new Rectangle(0, 0, 256, 256);
	g_pClearRect=new Rectangle(0, 0, (float)g_ShadowMapWidth, (float)g_ShadowMapWidth);
//	g_pObject=new C3DObject("teapot.x");
	g_pObject=new C3DObject("c:/taj.x", g_pEffect);

	g_Camera.SetPos(0, 3000.0f, 2500.0f);
	g_Camera.SetYPR(-Pi, +Pi/4, 0);
}



}



int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	g_hInstance=hInstance;

	MSG msg;

	Engine::InitEngine(g_Width, g_Height, 32, g_bWindowed);
	Engine::CreateFont();
	Engine::InitTest();

	while(1)
	{
        while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			bool bQuit = !GetMessage(&msg, NULL, 0, 0);

			TranslateMessage(&msg);
			DispatchMessage(&msg);
			if(bQuit)
			{
				return msg.wParam;
			}
		}

		if(g_bActive)
		{
			Engine::RenderLoop();
		}

		else WaitMessage();
	}
}
