/*	Renegade Scripts.dll
	Shader State Manager
	Copyright 2007 Jonathan Wilson, Mark Sararu

	This file is part of the Renegade scripts.dll
	The Renegade scripts.dll is free software; you can redistribute it and/or modify it under
	the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2, or (at your option) any later
	version. See the file COPYING for more details.
	In addition, an exemption is given to allow Run Time Dynamic Linking of this code with any closed source module that does not contain code covered by this licence.
	Only the source code to the module(s) containing the licenced code has to be released.
*/
#include "scripts.h"
#include "shadereng.h"
#include "shaderstatemanager.h"
#include "intrin.h"

ShaderStateManager *StateManager;

ShaderStateManager::ShaderStateManager()
{
	RenderStates		= new unsigned long[209];
	RenderStatesCached	= new bool[209];

	ZeroMemory(RenderStates,sizeof(unsigned long)*209);
	ZeroMemory(RenderStatesCached,sizeof(bool)*209);
	for (int i = 0; i < SM_CACHEDSAMPLERS; i++) 
	{
		SamplerStates[i]		= new unsigned long[13];
		SamplerStatesCached[i]	= new bool[13];
		TextureStates[i]		= new unsigned long[32];
		TextureStatesCached[i]	= new bool[32];
		Textures[i]				= 0;

		ZeroMemory(SamplerStates[i],sizeof(unsigned long)*13);
		ZeroMemory(SamplerStatesCached[i],sizeof(bool)*13);
		ZeroMemory(TextureStates[i],sizeof(unsigned long)*32);
		ZeroMemory(TextureStatesCached[i],sizeof(bool)*32);

		// Game startup defaults, need to be precached so stuff (doesn't) happen.
		SamplerStates[i][D3DSAMP_MAXANISOTROPY]			= 2;
		SamplerStatesCached[i][D3DSAMP_MAXANISOTROPY]	= true;
	}
	VertexShader = 0;
	PixelShader = 0;
	NPatchMode = 1.0f;

	//Moar game startup defaults
	RenderStates[D3DRS_RANGEFOGENABLE]					= (CurrentCaps->Caps.RasterCaps >> 0x10) && 1;
	RenderStates[D3DRS_FOGTABLEMODE]					= 0;
	RenderStates[D3DRS_FOGVERTEXMODE]					= 3;
	RenderStates[D3DRS_SPECULARMATERIALSOURCE]			= 0;
	RenderStates[D3DRS_COLORVERTEX]						= 1;
	TextureStates[0][D3DTSS_BUMPENVMAT00]				= F2DW(1.0f);
	TextureStates[0][D3DTSS_BUMPENVMAT01]				= F2DW(0.0f);
	TextureStates[0][D3DTSS_BUMPENVMAT10]				= F2DW(0.0f);
	TextureStates[0][D3DTSS_BUMPENVMAT11]				= F2DW(1.0f);
	TextureStates[1][D3DTSS_BUMPENVLSCALE]				= F2DW(1.0f);
	TextureStates[1][D3DTSS_BUMPENVLOFFSET]				= F2DW(0.0f);
	RenderStatesCached[D3DRS_RANGEFOGENABLE]			= true;
	RenderStatesCached[D3DRS_FOGTABLEMODE]				= true;
	RenderStatesCached[D3DRS_FOGVERTEXMODE]				= true;
	RenderStatesCached[D3DRS_SPECULARMATERIALSOURCE]	= true;
	RenderStatesCached[D3DRS_COLORVERTEX]				= true;
	TextureStatesCached[0][D3DTSS_BUMPENVMAT00]			= true;
	TextureStatesCached[0][D3DTSS_BUMPENVMAT01]			= true;
	TextureStatesCached[0][D3DTSS_BUMPENVMAT10]			= true;
	TextureStatesCached[0][D3DTSS_BUMPENVMAT11]			= true;
	TextureStatesCached[1][D3DTSS_BUMPENVLSCALE]		= true;
	TextureStatesCached[1][D3DTSS_BUMPENVLOFFSET]		= true;
}
ShaderStateManager::~ShaderStateManager()
{
	SAFE_DELETE_ARRAY(RenderStates);
	SAFE_DELETE_ARRAY(RenderStatesCached);
	for (int i = 0; i < SM_CACHEDSAMPLERS; i++) 
	{
		SAFE_DELETE_ARRAY(SamplerStates[i]);
		SAFE_DELETE_ARRAY(SamplerStatesCached[i]);
		SAFE_DELETE_ARRAY(TextureStates[i]);
		SAFE_DELETE_ARRAY(TextureStatesCached[i]);
		SAFE_RELEASE(Textures[i]);
	}

}

