///////////////////////////////////////////////////////////////////////////////
/// @file DDOpenGL.cpp
///
/// @brief Class for OpenGL driver
///
/// @author JesusFreak
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Feb 2002
/// @author JesusFreak
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

// Need global headers
#include "deGlobalTypes.hpp"
#include "DOpenGL.hpp"
#include "deBitmap.hpp"
#include "deMath.hpp"
#include "deString.hpp"

// We need the OpenGL headers
#include <gl\gl.h>		
#include <gl\glu.h>		
#include <gl\glaux.h>
#include "glext.h"

#define VERTEXBufferID	0x01005356		//"VS",0,1
#define TEXTURESTRUCTID 0x01005354		//"TS",0,1

//information about the driver
deDriverInfo DriverInfo;

// A flag to indicate if the driver information has been logged
deBoolean DriverInfoLogged = deFALSE;

#ifdef _DEBUG
deBoolean IsDestiny3DDriverDebug()
{
	//tell them that we are a Destiny3D driver
	return deTRUE;
}
#else
deBoolean IsDestiny3DDriver()
{
	return deTRUE;
}
#endif

IdeDriver * GetDriverInstance(long ID)
{
	// Return a new instance of this driver
	deOpenGL *NewInstance = new deOpenGL(ID);
	return NewInstance;
}

const deDriverInfo *GetDriverInfo(long StructSize)
{
	// Make sure we can talk to each other and use the same size structs
	if(StructSize != sizeof(deDriverInfo))
		return NULL;

	// Setup the type of driver this is
	IdeDriver_FillVersionInfo(&DriverInfo, __DATE__, __TIME__);
	DriverInfo.Name = "OpenGL";
	DriverInfo.DLL = NULL;

	// Return the name of this driver that people can show off
	return &DriverInfo;
}

deOpenGL::deOpenGL(long ID)
: m_TexturesList(97), m_VBufferList(997)
{
	// OpenGL variables
	m_Modes = NULL;
	RC = 0;
	DC = 0;	
	AlphaFunction = GL_ALWAYS;
	AlphaRef = 0;
	SrcBlendFactor = GL_ONE;
	DestBlendFactor = GL_ZERO;
	RenderStatesValid = deTRUE;
	
	// Base class variables
	memset(&m_CurrentDisplay, 0, sizeof(m_CurrentDisplay));
	m_CurWindowHandle = NULL;
	m_WindowPossible = NULL;
	m_AdapterCount = 0;
	m_ModeCount = NULL;
	m_Caps = NULL;

	// Set our unique ID
	m_UniqueID = ID;

	// Set the mirrored texture wrap flag to a default value
	MirroredTextureWrapFlag = GL_REPEAT;
	NVVertexProgramExt = deFALSE;
}

deOpenGL::~deOpenGL()
{
	// Free our VertexBuffers
	deTArray <IdeVertexBuffer**> array;
	VertexBufferEntry* Entry;
	IdeVertexBuffer* VBuffer;
		
	m_VBufferList.GetAllElements(array);
	for(int i = 0; i < array.Length(); i++)
	{
		VBuffer = *(array[i]);
		Entry = (VertexBufferEntry*)VBuffer->GetDriverBuffer(m_UniqueID, NULL);
		VBuffer->Lock();
		VBuffer->SetDriverBuffer(m_UniqueID, NULL, this);
		VBuffer->Unlock();
		if(Entry)
		{
			if (!Entry->SubBuffer)
			{
				long Counter;
				for(Counter = 0; Counter < IdeVertexBuffer::BUFFER_COUNT - 1; Counter++)
				{
					//if the buffer exists, free it
					if(Entry->Buffers[Counter])
					{
						free(Entry->Buffers[Counter]);
						Entry->Buffers[Counter] = NULL;
					}
				}
				if (Entry->IndexBuffer)
				{
					delete[] Entry->IndexBuffer;
					Entry->IndexBuffer = NULL;
				}
			}
			free(Entry);
		}
	}
	
	// Remove all our elements from the list
	m_VBufferList.RemoveAllElements();

	// Shutdown OpenGL	
	Shutdown();

	// Free our Display Mode List
	if(m_Modes)
	{
		for(int i = 0;i < m_AdapterCount;i++)
		{
			delete[] m_Modes[i];
		}
		delete[] m_Modes;
		m_Modes = NULL;
	}

	if(m_ModeCount)
	{
		delete[] m_ModeCount;
		m_ModeCount = NULL;
	}

	if(m_WindowPossible)
	{
		delete[] m_WindowPossible;
		m_WindowPossible = NULL;
	}

	if(m_Caps)
	{
		delete[] m_Caps;
		m_Caps = NULL;
	}

	return;
}

long deOpenGL::Release()
{
	delete this;
	return 0;
}

deDriverInfo *deOpenGL::GetInfo() const
{
	//return the driver's information
	return (deDriverInfo *)GetDriverInfo(sizeof(deDriverInfo));
}

deBoolean deOpenGL::Init(HWND WindowHandle)
{
	//set what window we are using for all this
	m_CurWindowHandle = WindowHandle;

	//if we haven't logged anything yet then log some data
	if(!DriverInfoLogged)
	{
		//put in the information for a new entry
		LogInfo("------------------------------------------\n");
		LogInfo("%s v%d.%d.%d\n", DriverInfo.Name, DriverInfo.Major, DriverInfo.Minor, DriverInfo.Revision);
		LogInfo("Compiled on: \t%s\n", __TIMESTAMP__);

		// Output what time this was logged
		SYSTEMTIME	Time;
		GetSystemTime(&Time);
		LogInfo("Current Time:\t%.2d/%.2d/%.2d %.2d:%.2d:%.2d\n\n", Time.wMonth, Time.wDay, Time.wYear, Time.wHour, Time.wMinute, Time.wSecond);
		DriverInfoLogged = deTRUE;
	}

	if(!EnumerateDisplayModes())
		return deFALSE;
	
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);	
	return deTRUE;
}


// adapter and mode counts for adapters
const IdeDriver::deDriverCaps * deOpenGL::GetCaps(long Adapter) const
{
	return &m_Caps[Adapter];
}

long deOpenGL::GetAdapterCount() const
{
	return m_AdapterCount;
}

long deOpenGL::GetModeCount(long Adapter) const
{
	return m_ModeCount[Adapter];
}

deBoolean deOpenGL::WindowedAllowed(long Adapter) const
{
	return m_WindowPossible[Adapter];
}

deBoolean deOpenGL::BeginFrame()
{
	return deTRUE;
}

deBoolean deOpenGL::EndFrame()
{
	Present();
	return deTRUE;
}

deBoolean deOpenGL::ClearFrame(deARGB Color, deBoolean ClearTarget, deBoolean ClearZBuffer, deBoolean ClearStencil)
{
	long	ClearFlags = 0;

	// If they want the target cleared, do it
	if(ClearTarget)
	{
		ClearFlags = ClearFlags | GL_COLOR_BUFFER_BIT;
		
		// Get the RGBA values from the Color variable
		float A, R, G, B;
		R = (float)((Color>>16)&0xff); 
		G = (float)((Color>>8)&0xff); 
		B = (float)((Color)&0xff);
		A = (float)((Color>>24)&0xff);

		// Make there values between 0.0f and 1.0f
		R = R/256.0f;
		G = G/256.0f;
		B = B/256.0f;
		A = A/256.0f;

		// Set the clear color
		glClearColor(R,G,B,A);
	}

	// If they want the zbuffer cleared, clear it
	if(ClearZBuffer)
		ClearFlags = ClearFlags | GL_DEPTH_BUFFER_BIT;

	// If they want the stencil cleared, do it
	if(ClearStencil)
		ClearFlags = ClearFlags | GL_STENCIL_BUFFER_BIT;
	
	// Clear whatever we have been told to clear
	glClear(ClearFlags);
	return deTRUE;
}

