/*	Renegade Scripts.dll
	3D drawing related engine classes and calls for shaders.dll
	Copyright 2007 Jonathan Wilson

	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 "engine_common.h"
#include "engine_vector.h"
#include "engine_threading.h"
#include "engine_string.h"
#include "engine_io.h"
#include "engine_math.h"
#include "engine_d3d.h"
#include "engine_def.h"
#include "engine_net.h"
#include "engine_obj.h"
#include "engine_3dre.h"
#include "engine_3d.h"
#include "shaderstatemanager.h"
#include "engine_diagnostics.h"
ScriptNotify Notify;
MessageWindowClass **MessageWindow;
void *Window_Add_Message;

void THUNK MessageWindowClass::Add_Message(WideStringClass *message,Vector3 *color,GameObject *obj,float f)
IMPLEMENT_THUNK(Window_Add_Message)

void depthbiasInit(unsigned int level);
static void (*ApplyDepthBias)(unsigned int) = depthbiasInit;


ScriptCommandsClass Commands;
unsigned int *render_state_changed;
RenderStateStruct *render_state;
bool *CurrentDX8LightEnables;
D3DCOLOR *AmbientColor;
bool *TriangleDraw;
bool *TexturingEnabled;
unsigned int *MinTextureFilters;
unsigned int *MagTextureFilters;
unsigned int *MipMapFilters;
D3DMATERIAL9 *DefaultMaterial;
void *InvalidateUnusedTextures;
void *InvalidateMeshCache;
bool CalculateTangents;
unsigned int *_VertexBufferCount;
unsigned int *_VertexBufferTotalVertices;
unsigned int *_VertexBufferTotalSize;
bool *_DynamicDX8VertexBufferInUse;
unsigned short *_DynamicDX8VertexBufferSize;
DX8VertexBufferClass **_DynamicDX8VertexBuffer;
unsigned short *_DynamicDX8VertexBufferOffset;
unsigned short _DynamicDeclerationVertexBufferSize = 0x2710;
DeclarationVertexBufferClass *_DynamicDeclerationVertexBuffer;
unsigned short _DynamicDeclerationVertexBufferOffset;
FVFInfoClass *_DynamicFVFInfo;
bool *_DynamicSortingVertexArrayInUse;
unsigned short *_DynamicSortingVertexArraySize;
SortingVertexBufferClass **_DynamicSortingVertexArray;
unsigned short *_DynamicSortingVertexArrayOffset;
void *NeedsNormals;
void *GetVertexNormals;
void *GetUserLighting;
void *GetColorArray;
void *GenerateTextureCategories;
D3DPRESENT_PARAMETERS8 *parameters;
char **sky;
RenderQuadClass *RenderQuad;
Vector3 *LightVector;
Vector3 *UnitLightVector;
Render2DClass **ScreenFade;
bool *ShaderDirty;
unsigned long *CurrentShader;
BlendLUT srcBlendLUT[5] = {{D3DBLEND_ZERO,false},{D3DBLEND_ONE,false},{D3DBLEND_SRCALPHA,true},{D3DBLEND_INVSRCALPHA,true}};
BlendLUT dstBlendLUT[6] = {{D3DBLEND_ZERO,false},{D3DBLEND_ONE,false},{D3DBLEND_SRCCOLOR,false},{D3DBLEND_INVSRCCOLOR,false},{D3DBLEND_SRCALPHA,true},{D3DBLEND_INVSRCALPHA,true}};
bool *FogEnable;
unsigned long *FogColor;
unsigned int *_PolygonCullMode;
float *NPatchesLevel;
DynamicVectorClass<Vector3> *TempVertexBuffer;
DynamicVectorClass<Vector3> *TempNormalBuffer;
void *UVArray;
void *GetDeformedVertexes;
void *tcrender;
void *RemoveHead;
void *RenderMatPass;
Matrix4 ViewMatrix;
Matrix4 WorldMatrix;
SurfaceClass **Surface;

void THUNK Invalidate_Unused_Textures(unsigned int u)
IMPLEMENT_THUNK(InvalidateUnusedTextures)
void THUNK Invalidate_Mesh_Cache()
IMPLEMENT_THUNK(InvalidateMeshCache)
bool THUNK MeshModelClass::Needs_Vertex_Normals()
IMPLEMENT_THUNK_RETURN(NeedsNormals)
Vector3 THUNK *MeshGeometryClass::Get_Vertex_Normal_Array()
IMPLEMENT_THUNK_RETURN(GetVertexNormals)
void THUNK DX8FVFCategoryContainer::Generate_Texture_Categories(Vertex_Split_Table *split,unsigned int vertex_offset)
IMPLEMENT_THUNK(GenerateTextureCategories)
Vector2 THUNK *MeshMatDescClass::Get_UV_Array_By_Index(int index, bool create)
IMPLEMENT_THUNK_RETURN(UVArray)
unsigned int THUNK *MeshMatDescClass::Get_Color_Array(int index, bool create)
IMPLEMENT_THUNK_RETURN(GetColorArray)
void THUNK DX8TextureCategoryClass::Render()
IMPLEMENT_THUNK(tcrender)
void THUNK DX8FVFCategoryContainer::Render_Procedural_Material_Passes()
IMPLEMENT_THUNK(RenderMatPass)
MultiListObjectClass THUNK *GenericMultiListClass::Internal_Remove_List_Head()
IMPLEMENT_THUNK_RETURN(RemoveHead)


EnlargeableBufferClass<Vector3> *TempTangentBuffer;

void InitEngine3D(unsigned int exe)
{
	switch(exe)
	{
		case 0: //game.exe
			Window_Add_Message = (void *)0x006A76E0;
			MessageWindow = (MessageWindowClass **)0x00855EDC;
			Surface = (SurfaceClass **)0x00830724;
			RenderMatPass = (void *)0x00537AC0;
			tcrender = (void *)0x0053A850;
			RemoveHead = (void *)0x005EE710;
			GetDeformedVertexes = (void *)0x00577670;
			UVArray = (void *)0x0053A010;
			TempVertexBuffer = (DynamicVectorClass<Vector3> *)0x00830650;
			TempNormalBuffer = (DynamicVectorClass<Vector3> *)0x00830638;
			NPatchesLevel = (float *)0x007FD66C;
			_PolygonCullMode = (unsigned int *)0x008093AC;
			FogColor = (unsigned long *)0x0083146C;
			FogEnable = (bool *)0x0083146A;
			CurrentShader = (unsigned long *)0x00836BD4;
			ShaderDirty = (bool *)0x008093A8;
			LightVector = (Vector3 *)0x0085DA10;
			UnitLightVector = (Vector3 *)0x0085D338;
			render_state_changed = (unsigned int *)0x00830728;
			render_state = (RenderStateStruct *)0x008310E0;
			CurrentDX8LightEnables = (bool *)0x00830730;
			AmbientColor = (D3DCOLOR *)0x00830AC4;
			TriangleDraw = (bool *)0x007FDC18;
			TexturingEnabled = (bool *)0x007FD670;
			MinTextureFilters = (unsigned int *)0x008315B4;
			MagTextureFilters = (unsigned int *)0x008315D4;
			MipMapFilters = (unsigned int *)0x00831594;
			DefaultMaterial = (D3DMATERIAL9 *)0x00809350;
			_VertexBufferCount = (unsigned int *)0x00831618;
			_VertexBufferTotalVertices = (unsigned int *)0x00831624;
			_VertexBufferTotalSize = (unsigned int *)0x00831620;
			InvalidateUnusedTextures = (void *)0x005502B0;
			InvalidateMeshCache = (void *)0x00529AA0;
			_DynamicDX8VertexBufferInUse = (bool *)0x00831670;
			_DynamicDX8VertexBufferSize = (unsigned short *)0x007FF110;
			_DynamicDX8VertexBuffer = (DX8VertexBufferClass **)0x00831674;
			_DynamicDX8VertexBufferOffset = (unsigned short *)0x00831678;
			_DynamicFVFInfo = (FVFInfoClass *)0x00831628;
			_DynamicSortingVertexArrayInUse = (bool *)0x00831664;
			_DynamicSortingVertexArraySize = (unsigned short *)0x0083166C;
			_DynamicSortingVertexArray = (SortingVertexBufferClass **)0x00831668;
			_DynamicSortingVertexArrayOffset = (unsigned short *)0x0083166E;
			NeedsNormals = (void *)0x0058ED30;
			GetVertexNormals = (void *)0x00593410;
			GetUserLighting = (void *)0x00579790;
			GetColorArray = (void *)0x00539470;
			GenerateTextureCategories = (void *)0x005396A0;
			parameters = (D3DPRESENT_PARAMETERS8 *)0x008313E8;
			sky = (char **)0x0085DB68;
			Commands = (ScriptCommandsClass)0x0085F490;
			ScreenFade = (Render2DClass **)0x0085E674;
			RenderQuad = new RenderQuadClass();
			TheShaderCaps = new ShaderCaps();
			StateManager = new ShaderStateManager();
			TempTangentBuffer = new EnlargeableBufferClass<Vector3>(0);
			CalculateTangents = false;
			break;
		default:
			Window_Add_Message = (void *)0x0;
			MessageWindow = (MessageWindowClass **)0x0;
			Surface = (SurfaceClass **)0x0;
			RenderMatPass = (void *)0x0;
			tcrender = (void *)0x0;
			RemoveHead = (void *)0x0;
			GetDeformedVertexes = (void *)0x0;
			UVArray = (void *)0x0;
			TempVertexBuffer = (DynamicVectorClass<Vector3> *)0x0;
			TempNormalBuffer = (DynamicVectorClass<Vector3> *)0x0;
			NPatchesLevel = (float *)0x0;
			_PolygonCullMode = (unsigned int *)0x0;
			FogColor = (unsigned long *)0x0;
			FogEnable = (bool *)0x0;
			CurrentShader = (unsigned long *)0x0;
			ShaderDirty = (bool *)0x0;
			StateManager = (ShaderStateManager*)0x0;
			TheShaderCaps = (ShaderCaps *)0x0;
			render_state_changed = (unsigned int *)0x0;
			render_state = (RenderStateStruct *)0x0;
			CurrentDX8LightEnables = (bool *)0x0;
			AmbientColor = (D3DCOLOR *)0x0;
			TriangleDraw = (bool *)0x0;
			TexturingEnabled = (bool *)0x0;
			MinTextureFilters = (unsigned int *)0x0;
			MagTextureFilters = (unsigned int *)0x0;
			MipMapFilters = (unsigned int *)0x0;
			DefaultMaterial = (D3DMATERIAL9 *)0x0;
			_VertexBufferCount = (unsigned int *)0x0;
			_VertexBufferTotalVertices = (unsigned int *)0x0;
			_VertexBufferTotalSize = (unsigned int *)0x0;
			InvalidateUnusedTextures = (void *)0x0;
			InvalidateMeshCache = (void *)0x0;
			_DynamicDX8VertexBufferInUse = (bool *)0x0;
			_DynamicDX8VertexBufferSize = (unsigned short *)0x0;
			_DynamicDX8VertexBuffer = (DX8VertexBufferClass **)0x0;
			_DynamicDX8VertexBufferOffset = (unsigned short *)0x0;
			_DynamicFVFInfo = (FVFInfoClass *)0x0;
			_DynamicSortingVertexArrayInUse = (bool *)0x0;
			_DynamicSortingVertexArraySize = (unsigned short *)0x0;
			_DynamicSortingVertexArray = (SortingVertexBufferClass **)0x0;
			_DynamicSortingVertexArrayOffset = (unsigned short *)0x0;
			NeedsNormals = (void *)0x0;
			GetVertexNormals = (void *)0x0;
			GetUserLighting = (void *)0x0;
			GetColorArray = (void *)0x0;
			GenerateTextureCategories = (void *)0x0;
			parameters = (D3DPRESENT_PARAMETERS8 *)0x0;
			sky = (char **)0x0;
			RenderQuad = (RenderQuadClass *)0x0;
			Commands = (ScriptCommandsClass)0x0;
			ScreenFade = (Render2DClass **)0x0085E674;
			TempTangentBuffer = (EnlargeableBufferClass<Vector3> *)0;
			CalculateTangents = false;
			break;
	}
}

void DestroyEngine3D()
{
	SAFE_DELETE(TheShaderCaps);
	SAFE_DELETE(RenderQuad);
	SAFE_DELETE(StateManager);
	SAFE_DELETE(TempTangentBuffer);
}

void Buffers_Apply()
{
	if (*render_state_changed & VERTEX_BUFFER_CHANGED)
	{
		if (render_state->vertex_buffer)
		{
			if ((!render_state->vertex_buffer_type) || (render_state->vertex_buffer_type == 2))
			{
				if (render_state->vertex_buffer->fvf_info)
				{
					Direct3DDevice->SetStreamSource(0,((DX8VertexBufferClass *)render_state->vertex_buffer)->Get_DX8_Vertex_Buffer(),0,render_state->vertex_buffer->FVF_Info().Get_FVF_Size());
					Direct3DDevice->SetFVF(render_state->vertex_buffer->FVF_Info().Get_FVF());
				}
				else
				{
					Direct3DDevice->SetStreamSource(0,((DX8VertexBufferClass *)render_state->vertex_buffer)->Get_DX8_Vertex_Buffer(),0,((DeclarationVertexBufferClass *)render_state->vertex_buffer)->DeclarationSize);
					Direct3DDevice->SetVertexDeclaration(((DeclarationVertexBufferClass *)render_state->vertex_buffer)->VertexDecl);
				}
			}
		}
		else
		{
			Direct3DDevice->SetStreamSource(0,0,0,0);
		}
		*render_state_changed &= ~VERTEX_BUFFER_CHANGED;
	}
	if (*render_state_changed & INDEX_BUFFER_CHANGED)
	{
		if (render_state->index_buffer)
		{
			if ((!render_state->index_buffer_type) || (render_state->index_buffer_type == 2))
			{
				Direct3DDevice->SetIndices(((DX8IndexBufferClass *)render_state->index_buffer)->Get_DX8_Index_Buffer());
			}
		}
		else
		{
			Direct3DDevice->SetIndices(0);
		}
		*render_state_changed &= ~INDEX_BUFFER_CHANGED;
	}
}

void Draw(unsigned int primitive_type, unsigned short start_index, unsigned short polygon_count, unsigned short min_vertex_index, unsigned short vertex_count)
{
	if (*TriangleDraw)
	{
		unsigned long count = vertex_count;
		unsigned long minvertexindex = 0;
		if (vertex_count < 3)
		{
			switch (render_state->vertex_buffer_type)
			{
			case 0:
			case 1:
				count = render_state->vertex_buffer->Get_Vertex_Count() - render_state->vba_offset;
				count -= render_state->index_base_offset;
				break;
			case 2:
			case 3:
				count = render_state->vba_count;
				break;
			}
		}
		else
		{
			minvertexindex = min_vertex_index;
		}
		switch (render_state->vertex_buffer_type)
		{
		case 0:
		case 2:
			if ((!render_state->index_buffer_type) || (render_state->index_buffer_type == 2))
			{
				int _baseVertexIndex = ((render_state->vba_offset & 0xFFFF) + (render_state->index_base_offset & 0xFFFF));
				Direct3DDevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primitive_type,_baseVertexIndex,minvertexindex & 0xFFFF,count & 0xFFFF,(start_index & 0xFFFF) + (render_state->iba_offset & 0xFFFF),polygon_count & 0xFFFF);
			}
			break;
		case 1:
		case 3:
			//doesnt actually appear to be triggered, if testing reveals bugs we can put this bit in
			break;
		}
	}
}

ShaderCaps* TheShaderCaps;
ShaderCaps::ShaderCaps()
{
	//D3DCAPS9 hal_caps;
	Direct3DDevice->GetDeviceCaps(&Direct3D9Caps);
	PixelShaderVersion = (float)(D3DSHADER_VERSION_MAJOR(Direct3D9Caps.PixelShaderVersion)+ (D3DSHADER_VERSION_MINOR(Direct3D9Caps.PixelShaderVersion) / 10));
	VertexShaderVersion = (float)(D3DSHADER_VERSION_MAJOR(Direct3D9Caps.VertexShaderVersion)+ (D3DSHADER_VERSION_MINOR(Direct3D9Caps.VertexShaderVersion) / 10));
	if ((Direct3D9Caps.RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS) && 
		(Direct3D9Caps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS))
	{
		DepthBiasSupported = true;
	}
	else
	{
		DepthBiasSupported = false;
	}
}

float ShaderCaps::VertexShaderVersion;
float ShaderCaps::PixelShaderVersion;
bool ShaderCaps::DepthBiasSupported;
D3DCAPS9 ShaderCaps::Direct3D9Caps;

Vector4 GetColorVector4(D3DCOLOR d3dcolor)
{
	Vector4 color;
	color.W = (float)HIBYTE(HIWORD(d3dcolor)) / (float)255;
	color.X = (float)LOBYTE(HIWORD(d3dcolor)) / (float)255;
	color.Y = (float)HIBYTE(LOWORD(d3dcolor)) / (float)255;
	color.Z = (float)LOBYTE(LOWORD(d3dcolor)) / (float)255;
	return color;
}

Matrix4* Get_Projection_Matrix()
{
	Matrix4 *m = new Matrix4;
	Direct3DDevice->GetTransform(D3DTS_PROJECTION,(D3DMATRIX *)m);
	return m;
}
void Get_Projection_Matrix(Matrix4* m)
{
	Direct3DDevice->GetTransform(D3DTS_PROJECTION,(D3DMATRIX *)m);
}
Vector3 GetColorVector3(D3DCOLOR d3dcolor)
{
	Vector3 color;
	color.X = (float)LOBYTE(HIWORD(d3dcolor)) / (float)255;
	color.Y = (float)HIBYTE(LOWORD(d3dcolor)) / (float)255;
	color.Z = (float)LOBYTE(LOWORD(d3dcolor)) / (float)255;
	return color;
}

Vector3 GetColorVector3(D3DCOLORVALUE color)
{
	Vector3 vColor;
	vColor.X = color.r;
	vColor.Y = color.g;
	vColor.Z = color.b;
	return vColor;
}

void Set_Light(int pos,D3DLIGHT8 *light)
{
	if (light)
	{
		Direct3DDevice->SetLight(pos,(D3DLIGHT9 *)light);
		Direct3DDevice->LightEnable(pos,true);
		CurrentDX8LightEnables[pos] = true;
	}
	else
	{
		if (CurrentDX8LightEnables[pos])
		{
			CurrentDX8LightEnables[pos] = false;
			Direct3DDevice->LightEnable(pos,false);
		}
	}
}

VertexBufferLockClass::VertexBufferLockClass(VertexBufferClass* buffer)
{
	VertexBuffer = buffer;
	buffer->Add_Ref();
}

VertexBufferClass::WriteLockClass::WriteLockClass(VertexBufferClass* VertexBuffer) : VertexBufferLockClass(VertexBuffer)
{
	if (!VertexBuffer->type)
	{
		((DX8VertexBufferClass *)VertexBuffer)->Get_DX8_Vertex_Buffer()->Lock(0,0,&Vertices,0);
	}
	else if (VertexBuffer->type == 1)
	{
		Vertices = ((SortingVertexBufferClass *)VertexBuffer)->VertexBuffer;
	}
}
VertexBufferClass::WriteLockClass::~WriteLockClass()
{
	if (!VertexBuffer->type)
	{
		((DX8VertexBufferClass *)VertexBuffer)->Get_DX8_Vertex_Buffer()->Unlock();
	}
	VertexBuffer->Release_Ref();
}

VertexBufferClass::AppendLockClass::AppendLockClass(VertexBufferClass* VertexBuffer,unsigned int start_index,unsigned int index_range) : VertexBufferLockClass(VertexBuffer)
{
	if (!VertexBuffer->type)
	{
		int x;
		if (VertexBuffer->fvf_info)
		{
			x = VertexBuffer->fvf_info->Get_FVF_Size();
		}
		else
		{
			x = ((DeclarationVertexBufferClass *)VertexBuffer)->DeclarationSize;
		}
		((DX8VertexBufferClass *)VertexBuffer)->Get_DX8_Vertex_Buffer()->Lock(start_index*x,index_range*x,&Vertices,0);
	}
	else if (VertexBuffer->type == 1)
	{
		Vertices = &((SortingVertexBufferClass *)VertexBuffer)->VertexBuffer[start_index];
	}
}

VertexBufferClass::AppendLockClass::~AppendLockClass()
{
	if (!VertexBuffer->type)
	{
		((DX8VertexBufferClass *)VertexBuffer)->Get_DX8_Vertex_Buffer()->Unlock();
	}
	VertexBuffer->Release_Ref();
}

void Set_Vertex_Buffer(VertexBufferClass *buffer)
{
	render_state->vba_offset = 0;
	render_state->vba_count = 0;
	if (render_state->vertex_buffer)
	{
		render_state->vertex_buffer->Release_Engine_Ref();
	}
	if (buffer)
	{
		buffer->Add_Ref();
	}
	if (render_state->vertex_buffer)
	{
		render_state->vertex_buffer->Release_Ref();
	}
	render_state->vertex_buffer = buffer;
	if (buffer)
	{
		buffer->Add_Engine_Ref();
		*render_state_changed |= VERTEX_BUFFER_CHANGED;
		render_state->vertex_buffer_type = buffer->Type();
	}
	else
	{
		*render_state_changed |= VERTEX_BUFFER_CHANGED;
		render_state->index_buffer_type = 4;
	}
}

void Set_Vertex_Buffer(DynamicVBAccessClass& vba_)
{
	if (render_state->vertex_buffer)
	{
		render_state->vertex_buffer->Release_Engine_Ref();
	}
	render_state->vertex_buffer_type = vba_.Get_Type();
	render_state->vba_offset = vba_.VertexBufferOffset;
	render_state->vba_count = vba_.Get_Vertex_Count();
	if (vba_.VertexBuffer)
	{
		vba_.VertexBuffer->Add_Ref();
		vba_.VertexBuffer->Add_Engine_Ref();
	}
	if (render_state->vertex_buffer)
	{
		render_state->vertex_buffer->Release_Ref();
	}
	render_state->vertex_buffer = vba_.VertexBuffer;
	*render_state_changed |= VERTEX_BUFFER_CHANGED;
}

void Set_Vertex_Buffer(DynamicDeclerationAccessClass& vba_)
{
	if (render_state->vertex_buffer)
	{
		render_state->vertex_buffer->Release_Engine_Ref();
	}
	render_state->vertex_buffer_type = vba_.Get_Type();
	render_state->vba_offset = vba_.VertexBufferOffset;
	render_state->vba_count = vba_.Get_Vertex_Count();
	if (vba_.VertexBuffer)
	{
		vba_.VertexBuffer->Add_Ref();
		vba_.VertexBuffer->Add_Engine_Ref();
	}
	if (render_state->vertex_buffer)
	{
		render_state->vertex_buffer->Release_Ref();
	}
	render_state->vertex_buffer = vba_.VertexBuffer;
	*render_state_changed |= VERTEX_BUFFER_CHANGED;
}

void Set_Index_Buffer(IndexBufferClass* ib,unsigned short index_base_offset)
{
	render_state->iba_offset = 0;
	if (render_state->index_buffer)
	{
		render_state->index_buffer->Release_Engine_Ref();
	}
	if (ib)
	{
		ib->Add_Ref();
	}
	if (render_state->index_buffer)
	{
		render_state->index_buffer->Release_Ref();
	}
	render_state->index_buffer = ib;
	render_state->index_base_offset = index_base_offset;
	if (ib)
	{
		ib->Add_Engine_Ref();
		render_state->index_buffer_type = ib->Type();
		*render_state_changed |= INDEX_BUFFER_CHANGED;
	}
	else
	{
		*render_state_changed |= INDEX_BUFFER_CHANGED;
		render_state->index_buffer_type = 4;
	}
}

void Set_Index_Buffer(DynamicIBAccessClass& iba_,unsigned short index_base_offset)
{
	if (render_state->index_buffer)
	{
		render_state->index_buffer->Release_Engine_Ref();
	}
	render_state->index_base_offset = index_base_offset;
	render_state->index_buffer_type = iba_.Get_Type();
	render_state->iba_offset = iba_.IndexBufferOffset;
	if (iba_.IndexBuffer)
	{
		iba_.IndexBuffer->Add_Ref();
		iba_.IndexBuffer->Add_Engine_Ref();
	}
	if (render_state->index_buffer)
	{
		render_state->index_buffer->Release_Ref();
	}
	render_state->index_buffer = iba_.IndexBuffer;
	*render_state_changed |= INDEX_BUFFER_CHANGED;
}

void DynamicVBAccessClass::Allocate_Sorting_Dynamic_Buffer()
{
	(*_DynamicSortingVertexArrayInUse) = true;
	int count = VertexCount + (*_DynamicSortingVertexArrayOffset);
	if (count > (*_DynamicSortingVertexArraySize))
	{
		if (*_DynamicSortingVertexArray)
		{
			(*_DynamicSortingVertexArray)->Release_Ref();
		}
		(*_DynamicSortingVertexArray) = 0;
		(*_DynamicSortingVertexArraySize) = VertexCount;
		if (VertexCount > 0x1388)
		{
			(*_DynamicSortingVertexArraySize) = 0x1388;
		}
	}
	if (!(*_DynamicSortingVertexArray))
	{
		(*_DynamicSortingVertexArray) = new SortingVertexBufferClass((*_DynamicSortingVertexArraySize));
		(*_DynamicSortingVertexArrayOffset) = 0;
	}
	(*_DynamicSortingVertexArray)->Add_Ref();
	if (VertexBuffer)
	{
		VertexBuffer->Release_Ref();
	}
	VertexBuffer = (*_DynamicSortingVertexArray);
	VertexBufferOffset = (*_DynamicSortingVertexArrayOffset);
}

void DynamicVBAccessClass::Allocate_DX8_Dynamic_Buffer()
{
	(*_DynamicDX8VertexBufferInUse) = true;
	if (VertexCount > (*_DynamicDX8VertexBufferSize))
	{
		if (*_DynamicDX8VertexBuffer)
		{
			(*_DynamicDX8VertexBuffer)->Release_Ref();
		}
		(*_DynamicDX8VertexBuffer) = 0;
		(*_DynamicDX8VertexBufferSize) = VertexCount;
		if (VertexCount > 0x1388)
		{
			(*_DynamicDX8VertexBufferSize) = 0x1388;
		}
	}
	if (!(*_DynamicDX8VertexBuffer))
	{
		unsigned int u = DX8VertexBufferClass::USAGE_DYNAMIC;
		if (CurrentCaps->SupportNPatches)
		{
			u |= DX8VertexBufferClass::USAGE_NPATCHES;
		}
		(*_DynamicDX8VertexBuffer) = new DX8VertexBufferClass(0x252,(*_DynamicDX8VertexBufferSize),(DX8VertexBufferClass::UsageType)u);
		(*_DynamicDX8VertexBufferOffset) = 0;
	}
	unsigned short offset = (*_DynamicDX8VertexBufferOffset) + VertexCount;
	if (offset > (*_DynamicDX8VertexBufferSize))
	{
		(*_DynamicDX8VertexBufferOffset) = 0;
	}
	(*_DynamicDX8VertexBuffer)->Add_Ref();
	if (VertexBuffer)
	{
		VertexBuffer->Release_Ref();
	}
	VertexBuffer = (*_DynamicDX8VertexBuffer);
	VertexBufferOffset = (*_DynamicDX8VertexBufferOffset);
}

DynamicVBAccessClass::DynamicVBAccessClass(unsigned int t,unsigned int fvf,unsigned short vertex_count_)
{
	FVFInfo = _DynamicFVFInfo;
	Type = t;
	VertexCount = vertex_count_;
	VertexBuffer = 0;
	if (t == 2)
	{
		Allocate_DX8_Dynamic_Buffer();
	}
	else
	{
		Allocate_Sorting_Dynamic_Buffer();
	}
}
DynamicVBAccessClass::~DynamicVBAccessClass()
{
	if (Type == 2)
	{
		(*_DynamicDX8VertexBufferInUse) = false;
		(*_DynamicDX8VertexBufferOffset) = (*_DynamicDX8VertexBufferOffset) + VertexCount;
	}
	else
	{
		(*_DynamicSortingVertexArrayInUse) = false;
		(*_DynamicSortingVertexArrayOffset) = (*_DynamicSortingVertexArrayOffset) + VertexCount;
	}
	VertexBuffer->Release_Ref();
}

DynamicVBAccessClass::WriteLockClass::WriteLockClass(DynamicVBAccessClass* dynamic_vb_access_)
{
	DynamicVBAccess = dynamic_vb_access_;
	if (DynamicVBAccess->Get_Type() == 2)
	{
		unsigned int lock = D3DLOCK_NOSYSLOCK;
		if (!DynamicVBAccess->VertexBufferOffset)
		{
			lock |= D3DLOCK_DISCARD;
		}
		else
		{
			lock |= D3DLOCK_NOOVERWRITE;
		}
		((DX8VertexBufferClass *)DynamicVBAccess->VertexBuffer)->Get_DX8_Vertex_Buffer()->Lock(DynamicVBAccess->VertexBufferOffset * ((*_DynamicDX8VertexBuffer)->FVF_Info().Get_FVF_Size()),(DynamicVBAccess->Get_Vertex_Count() * DynamicVBAccess->VertexBuffer->FVF_Info().Get_FVF_Size()),(void **)&Vertices,lock);
	}
	else if (DynamicVBAccess->Get_Type() == 3)
	{
		Vertices = ((SortingVertexBufferClass *)DynamicVBAccess->VertexBuffer)->VertexBuffer + DynamicVBAccess->VertexBufferOffset;
	}
}
DynamicVBAccessClass::WriteLockClass::~WriteLockClass()
{
	if (DynamicVBAccess->Get_Type() == 2)
	{
		((DX8VertexBufferClass *)DynamicVBAccess->VertexBuffer)->Get_DX8_Vertex_Buffer()->Unlock();
	}
}

void DynamicDeclerationAccessClass::Allocate_Sorting_Dynamic_Buffer()
{
	(*_DynamicSortingVertexArrayInUse) = true;
	int count = VertexCount + (*_DynamicSortingVertexArrayOffset);
	if (count > (*_DynamicSortingVertexArraySize))
	{
		if (*_DynamicSortingVertexArray)
		{
			(*_DynamicSortingVertexArray)->Release_Ref();
		}
		(*_DynamicSortingVertexArray) = 0;
		(*_DynamicSortingVertexArraySize) = VertexCount;
		if (VertexCount > 0x1388)
		{
			(*_DynamicSortingVertexArraySize) = 0x1388;
		}
	}
	if (!(*_DynamicSortingVertexArray))
	{
		(*_DynamicSortingVertexArray) = new SortingVertexBufferClass((*_DynamicSortingVertexArraySize));
		(*_DynamicSortingVertexArrayOffset) = 0;
	}
	(*_DynamicSortingVertexArray)->Add_Ref();
	if (VertexBuffer)
	{
		VertexBuffer->Release_Ref();
	}
	VertexBuffer = (*_DynamicSortingVertexArray);
	VertexBufferOffset = (*_DynamicSortingVertexArrayOffset);
}

void DynamicDeclerationAccessClass::Allocate_Decleration_Dynamic_Buffer()
{
	if (VertexCount > _DynamicDeclerationVertexBufferSize)
	{
		if (_DynamicDeclerationVertexBuffer)
		{
			_DynamicDeclerationVertexBuffer->Release_Ref();
		}
		_DynamicDeclerationVertexBuffer = 0;
		_DynamicDeclerationVertexBufferSize = VertexCount;
		if (VertexCount > 0x1388)
		{
			_DynamicDeclerationVertexBufferSize = 0x1388;
		}
	}
	if (!_DynamicDeclerationVertexBuffer)
	{
		unsigned int u = DX8VertexBufferClass::USAGE_DYNAMIC;
		if (CurrentCaps->SupportNPatches)
		{
			u |= DX8VertexBufferClass::USAGE_NPATCHES;
		}
		_DynamicDeclerationVertexBuffer = new DeclarationVertexBufferClass(0x252,_DynamicDeclerationVertexBufferSize,(DX8VertexBufferClass::UsageType)u);
		_DynamicDeclerationVertexBufferOffset = 0;
	}
	unsigned short offset = _DynamicDeclerationVertexBufferOffset + VertexCount;
	if (offset > _DynamicDeclerationVertexBufferSize)
	{
		_DynamicDeclerationVertexBufferOffset = 0;
	}
	_DynamicDeclerationVertexBuffer->Add_Ref();
	if (VertexBuffer)
	{
		VertexBuffer->Release_Ref();
	}
	VertexBuffer = _DynamicDeclerationVertexBuffer;
	VertexBufferOffset = _DynamicDeclerationVertexBufferOffset;
}

DynamicDeclerationAccessClass::DynamicDeclerationAccessClass(unsigned int t,unsigned int fvf,unsigned short vertex_count_)
{
	FVFInfo = _DynamicFVFInfo;
	Type = t;
	VertexCount = vertex_count_;
	VertexBuffer = 0;
	if (t == 2)
	{
		Allocate_Decleration_Dynamic_Buffer();
	}
	else
	{
		Allocate_Sorting_Dynamic_Buffer();
	}
}
DynamicDeclerationAccessClass::~DynamicDeclerationAccessClass()
{
	if (Type == 2)
	{
		_DynamicDeclerationVertexBufferOffset = _DynamicDeclerationVertexBufferOffset + (unsigned short)VertexCount;
	}
	else
	{
		(*_DynamicSortingVertexArrayInUse) = false;
		(*_DynamicSortingVertexArrayOffset) = (*_DynamicSortingVertexArrayOffset) + VertexCount;
	}
	VertexBuffer->Release_Ref();
}

DynamicDeclerationAccessClass::WriteLockClass::WriteLockClass(DynamicDeclerationAccessClass* dynamic_decleration_access_)
{
	DynamicDeclerationAccess = dynamic_decleration_access_;
	if (DynamicDeclerationAccess->Get_Type() == 2)
	{
		unsigned int lock = D3DLOCK_NOSYSLOCK;
		if (!DynamicDeclerationAccess->VertexBufferOffset)
		{
			lock |= D3DLOCK_DISCARD;
		}
		else
		{
			lock |= D3DLOCK_NOOVERWRITE;
		}
		((DeclarationVertexBufferClass *)DynamicDeclerationAccess->VertexBuffer)->Get_DX8_Vertex_Buffer()->Lock(DynamicDeclerationAccess->VertexBufferOffset * (_DynamicDeclerationVertexBuffer->DeclarationSize),(DynamicDeclerationAccess->Get_Vertex_Count() * ((DeclarationVertexBufferClass *)DynamicDeclerationAccess->VertexBuffer)->DeclarationSize),(void **)&Vertices,lock);
	}
	else if (DynamicDeclerationAccess->Get_Type() == 3)
	{
		Vertices = ((SortingVertexBufferClass *)DynamicDeclerationAccess->VertexBuffer)->VertexBuffer + DynamicDeclerationAccess->VertexBufferOffset;
	}
}
DynamicDeclerationAccessClass::WriteLockClass::~WriteLockClass()
{
	if (DynamicDeclerationAccess->Get_Type() == 2)
	{
		((DeclarationVertexBufferClass *)DynamicDeclerationAccess->VertexBuffer)->Get_DX8_Vertex_Buffer()->Unlock();
	}
}

VertexBufferClass::VertexBufferClass(unsigned int type_,unsigned int FVF,unsigned short vertex_count_)
{
	NumRefs = 1;
	type = type_;
	VertexCount = vertex_count_;
	engine_refs = 0;
	fvf_info = new FVFInfoClass(FVF);
	(*_VertexBufferCount)++;
	(*_VertexBufferTotalVertices) += VertexCount;
	(*_VertexBufferTotalSize) += VertexCount * fvf_info->Get_FVF_Size();
}

VertexBufferClass::VertexBufferClass(unsigned int type_,unsigned short vertex_count_)
{
	NumRefs = 1;
	type = type_;
	VertexCount = vertex_count_;
	engine_refs = 0;
	fvf_info = NULL;
	(*_VertexBufferCount)++;
	(*_VertexBufferTotalVertices) += VertexCount;
}

VertexBufferClass::~VertexBufferClass()
{
	(*_VertexBufferCount)--;
	(*_VertexBufferTotalVertices) -= VertexCount;
	if (fvf_info)
	{
		(*_VertexBufferTotalSize) -= VertexCount * fvf_info->Get_FVF_Size();
		delete fvf_info;
	}
}

DX8VertexBufferClass::DX8VertexBufferClass(unsigned int FVF,unsigned short vertex_count_,UsageType usage) : VertexBufferClass(0,FVF,vertex_count_)
{
	VertexBuffer = 0;
	Create_Vertex_Buffer(usage);
}

DX8VertexBufferClass::DX8VertexBufferClass(unsigned short vertex_count_,UsageType usage) : VertexBufferClass(0,vertex_count_)
{
	VertexBuffer = 0;
}

DX8VertexBufferClass::~DX8VertexBufferClass()
{
	VertexBuffer->Release();
}

DeclarationVertexBufferClass::DeclarationVertexBufferClass(unsigned int FVF,unsigned short vertex_count_,UsageType usage) : DX8VertexBufferClass(vertex_count_,usage)
{
	location_offset = 0;
	blend_offset = 0;
	ElementCount = 0;
	if ((FVF & D3DFVF_XYZ) == D3DFVF_XYZ)
	{
		ElementCount++;
		blend_offset = 0x0C;
	}
	normal_offset = blend_offset;
	if (((FVF & D3DFVF_XYZB4) == D3DFVF_XYZB4) && ((FVF & D3DFVF_LASTBETA_UBYTE4) == D3DFVF_LASTBETA_UBYTE4))
	{
		ElementCount++;
		normal_offset = blend_offset + 0x10;
	}
	diffuse_offset = normal_offset;
	HasNormals = false;
	if ((FVF & D3DFVF_NORMAL) == D3DFVF_NORMAL)
	{
		ElementCount++;
		diffuse_offset = normal_offset + 0x0C;
		HasNormals = true;
	}
	specular_offset = diffuse_offset;
	HasDiffuse = false;
	if ((FVF & D3DFVF_DIFFUSE) == D3DFVF_DIFFUSE)
	{
		ElementCount++;
		specular_offset = diffuse_offset + 4;
		HasDiffuse = true;
	}
	texcoord_offset[0] = specular_offset;
	if ((FVF & D3DFVF_SPECULAR) == D3DFVF_SPECULAR)
	{
		ElementCount++;
		texcoord_offset[0] = specular_offset + 4;
	}
	texcount = (FVF / 0x100);
	HasTexCoords = false;
	if (texcount)
	{
		HasTexCoords = true;
		ElementCount++;
	}
	if (texcount > 8) 
	{
		texcount = 8;
	}
	for (unsigned int i = 1;i < texcount;i++)
	{
		ElementCount++;
		texcoord_offset[i] = texcoord_offset[i-1] + 8;
	}
	if (HasTexCoords && HasNormals)
	{
		ElementCount++;
		if (texcount)
		{
			tangent_offset = texcoord_offset[texcount-1] + 8;
		}
		else
		{
			tangent_offset = texcoord_offset[0];
		}
		ElementCount++;
		HasTangents = true;
	}
	else
	{
		tangent_offset = 0;
		HasTangents = false;
	}
	Elements = new D3DVERTEXELEMENT9[ElementCount+1];
	int element = 0;
	if ((FVF & D3DFVF_XYZ) == D3DFVF_XYZ)
	{
		Elements[element].Stream = 0;
		Elements[element].Offset = location_offset;
		Elements[element].Type = D3DDECLTYPE_FLOAT3;
		Elements[element].Method = D3DDECLMETHOD_DEFAULT;
		Elements[element].Usage = D3DDECLUSAGE_POSITION;
		Elements[element].UsageIndex = 0;
		element++;
	}
	if ((FVF & D3DFVF_NORMAL) == D3DFVF_NORMAL)
	{
		Elements[element].Stream = 0;
		Elements[element].Offset = normal_offset;
		Elements[element].Type = D3DDECLTYPE_FLOAT3;
		Elements[element].Method = D3DDECLMETHOD_DEFAULT;
		Elements[element].Usage = D3DDECLUSAGE_NORMAL;
		Elements[element].UsageIndex = 0;
		element++;
	}
	if ((FVF & D3DFVF_DIFFUSE) == D3DFVF_DIFFUSE)
	{
		Elements[element].Stream = 0;
		Elements[element].Offset = diffuse_offset;
		Elements[element].Type = D3DDECLTYPE_D3DCOLOR;
		Elements[element].Method = D3DDECLMETHOD_DEFAULT;
		Elements[element].Usage = D3DDECLUSAGE_COLOR;
		Elements[element].UsageIndex = 0;
		element++;
	}
	if ((FVF & D3DFVF_SPECULAR) == D3DFVF_SPECULAR)
	{
		Elements[element].Stream = 0;
		Elements[element].Offset = specular_offset;
		Elements[element].Type = D3DDECLTYPE_D3DCOLOR;
		Elements[element].Method = D3DDECLMETHOD_DEFAULT;
		Elements[element].Usage = D3DDECLUSAGE_COLOR;
		Elements[element].UsageIndex = 1;
		element++;
	}
	for (unsigned char i = 0;i < texcount;i++)
	{
		Elements[element].Stream = 0;
		Elements[element].Offset = texcoord_offset[i];
		Elements[element].Type = D3DDECLTYPE_FLOAT2;
		Elements[element].Method = D3DDECLMETHOD_DEFAULT;
		Elements[element].Usage = D3DDECLUSAGE_TEXCOORD;
		Elements[element].UsageIndex = i;
		element++;
	}
	if (HasTangents)
	{
		Elements[element].Stream = 0;
		Elements[element].Offset = tangent_offset;
		Elements[element].Type = D3DDECLTYPE_FLOAT3;
		Elements[element].Method = D3DDECLMETHOD_DEFAULT;
		Elements[element].Usage = D3DDECLUSAGE_TANGENT;
		Elements[element].UsageIndex = 0;
		element++;
	}
	Elements[element].Stream = 0xFF;
	Elements[element].Offset = 0;
	Elements[element].Type = D3DDECLTYPE_UNUSED;
	Elements[element].Method = 0;
	Elements[element].Usage = 0;
	Elements[element].UsageIndex = 0;
	DeclarationSize = D3DXGetDeclVertexSize(Elements,0);
	(*_VertexBufferTotalSize) += VertexCount * DeclarationSize;
	Direct3DDevice->CreateVertexDeclaration(Elements,&VertexDecl);
	delete[] Elements;
	Create_Declaration_Vertex_Buffer(usage);
}

DeclarationVertexBufferClass::~DeclarationVertexBufferClass()
{
	(*_VertexBufferTotalSize) -= VertexCount * DeclarationSize;
	VertexDecl->Release();
}

void DeclarationVertexBufferClass::Create_Declaration_Vertex_Buffer(UsageType Usage)
{
	unsigned int usage = D3DUSAGE_WRITEONLY;
	if (Usage == USAGE_DYNAMIC)
	{
		usage |= D3DUSAGE_DYNAMIC;
	}
	if (Usage == USAGE_SOFTWAREPROCESSING)
	{
		usage |= D3DUSAGE_SOFTWAREPROCESSING;
	}
	if (Usage == USAGE_NPATCHES)
	{
		usage |= D3DUSAGE_NPATCHES;
	}
	unsigned int length = VertexCount * DeclarationSize;
	unsigned int pool = ((~Usage) & 1);
	HRESULT res = Direct3DDevice->CreateVertexBuffer(length,usage,0,(D3DPOOL)pool,&VertexBuffer,NULL);
 	if (FAILED(res))
	{
		Invalidate_Unused_Textures(0x1388);
		Invalidate_Mesh_Cache();
		res = Direct3DDevice->CreateVertexBuffer(length,usage,0,(D3DPOOL)pool,&VertexBuffer,NULL);
	}
}

void DX8VertexBufferClass::Create_Vertex_Buffer(UsageType Usage)
{
	unsigned int usage = D3DUSAGE_WRITEONLY;
	if (Usage == USAGE_DYNAMIC)
	{
		usage |= D3DUSAGE_DYNAMIC;
	}
	if (Usage == USAGE_SOFTWAREPROCESSING)
	{
		usage |= D3DUSAGE_SOFTWAREPROCESSING;
	}
	if (Usage == USAGE_NPATCHES)
	{
		usage |= D3DUSAGE_NPATCHES;
	}
	unsigned int length = VertexCount * fvf_info->Get_FVF_Size();
	unsigned int pool = ((~Usage) & 1);
	if (FAILED(Direct3DDevice->CreateVertexBuffer(length,usage,fvf_info->Get_FVF(),(D3DPOOL)pool,&VertexBuffer,NULL)))
	{
		Invalidate_Unused_Textures(0x1388);
		Invalidate_Mesh_Cache();
		Direct3DDevice->CreateVertexBuffer(length,usage,fvf_info->Get_FVF(),(D3DPOOL)pool,&VertexBuffer,NULL);
	}
}

SortingVertexBufferClass::SortingVertexBufferClass(unsigned short VertexCount) : VertexBufferClass(1,0x252,VertexCount)
{
	VertexBuffer = new VertexFormatXYZNDUV2[VertexCount];
}

SortingVertexBufferClass::~SortingVertexBufferClass()
{
	delete[] VertexBuffer;
}

FVFInfoClass::FVFInfoClass(unsigned int FVF_)
{
	FVF = FVF_;
	fvf_size = D3DXGetFVFVertexSize(FVF);
	location_offset = 0;
	blend_offset = 0;
	if ((FVF & D3DFVF_XYZ) == D3DFVF_XYZ)
	{
		blend_offset = 0x0C;
	}
	normal_offset = blend_offset;
	if (((FVF & D3DFVF_XYZB4) == D3DFVF_XYZB4) && ((FVF & D3DFVF_LASTBETA_UBYTE4) == D3DFVF_LASTBETA_UBYTE4))
	{
		normal_offset = blend_offset + 0x10;
	}
	diffuse_offset = normal_offset;
	if ((FVF & D3DFVF_NORMAL) == D3DFVF_NORMAL)
	{
		diffuse_offset = normal_offset + 0x0C;
	}
	specular_offset = diffuse_offset;
	if ((FVF & D3DFVF_DIFFUSE) == D3DFVF_DIFFUSE)
	{
		specular_offset = diffuse_offset + 4;
	}
	texcoord_offset[0] = specular_offset;
	if ((FVF & D3DFVF_SPECULAR) == D3DFVF_SPECULAR)
	{
		texcoord_offset[0] = specular_offset + 4;
	}
	int a = 1;
	for (unsigned int i = 0x0F;(i - 0xE) < 8;i++)
	{
		texcoord_offset[a] = texcoord_offset[a-1];
		if ((FVF & (3 << i)) == (unsigned int)(3 << i))
		{
			texcoord_offset[a] = texcoord_offset[a-1] + 4;
		}
		else
		{
			texcoord_offset[a] = texcoord_offset[a-1] + 8;
		}
		a++;
	}
}

unsigned int *Get_User_Lighting_Array(char *MeshClass, bool b)
{
	_asm {
		movzx eax, b
		push eax
		mov ecx, MeshClass
		mov eax, GetUserLighting
		call eax
	}
}

unsigned int *Get_Deformed_Vertexes(char *MeshClass,Vector3* dst_vert,Vector3* dst_norm)
{
	_asm {
		mov eax, dst_norm
		push eax
		mov eax, dst_vert
		push eax
		mov ecx, MeshClass
		mov eax, GetDeformedVertexes
		call eax
	}
}

inline void CalculateTangentArray(long vertexCount, const Vector3 *vertex, const Vector3 *normal, 
							const Vector2 *vertex_UV, long triangleCount, const Vector3i *triangle, 
							Vector3* tangentArray)
{
	//Vector3 *tan = TempTangentCalculationBuffer->Enlarge(vertexCount);
	ZeroMemory(tangentArray, vertexCount * sizeof(Vector3));
 
	for (long a = 0; a < triangleCount; a++)
	{
		long i0 = triangle->I;
		long i1 = triangle->J;
		long i2 = triangle->K;
 
		Vector3 side_0 = vertex[i0] - vertex[i2];
		Vector3 side_1 = vertex[i1] - vertex[i2];
 
		float delta_V_0 = vertex_UV[i0].Y - vertex_UV[i2].Y;
		float delta_V_1 = vertex_UV[i1].Y - vertex_UV[i2].Y;
 
		Vector3 face_tangent = side_0 * delta_V_1 - side_1 * delta_V_0;
 
		tangentArray[i0] += face_tangent;
		tangentArray[i1] += face_tangent;
		tangentArray[i2] += face_tangent;

		triangle++;
	}
 
	for (long a = 0; a < vertexCount; a++)
	{
		const Vector3& n = normal[a];
		Vector3& t = tangentArray[a];
 
		// Gram-Schmidt orthogonalize
		tangentArray[a] = Vector3::Normalize(t * Vector3::Dot(n, t) - n);
 
	}
}

void Draw_Skin(char *fvfcc)
{
	DX8SkinFVFCategoryContainer *sfvf = (DX8SkinFVFCategoryContainer *)fvfcc;
	if (!sfvf->AnythingToRender)
	{
		return;
	}
	sfvf->AnythingToRender = false;
	Set_Vertex_Buffer((VertexBufferClass *)0);
	DynamicDeclerationAccessClass vb = DynamicDeclerationAccessClass(sfvf->sorting + 2,0x252,(unsigned short)sfvf->VisibleVertexCount);
	unsigned int renderedVertexCount = 0;
	if (sfvf->VisibleSkinHead)
	{
		DynamicDeclerationAccessClass::WriteLockClass l = DynamicDeclerationAccessClass::WriteLockClass::WriteLockClass(&vb);
		VertexFormatXYZNDUV2* dest_verts = 0;
		VertexFormatXYZNDUV2Extra* dest_verts_decleration = 0;
		if (sfvf->sorting)
		{
			dest_verts = l.Get_Formatted_Vertex_Array();
		}
		else
		{
			dest_verts_decleration = l.Get_Formatted_Declaration_Vertex_Array();
		}
		MeshClass *mesh = sfvf->VisibleSkinHead;
		do
		{
			MeshModelClass* mmc = *(MeshModelClass **)(((char *)mesh) + 0x88);
			int mesh_vertex_count = mmc->VertexCount;
			if (TempVertexBuffer->Length() < mesh_vertex_count)
			{
				TempVertexBuffer->Resize(mesh_vertex_count,0);
			}
			if (TempNormalBuffer->Length() < mesh_vertex_count)
			{
				TempNormalBuffer->Resize(mesh_vertex_count,0);
			}
			Vector2* uv0 = mmc->CurMatDesc->Get_UV_Array_By_Index(0,false);
			Vector2* uv1 = mmc->CurMatDesc->Get_UV_Array_By_Index(1,false);
			unsigned int *color = mmc->CurMatDesc->Get_Color_Array(0,false);
			Get_Deformed_Vertexes((char *)mesh,&((*TempVertexBuffer)[0]),&((*TempNormalBuffer)[0]));
			if (!vb.VertexBuffer->fvf_info)
			{
				if (((DeclarationVertexBufferClass *)(vb.VertexBuffer))->HasTangents)
				{
					if (uv0)
					{
						TempTangentBuffer->Enlarge(mmc->VertexCount);
						CalculateTangentArray(mmc->VertexCount,&((*TempVertexBuffer)[0]),
											 &((*TempNormalBuffer)[0]),
											 uv0,mmc->PolyCount, mmc->Poly->Get_Array(),
											 TempTangentBuffer->GetBuffer());
					}
				}
			}
			if (mesh_vertex_count > 0)
			{
				if (sfvf->sorting)
				{
					for (int count = 0;count < mesh_vertex_count;count++)
					{
						dest_verts[renderedVertexCount+count].x = (*TempVertexBuffer)[count].X;
						dest_verts[renderedVertexCount+count].y = (*TempVertexBuffer)[count].Y;
						dest_verts[renderedVertexCount+count].z = (*TempVertexBuffer)[count].Z;
						dest_verts[renderedVertexCount+count].nx = (*TempNormalBuffer)[count].X;
						dest_verts[renderedVertexCount+count].ny = (*TempNormalBuffer)[count].Y;
						dest_verts[renderedVertexCount+count].nz = (*TempNormalBuffer)[count].Z;
						if (color)
						{
							dest_verts[renderedVertexCount+count].diffuse = color[count];
						}
						else
						{
							dest_verts[renderedVertexCount+count].diffuse = 0;
						}
						if (uv0)
						{
							dest_verts[renderedVertexCount+count].u1 = uv0[count].X;
							dest_verts[renderedVertexCount+count].v1 = uv0[count].Y;
						}
						else
						{
							dest_verts[renderedVertexCount+count].u1 = 0;
							dest_verts[renderedVertexCount+count].v1 = 0;
						}
						if (uv1)
						{
							dest_verts[renderedVertexCount+count].u2 = uv1[count].X;
							dest_verts[renderedVertexCount+count].v2 = uv1[count].Y;
						}
						else
						{
							dest_verts[renderedVertexCount+count].u2 = 0;
							dest_verts[renderedVertexCount+count].v2 = 0;
						}
					}
				}
				else
				{
					for (int count = 0;count < mesh_vertex_count;count++)
					{
						dest_verts_decleration[renderedVertexCount+count].x = (*TempVertexBuffer)[count].X;
						dest_verts_decleration[renderedVertexCount+count].y = (*TempVertexBuffer)[count].Y;
						dest_verts_decleration[renderedVertexCount+count].z = (*TempVertexBuffer)[count].Z;
						dest_verts_decleration[renderedVertexCount+count].nx = (*TempNormalBuffer)[count].X;
						dest_verts_decleration[renderedVertexCount+count].ny = (*TempNormalBuffer)[count].Y;
						dest_verts_decleration[renderedVertexCount+count].nz = (*TempNormalBuffer)[count].Z;
						if (color)
						{
							dest_verts_decleration[renderedVertexCount+count].diffuse = color[count];
						}
						else
						{
							dest_verts_decleration[renderedVertexCount+count].diffuse = 0;
						}
						if (uv0)
						{
							dest_verts_decleration[renderedVertexCount+count].u1 = uv0[count].X;
							dest_verts_decleration[renderedVertexCount+count].v1 = uv0[count].Y;
						}
						else
						{
							dest_verts_decleration[renderedVertexCount+count].u1 = 0;
							dest_verts_decleration[renderedVertexCount+count].v1 = 0;
						}
						if (uv1)
						{
							dest_verts_decleration[renderedVertexCount+count].u2 = uv1[count].X;
							dest_verts_decleration[renderedVertexCount+count].v2 = uv1[count].Y;
						}
						else
						{
							dest_verts_decleration[renderedVertexCount+count].u2 = 0;
							dest_verts_decleration[renderedVertexCount+count].v2 = 0;
						}
						if (TempTangentBuffer)
						{
							dest_verts_decleration[renderedVertexCount+count].Tangent = (*TempTangentBuffer)[count];
						}
						else
						{
							dest_verts_decleration[renderedVertexCount+count].Tangent = Vector3();
						}	
					}				
				}
			}
			*((int *)(((char *)mesh) + 0x94)) = renderedVertexCount;
			renderedVertexCount += mesh_vertex_count;
			mesh = *((MeshClass **)(((char *)mesh) + 0x98));
		} while (mesh);
	}
	Set_Vertex_Buffer(vb);
	Set_Index_Buffer(sfvf->index_buffer,0);
	if (sfvf->passes > 0)
	{
		for (unsigned int i = 0;i < sfvf->passes;i++)
		{
			DX8TextureCategoryClass *t;
			GenericMultiListClass *g = &sfvf->visible_texture_category_list[i];
			t = (DX8TextureCategoryClass *)g->Internal_Remove_List_Head();
			while (t)
			{
				t->Render();
				t = (DX8TextureCategoryClass *)g->Internal_Remove_List_Head();
			}
		}
	}
	sfvf->Render_Procedural_Material_Passes();
	sfvf->VisibleSkinHead = 0;
	sfvf->VisibleVertexCount = 0;
}

Vertex_Split_Table::Vertex_Split_Table(char *MeshClass)
{
	mc = MeshClass;
	mmc = *(MeshModelClass **)(MeshClass + 0x88);
	npatch_enable = false;
	allocated_polygon_array = false;
	if ((CurrentCaps->SupportNPatches) && (mmc->Needs_Vertex_Normals()) && (mmc->Flags & MeshGeometryClass::ALLOW_NPATCHES))
	{
		npatch_enable = true;
	}
	polygon_count = mmc->PolyCount;
	if (mmc->GapFiller)
	{
		polygon_count += mmc->GapFiller->PolygonCount;
	}
	allocated_polygon_array = true;
	polygon_array = new Vector3i[polygon_count];
	memcpy(polygon_array+mmc->PolyCount,mmc->GapFiller->PolygonArray,mmc->GapFiller->PolygonCount*sizeof(Vector3i));
	if (mmc->GapFiller)
	{
		memcpy(polygon_array+mmc->PolyCount*sizeof(Vector3i),mmc->GapFiller->PolygonArray,mmc->GapFiller->PolygonCount*sizeof(Vector3i));
	}
}
void Draw_Rigid(char *fvfcc,char *MeshClass)
{
	Vertex_Split_Table split = Vertex_Split_Table(MeshClass);
	int vc = split.mmc->VertexCount;
	DX8RigidFVFCategoryContainer *rfvf = (DX8RigidFVFCategoryContainer *)fvfcc;
	if (!rfvf->vertex_buffer)
	{
		int vc2 = 0xFA0;
		if (vc > 0xFA0)
		{
			vc2 = vc;
		}
		if (rfvf->sorting)
		{
			rfvf->vertex_buffer = new SortingVertexBufferClass((unsigned short)vc2);
		}
		else
		{
			int usage = 0;
			if (CurrentCaps->SupportNPatches && (*NPatchesLevel))
			{
				usage = DX8VertexBufferClass::USAGE_NPATCHES;
			}
			rfvf->vertex_buffer = new DeclarationVertexBufferClass(rfvf->FVF,(unsigned short)vc2,(DX8VertexBufferClass::UsageType)usage);
		}
	}
	VertexBufferClass::AppendLockClass Lock = VertexBufferClass::AppendLockClass(rfvf->vertex_buffer,rfvf->used_vertices,split.mmc->VertexCount);
	char *vbarray = (char *)Lock.Get_Vertex_Array();
	Vector3 *varray = split.mmc->Vertex->Get_Array();
	Vector3 *narray = split.mmc->Get_Vertex_Normal_Array();
	unsigned int *ularray;
	if (Get_User_Lighting_Array(split.mc,false))
	{
		ularray = Get_User_Lighting_Array(split.mc,false);
	}
	else
	{
		ularray = split.mmc->CurMatDesc->Get_Color_Array(0,false);
	}
	unsigned int *carray = split.mmc->CurMatDesc->Get_Color_Array(1,false);
	unsigned int texcount = (rfvf->FVF / 0x100);
	bool hasnormals = false;
	bool hasdiffuse = false;
	bool hasspecular = false;
	if (rfvf->FVF & D3DFVF_NORMAL)
	{
		hasnormals = true;
	}
	if (rfvf->FVF & D3DFVF_DIFFUSE)
	{
		hasdiffuse = true;
	}
	if (rfvf->FVF & D3DFVF_SPECULAR)
	{
		hasspecular = true;
	}
	int locationoffset;
	int normaloffset;
	int diffuseoffset;
	int specularoffset;
	int tangentoffset;
	int texoffset[8];
	int fvfsize;
	if (rfvf->vertex_buffer->fvf_info)
	{
		FVFInfoClass fvfd = rfvf->vertex_buffer->FVF_Info();
		locationoffset = fvfd.Get_Location_Offset();
		normaloffset = fvfd.Get_Normal_Offset();
		diffuseoffset = fvfd.Get_Diffuse_Offset();
		specularoffset = fvfd.Get_Specular_Offset();
		tangentoffset = 0;
		for (unsigned int t = 0;t < 8;t++)
		{
			texoffset[t] = fvfd.Get_Tex_Offset(t);
		}
		fvfsize = fvfd.Get_FVF_Size();
	}
	else
	{
		DeclarationVertexBufferClass *d = (DeclarationVertexBufferClass *)rfvf->vertex_buffer;
		locationoffset = d->location_offset;
		normaloffset = d->normal_offset;
		diffuseoffset = d->diffuse_offset;
		specularoffset = d->specular_offset;
		if (CalculateTangents)
		{
			tangentoffset = d->tangent_offset;
		}
		else
		{
			tangentoffset = 0;
		}
		for (unsigned int t = 0;t < 8;t++)
		{
			texoffset[t] = d->texcoord_offset[t];
		}
		fvfsize = d->DeclarationSize;
	}

	if (tangentoffset)
	{
		int vertexCount = split.mmc->Vertex->Get_Count();
		TempTangentBuffer->Enlarge(vertexCount);
		Vector2 *uv = split.mmc->CurMatDesc->UV[0]->Get_Array();
		if (uv)
		{
			CalculateTangentArray(vertexCount,split.mmc->Vertex->Get_Array(),split.mmc->Get_Vertex_Normal_Array(),
				uv,split.polygon_count,split.polygon_array,TempTangentBuffer->GetBuffer());
		}
	}

	for (int vertexcount = 0;vertexcount < split.mmc->VertexCount;vertexcount++)
	{
		((Vector3 *)(vbarray + locationoffset))->X = varray->X;
		((Vector3 *)(vbarray + locationoffset))->Y = varray->Y;
		((Vector3 *)(vbarray + locationoffset))->Z = varray->Z;
		if (hasnormals && narray)
		{
			((Vector3 *)(vbarray + normaloffset))->X = narray->X;
			((Vector3 *)(vbarray + normaloffset))->Y = narray->Y;
			((Vector3 *)(vbarray + normaloffset))->Z = narray->Z;
		}
		if (tangentoffset)
		{
			*((Vector3 *)(vbarray + tangentoffset)) = (*TempTangentBuffer)[vertexcount];
		}
		if (hasdiffuse)
		{
			if (ularray)
			{
				*((unsigned int *)(vbarray + diffuseoffset)) = *ularray;
			}
			else
			{
				*((unsigned int *)(vbarray + diffuseoffset)) = 0xFFFFFFFF;
			}
		}
		if (hasspecular)
		{
			if (carray)
			{
				*((unsigned int *)(vbarray + specularoffset)) = *carray;
			}
			else
			{
				*((unsigned int *)(vbarray + specularoffset)) = 0xFFFFFFFF;
			}
		}
		for (unsigned int tc = 0;tc < texcount;tc++)
		{
			UVBufferClass *uv = split.mmc->CurMatDesc->UV[tc];
			if (uv)
			{
				Vector2 *iv = uv->Get_Array();
				if (iv)
				{
					Vector2 *ov = ((Vector2 *)(vbarray + texoffset[tc]));
					ov->X = iv[vertexcount].X;
					ov->Y = iv[vertexcount].Y;
				}
			}
		}
		vbarray += fvfsize;
		varray++;
		narray++;
		if (ularray)
		{
			ularray++;
		}
		if (carray)
		{
			carray++;
		}
	}
	rfvf->Generate_Texture_Categories(&split,rfvf->used_vertices);
	rfvf->used_vertices += split.mmc->VertexCount;
}

Vector3 *Get_Sky_Color()
{
	char *c = *sky;
	c += 0x8C;
	return (Vector3 *)c;
}

RenderQuadClass::RenderQuadClass()
{
	Shader = new ShaderClass();
	Shader->ShaderBits = 0x984B7;
}

RenderQuadClass::~RenderQuadClass()
{
	delete Shader;
	Shader = 0;
}

HRESULT RenderQuadClass::Draw(float x, float y, float width, float height, float tu_scale, float tv_scale) 
{
	QuadVertex quad[4] =
	{
		{x + width -0.5f,	y - 0.5f,			1.0f, 1.0f,		1.0f * tu_scale, 0.0f * tv_scale, 1.0f, 0.0f},
		{x - 0.5f,			y -0.5f,			1.0f, 1.0f,		0.0f * tu_scale, 0.0f * tv_scale, 0.0f, 0.0f},
		{x + width -0.5f,	y + height - 0.5f,	1.0f, 1.0f,		1.0f * tu_scale, 1.0f * tv_scale, 1.0f, 1.0f},
		{y - 0.5f,			y + height - 0.5f,	1.0f, 1.0f,		0.0f * tu_scale, 1.0f * tv_scale, 0.0f, 1.0f}
	};
	Direct3DDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX2);
	Shader->Apply();
	HRESULT res = Direct3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2,quad,sizeof(QuadVertex));
	return res;
}

HRESULT RenderQuadClass::Draw(float x, float y, float width, float height, float tu_scale, float tv_scale, IDirect3DTexture9 *texture)
{
	StateManager->SetTexture(0,texture);
	StateManager->SetTexture(1,NULL);
	HRESULT res = this->Draw(x,y,width,height,tu_scale,tv_scale);
	return res;
}

HRESULT RenderQuadClass::Draw(float x, float y, float width, float height, IDirect3DTexture9 *texture)
{
	return this->Draw(x,y,width,height,1,1,texture);
}

HRESULT RenderQuadClass::Draw(float x, float y, IDirect3DTexture9 *texture) 
{
	D3DSURFACE_DESC desc;
	texture->GetLevelDesc(0,&desc);
	return this->Draw(x,y,(float)desc.Width,(float)desc.Height,1,1,texture);
}

void ShaderClass::Apply()
{
	int TextureOpCaps = CurrentCaps->Caps.TextureOpCaps;
	unsigned int bits;
	if (*ShaderDirty)
	{
		bits = 0xFFFFFFFF;
	}
	else
	{
		bits = (*CurrentShader) ^ ShaderBits;
	}
	if (bits)
	{
		(*CurrentShader) = ShaderBits;
		(*ShaderDirty) = false;
		D3DBLEND SrcBlend;
		D3DBLEND DstBlend;
		if (bits & (MASK_ALPHATEST | MASK_SRCBLEND | MASK_DSTBLEND | MASK_COLORMASK))
		{
			if (ShaderBits & MASK_COLORMASK)
			{
				SrcBlend = srcBlendLUT[((ShaderBits & MASK_SRCBLEND) >> 0xE)].blend;
				DstBlend = dstBlendLUT[((ShaderBits & MASK_DSTBLEND) >> 5)].blend;
			}
			else
			{
				SrcBlend = D3DBLEND_ZERO;
				DstBlend = D3DBLEND_ONE;
			}
			bool AlphaBlend = false;
			if ((SrcBlend != D3DBLEND_ONE) || (DstBlend != D3DBLEND_ZERO))
			{
				StateManager->SetRenderState(D3DRS_SRCBLEND,SrcBlend);
				StateManager->SetRenderState(D3DRS_DESTBLEND,DstBlend);
				AlphaBlend = true;
			}
			StateManager->SetRenderState(D3DRS_ALPHABLENDENABLE,AlphaBlend);
			bool AlphaTest = false;
			if (ShaderBits & MASK_ALPHATEST)
			{
				if (SrcBlend == D3DBLEND_INVSRCALPHA)
				{
					StateManager->SetRenderState(D3DRS_ALPHAREF,0x9F);
					StateManager->SetRenderState(D3DRS_ALPHAFUNC,D3DCMP_LESSEQUAL);
				}
				else
				{
					StateManager->SetRenderState(D3DRS_ALPHAREF,0x60);
					StateManager->SetRenderState(D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL);
				}
				AlphaTest = true;
			}
			StateManager->SetRenderState(D3DRS_ALPHATESTENABLE,AlphaTest);
			bits &= 0x0FFFB3F0F;
			if (!bits)
			{
				return;
			}
		}
		if (bits & MASK_FOG)
		{
			if ((CurrentCaps->SupportFog) && (*FogEnable))
			{
				switch ((ShaderBits & MASK_FOG) >> 8)
				{
				case FOG_DISABLE:
					StateManager->SetRenderState(D3DRS_FOGENABLE,false);
					break;
				case FOG_ENABLE:
					StateManager->SetRenderState(D3DRS_FOGENABLE,true);
					StateManager->SetRenderState(D3DRS_FOGCOLOR,(*FogColor));
					break;
				case FOG_SCALE_FRAGMENT:
					StateManager->SetRenderState(D3DRS_FOGENABLE,true);
					StateManager->SetRenderState(D3DRS_FOGCOLOR,0);
					break;
				case FOG_WHITE:
					StateManager->SetRenderState(D3DRS_FOGENABLE,true);
					StateManager->SetRenderState(D3DRS_FOGCOLOR,0xFFFFFF);
					break;
				default:
					StateManager->SetRenderState(D3DRS_FOGENABLE,false);
					break;
				}
			}
			else
			{
				StateManager->SetRenderState(D3DRS_FOGENABLE,false);
			}
			bits &= 0xFFFFFCFF;
			if (!bits)
			{
				return;
			}
		}
		if (bits & (MASK_PRIGRADIENT | MASK_TEXTURING))
		{
			unsigned int co,ca1,ca2,ao,aa1,aa2;
			if (ShaderBits & MASK_TEXTURING)
			{
				switch ((ShaderBits & MASK_PRIGRADIENT) >> 0x0A)
				{
				case GRADIENT_DISABLE:
					co = D3DTOP_SELECTARG1;
					ca1 = D3DTA_TEXTURE;
					ca2 = D3DTA_CURRENT;
					ao = D3DTOP_SELECTARG1;
					aa1 = D3DTA_TEXTURE;
					aa2 = D3DTA_CURRENT;
					break;
				case GRADIENT_ADD:
					if (TextureOpCaps & D3DTEXOPCAPS_ADD)
					{
						co = D3DTOP_ADD;
					}
					else
					{
						co = D3DTOP_MODULATE;
					}
					ca1 = D3DTA_TEXTURE;
					ca2 = D3DTA_DIFFUSE;
					ao = D3DTOP_MODULATE;
					aa1 = D3DTA_TEXTURE;
					aa2 = D3DTA_DIFFUSE;
					break;
				case GRADIENT_BUMPENVMAP:
					if (TextureOpCaps & D3DTEXOPCAPS_BUMPENVMAP)
					{
						co = D3DTOP_BUMPENVMAP;
						ca1 = D3DTA_TEXTURE;
						ca2 = D3DTA_DIFFUSE;
						ao = D3DTOP_SELECTARG1;
						aa1 = D3DTA_TEXTURE;
						aa2 = D3DTA_CURRENT;
					}
					else
					{
						co = D3DTOP_SELECTARG1;
						ca1 = D3DTA_DIFFUSE;
						ca2 = D3DTA_DIFFUSE;
						ao = D3DTOP_SELECTARG1;
						aa1 = D3DTA_DIFFUSE;
						aa2 = D3DTA_DIFFUSE;
					}
					break;
				case GRADIENT_BUMPENVMAPLUMINANCE:
					if (TextureOpCaps & D3DTEXOPCAPS_BUMPENVMAPLUMINANCE)
					{
						co = D3DTOP_BUMPENVMAPLUMINANCE;
						ca1 = D3DTA_TEXTURE;
						ca2 = D3DTA_DIFFUSE;
						ao = D3DTOP_SELECTARG1;
						aa1 = D3DTA_TEXTURE;
						aa2 = D3DTA_CURRENT;
					}
					else
					{
						co = D3DTOP_SELECTARG1;
						ca1 = D3DTA_DIFFUSE;
						ca2 = D3DTA_DIFFUSE;
						ao = D3DTOP_SELECTARG1;
						aa1 = D3DTA_DIFFUSE;
						aa2 = D3DTA_DIFFUSE;
					}
					break;
				case GRADIENT_DOTPRODUCT3:
					if (TextureOpCaps & D3DTEXOPCAPS_DOTPRODUCT3)
					{
						co = D3DTOP_DOTPRODUCT3;
						ca1 = D3DTA_TEXTURE;
						ca2 = D3DTA_DIFFUSE;
						ao = D3DTOP_SELECTARG1;
						aa1 = D3DTA_TEXTURE;
						aa2 = D3DTA_CURRENT;
					}
					else
					{
						co = D3DTOP_SELECTARG1;
						ca1 = D3DTA_DIFFUSE;
						ca2 = D3DTA_DIFFUSE;
						ao = D3DTOP_SELECTARG1;
						aa1 = D3DTA_DIFFUSE;
						aa2 = D3DTA_DIFFUSE;
					}
					break;
				default:
					co = D3DTOP_MODULATE;
					ca1 = D3DTA_TEXTURE;
					ca2 = D3DTA_DIFFUSE;
					ao = D3DTOP_MODULATE;
					aa1 = D3DTA_TEXTURE;
					aa2 = D3DTA_DIFFUSE;
					break;
				}
			}
			else
			{
				co = D3DTOP_SELECTARG2;
				ca1 = D3DTA_TEXTURE;
				ca2 = D3DTA_DIFFUSE;
				ao = D3DTOP_SELECTARG2;
				aa1 = D3DTA_TEXTURE;
				aa2 = D3DTA_DIFFUSE;
			}
			
			StateManager->SetTextureStageState(0,D3DTSS_COLOROP,co);
			StateManager->SetTextureStageState(0,D3DTSS_COLORARG1,ca1);
			StateManager->SetTextureStageState(0,D3DTSS_COLORARG2,ca2);
			StateManager->SetTextureStageState(0,D3DTSS_ALPHAOP,ao);
			StateManager->SetTextureStageState(0,D3DTSS_ALPHAARG1,aa1);
			StateManager->SetTextureStageState(0,D3DTSS_ALPHAARG2,aa2);
			bits &= 0xFFFFE3FF;
		}
		if (bits & (MASK_POSTDETAILCOLORFUNC | MASK_TEXTURING))
		{
			unsigned int co,ca1,ca2;
			co = D3DTOP_DISABLE;
			ca1 = D3DTA_TEXTURE;
			ca2 = D3DTA_CURRENT;
			if (ShaderBits & MASK_TEXTURING)
			{
				switch ((ShaderBits & MASK_POSTDETAILCOLORFUNC) >> 0x14)
				{
				case DETAILCOLOR_DETAIL:
					if (TextureOpCaps & D3DTEXOPCAPS_SELECTARG1)
					{
						co = D3DTOP_SELECTARG1;
					}
					break;
				case DETAILCOLOR_SCALE:
					if (TextureOpCaps & D3DTEXOPCAPS_MODULATE)
					{
						co = D3DTOP_MODULATE;
					}
					break;
				case DETAILCOLOR_INVSCALE:
					if (TextureOpCaps & D3DTEXOPCAPS_ADDSMOOTH)
					{
						co = D3DTOP_ADDSMOOTH;
						break;
					}
				case DETAILCOLOR_ADD:
					if (TextureOpCaps & D3DTEXOPCAPS_ADD)
					{
						co = D3DTOP_ADD;
					}
					break;
				case DETAILCOLOR_SUB:
					if (TextureOpCaps & D3DTEXOPCAPS_SUBTRACT)
					{
						co = D3DTOP_SUBTRACT;
					}
					break;
				case DETAILCOLOR_SUBR:
					if (TextureOpCaps & D3DTEXOPCAPS_SUBTRACT)
					{
						co = D3DTOP_SUBTRACT;
						ca1 = D3DTA_CURRENT;
						ca2 = D3DTA_TEXTURE;
					}
					break;
				case DETAILCOLOR_BLEND:
					if (TextureOpCaps & D3DTEXOPCAPS_BLENDTEXTUREALPHA)
					{
						co = D3DTOP_BLENDTEXTUREALPHA;
					}
					break;
				case DETAILCOLOR_DETAILBLEND:
					if (TextureOpCaps & D3DTEXOPCAPS_BLENDCURRENTALPHA)
					{
						co = D3DTOP_BLENDCURRENTALPHA;
					}
					break;
				}
			}
			StateManager->SetTextureStageState(1,D3DTSS_COLOROP,co);
			StateManager->SetTextureStageState(1,D3DTSS_COLORARG1,ca1);
			StateManager->SetTextureStageState(1,D3DTSS_COLORARG2,ca2);
		}
		bits &= 0xFF0FFFFF;
		if (bits & (MASK_POSTDETAILALPHAFUNC | MASK_TEXTURING))
		{
			unsigned int ao;
			ao = D3DTOP_DISABLE;
			if (ShaderBits & MASK_TEXTURING)
			{
				switch ((ShaderBits & MASK_POSTDETAILALPHAFUNC) >> 0x18)
				{
				case DETAILALPHA_DETAIL:
					if (TextureOpCaps & D3DTEXOPCAPS_SELECTARG1)
					{
						ao = D3DTOP_SELECTARG1;
					}
					break;
				case DETAILALPHA_SCALE:
					if (TextureOpCaps & D3DTEXOPCAPS_MODULATE)
					{
						ao = D3DTOP_MODULATE;
					}
					break;
				case DETAILALPHA_INVSCALE:
					if (TextureOpCaps & D3DTEXOPCAPS_ADDSMOOTH)
					{
						ao = D3DTOP_ADDSMOOTH;
					}
					break;
				}
			}
			StateManager->SetTextureStageState(1,D3DTSS_ALPHAOP,ao);
			StateManager->SetTextureStageState(1,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
			StateManager->SetTextureStageState(1,D3DTSS_ALPHAARG2,D3DTA_CURRENT);
		}
		bits &= 0xF8FEFFFF;
		if (bits)
		{
			StateManager->SetRenderState(D3DRS_SPECULARENABLE,((ShaderBits & MASK_SECGRADIENT) >> 0xD));
			StateManager->SetRenderState(D3DRS_ZFUNC,((ShaderBits & MASK_DEPTHCOMPARE) + 1));
			StateManager->SetRenderState(D3DRS_ZWRITEENABLE,((ShaderBits & MASK_DEPTHMASK) >> 0x3));
			if (ShaderBits & MASK_CULLMODE)
			{
				StateManager->SetRenderState(D3DRS_CULLMODE,(*_PolygonCullMode));
			}
			else
			{
				StateManager->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
			}
			if (bits & MASK_NPATCHENABLE)
			{
				float patches = 1.0;
				if (ShaderBits & MASK_NPATCHENABLE)
				{
					patches = (*NPatchesLevel);
				}
				StateManager->SetNPatchMode(patches);
			}
		}
	}
}

void ResetDeclerationBuffer()
{
	if (_DynamicDeclerationVertexBuffer)
	{
		_DynamicDeclerationVertexBuffer->Release_Ref();
	}
	_DynamicDeclerationVertexBuffer = 0;
	_DynamicDeclerationVertexBufferSize = 0x1388;
	_DynamicDeclerationVertexBufferOffset = 0;
}

SurfaceClass *Font3DDataClass::Minimize_Font_Image(SurfaceClass *surface)
{
	SurfaceClass::SurfaceDescription sd;
	int size;
	surface->Get_Description(sd);
	if (sd.Height < 256)
	{
		size = 128;
	}
	else
	{
		size = 256;
	}

	unsigned char fixedCharWidth = (unsigned char) (sd.Width / 16);
	unsigned char fixedCharHeight = (unsigned char) (sd.Height / 16);
	
	SurfaceClass *dest = new SurfaceClass(size,size,WW3D_FORMAT_A4R4G4B4);

	int sourcePitch = 0;
	int destPitch = 0;
	void *sourceSurface = surface->Lock_ReadOnly(&sourcePitch);
	void *destSurface = dest->Lock(&destPitch);

	memset(destSurface,0x00,size * size * 2); //BLACK!
	
	int destX = 0;
	int destY = 0;

	for (unsigned int i = 0; i < 16; i++)
	{
		for (unsigned int j = 0; j < 16; j++)
		{					
			char charMin = fixedCharWidth;
			char charMax = 0;

			{	// Pass 1
				unsigned char *row = (unsigned char *)(sourceSurface) + (i * fixedCharHeight * sourcePitch) + (j * fixedCharWidth * 2);
				for (int k = 0; k < fixedCharHeight; k++)
				{
					for (int l = 0; l < fixedCharWidth; l++)
					{
						unsigned short pixel = ((unsigned short *)row)[l];	
						if (pixel != 0x0000)
						{
							if (l < charMin)
							{
								charMin = (char)l;
							}
							if (l > charMax)
							{
								charMax = (char)l;
							}
						}
					}
					row += sourcePitch;
				}
			}

			char charWidth = (charMax - charMin);
			if (charWidth == -fixedCharWidth)
			{
				charWidth = 0;
			}
			else 
			{
				// Pass 2
				charWidth++;
				if ((destX + charWidth) > size) //Is there enough room left on this row?
				{
					destX = 0; // If not, start a new row.
					destY += fixedCharHeight;
				}

				unsigned char *sourceRow = (unsigned char *)(sourceSurface) + (i * fixedCharHeight * sourcePitch) + (j * fixedCharWidth * 2) + (charMin * 2);
				unsigned char *destRow = (unsigned char *)(destSurface) + (destY * destPitch) + (destX * 2);
				for (int k = 0; k < fixedCharHeight; k++)
				{
					memcpy(destRow,sourceRow,charWidth * 2);
					
					sourceRow += sourcePitch;
					destRow += destPitch;
				}
				destX += charWidth;
			}
			
			int tableIndex = (i * 16) + j;
			if (charWidth)
			{
				UOffsetTable[tableIndex]	= (float) (destX - charWidth) / (float) size;
				VOffsetTable[tableIndex]	= (float) destY / (float)size;
				UWidthTable[tableIndex]		= (float) charWidth / (float) size;
				CharWidthTable[tableIndex]	= charWidth;
			}
			else
			{
				UOffsetTable[tableIndex]	= 0;
				VOffsetTable[tableIndex]	= 0;
				UWidthTable[tableIndex]		= 0;
				CharWidthTable[tableIndex]	= 0;
			}
		}
	}

	VHeight = (float) fixedCharHeight / (float) size;
	CharHeight = fixedCharHeight;

	surface->Unlock();
	dest->Unlock();

	surface->Release_Ref();
	*Surface = dest;
	return dest;
}

FontCharsClass::FontCharsClass()
{
	//not implemented
}

FontCharsClass::~FontCharsClass()
{
	//not implemented
}

FontCharsClass::CharDataStruct *FontCharsClass::Get_Char_Data(wchar_t ch)
{
	CharDataStruct *c;
	if (ch < 256)
	{
		c = ASCIICharArray[ch];
	}
	else
	{
		Grow_Unicode_Array(ch);
		c = UnicodeCharArray[ch - FirstUnicodeChar];
	}
	if (!c)
	{
		c = Store_GDI_Char(ch);
	}
	return c;
}

int FontCharsClass::Get_Char_Width(wchar_t ch)
{
	CharDataStruct *c = Get_Char_Data(ch);
	if (c)
	{
		return c->Width;
	}
	return 0;
}

int FontCharsClass::Get_Char_Spacing(wchar_t ch)
{
	CharDataStruct *c = Get_Char_Data(ch);
	if (c)
	{
		if (c->Width)
		{
			return c->Width + 1;
		}
	}
	return 0;
}

void FontCharsClass::Blit_Char(wchar_t ch,unsigned short *dest_ptr,int dest_stride,int x,int y)
{
	int width = Get_Char_Width(ch);
	if (width)
	{

	}
}

FontCharsClass::CharDataStruct *FontCharsClass::Store_GDI_Char(wchar_t ch)
{
	SIZE char_size;
	RECT rect;
	int ofs = 0;
	GetTextExtentPoint32W(MemDC,&ch,1,&char_size);
	if ((ch == 'W') || (ch == 'V'))
	{
		if (_stricmp(GDIFontName,"Arial MT"))
		{
			ofs = 1;
			char_size.cx++;
		}
	}
	rect.left = 0;
	rect.top = 0;
	rect.right = PointSize * 2;
	rect.bottom = PointSize * 2;
	ExtTextOutW(MemDC,ofs,0,ETO_OPAQUE,&rect,&ch,1,0);
	Update_Current_Buffer(char_size.cx);
	int x = ((PointSize * 6) + 3) & -4;
	if (char_size.cy)
	{
		int row = 0;
		int pos = 0;
		do
		{
			if (char_size.cx > 0)
			{
				unsigned short *c;
				int i;
				for (i = 0,c = BufferList[BufferList.Count()-1] + (CurrPixelOffset * 2);i < char_size.cx;i++,c++)
				{
					unsigned char fc = *(GDIBitmapBits + pos);
					unsigned short x2 = fc ? 0xFFF : 0;
					unsigned short y = fc / 8;
					*c = ((y * 4096) | x2);
				}
			}
			pos += x;
		} while (row++ < char_size.cy);
	}
	CharDataStruct *c = new CharDataStruct;
	c->Value = ch;
	c->Width = (short)char_size.cx;
	c->Buffer = BufferList[BufferList.Count()-1] + (CurrPixelOffset * 2);
	if (ch < 256)
	{
		ASCIICharArray[ch] = c;
	}
	else
	{
		UnicodeCharArray[ch - FirstUnicodeChar] = c;
	}
	CurrPixelOffset += CharHeight * char_size.cx;
	return c;
}

void FontCharsClass::Update_Current_Buffer(int char_width)
{
	if ((!BufferList.Count()) || (((CharHeight * char_width) + CurrPixelOffset) > 32768))
	{
		//BufferList.Add(new unsigned short[32768]);
		CurrPixelOffset = 0;
	}
}

void FontCharsClass::Create_GDI_Font(const char *font_name)
{
	HDC dc = GetDC(NULL);
	int x = MulDiv(PointSize,GetDeviceCaps(dc,LOGPIXELSY),72);
	unsigned int weight = IsBold ? 700 : 400;
	unsigned int charset;
	switch (GetACP())
	{
	case 932:
		charset = SHIFTJIS_CHARSET;
		break;
	case 936:
	case 950:
		charset = CHINESEBIG5_CHARSET;
		break;
	case 949:
		charset = HANGUL_CHARSET;
		break;
	default:
		charset = DEFAULT_CHARSET;
		break;
	}
	GDIFont = CreateFont(-x,0,0,0,weight,0,0,0,charset,0,0,4,VARIABLE_PITCH,font_name);
	BITMAPINFO bitmap_info;
	bitmap_info.bmiHeader.biWidth = PointSize * 2;
	bitmap_info.bmiHeader.biHeight = (-PointSize) * 2;
	bitmap_info.bmiHeader.biSize = sizeof(bitmap_info);
	bitmap_info.bmiHeader.biPlanes = 1;
	bitmap_info.bmiHeader.biBitCount = 24;
	bitmap_info.bmiHeader.biCompression = 0;
	bitmap_info.bmiHeader.biSizeImage = PointSize * PointSize * 12;
	bitmap_info.bmiHeader.biXPelsPerMeter = 0;
	bitmap_info.bmiHeader.biYPelsPerMeter = 0;
	bitmap_info.bmiHeader.biClrUsed = 0;
	bitmap_info.bmiHeader.biClrImportant = 0;
	GDIBitmap = CreateDIBSection(dc,&bitmap_info,0,(void **)&GDIBitmapBits,0,0);
	MemDC = CreateCompatibleDC(0);
	OldGDIBitmap = (HBITMAP)SelectObject(MemDC,GDIBitmap);
	OldGDIFont = (HFONT)SelectObject(MemDC,GDIFont);
	SetBkColor(MemDC,0);
	SetTextColor(MemDC,0xFFFFFF);
	TEXTMETRIC text_metric;
	GetTextMetricsA(MemDC,&text_metric);
	CharHeight = text_metric.tmHeight;
}

void FontCharsClass::Free_GDI_Font()
{
	if (GDIFont)
	{
		SelectObject(MemDC,OldGDIFont);
		DeleteObject(GDIFont);
		GDIFont = 0;
	}
	if (GDIBitmap)
	{
		SelectObject(MemDC,OldGDIBitmap);
		DeleteObject(GDIBitmap);
		GDIBitmap = 0;
	}
	if (MemDC)
	{
		DeleteDC(MemDC);
		MemDC = 0;
	}
}

void FontCharsClass::Initalize_GDI_Font(const char *font_name,int point_size, bool is_bold)
{
	Name.Format("%s%d",font_name,point_size);
	GDIFontName = font_name;
	IsBold = is_bold;
	PointSize = point_size;
	Create_GDI_Font(font_name);
}

bool FontCharsClass::Is_Font(const char *font_name,int point_size, bool is_bold)
{
	if ((!_stricmp(GDIFontName,font_name)) && (PointSize == point_size) && (IsBold == is_bold))
	{
		return true;
	}
	return false;
}

void FontCharsClass::Grow_Unicode_Array(wchar_t ch)
{
	//not implemented
}

void FontCharsClass::Free_Character_Arrays()
{
	if (UnicodeCharArray)
	{
		wchar_t c = (LastUnicodeChar - FirstUnicodeChar) + 1;
		if (c > 0)
		{
			for (unsigned int i = 0; i < c;i++)
			{
				delete UnicodeCharArray[i];
				UnicodeCharArray[i] = 0;
			}
		}
		delete UnicodeCharArray;
		UnicodeCharArray = 0;
	}
	for (unsigned int i = 0;i < 256;i++)
	{
		delete ASCIICharArray[i];
		ASCIICharArray[i] = 0;
	}
}

WideStringClass *CharToWSC(const char *string)
{
	WideStringClass *wsc = new WideStringClass;
	wsc->Get_String(0,false);
	wsc->Convert_From(string);
	return wsc;
}

void AddCombatMessage(const char *message, unsigned int red, unsigned int green, unsigned int blue)
{
	Vector3 color;
	color.X = (float)(red/255.0);
	color.Y = (float)(green/255.0);
	color.Z = (float)(blue/255.0);
	WideStringClass *wcs = CharToWSC(message);
	MessageWindowClass *messagewindow = *MessageWindow;
	messagewindow->Add_Message(wcs,&color,0,0);
	delete wcs;
}

void depthbias_rs(unsigned int level)
{
	float _level = 1 - (level * 0.3f);
	StateManager->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS,reinterpret_cast<DWORD&>(_level));
}

void depthbias_view(unsigned int level)
{
	D3DVIEWPORT9 viewport;
	Direct3DDevice->GetViewport(&viewport);
	if (level)
	{
		viewport.MinZ -= 0.0000152588f;
		viewport.MaxZ -= 0.0000152588f; 
	}
	else
	{
		viewport.MinZ = 0.0f;
		viewport.MaxZ = 1.0f; 
	}
	Direct3DDevice->SetViewport(&viewport);
}

void depthbiasInit(unsigned int level)
{
	if(ShaderCaps::DepthBiasSupported == true)
	{
		ApplyDepthBias = depthbias_rs;
	}
	else
	{
		ApplyDepthBias = depthbias_view;
	}
	ApplyDepthBias(level);
};

extern "C" {
SurfaceClass __declspec(dllexport) *DoFont(Font3DDataClass *font,SurfaceClass *surface)
{
	return font->Minimize_Font_Image(surface);
}

void __declspec(dllexport) SetRenderState(D3DRENDERSTATETYPE State,DWORD Value)
{
	DebugEventStart(DEBUG_COLOR1,L"SetRenderState<%d>",State);
	if (State == 153)
	{
		if (ShaderCaps::Direct3D9Caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		{
			Direct3DDevice->SetSoftwareVertexProcessing(Value);
		}
	}
	else if (State == 47)
	{
		ApplyDepthBias(Value);
	}
	else
	{
		StateManager->SetRenderState(State,Value);
	}
	DebugEventEnd();
}

void __declspec(dllexport) SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value)
{
	DebugEventStart(DEBUG_COLOR1,L"SetTextureStageState<%d>[%d]",Stage,Type);
	if (Type == 21)
	{
		StateManager->SetSamplerState(Stage, D3DSAMP_MAXANISOTROPY, Value);
	}
	else
	{
		StateManager->SetTextureStageState(Stage,Type,Value);
	}
	DebugEventEnd();
}

void __declspec(dllexport) DynamicVBReset(bool b)
{
	(*_DynamicSortingVertexArrayOffset) = 0;
	if (b)
	{
		(*_DynamicDX8VertexBufferOffset) = 0;
		_DynamicDeclerationVertexBufferOffset = 0;
	}
}

void __declspec(dllexport) SetViewport(CONST D3DVIEWPORT8* pViewport)
{
	Direct3DDevice->SetViewport(pViewport); // They are identical
}

void __declspec(dllexport) SetRenderTarget(myIDirect3DSurface8* pRenderTarget,myIDirect3DSurface8* pNewZStencil)
{
	StateManager->SetRenderTarget(0,pRenderTarget->surface9);
	if (pNewZStencil != NULL)
	{
		Direct3DDevice->SetDepthStencilSurface(pNewZStencil->surface9);
	}
	else
	{
		Direct3DDevice->SetDepthStencilSurface(NULL);
	}
}

void __declspec(dllexport) SetTransform(D3DTRANSFORMSTATETYPE State,const Matrix4 &matrix)
{
	DebugEventStart(DEBUG_COLOR1,L"SetTransform<%d>",State);
	/*if (State == D3DTS_WORLD) 
	{
		WorldMatrix = matrix;
	}
	else if (State == D3DTS_VIEW) 
	{
		ViewMatrix = matrix;
	}
	*/
	Direct3DDevice->SetTransform(State,(D3DMATRIX *)&matrix);
	DebugEventEnd();
}
void __declspec(dllexport) CopySurface(myIDirect3DSurface8 *pSourceSurface,myIDirect3DSurface8 *pDestinationSurface)
{
	D3DSURFACE_DESC desc;
	HRESULT res = ((myIDirect3DSurface8 *)pSourceSurface)->surface9->GetDesc(&desc);
	D3DLOCKED_RECT src,dst;
	unsigned int formatsize = 4;
	if ((desc.Format == D3DFMT_A4R4G4B4)|| (desc.Format == D3DFMT_R5G6B5))
	{
		formatsize = 2;
	}
	res = ((myIDirect3DSurface8 *)pDestinationSurface)->surface9->LockRect(&dst,NULL,0);
	res = ((myIDirect3DSurface8 *)pSourceSurface)->surface9->LockRect(&src,NULL,D3DLOCK_READONLY);
	memcpy(dst.pBits,src.pBits,desc.Width * desc.Height * formatsize);
	((myIDirect3DSurface8 *)pDestinationSurface)->surface9->UnlockRect();
	((myIDirect3DSurface8 *)pSourceSurface)->surface9->UnlockRect();
}
}