void ShaderStateManager::Reset()
{
	ZeroMemory(RenderStates,sizeof(unsigned long)*209);
	ZeroMemory(RenderStatesCached,sizeof(bool)*209);

	for (int i = 0; i < SM_CACHEDSAMPLERS; i++) 
	{
		ZeroMemory(SamplerStates[i],sizeof(unsigned long)*13);
		ZeroMemory(SamplerStatesCached[i],sizeof(bool)*13);
		ZeroMemory(TextureStates[i],sizeof(unsigned long)*32);
		ZeroMemory(TextureStatesCached[i],sizeof(bool)*32);
		if (Textures[i])
		{
			Textures[i]->Release();
		}
		Textures[i] = 0;
	}
	VertexShader = 0;
	PixelShader = 0;
}
HRESULT ShaderStateManager::QueryInterface(const IID &iid, LPVOID *ppv)
{
	if (iid == IID_IUnknown || iid == IID_ID3DXEffectStateManager)
	{
		*ppv = static_cast<ID3DXEffectStateManager*>(this);
	}
	else
	{
		*ppv = NULL;
		return E_NOINTERFACE;
	}
	reinterpret_cast<IUnknown*>(this)->AddRef();
	return S_OK;
}
unsigned long ShaderStateManager::AddRef()
{
	return (unsigned long)InterlockedIncrement(&ref);
}
unsigned long ShaderStateManager::Release()
{
	if (InterlockedDecrement(&ref) == 0L)
	{
		delete this;
		return 0L;
	}
	return (unsigned long) ref;
}
HRESULT ShaderStateManager::LightEnable(DWORD Index,BOOL Enable)
{
	bool b = Enable;
	if (CurrentDX8LightEnables[Index] == b) return S_OK;
	CurrentDX8LightEnables[Index] = Enable;
	return Direct3DDevice->LightEnable(Index,Enable);
}
HRESULT ShaderStateManager::SetFVF(DWORD FVF)
{
	return Direct3DDevice->SetFVF(FVF);
}
HRESULT ShaderStateManager::SetLight(DWORD Index,CONST D3DLIGHT9* pLight)
{
	return Direct3DDevice->SetLight(Index,pLight);
}
HRESULT ShaderStateManager::SetMaterial(CONST D3DMATERIAL9* pMaterial)
{
	return Direct3DDevice->SetMaterial(pMaterial);
}
HRESULT ShaderStateManager::SetNPatchMode(FLOAT nSegments)
{
	if (NPatchMode == nSegments)
	{
		return S_OK;
	}
	NPatchMode = nSegments;
	return Direct3DDevice->SetNPatchMode(nSegments);
}
HRESULT ShaderStateManager::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value)
{
  if ((RenderStates[State] == Value) && (RenderStatesCached[State] == true))
	{
		return S_OK;
	}
	RenderStates[State] = Value;
	RenderStatesCached[State] = true;
	return Direct3DDevice->SetRenderState(State,Value);
}

HRESULT ShaderStateManager::GetRenderState(D3DRENDERSTATETYPE State, DWORD *pValue)
{
	if (RenderStatesCached[State] == true)
	{
		*pValue = RenderStates[State];
		return S_OK;
	}
	HRESULT res = Direct3DDevice->GetRenderState(State,pValue);
	RenderStates[State] = *pValue;
	RenderStatesCached[State] = true;
	return res;
}


DWORD ShaderStateManager::GetRenderState(D3DRENDERSTATETYPE State)
{
	if (RenderStatesCached[State] == true) 
	{
		return RenderStates[State];
	}
	DWORD retval;
	Direct3DDevice->GetRenderState(State,&retval);
	RenderStates[State] = retval;
	RenderStatesCached[State] = true;
	return retval;
}

HRESULT ShaderStateManager::SetRenderTarget(DWORD RenderTargetIndex,LPDIRECT3DSURFACE9 pRenderTarget)
{
	if (RenderTargetIndex)
	{
		return Direct3DDevice->SetRenderTarget(RenderTargetIndex,pRenderTarget);
	}																				 
	if (RenderTarget == pRenderTarget)
	{
		return S_OK;
	}
	RenderTarget = pRenderTarget;
	return Direct3DDevice->SetRenderTarget(RenderTargetIndex,pRenderTarget);
}