deBoolean deOpenGL::FillBufferInfo(VertexBufferEntry *BufferEntry, IdeVertexBuffer *Vertex, IdeVertexBuffer::BufferType BufferType, deBoolean WipeBuffer)
{
	void *							VertexBuffer = NULL;
	long							BufferSize;
	void *							Buffer;
	IdeVertexBuffer::BufferDataType	BufferDataType;
	deBoolean                       SubBuffer;
	deBoolean						DynamicVertex;

	if(Vertex->GetVBufferType() == IdeVertexBuffer::BUFFERTYPE_SUB) 
       return deTRUE;
		
	//if any buffer existed for the Buffer and we don't want it, wipe it
	if(WipeBuffer && BufferEntry->Buffers[BufferType])
	{
		free(BufferEntry->Buffers[BufferType]);
		BufferEntry->Buffers[BufferType] = NULL;
	}

	//grab the buffer
	if (!Vertex->BufferExists(BufferType))
	{
		//if no buffer, just return
		return deTRUE;
	}
	
	Buffer = Vertex->GetBuffer(BufferType);

	//first, figure out the size of the Buffer
	BufferDataType = Vertex->GetBufferDataType(BufferType, &BufferSize);

	//set the size property for the buffer
	BufferEntry->EntrySize[BufferType] = BufferSize;

	DynamicVertex = Vertex->GetDynamicStatusVertex();
	BufferEntry->Dynamic = DynamicVertex;

	//figure out the size of the vertex buffer
	BufferSize = BufferEntry->PointCount * BufferSize;
	
	if (BufferEntry->PointCount == 0)
		return deTRUE;

	//if no buffer exists, then create one
	if(!BufferEntry->Buffers[BufferType])
	{
		BufferEntry->Buffers[BufferType] = (void*)malloc(BufferSize);
		if(!BufferEntry->Buffers[BufferType])
		{
			return deFALSE;
		}
	}

	if(BufferType == IdeVertexBuffer::BUFFER_DIFFUSE)
	{
		unsigned char * InBuffer = (unsigned char*)Buffer;
		for(int i = 0;i < BufferEntry->PointCount;++i)
		{
			int Index = i * 4;
			
			// Switch the R and B values of the diffuse color
			unsigned char B = InBuffer[Index];
			InBuffer[Index] = InBuffer[Index+2]; 	
			InBuffer[Index+2] = B; 	
		}
	}

	//fill it
	memcpy(BufferEntry->Buffers[BufferType], Buffer, BufferSize);
	return deTRUE;
}

deBoolean deOpenGL::AddVertexBuffer(IdeVertexBuffer *Vertex)
{
	VertexBufferEntry *		VertexBufferInfo;
	long					Counter;

	//make sure we don't already have a shader set, if a shader exists, then fail
	if(Vertex->GetDriverBuffer(m_UniqueID, NULL) != NULL)
		return deFALSE;

	//create the vertex Buffer info buffer

	//create some memory for the Buffer
	VertexBufferInfo = (VertexBufferEntry *)malloc(sizeof(VertexBufferEntry));
	if(!VertexBufferInfo)
		return deFALSE;

	//clear it all out
	memset(VertexBufferInfo, 0, sizeof(VertexBufferEntry));

	//set some flags on the buffer
	VertexBufferInfo->ID = VERTEXBufferID;

	// Set the buffer's unique ID
//	VertexBufferInfo->UniqueID = glGenLists(1);

	//how many buffers exist minus the index buffer
	Counter = Vertex->BUFFER_COUNT - 1;

	//allocate memory for the buffer entries
	VertexBufferInfo->SubBuffer = (Vertex->GetVBufferType() == IdeVertexBuffer::BUFFERTYPE_SUB);

	//lock the buffer so we can set it
	if(!Vertex->Lock())
	{
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}

	//set the buffer in the vertex buffer class
	if (!Vertex->SetDriverBuffer(m_UniqueID, VertexBufferInfo, this))
	{
		Vertex->Unlock();
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}
	
	//unlock it so we can process
	if(!Vertex->Unlock())
	{
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}

	 m_VBufferList.AddElement(Vertex, (DWORD)(Vertex));

	//process the data, pass all bits set to show a new buffer
	return ProcessVertexBuffer(Vertex, VertexBufferInfo, 0xFFFFFFFF);
}

deBoolean deOpenGL::RemoveVertexBuffer(IdeVertexBuffer *Vertex)
{
	long				Counter;
	VertexBufferEntry *	VertexBufferInfo;
	deBoolean			SubBuffer;
	
	//remove a vertex Buffer
	if(!Vertex->Lock())
		return deFALSE;

	SubBuffer = (Vertex->GetVBufferType() == Vertex->BUFFERTYPE_SUB);

	//get our info
	VertexBufferInfo = (VertexBufferEntry *)Vertex->GetDriverBuffer(m_UniqueID, NULL);

	//make sure we have a Buffer set
	if(!VertexBufferInfo)
	{
		Vertex->Unlock();
		return deFALSE;
	}

	//remove it from the class
	if(!Vertex->SetDriverBuffer(m_UniqueID, NULL, this))
	{
		Vertex->Unlock();
		return deFALSE;
	}

	//make sure the ID matches, if not, we won't try releasing it as who
	//knows what it is
	if(VertexBufferInfo->ID != VERTEXBufferID)
	{
		Vertex->Unlock();
		return deFALSE;
	}
	
	// Delete the buffer's display list
//	glDeleteLists(VertexBufferInfo->UniqueID,1); // Remove display lists for now

	if(VertexBufferInfo->Buffers)
	{
		if(!SubBuffer)
		{
			//release all vertex buffers (don't count the index buffer)
			for(Counter = 0; Counter < IdeVertexBuffer::BUFFER_COUNT - 1; Counter++)
			{
				//if the buffer exists, free it
				if(VertexBufferInfo->Buffers[Counter])
					free(VertexBufferInfo->Buffers[Counter]);
			}
		}
	}

	//if the index buffer exists, free it
	if(!SubBuffer && VertexBufferInfo->IndexBuffer)
	{
		delete[] VertexBufferInfo->IndexBuffer;
		VertexBufferInfo->IndexBuffer = NULL;
	}

	//free any memory it held
	free(VertexBufferInfo);

	if (!Vertex->Unlock())
		return deFALSE;

	m_VBufferList.RemoveElement((DWORD)(Vertex));

	return deTRUE;
}

