/*	Renegade Scripts.dll
	Glass Shader Class
	Copyright 2007 Mark Sararu, Jonathan Wilson
	glass.fx copyright 2002-2004 NVIDIA Corporation

	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 "resourcemanager.h"
#include "shaderstatemanager.h"
#include "effect.h"
#include "shader.h"
#include "cubetexture.h"
#include "glassshader.h"

const unsigned char fresnelTextureConst[385] = 
{
	0x44,0x44,0x53,0x20,0x7c,0x00,0x00,0x00,0x07,0x10,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
	0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfa,0xf7,0xf4,0xf1,0xee,0xeb,0xe8,0xe6,0xe2,0xe0,0xdd,
	0xda,0xd7,0xd5,0xd2,0xd0,0xcd,0xca,0xc7,0xc6,0xc3,0xc1,0xbe,0xbc,0xb9,0xb7,0xb4,0xb3,0xb0,0xae,0xab,
	0xaa,0xa7,0xa6,0xa3,0xa2,0x9f,0x9e,0x9b,0x9a,0x98,0x96,0x94,0x93,0x90,0x8f,0x8d,0x8c,0x89,0x88,0x86,
	0x85,0x83,0x82,0x80,0x7f,0x7d,0x7c,0x7a,0x79,0x77,0x76,0x74,0x73,0x72,0x71,0x6f,0x6e,0x6c,0x6c,0x6a,
	0x69,0x68,0x67,0x65,0x65,0x63,0x62,0x61,0x61,0x5f,0x5e,0x5d,0x5d,0x5b,0x5b,0x59,0x59,0x57,0x57,0x56,
	0x55,0x54,0x54,0x52,0x52,0x51,0x51,0x4f,0x4f,0x4e,0x4e,0x4d,0x4c,0x4b,0x4b,0x4a,0x4a,0x49,0x49,0x48,
	0x48,0x47,0x46,0x45,0x45,0x44,0x44,0x43,0x43,0x43,0x42,0x41,0x42,0x41,0x41,0x40,0x40,0x3f,0x3f,0x3e,
	0x3f,0x3e,0x3e,0x3d,0x3d,0x3c,0x3c,0x3c,0x3c,0x3b,0x3b,0x3a,0x3b,0x3a,0x3a,0x39,0x3a,0x39,0x39,0x39,
	0x39,0x38,0x38,0x38,0x38,0x37,0x38,0x37,0x37,0x37,0x37,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x35,
	0x36,0x35,0x35,0x35,0x35,0x35,0x35,0x34,0x35,0x34,0x35,0x34,0x35,0x34,0x34,0x34,0x34,0x34,0x34,0x34,
	0x34,0x34,0x34,0x33,0x34,0x34,0x34,0x33,0x34,0x33,0x34,0x33,0x34,0x33,0x34,0x33,0x34,0x33,0x34,0x33,
	0x34,0x33,0x33,0x33,0x34,0x33,0x33,0x33,0x34,0x33,0x33,0x33,0x34,0x33,0x33,0x33,0x33,0x33,0x33,0x33,
	0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,
	0x33,0x33,0x33,0x33,0x33
};

GlassShaderClass::GlassShaderClass() : ProgrammableShaderClass()
{
	ReflectStrength = 1.0f;
	RefractStrength = 1.0f;
	EnvironmentMap = 0;
	PixelShaderVersion = 2.0;
	VertexShaderVersion = 2.0;
}

GlassShaderClass::~GlassShaderClass()
{
	SAFE_DELETE_ARRAY(EnvironmentMap);
	ProgrammableShaderClass::~ProgrammableShaderClass();
}

void GlassShaderClass::Load(ChunkLoadClass &cload)
{
	while (cload.Open_Micro_Chunk())
	{
		switch(cload.Cur_Micro_Chunk_ID())
		{
			case MC_GLASS_MATERIALNAME:
				Name = new char[cload.Cur_Micro_Chunk_Length()];
				cload.Read(Name,cload.Cur_Micro_Chunk_Length());
				CRC32 = CRC_Memory((unsigned char *)_strlwr(Name),strlen(Name),0);
				break;
			case MC_GLASS_FXFILENAME:
				Filename = new char[cload.Cur_Micro_Chunk_Length()];
				cload.Read(Filename,cload.Cur_Micro_Chunk_Length());
				#ifndef SDBEDIT
				Effect = new EffectClass(Filename);
				#endif
				break;
			case MC_GLASS_REFLECTSTRENGTH:
				cload.Read(&ReflectStrength,sizeof(float));
				break;
			case MC_GLASS_REFRACTSTRENGTH:
				cload.Read(&RefractStrength,sizeof(float));
				break;
			case MC_GLASS_ENVIRONMENTMAP:
				EnvironmentMap = new char[cload.Cur_Micro_Chunk_Length()];
				cload.Read(EnvironmentMap,cload.Cur_Micro_Chunk_Length());
				break;
		}
		cload.Close_Micro_Chunk();
	}
}

void GlassShaderClass::Save(ChunkSaveClass &csave)
{
	csave.Begin_Chunk(CHUNK_GLASS_SHADER);
	csave.Begin_Micro_Chunk(MC_GLASS_MATERIALNAME);
	csave.Write(Name,strlen(Name) + 1);
	csave.End_Micro_Chunk();
	csave.Begin_Micro_Chunk(MC_GLASS_FXFILENAME);
	csave.Write(Filename,strlen(Filename) + 1);
	csave.End_Micro_Chunk();
	csave.Begin_Micro_Chunk(MC_GLASS_REFLECTSTRENGTH);
	csave.Write(&ReflectStrength,4);
	csave.End_Micro_Chunk();
	csave.Begin_Micro_Chunk(MC_GLASS_REFLECTSTRENGTH);
	csave.Write(&RefractStrength,4);
	csave.End_Micro_Chunk();
	csave.Begin_Micro_Chunk(MC_GLASS_ENVIRONMENTMAP);
	csave.Write(EnvironmentMap,strlen(EnvironmentMap) + 1);
	csave.End_Micro_Chunk();
	csave.End_Chunk();
}

#ifndef SDBEDIT
void GlassShaderClass::Release_Resources()
{
	if (!Loaded)
	{
		return;
	}
	ReleaseTextures();
	Effect->OnDeviceLost(); 
	Loaded = false;
}

void GlassShaderClass::Reload_Resources()
{
	if ((PixelShaderVersion > ShaderCaps::PixelShaderVersion) || (VertexShaderVersion > ShaderCaps::VertexShaderVersion))
	{
		return;
	}
	if (Loaded)
	{
		return;
	}
	if (Effect->Initialized != true)
	{
		Effect->Init();
	}
	else 
	{
		Effect->OnDeviceReset();
	}
	LoadTextures();
	Loaded = true;
}

void GlassShaderClass::LoadTextures()
{
	D3DXCreateTextureFromFileInMemory(Direct3DDevice,fresnelTextureConst,385,&texture_Fresnel);
	texture_Environment = new CubeTextureClass(EnvironmentMap);
}

void GlassShaderClass::ReleaseTextures()
{
	if(texture_Environment)
	{
		texture_Environment->Release_Ref();
	}
	SAFE_RELEASE(texture_Fresnel);
}

void GlassShaderClass::Render(unsigned int primitive_type, unsigned short start_index, unsigned short polygon_count, unsigned short min_vertex_index, unsigned short vertex_count)
{
	unsigned int cPasses;
	ApplyRenderState();
	ShaderStateSaver saver = ShaderStateSaver(D3DRS_FOGENABLE,3.0f);
	StateManager->SetRenderState(D3DRS_FOGENABLE,false);
	Effect->Begin(&cPasses,0);
	for (unsigned int pass = 0; pass < cPasses; pass++)
	{
		Effect->BeginPass(pass);
		Effect->CommitChanges();
		Draw(primitive_type,start_index,polygon_count,min_vertex_index,vertex_count);
		Effect->EndPass();
	}
	Effect->End();
}

void GlassShaderClass::ApplyRenderState()
{
	ApplyTransform();
	Effect->SetTechnique("ps20");
	if (texture_Environment->Initialized != true)
	{
		texture_Environment->Initialize();
	} 
	Effect->SetTexture("cubeMap",texture_Environment->D3DTexture);
	Effect->SetTexture("fresnelTex",texture_Fresnel);
	Effect->SetFloat("refractStrength",RefractStrength);
	Effect->SetFloat("reflectStrength",ReflectStrength);
	Buffers_Apply();
	render_state->shader.Apply();
	*render_state_changed &= 0x0C0000;
}

void GlassShaderClass::ApplyTransform()
{
	Matrix4 projection = *Get_Projection_Matrix();
	Matrix4 view = render_state->view;
	Matrix4 viewInverse = view.Inverse();
	Matrix4 world = render_state->world;
	Matrix4 worldInverse = world.Inverse();
	Matrix4 worldViewProjection = world * view * projection;
	Effect->SetMatrix("world",&world);
	Effect->SetMatrix("wvp",&worldViewProjection);
	Effect->SetMatrix("viewI",&viewInverse);
	Effect->SetMatrixTranspose("worldIT",&worldInverse);
}

ShaderRegistrant<GlassShaderClass> GlassShaderRegistrant(CHUNK_GLASS_SHADER);
#endif