HRESULT ShaderStateManager::SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value)
{
	if ((SamplerStates[Sampler][Type] == Value) && (SamplerStatesCached[Sampler][Type] == true))
	{
		return S_OK;
	}
	SamplerStates[Sampler][Type] = Value;
	SamplerStatesCached[Sampler][Type] = true;
	return Direct3DDevice->SetSamplerState(Sampler,Type,Value);
}
HRESULT ShaderStateManager::GetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD *pValue)
{
	if (SamplerStatesCached[Sampler][Type] == true) 
	{
		*pValue = SamplerStates[Sampler][Type];
		return S_OK;
	}
	HRESULT res = Direct3DDevice->GetSamplerState(Sampler,Type,pValue);
	SamplerStates[Sampler][Type] = *pValue;
	SamplerStatesCached[Sampler][Type] = true;
	return res;
}
HRESULT ShaderStateManager::SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value)
{
	if ((TextureStates[Stage][Type] == Value) && (TextureStatesCached[Stage][Type] == true))
	{
		return S_OK;
	}
	TextureStates[Stage][Type] = Value;
	TextureStatesCached[Stage][Type] = true;
	return Direct3DDevice->SetTextureStageState(Stage,Type,Value);
}
HRESULT ShaderStateManager::GetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD *pValue)
{
	if (TextureStatesCached[Stage][Type] == true) 
	{
		*pValue = TextureStates[Stage][Type];
		return S_OK;
	}
	HRESULT res = Direct3DDevice->GetTextureStageState(Stage,Type,pValue);
	TextureStates[Stage][Type] = *pValue;
	TextureStatesCached[Stage][Type] = true;
	return res;
}
HRESULT ShaderStateManager::SetTexture(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture)
{
	if (Textures[Stage] == pTexture)
	{
		return S_OK;
	}
	if (Textures[Stage] != NULL)
	{
		Textures[Stage]->Release();
	}
	if (pTexture != NULL)
	{
		pTexture->AddRef();
	}
	Textures[Stage] = pTexture;
	return Direct3DDevice->SetTexture(Stage,pTexture);
}

HRESULT ShaderStateManager::SetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX* pMatrix)
{
  return Direct3DDevice->SetTransform(State,pMatrix);
}

/*Pixel Shader*/
HRESULT ShaderStateManager::SetPixelShader(LPDIRECT3DPIXELSHADER9 pShader)
{
	if (PixelShader == pShader)
	{
		return S_OK;
	}
	PixelShader = pShader;
	return Direct3DDevice->SetPixelShader(pShader);
}
HRESULT ShaderStateManager::SetPixelShaderConstantB(UINT StartRegister, CONST BOOL* pConstantData, UINT RegisterCount)
{
	return Direct3DDevice->SetPixelShaderConstantB(StartRegister,pConstantData,RegisterCount);
}
HRESULT ShaderStateManager::SetPixelShaderConstantF(UINT StartRegister, CONST FLOAT* pConstantData, UINT RegisterCount)
{
	return Direct3DDevice->SetPixelShaderConstantF(StartRegister,pConstantData,RegisterCount);
}
HRESULT ShaderStateManager::SetPixelShaderConstantI(UINT StartRegister, CONST INT* pConstantData, UINT RegisterCount)
{
	return Direct3DDevice->SetPixelShaderConstantI(StartRegister,pConstantData,RegisterCount);
}

/*Vertex Shader*/
HRESULT ShaderStateManager::SetVertexShader(LPDIRECT3DVERTEXSHADER9 pShader)
{
	if (VertexShader == pShader)
	{
		return S_OK;
	}
	VertexShader = pShader;
	return Direct3DDevice->SetVertexShader(pShader);
}
HRESULT ShaderStateManager::SetVertexShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT RegisterCount)
{
	return Direct3DDevice->SetVertexShaderConstantB(StartRegister,pConstantData,RegisterCount);
}
HRESULT ShaderStateManager::SetVertexShaderConstantF(UINT StartRegister, CONST FLOAT* pConstantData, UINT RegisterCount)
{
	return Direct3DDevice->SetVertexShaderConstantF(StartRegister,pConstantData,RegisterCount);
}
HRESULT ShaderStateManager::SetVertexShaderConstantI(UINT StartRegister, CONST INT* pConstantData, UINT RegisterCount)
{
	return Direct3DDevice->SetVertexShaderConstantI(StartRegister,pConstantData,RegisterCount);
}