deBoolean deOpenGL::ProcessVertexBuffer(IdeVertexBuffer *VBuffer, VertexBufferEntry *VertexBufferInfo, long BufferFlags)
{
	deBoolean					WipeBuffers;
	IdeVertexBuffer*			StreamBuffer;
	IdeVertexBuffer::BufferType	Counter;
	void *						Buffer;
	deBoolean					SubVB, SubIB;
	void *						IndexBuffer = NULL;
	deBoolean					Changed = deFALSE;
	deBoolean					DynamicIndex;
	deBoolean					is32;
	long						IndexCount;
	
	//see if we can lock the buffer
	if(!VBuffer->Lock())
		return deFALSE;

	//if no info exists, someone is being an idiot so do an add instead
	if(!VertexBufferInfo)
	{
		DEBUGRETURN(deFALSE, "ProcessVertexBuffer: Cannot process buffer with no info");
	}

	StreamBuffer = VBuffer->GetBoundDataBuffer();
	if(!StreamBuffer)
		StreamBuffer = VBuffer;
	else
	{
		if(!UpdateVertexBuffer(StreamBuffer))
			goto process_fail;
		if(!StreamBuffer->Lock())
			goto process_fail;
	}

	//see if we have to wipe the buffers out. If the point count differs then the buffers
	//are not the correct size
	WipeBuffers = ((VBuffer->GetVertexCount() == VertexBufferInfo->PointCount) ? deFALSE : deTRUE);

	SubVB = (StreamBuffer->GetVBufferType() == StreamBuffer->BUFFERTYPE_SUB);
	SubIB = (VBuffer->GetVBufferType() == VBuffer->BUFFERTYPE_SUB);

	//set the point count and tri count for the VBuffer buffer incase they changed
	VertexBufferInfo->PointCount = StreamBuffer->GetVertexCount();
	VertexBufferInfo->TriCount = VBuffer->GetTriCount();
	
	if (SubVB)
		VertexBufferInfo->PointStart = StreamBuffer->GetVertexStart();
	else
		VertexBufferInfo->PointStart = 0;

	if (SubIB)
		VertexBufferInfo->IndexStart = VBuffer->GetIndexStart();
	else
		VertexBufferInfo->IndexStart = 0;

	//set the type of rendering incase it changed
	switch(VBuffer->GetRenderType())
	{
		case VBuffer->RENDER_POINTLIST:
			VertexBufferInfo->RenderType = GL_POINTS;
			break;

		case VBuffer->RENDER_LINELIST:
			VertexBufferInfo->RenderType = GL_LINES;
			break;

		case VBuffer->RENDER_LINESTRIP:
			VertexBufferInfo->RenderType = GL_LINE_STRIP;
			break;

		case VBuffer->RENDER_TRIANGLELIST:
			VertexBufferInfo->RenderType = GL_TRIANGLES;
			break;

		case VBuffer->RENDER_TRIANGLESTRIP:
			VertexBufferInfo->RenderType = GL_TRIANGLE_STRIP;
			break;

		case VBuffer->RENDER_TRIANGLEFAN:
			VertexBufferInfo->RenderType = GL_TRIANGLE_FAN;
			break;

		default:
			RemoveVertexBuffer(VBuffer);
			return deFALSE;
	}


	if(BufferFlags)
	{
		
		if (SubVB)
		{
			IdeVertexBuffer * Parent = StreamBuffer->GetParentBuffer();
			VertexBufferEntry *	ParentBufferInfo;
			if (!Parent)
				goto process_fail;
			UpdateVertexBuffer(Parent);
			if (!Parent->Lock())
				goto process_fail;
			ParentBufferInfo = (VertexBufferEntry*)Parent->GetDriverBuffer(m_UniqueID, NULL);
			Parent->Unlock();
			if (!ParentBufferInfo)
				goto process_fail;
			
			for(Counter = StreamBuffer->BUFFER_POSITION; Counter < StreamBuffer->BUFFER_INDICES16; ((int&)Counter)++)
			{
				StreamBuffer->GetBufferDataType(Counter, &(VertexBufferInfo->EntrySize[Counter]));
				VertexBufferInfo->Buffers[Counter] = ParentBufferInfo->Buffers[Counter];
			}
		}
		else if (BufferFlags)
		{
			//go thru the buffers that have changed and update them
			for(Counter = StreamBuffer->BUFFER_POSITION; Counter < StreamBuffer->BUFFER_INDICES16; ((int&)Counter)++)
			{
				//if a buffer is changed, update it
				if(BufferFlags & (1 << Counter))
				{
					//got a changed buffer, pull it and update it
					if(!FillBufferInfo(VertexBufferInfo, StreamBuffer, Counter, WipeBuffers))
					{
						RemoveVertexBuffer(StreamBuffer);
						return deFALSE;
					}
				}
			}
		}

		if(VBuffer->BufferExists(VBuffer->BUFFER_INDICES16) || VBuffer->BufferExists(VBuffer->BUFFER_INDICES32))
		{
			if (SubIB)
			{
				IdeVertexBuffer * Parent = VBuffer->GetParentBuffer();
				IdeVertexBuffer * ParentDataBuffer = Parent->GetBoundDataBuffer();
				if (ParentDataBuffer)
					VertexBufferInfo->PointCount = ParentDataBuffer->GetVertexCount();
				VertexBufferEntry *	ParentBufferInfo;
				if (!Parent)
					goto process_fail;
				UpdateVertexBuffer(Parent);
				if (!Parent->Lock())
					goto process_fail;
				ParentBufferInfo = (VertexBufferEntry*)Parent->GetDriverBuffer(m_UniqueID, NULL);
				Parent->Unlock();
				if (!ParentBufferInfo)
					goto process_fail;
				
				VertexBufferInfo->IndexBuffer = ParentBufferInfo->IndexBuffer;
				VertexBufferInfo->IndexCount = VBuffer->GetIndexCount();
			}
			else if (BufferFlags & (1 << VBuffer->BUFFER_INDICES16 | 1 << VBuffer->BUFFER_INDICES32))
			{
				is32 = VBuffer->BufferExists(VBuffer->BUFFER_INDICES32);
				DynamicIndex = VBuffer->GetDynamicStatusIndex();
				//if the index count differs, destroy the old buffer if it exists
				IndexCount = VBuffer->GetIndexCount();
				if(IndexCount != VertexBufferInfo->IndexCount && VertexBufferInfo->IndexBuffer)
				{
					delete[] VertexBufferInfo->IndexBuffer;
					VertexBufferInfo->IndexBuffer = NULL;
				}

				//grab the buffer
				if (is32)
					Buffer = VBuffer->GetBuffer(VBuffer->BUFFER_INDICES32);
				else
					Buffer = VBuffer->GetBuffer(VBuffer->BUFFER_INDICES16);
					
				//if a buffer, update it, otherwise, wipe it out
				if(Buffer)
				{
					//got a buffer, update it
					DWORD Flags;

					//set the number of index entries
					VertexBufferInfo->IndexCount = IndexCount;

					//set the counter to the size of the buffer
					if (is32)
						IndexCount = IndexCount * sizeof(int);
					else
						IndexCount = IndexCount * sizeof(short);

					//create the buffer if it doesn't exist
					if(!VertexBufferInfo->IndexBuffer)
					{
					
						//create an index buffer
						if(is32)
						{
							VertexBufferInfo->IndexBuffer = new int[IndexCount];
							VertexBufferInfo->IndexType = GL_UNSIGNED_INT;
						}
						else
						{
							VertexBufferInfo->IndexBuffer = new short[IndexCount];
							VertexBufferInfo->IndexType = GL_UNSIGNED_SHORT;
						}
					
						if(!VertexBufferInfo->IndexBuffer)
						{
							RemoveVertexBuffer(VBuffer);
							return deFALSE;
						}
					}

					//copy it
					memcpy(VertexBufferInfo->IndexBuffer, Buffer, IndexCount);
				}
				else
				{
					//no index buffer, make sure non exists
					if(VertexBufferInfo->IndexBuffer)
					{
						delete[] VertexBufferInfo->IndexBuffer;
						VertexBufferInfo->IndexBuffer = NULL;
						VertexBufferInfo->IndexCount = 0;
					}
				}
			}
		}
	}
	
/*	// Create a display list
	if(Changed)
	{
		SetVertexBuffer(VertexBufferInfo);	

		if(VBuffer->GetVBufferType() == IdeVertexBuffer::BUFFERTYPE_SUB && !VertexBufferInfo->Dynamic)
		{
			glNewList(VertexBufferInfo->UniqueID,GL_COMPILE);
		
			if(VertexBufferInfo->IndexBuffer)
			{
				glDrawRangeElementsEXT(VertexBufferInfo->RenderType,VertexBufferInfo->IndexStart,VertexBufferInfo->PointStart + (VertexBufferInfo->TriCount-2),VertexBufferInfo->IndexCount-VertexBufferInfo->IndexStart,VertexBufferInfo->IndexType,VertexBufferInfo->IndexBuffer);
			}
			else
			{
				glDrawArrays(VertexBufferInfo->RenderType,VertexBufferInfo->PointStart,VertexBufferInfo->PointCount);
			}
	
			glEndList();
		}
	}*/

	//tell the vertex buffer that we updated the buffers
	if(!VBuffer->SetDriverBuffer(m_UniqueID, VertexBufferInfo, this))
		goto process_fail;

	//unlock the vertex buffer
	if(!VBuffer->Unlock())
		return deFALSE;

	if (StreamBuffer != VBuffer)
		if (!StreamBuffer->Unlock())
			return deFALSE;

	return deTRUE;
process_fail:
	VBuffer->Unlock();
	if (StreamBuffer != VBuffer)
		StreamBuffer->Unlock();
	RemoveVertexBuffer(VBuffer);
	return deFALSE;
}

void deOpenGL::SetVertexBuffer(VertexBufferEntry * VertexBufferInfo)
{
	glVertexPointer(3,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_POSITION]);
	glColorPointer(4,GL_UNSIGNED_BYTE,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_DIFFUSE]);	
	
	// Our normal buffer
	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_NORMAL])
	{
		glEnableClientState(GL_NORMAL_ARRAY);
		glNormalPointer(GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_NORMAL]);	
	}
	else
	{
		glDisableClientState(GL_NORMAL_ARRAY);
	}

	// Our texture coordinates
	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS0])
	{
		glClientActiveTextureARB(GL_TEXTURE0_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS0]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE0_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS1])
	{
		glClientActiveTextureARB(GL_TEXTURE1_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS1]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE1_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS2])
	{
		glClientActiveTextureARB(GL_TEXTURE2_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS2]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE2_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS3])
	{
		glClientActiveTextureARB(GL_TEXTURE3_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS3]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE3_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS4])
	{
		glClientActiveTextureARB(GL_TEXTURE4_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS4]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE4_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS5])
	{
		glClientActiveTextureARB(GL_TEXTURE5_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS5]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE5_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS6])
	{
		glClientActiveTextureARB(GL_TEXTURE6_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS6]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE6_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	if(VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS7])
	{
		glClientActiveTextureARB(GL_TEXTURE7_ARB); 
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0,VertexBufferInfo->Buffers[IdeVertexBuffer::BUFFER_TEXTURECOORDS7]);
	}
	else
	{
		glClientActiveTextureARB(GL_TEXTURE7_ARB); 
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}
		
}

deBoolean deOpenGL::UpdateVertexBuffer(IdeVertexBuffer *Vertex)
{
	VertexBufferEntry *		VertexBufferInfo;
	long					BufferFlags;

	//update a vertex Buffer and tell the vertex buffer about it

	//grab our info
	VertexBufferInfo = (VertexBufferEntry *)Vertex->GetDriverBuffer(m_UniqueID, &BufferFlags);

	//if now vertex Buffer info, call the add
	if(!VertexBufferInfo)
		return AddVertexBuffer(Vertex);

	//got an entry, verify that it is valid
	if(VertexBufferInfo->ID != VERTEXBufferID)
		return deFALSE;

	//call the function to process it
	return ProcessVertexBuffer(Vertex, VertexBufferInfo, BufferFlags);
}

deBoolean deOpenGL::Render(IdeVertexBuffer * VBuffer, long IndexOffset, IdeVertexBuffer *IndexOverride)
{
	VertexBufferEntry			*VertexBufferInfo;
	VertexBufferEntry			*IndexBufferInfo;

	// Set the matrix mode
	glMatrixMode(GL_MODELVIEW); 
		
	//get the shader info
	VertexBufferInfo = (VertexBufferEntry *)VBuffer->GetDriverBuffer(m_UniqueID, NULL);

	//if no buffer, return
	if(!VertexBufferInfo)
		DEBUGRETURN(deFALSE, "Render: VBuffer has no OpenGL info");

	//make sure it is a valid shader
	if (VertexBufferInfo->ID != VERTEXBufferID)
		return deFALSE;

	if(IndexOverride)
	{
		IndexBufferInfo = (VertexBufferEntry *)IndexOverride->GetDriverBuffer(m_UniqueID, NULL);
		if(IndexBufferInfo == NULL)
			DEBUGRETURN(deFALSE, "Render: IndexOverride has no OpenGL info");
	}
	else
		IndexBufferInfo = NULL;

	if(IndexBufferInfo && IndexBufferInfo->ID != VERTEXBufferID)
		return deFALSE;

	// From D3D Driver
	/*
	 VertexShader = GetFixedFunctionShader(VBuffer); 
	if (!VertexShader)
		return deFALSE;

  //set the vertex shader
	if (VertexShader->VertexShader != m_LastShader)
	{
		Result = m_D3DDevice->SetVertexShader(VertexShader->VertexShader);
		if(Result != D3D_OK)
		{
			LogInfo("Failure while setting vertex shader\n");
			ReportErrorHR(Result);

			return deFALSE;
		}
		m_LastShader = VertexShader->VertexShader;
	}
	*/

	// Set the Vertex Buffer
	SetVertexBuffer(VertexBufferInfo);

	//if we have an index buffer, render with it, otherwise go without it
	if(VertexBufferInfo->IndexBuffer || IndexBufferInfo)
	{
		int	TriCount, IndexStart, VertexStart, IndexCount;
		GLenum IndexType;
		void *IndexBuffer = NULL;
		int PrimType;
		if (IndexBufferInfo)
		{
			TriCount = IndexBufferInfo->TriCount;
			IndexStart = IndexBufferInfo->IndexStart;
			PrimType = IndexBufferInfo->RenderType;
			IndexBuffer = IndexBufferInfo->IndexBuffer;
			IndexCount = IndexBufferInfo->IndexCount;
			IndexType = IndexBufferInfo->IndexType;
		}
		else
		{
			TriCount = VertexBufferInfo->TriCount;
			IndexStart = VertexBufferInfo->IndexStart;
			PrimType = VertexBufferInfo->RenderType;
			IndexBuffer = VertexBufferInfo->IndexBuffer;
			IndexCount = VertexBufferInfo->IndexCount;
			IndexType = VertexBufferInfo->IndexType;
		}

		VertexStart = VertexBufferInfo->PointStart;
		if(VertexStart < 0)
			VertexStart = 0;
		VertexStart += IndexOffset;

		if(TriCount > 65535)
			TriCount = 65535;

		glDrawRangeElementsEXT(PrimType,IndexStart,VertexStart + (TriCount-2),
			IndexCount-IndexStart,IndexType,IndexBuffer);

	//	glDrawRangeElementsEXT(PrimType,TriCount,IndexStart,
		//	IndexCount,IndexType,IndexBuffer);

	}
	else
	{
		glDrawArrays(VertexBufferInfo->RenderType,VertexBufferInfo->PointStart,VertexBufferInfo->PointCount);
	}

/*	if(VertexBuffer->Dynamic)
	{
		if(VertexBufferInfo->IndexBuffer)
		{
			glDrawRangeElementsEXT(VertexBufferInfo->RenderType,VertexBufferInfo->IndexStart,VertexBufferInfo->PointStart + (VertexBufferInfo->TriCount-2),VertexBufferInfo->IndexCount-VertexBufferInfo->IndexStart,VertexBufferInfo->IndexType,VertexBufferInfo->IndexBuffer);
		}
		else
		{
			glDrawArrays(VertexBufferInfo->RenderType,VertexBufferInfo->PointStart,VertexBufferInfo->PointCount);
		}
	}
//	else
	{
	//	glCallList(VertexBuffer->UniqueID);
	}
*/
	return deTRUE;
}

IdeDriver::Types deOpenGL::GetType() const
{
	// Let people know our type
	return IdeDriver::DRIVER_OPENGL;
}

deBoolean deOpenGL::AddBitmap(IdeBitmap *Bitmap)
{
	// The new texture entry
	TextureEntry *		NewTexture;

	if(!Bitmap)
		return deFALSE;
	
	NewTexture = (TextureEntry *)Bitmap->GetDriverData(m_UniqueID);
	if (NewTexture && NewTexture->ID == TEXTURESTRUCTID)
		return deTRUE;
	
	//verify that the current display allows it
	if(!ValidFormat(m_CurrentDisplay.Adapter, RESOURCE_TEXTURE, Bitmap->GetFormat(), ZPP_UNKNOWN,Bitmap->GetFormat()))
		return NULL;

	//allocate some memory for it
	NewTexture = (TextureEntry *)malloc(sizeof(*NewTexture));
	if(!NewTexture)
		return NULL;

	//set it's ID
	NewTexture->ID = TEXTURESTRUCTID;

	// Create the texture
	glGenTextures(1,&NewTexture->Texture);

	// Typical Texture Generation Using Data From The Bitmap
	glBindTexture(GL_TEXTURE_2D,NewTexture->Texture);

	// Get the OpenGL Format
	GLint InternalFormat;
	GLenum Format;
	GLenum Type;
	if(!ToGLBPP(Bitmap->GetFormat(),&InternalFormat,&Format,&Type))
	{
		return NULL;
	}

	// Generate The Texture
	glTexImage2D(GL_TEXTURE_2D,0,InternalFormat,Bitmap->GetWidth(),Bitmap->GetHeight(),0,
		Format,Type,Bitmap->GetData());	

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);	// Linear Filtering
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);	// Linear Filtering

	Bitmap->SetDriverData(m_UniqueID, NewTexture, this);

	//texture is added, return success
	return deTRUE;
}

deBoolean deOpenGL::AddBitmapProxy(IdeBitmapProxy *BitmapProxy)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::RemoveBitmap(IdeBitmap *Bitmap)
{
	TextureEntry *	Texture;
	
	// Make sure the pointer is valid
	if(!Bitmap)
		return deFALSE;

	Texture = (TextureEntry *)Bitmap->GetDriverData(m_UniqueID);
	if (!Bitmap->SetDriverData(m_UniqueID, NULL, this))
		return deFALSE;
	
	if (!Texture)
		return deFALSE;

	// Delete the texture from OpenGL
	glDeleteTextures(1,&Texture->Texture);

	//make sure it is a valid struct
	if(Texture->ID != TEXTURESTRUCTID)
		return deFALSE;
	
	free(Texture);
	return deTRUE;
}

deBoolean deOpenGL::RenderTo(IdeBitmap *pBitmap, long CubeMapFace)
{
	if(pBitmap == NULL)
	{
		return deTRUE;
	}
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::ScreenShot(IdeBitmap * &pBitmap, deBoolean GrabWindow)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::GetVideoTexture(IdeBitmap* pBitmap, long CubeMapFace)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}


deBoolean deOpenGL::SetDepthRange(deFloat MinZ, deFloat MaxZ)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}
		
deBoolean deOpenGL::SetFOVClipping(deFloat FOV, deFloat Aspect, deFloat ClipNear, deFloat ClipFar)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(FOV*(180.0f/DE_PI),Aspect,ClipNear,ClipFar);
	return deTRUE;
}

deBoolean deOpenGL::SetOrthoClipping(deFloat Width, deFloat Height, deFloat ClipNear, deFloat ClipFar, deBoolean Invert)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-Width/2,Width/2,-Height/2,Height/2,ClipNear,ClipFar);
	
	// Invert the Y-axis
	if(Invert)
	{
		glScalef(1.0f,-1.0f,1.0f);
	}

	return deTRUE;
}

deBoolean deOpenGL::SetFOVClippingSubRect(deFloat FOVY, deFloat Aspect, deFloat ClipNear, deFloat ClipFar, deRect SubRect)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetFOVClippingSubRectFloat(deFloat FOVY, deFloat Aspect, deFloat ClipNear, deFloat ClipFar, deFloatRect SubRect)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetOrthoClippingSubRect(deFloat Width, deFloat Height, deFloat ClipNear, deFloat ClipFar, deBoolean Invert, deRect SubRect)
{
	deFloatRect FSubRect;
	deVec4d M44[4][4];
	
	if (SubRect.Left == 0 && SubRect.Right == 0 && SubRect.Top == 0 && SubRect.Bottom == 0)
	{
		FSubRect.Top = 0;
		FSubRect.Left = 0;
		FSubRect.Right = 1;
		FSubRect.Bottom = 1;
	}
	else
	{
		FSubRect.Top = (SubRect.Top - m_CurrentViewport.Y)/((deFloat)m_CurrentViewport.Height);
		FSubRect.Left = (SubRect.Left - m_CurrentViewport.X)/((deFloat)m_CurrentViewport.Width);
		FSubRect.Right = (SubRect.Right - m_CurrentViewport.X)/((deFloat)m_CurrentViewport.Width);
		FSubRect.Bottom = (SubRect.Bottom - m_CurrentViewport.Y)/((deFloat)m_CurrentViewport.Height);
	}
	
	memset(M44, 0, sizeof(M44));
	
	FSubRect.Left *= Width;
	FSubRect.Right *= Width;
	FSubRect.Top *= Height;
	FSubRect.Bottom *= Height;
	
	M44[1][1] = 2/(FSubRect.Right-FSubRect.Left);
	//	Matrix._12 = 0;
	//	Matrix._13 = 0;
	//	Matrix._14 = 0;
	
	//	Matrix._21 = 0;
	M44[2][2] = 2/(FSubRect.Bottom-FSubRect.Top);
	//	Matrix._23 = 0;
	//	Matrix._24 = 0;
	if (Invert)
		M44[2][2] *= -1;
	
	//	Matrix._31 = 0;
	//	Matrix._32 = 0;
	M44[3][3] = 1/(ClipFar-ClipNear);
	//	Matrix._34 = 0;
	
	M44[4][1] = (FSubRect.Left+FSubRect.Right)/(FSubRect.Left-FSubRect.Right);
	M44[4][2] = (FSubRect.Top+FSubRect.Bottom)/(FSubRect.Bottom-FSubRect.Top);
	M44[4][3] = ClipNear/(ClipNear-ClipFar);
	M44[4][4] = 1;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	//load the matrix
	glLoadMatrixf(M44);

	// Scale the Z-Axis by -1
	glScalef(1.0f, 1.0f, -1.0f);
	
	//it passed, tell them so
	return deTRUE;
}

deBoolean deOpenGL::SetOrthoClippingSubRectFloat(deFloat Width, deFloat Height, deFloat ClipNear, deFloat ClipFar, deBoolean Invert, deFloatRect SubRect)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetCameraTransform(deTransformInfo *M)
{
	GLfloat	M44[4][4];

	((deTransform*)M)->ToFloat44(M44, deFALSE);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();	

	glLoadMatrixf((const float *)&M44[0][0]);

	// Scale the Z-Axis by -1
	glScalef(1.0f, 1.0f, -1.0f);

	return deTRUE;
}

deBoolean deOpenGL::SetCameraTransform(deVertex *Eye, deVertex *At, deVertex *Up)
{
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();	

	gluLookAt(-Eye->x,Eye->y,Eye->z,// camera location
		-At->x,At->y,At->z, // looking at origin of camera space
		-Up->x,Up->y,Up->z); // up vector

	// Scale the Z-Axis by -1
	glScalef(1.0f, 1.0f, -1.0f);
	
	return deTRUE;
}

deBoolean deOpenGL::SetCameraTranslation(deVertex *Translation)
{
//	glMatrixMode(GL_PROJECTION);    /* prepare for and then */ 
//	glTranslatef(Translation->x,Translation->y,Translation->z); /* viewing transformation */
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetCameraRotation(deVertex *Rotation)
{
//	glMatrixMode(GL_PROJECTION); 
//	glRotatef(0.0f,Rotation->x,Rotation->y,Rotation->z); 
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::ResetCameraIdentity()
{
//	glMatrixMode(GL_MODELVIEW);
//  glLoadIdentity(); 
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetWorldTransform(deTransformInfo *M)
{
	GLfloat	M44[4][4];

	((deTransform*)M)->ToFloat44(M44, deFALSE);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();	

	glLoadMatrixf((const float *)&M44[0][0]);

	// Scale the Z-Axis by -1
	glScalef(1.0f, 1.0f, -1.0f);

	return deTRUE;
}

deBoolean deOpenGL::SetWorldTransform(deVertex *Eye, deVertex *At, deVertex *Up)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();	

	gluLookAt(-Eye->x,Eye->y,Eye->z,// camera location
		-At->x,At->y,At->z, // looking at origin of camera space
		-Up->x,Up->y,Up->z); // up vector

	// Scale the Z-Axis by -1
	glScalef(1.0f, 1.0f, -1.0f);
	
	return deTRUE;
}

deBoolean deOpenGL::SetWorldTranslation(deVertex *Translation)
{
//	glMatrixMode(GL_MODELVIEW);
//  glLoadIdentity();
//	glTranslatef(Translation->x,Translation->y,Translation->z);
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetWorldRotation(deVertex *Rotation)
{
	//glMatrixMode(GL_MODELVIEW);
//	glRotatef(0.0f,Rotation->x,Rotation->y,Rotation->z); 
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::ResetWorldIdentity()
{
	//glMatrixMode(GL_MODELVIEW); 
	//glLoadIdentity(); 
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetTextureTransform(long StageNum, deTransformInfo * Mat)
{
	return deFALSE;
}

deBoolean deOpenGL::SetViewport(long X, long Y, long Width, long Height, float MinZ, float MaxZ)
{
	RECT Rect;
	GetClientRect(m_CurWindowHandle,&Rect);

	// Until I figure out what I'm doing.
	long Y2 = (Rect.bottom-Rect.top)-(Y+Height);

	// Set the clipping rect if we have clipping enabled
	if(GetRenderState(IdeDriver::RENDER_CLIPPINGENABLE))
	{
		glScissor(X,Y2,Width,Height);
	}

	// Set our viewport
	glViewport(X,Y2,Width,Height);

	// Set our depth range
	glDepthRange(MinZ,MaxZ);
	return deTRUE;
}

deBoolean deOpenGL::ValidFormat(long Adapter, Resource ResourceType, BPP BPPType, ZPP ZPPType, BPP FrontBuffer, long ExtraFlag) const
{
#pragma todo(handle the depthstencil thing if you can - Assassin)
	// Return if a display format can be used for the requested resource
	switch(ResourceType)
	{
		case RESOURCE_DISPLAY:
			// Return if the BPP be used for the backbuffer?
			return deTRUE;
			break;

		case RESOURCE_DISPLAYWINDOWED:
			// Return if the BPP be used for the backbuffer in windowed mode.
			return deTRUE;
			break;

		case RESOURCE_TEXTURE:
			// Return if the BPP can be used for a texture
			// Convert the Destiny BPP format to OpenGL formats.
			GLint InternalFormat;
			GLenum Format;
			GLenum Type;
			if(!ToGLBPP(BPPType,&InternalFormat,&Format,&Type))
			{
				return NULL;
			}

			// Create a proxy texture
			glTexImage2D(GL_PROXY_TEXTURE_2D,0,InternalFormat,256,256,0,Format,Type,NULL);

			// Get the width of the proxy texture
			GLint Width;
			glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&Width);

			// See if the proxy texture of created correctly
			if(Width == 0)
			{
				return deFALSE;
			}
			return deTRUE;
			break;
	}

	return deFALSE;
}

void deOpenGL::InvalidateRenderStates(deBoolean Valid)
{
	RenderStatesValid = Valid;
}

deBoolean deOpenGL::ToGLBPP(IdeDriver::BPP BPPType,GLint * InternalFormat,GLenum * Format,GLenum * Type) const
{
	// Convert our BPP type to a Open GL format type
	switch(BPPType)
	{
		case IdeDriver::BPP_X1R5G5B5:
			  *InternalFormat = GL_RGB5;
			  *Format = GL_BGR_EXT;
			  *Type = GL_BYTE;
			  return deTRUE;

		case IdeDriver::BPP_A1R5G5B5:
			  *InternalFormat = GL_RGB5_A1;
			  *Format = GL_BGRA_EXT;
			  *Type = GL_BYTE;
			  return deTRUE;

		case IdeDriver::BPP_X4R4G4B4:
			*InternalFormat = GL_RGB12;
			 *Format = GL_BGR_EXT;
			 *Type = GL_BYTE;
			return deTRUE;

		case IdeDriver::BPP_A4R4G4B4:
			*InternalFormat = GL_RGBA16;
			*Format = GL_BGRA_EXT;
			*Type = GL_BYTE;
			return deTRUE;

		case IdeDriver::BPP_A8:
			 *InternalFormat = GL_ALPHA8;
			 *Format = GL_ALPHA;
			 *Type = GL_UNSIGNED_BYTE;
			 return deTRUE;

		case IdeDriver::BPP_R8G8B8:
			*InternalFormat = 3;
			*Format = GL_BGR_EXT;
			*Type = GL_UNSIGNED_BYTE;
			return deTRUE;
			
		case IdeDriver::BPP_A8R8G8B8:
			*InternalFormat = 4;
			*Format = GL_BGRA_EXT;
			*Type = GL_UNSIGNED_BYTE;
			return deTRUE;

		case IdeDriver::BPP_X8R8G8B8:
			*InternalFormat = 4;
			*Format = GL_BGRA_EXT;
			*Type = GL_UNSIGNED_BYTE;
			return deTRUE;

		case IdeDriver::BPP_L8:
  			 *InternalFormat = GL_LUMINANCE8;
			 *Format = GL_LUMINANCE;
			 *Type = GL_UNSIGNED_BYTE;
			 return deTRUE;

		case IdeDriver::BPP_A8L8:
			 *InternalFormat = GL_LUMINANCE8_ALPHA8;
			 *Format = GL_LUMINANCE_ALPHA;
			 *Type = GL_UNSIGNED_BYTE;
			 return deTRUE;

		case IdeDriver::BPP_A4L4:
			 *InternalFormat = GL_LUMINANCE4_ALPHA4;
			 *Format = GL_LUMINANCE_ALPHA;
			 *Type = GL_UNSIGNED_BYTE;
			 return deTRUE;
	}

	*InternalFormat = 0;
	*Format = GL_RGBA;
	*Type = GL_UNSIGNED_BYTE;
	return deFALSE;
}

unsigned char deOpenGL::GetBPP(IdeDriver::BPP BPPType)
{
	switch(BPPType)
	{
		case IdeDriver::BPP_X1R5G5B5:
		case IdeDriver::BPP_A1R5G5B5:
		case IdeDriver::BPP_A4R4G4B4:
		case IdeDriver::BPP_X4R4G4B4:
		case IdeDriver::BPP_A8R3G3B2:
		case IdeDriver::BPP_R5G6B5:
		case IdeDriver::BPP_A8P8:
		case IdeDriver::BPP_A8L8:
		case IdeDriver::BPP_V8U8:
		case IdeDriver::BPP_L6V5U5:
			return 16;

		case IdeDriver::BPP_A8:
		case IdeDriver::BPP_R3G3B2:
		case IdeDriver::BPP_P8:
		case IdeDriver::BPP_L8:
		case IdeDriver::BPP_A4L4:
			return 8;			

		case IdeDriver::BPP_A2B10G10R10:
		case IdeDriver::BPP_G16R16:
		case IdeDriver::BPP_A8R8G8B8:
		case IdeDriver::BPP_X8R8G8B8:
		case IdeDriver::BPP_Q8W8V8U8:
		case IdeDriver::BPP_V16U16:
		case IdeDriver::BPP_W11V11U10:
		case IdeDriver::BPP_X8L8V8U8:
		case IdeDriver::BPP_A2W10V10U10:
			return 32;

		case IdeDriver::BPP_R8G8B8:
			return 24;
	}

	return 16;
}

IdeDriver::BPP deOpenGL::GetDestinyBPP(long BPP) const
{
	// Return a Destiny BPP with the same number of bytes as the BPP we were passed
	switch(BPP)
	{
		case 16:
			//return IdeDriver::BPP_X1R5G5B5;
			return IdeDriver::BPP_R5G6B5;

		case 24:
			return IdeDriver::BPP_R8G8B8;

		case 32:
			return IdeDriver::BPP_A8R8G8B8;

	}
	return IdeDriver::BPP_UNKNOWN;
}

deBoolean deOpenGL::AddShader(IdeShader * Shader)
{
	return deFALSE;
}

deBoolean deOpenGL::RemoveShader(IdeShader * Shader)
{
	return deFALSE;
}

deBoolean deOpenGL::SetShader(IdeShader * Shader)
{
	return deFALSE;
}

/*
unsigned char transform_refract [] = 
		"!!VP1.0 # Refraction\n"
        "# transform vertex position to eye space\n"
		"DP4	R9.x, c[8], v[OPOS] ;\n"
		"DP4	R9.y, c[9], v[OPOS] ;\n"
		"DP4	R9.z, c[10], v[OPOS] ;\n"
		"DP4	R9.w, c[11], v[OPOS] ;\n"
        "\n"
        "# transform normal to eye space\n"
		"DP3	R11.x, c[4], v[NRML] ;\n"
		"DP3	R11.y, c[5], v[NRML] ;\n"
		"DP3	R11.z, c[6], v[NRML] ;\n"
        "\n"
		"# vertex->eye vector\n"
		"ADD	R0, -R9, c[59];     # c[59] = eye position in eye space, usually (0,0,0,1)\n"
        "\n"
        "# normalize\n"
        "DP3	R8.w, R0, R0;\n"
		"RSQ	R8.w, R8.w;\n"
		"MUL	R8, R0, R8.w;       # r8 = eye/incident vector\n"
        "\n"
        "# refraction, Renderman style\n"
        "\n"
        "# float IdotN = I.N;\n"
        "# float k = 1 - eta*eta*(1 - IdotN*IdotN);\n"
        "# return k < 0 ? (0,0,0) : eta*I - (eta*IdotN + sqrt(k))*N;\n"
        "\n"
		"DP3	R0.x, R11, -R8;             # r0 = N.I\n"
        "\n"
		"MAD	R1.x, -R0.x, R0.x, c[64].y; # r1 = -IdotN*IdotN + 1\n"
		"MUL	R1.x, R1.x, c[58].y;        # r1 = -(r1*eta*eta)+1\n"
        "ADD    R1.x, c[64].y, -R1.x;\n"
        "\n"
        "RSQ    R2.x, R1.x;\n"
        "RCP    R2.x, R2.x;\n"
        "MAD    R2.x, c[58].x, R0.x, R2.x;\n"
        "MUL    R2, R11, R2.x;\n"
        "MAD    R2, c[58].x, -R8, R2;\n"
        "\n"
        "# transform refracted ray by cubemap transform\n"
        "DP3    o[TEX0].x, c[12], R2;\n"
        "DP3    o[TEX0].y, c[13], R2;\n"
        "DP3    o[TEX0].z, c[14], R2;\n"
        "\n"
        "# calculate reflection\n"
        "\n"
        "MUL    R0, R11, c[64].z;\n"
        "DP3    R3.w, R11, R8;\n"
        "MAD    R3, R3.w, R0, -R8;\n"
        "\n"
        "# transform reflected ray by cubemap transform\n"
        "DP3    o[TEX1].x, c[12], R3;\n"
        "DP3    o[TEX1].y, c[13], R3;\n"
        "DP3    o[TEX1].z, c[14], R3;\n"
        "\n"
        "# cheesy Fresnel approximation = (1-(I.N))^p\n"
        "DP3    R0.x, R8, R11;\n"
        "ADD    R0.x, c[64].y, -R0.x;\n"
        "MUL    R0.x, R0.x, R0.x;\n"
        "MUL    o[COL0], R0.x, c[62];\n"
        "\n"
        "# transform vertex to clip space\n"
		"DP4	o[HPOS].x, c[0], v[OPOS] ;\n"
		"DP4	o[HPOS].y, c[1], v[OPOS] ;\n"
		"DP4	o[HPOS].z, c[2], v[OPOS] ;\n"
		"DP4	o[HPOS].w, c[3], v[OPOS] ;\n"
        "\n"
		"END";

deBoolean deOpenGL::AddShader(IdeShader * Shader)
{
	if(Shader->GetShaderType() == IdeShader::ShaderType::SHADER_VERTEX)
	{
		if(!NVVertexProgramExt)
			return deFALSE;

		deString OGLShaderCode;
		if(!DisassembleShader(Shader,&OGLShaderCode))
			return deFALSE;

		unsigned int VProgramID = 0; 
		glGenProgramsNV(1, &VProgramID); 
		glBindProgramNV(GL_VERTEX_PROGRAM_NV,VProgramID); 
	
		//long Size = strlen(OGLShaderCode);
		//glLoadProgramNV(GL_VERTEX_PROGRAM_NV,VProgramID,Size,(const unsigned char*)(const char*)OGLShaderCode); 

		long Size = strlen((const char*)transform_refract);
		glLoadProgramNV(GL_VERTEX_PROGRAM_NV,VProgramID,Size,transform_refract); 
	
		if(!glIsProgramNV(VProgramID))
			return deFALSE;

		Shader->SetDriverShader((void*)VProgramID);
		return deTRUE;
	}
	else if(Shader->GetShaderType() == IdeShader::ShaderType::SHADER_PIXEL)
	{
		return deFALSE;
	}

	return deFALSE;

}

deBoolean deOpenGL::RemoveShader(IdeShader * Shader)
{
#pragma todo(unfinished function in deOpenGL)
	return deFALSE;
}

deBoolean deOpenGL::SetShader(IdeShader * Shader)
{
	if(!Shader)
		return deFALSE;

	if(Shader->GetShaderType() == IdeShader::ShaderType::SHADER_VERTEX)
	{
		if(!NVVertexProgramExt)
			return deFALSE;

		glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV);

		// Enable vertex programs
		glEnable(GL_VERTEX_PROGRAM_NV); 

		// Bind the vertex program
		glBindProgramNV(GL_VERTEX_PROGRAM_NV, (unsigned int)Shader->GetDriverShader());
		return deTRUE;
	}
	
	return deFALSE;
}

deBoolean deOpenGL::DisassembleShader(IdeShader * Shader,deString * Code)
{
	deTArray<IdeShader::deShaderInstruction> * Instructions = (deTArray<IdeShader::deShaderInstruction>*)Shader->GetInstructions();
	for(int i = 0;i < Instructions->Length();++i)
	{
		if(!DisassembleShaderInstruction(&(*Instructions)[i],Code))
		{
			return deFALSE;
		}
	}

	*Code += "END";

	return deTRUE;
}

void DisassembleShaderOffset(DWORD IndexBase,DWORD Argument,deString * Code)
{
	int Index = (Argument-IndexBase)/0x1000000;
	char cIndex[3];
	itoa(Index,cIndex,10);
	
	if((Argument & IdeShader_Argument_X) || (Argument & IdeShader_Argument_Y) || (Argument & IdeShader_Argument_Z) || (Argument & IdeShader_Argument_W))
	{
		*Code += ".";
		if(Argument & IdeShader_Argument_X)
		{
			*Code += "x";
		}
		
		if(Argument & IdeShader_Argument_Y)
		{
			*Code += "y";
		}

		if(Argument & IdeShader_Argument_Z)
		{
			*Code += "z";
		}
		
		if(Argument & IdeShader_Argument_W)
		{
			*Code += "w";
		}
	}
}

void DisassembleShaderIndex(DWORD IndexBase,DWORD Argument,deString * Code)
{
	int Index = (Argument-IndexBase)/0x1000000;
	char cIndex[3];
	itoa(Index,cIndex,10);
	*Code += cIndex;
	
}

void DisassembleShaderIndexAndOffset2(DWORD IndexBase,DWORD Argument,deString * Code)
{
	int Index = (Argument-IndexBase)/0x1000000;
	char cIndex[3];
	itoa(Index,cIndex,10);
	*Code += cIndex;
	
	if((Argument & IdeShader_Argument_X) || (Argument & IdeShader_Argument_Y) || (Argument & IdeShader_Argument_Z) || (Argument & IdeShader_Argument_W))
	{
		*Code += ".";
		if(Argument & IdeShader_Argument_X)
		{
			*Code += "x";
		}
		
		if(Argument & IdeShader_Argument_Y)
		{
			*Code += "y";
		}

		if(Argument & IdeShader_Argument_Z)
		{
			*Code += "z";
		}
		
		if(Argument & IdeShader_Argument_W)
		{
			*Code += "w";
		}
	}
}

deBoolean deOpenGL::DisassembleShaderArgument(DWORD Argument,deString * Code)
{
	if(Argument & IdeShader_Argument_Negate)
	{
		*Code += "-";
	}

	if(Argument == IdeShader::Argument::ARGUMENT_VERSION_1)
	{
		*Code += "1.0";
	}
	else if((IdeShader::Argument::ARGUMENT_CONSTANT <= Argument) && (Argument < IdeShader::Argument::ARGUMENT_TEMP))
	{
		*Code += " c";
		DisassembleShaderIndexAndOffset(IdeShader::Argument::ARGUMENT_CONSTANT,Argument,Code);		

	}
	else if((IdeShader::Argument::ARGUMENT_TEMP <= Argument) && (Argument < IdeShader::Argument::ARGUMENT_VERTEX))
	{
		*Code += " R";
		DisassembleShaderIndexAndOffset2(IdeShader::Argument::ARGUMENT_TEMP,Argument,Code);	
	}
	else if((IdeShader::Argument::ARGUMENT_VERTEX <= Argument) && (Argument < IdeShader::Argument::ARGUMENT_OUTPOS))
	{
		*Code += " v";
		DisassembleShaderIndexAndOffset(IdeShader::Argument::ARGUMENT_VERTEX,Argument,Code);	

	}
	else if((IdeShader::Argument::ARGUMENT_OUTPOS <= Argument) && (Argument < IdeShader::Argument::ARGUMENT_OUTCOLOR))
	{
		*Code += " o[HPOS]";
		DisassembleShaderOffset(IdeShader::Argument::ARGUMENT_OUTPOS,Argument,Code);	
	}
	else if((IdeShader::Argument::ARGUMENT_OUTCOLOR <= Argument) && (Argument <= IdeShader::Argument::ARGUMENT_OUTFOG))
	{
		*Code += " o[COL";
		DisassembleShaderIndex(IdeShader::Argument::ARGUMENT_OUTCOLOR,Argument,Code);
			
		*Code += "]";
		DisassembleShaderOffset(IdeShader::Argument::ARGUMENT_OUTCOLOR,Argument,Code);
	}
	else if((IdeShader::Argument::ARGUMENT_OUTFOG <= Argument) && (Argument < IdeShader::Argument::ARGUMENT_OUTPTS))
	{
		*Code += " o[FOG]";
		DisassembleShaderIndexAndOffset(IdeShader::Argument::ARGUMENT_OUTFOG,Argument,Code);	
	}
	else if((IdeShader::Argument::ARGUMENT_OUTPTS <= Argument) && (Argument < IdeShader::Argument::ARGUMENT_OUTTEX))
	{
		*Code += " o[PTS]";
		DisassembleShaderIndexAndOffset(IdeShader::Argument::ARGUMENT_OUTPTS,Argument,Code);	
	}
	else if((IdeShader::Argument::ARGUMENT_OUTTEX <= Argument) && (Argument < IdeShader::Argument::ARGUMENT_OUTTEX_END))
	{
		*Code += " o[TEX";

		DisassembleShaderIndex(IdeShader::Argument::ARGUMENT_OUTTEX,Argument,Code);
			
		*Code += "]";
		DisassembleShaderOffset(IdeShader::Argument::ARGUMENT_OUTTEX,Argument,Code);	
	}
	else
	{
		return deFALSE;
	}

	return deTRUE;
}

void deOpenGL::DisassembleShaderIndexAndOffset(DWORD IndexBase,DWORD Argument,deString * Code)
{
	*Code += "[";
	int Index = (Argument-IndexBase)/0x1000000;

	if(IndexBase == IdeShader::Argument::ARGUMENT_VERTEX)
	{
		switch(Index)
		{
			case 0:
				*Code += "OPOS";
				break;

			case 1:
				*Code += "NRML";
				break;
		
			case 2:
				*Code += "TEX0";
				break;
		
			case 3:
				*Code += "TEX1";
				break;
			
			case 4:
				*Code += "TEX2";
				break;
		
			case 5:
				*Code += "TEX3";
				break;
		
			case 6:
				*Code += "TEX4";
				break;
			
			case 7:
				*Code += "TEX5";
				break;
			
			case 8:
				*Code += "TEX6";
				break;
		
			case 9:
				*Code += "TEX7";
				break;
		
			case 10:
				*Code += "COL0";
				break;		

			case 11:
				*Code += "COL1";
				break;	
		}
	}
	else
	{
		char cIndex[3];
		itoa(Index,cIndex,10);
		*Code += cIndex;
	}
	*Code += "]";
	
	if((Argument & IdeShader_Argument_X) || (Argument & IdeShader_Argument_Y) || (Argument & IdeShader_Argument_Z) || (Argument & IdeShader_Argument_W))
	{
		*Code += ".";
		if(Argument & IdeShader_Argument_X)
		{
			*Code += "x";
		}
		
		if(Argument & IdeShader_Argument_Y)
		{
			*Code += "y";
		}

		if(Argument & IdeShader_Argument_Z)
		{
			*Code += "z";
		}
		
		if(Argument & IdeShader_Argument_W)
		{
			*Code += "w";
		}
	}
}

deBoolean deOpenGL::DisassembleShaderInstruction(IdeShader::deShaderInstruction * Instruction,deString * Code)
{
	// Find out what type of instruction it is
	switch(Instruction->Type)
	{
		case IdeShader::deShaderInstructionType::INSTRUCTION_VERSION_VERTEX:
			*Code += "!!VP";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_VERSION_PIXEL:
			*Code += "DE_PIXEL_SHADER";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_ADD:
			*Code += "ADD";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_DP3:
			*Code += "DP3";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_DP4:
			*Code += "DP4";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_DST:
			*Code += "DST";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_EXPP:
			*Code += "EXPP";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_LIT:
			*Code += "LIT";
			break;
						
		case IdeShader::deShaderInstructionType::INSTRUCTION_LOGP:
			*Code += "LOGP";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_MAD:
			*Code += "MAD";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_MAX:
			*Code += "MAX";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_MIN:
			*Code += "MIN";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_MOV:
			*Code += "MOV";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_MUL:
			*Code += "MUL";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_RCP:
			*Code += "RCP";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_RSQ:
			*Code += "RSQ";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_SGE:
			*Code += "SGE";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_SLT:
			*Code += "SLT";
			break;

		case IdeShader::deShaderInstructionType::INSTRUCTION_SUB:
			*Code += "SUB";
			break;

		default:
			return deFALSE;
	}

	// Disassemble the instruction's arguments
	for(int i = 0;i < 3 && Instruction->Arguments[i] != IdeShader::Argument::ARGUMENT_INVALID;++i)
	{
		if(i != 0)
			*Code += ",";

		if(!DisassembleShaderArgument(Instruction->Arguments[i],Code))
			return deFALSE;
	}

	if(Instruction->Type != IdeShader::deShaderInstructionType::INSTRUCTION_VERSION_VERTEX)
		*Code += ";";

	*Code += "\n";
	return deTRUE;
}

*/