//////////////////////////////////////////////////////////////////////////////////////////
/// @file deQ3Loader.cpp
///
/// @brief Quake 3 BSP loader - do not create content in toolsets with restrictive licenses,
///        Novus Delta will not be held liable for any infringements made on toolset licenses.
///
/// @author Assassin
///
/// 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 Mar 2003
/// @author Assassin
/// @remarks Creation
///
//////////////////////////////////////////////////////////////////////////////////////////

#include "deBrush_priv.hpp"
#include "deDriver.hpp"
#include "deBitmap.hpp"
#include "deFileSystem.hpp"

#pragma pack(push, 4)

enum q3_lump_t
{
	q3lump_Entities = 0,
	q3lump_Textures,
	q3lump_Planes,
	q3lump_Nodes,
	q3lump_Leafs,
	q3lump_LeafFaces,
	q3lump_LeafBrushes,
	q3lump_Models,
	q3lump_Brushes,
	q3lump_BrushSides,
	q3lump_Vertices,
	q3lump_MeshVerts,
	q3lump_Shaders,
	q3lump_Faces,
	q3lump_Lightmaps,
	q3lump_LightVolumes,
	q3lump_VisData,
	q3lump_MaxLumps // = 17
};

struct q3_direntry
{
	int		offset;
	int		length;
};

struct q3_header
{
	char			magic[4];
	int				version;
	q3_direntry		direntries[17];
};

struct q3_texture
{
	char	filename[64];
	int		flags;
	int		contents;
};

struct q3_lightmap
{
	BYTE	mapdata[128][128][3];	//Lightmap color data. RGB 
};

struct q3_vertex
{
	float	position[3];
	float	texcoord[2][2];	// [0]=surface, [1]=lightmap
	float	normal[3];
	DWORD	color;			// ABGR on intel hardware
};

struct q3_meshvert
{
	int offset;
};

struct q3_face
{
	int		texture;		//Texture index.  
	int 	effect;			//Index into lump 12 (Effects), or -1.  
	int 	type;			//Face type. 1=polygon, 2=patch, 3=mesh, 4=billboard  
	int 	vertex;			//Index of first vertex.  
	int 	n_vertexes;		//Number of vertices.  
	int 	meshvert;		//Index of first meshvert.  
	int 	n_meshverts;	//Number of meshverts.  
	int 	lm_index;		//Lightmap index.  
	int 	lm_start[2];	//Corner of this face's lightmap image in lightmap.  
	int 	lm_size[2];		//Size of this face's lightmap image in lightmap.  
	float 	lm_origin[3];	//World space origin of lightmap.  
	float	lm_vecs[2][3];	//World space lightmap s and t unit vectors.  
	float 	normal[3];		//Surface normal.  
	int 	patch_dim[2];	//Patch dimensions.  
};

struct q3_bspdata
{
	q3_header		header;
	q3_texture*		textures;
	q3_lightmap*	lightmaps;
	q3_vertex*		vertices;
	q3_face*		faces;
	q3_meshvert*	meshverts;
};

#pragma pack(pop)


static deBoolean ReadQ3BSPFile(const char * filename, q3_bspdata & q3file)
{
	if (!filename)
		DEBUGRETURN(deFALSE, "LoadFromQ3BSP: Paramater 'filename' was NULL");
	
	IdeFSReal*		FS = NULL;
	IdeFile*		File = NULL;
	long			size;
	q3_lump_t		counter;

	q3file.faces = NULL;
	q3file.lightmaps = NULL;
	q3file.textures = NULL;
	q3file.vertices = NULL;
	
	// create our file
	FS = IdeFileSystem_CreateFSReal();

	if (!FS)
	{
		DEBUGMSG1("Could not create file to read Q3 BSP file '%s'", filename);
		goto return_failure;
	}
	
	File = FS->Open(filename, deFile_Open_Existing | deFile_Open_Read);

	// open the requested filename
	if (!File)
	{
		DEBUGMSG1("Could not load Quake3 BSP file '%s'", filename);
		goto return_failure;
	}
	
	// grab the header
	size = sizeof(q3_header);
	if (size != File->Read(&(q3file.header), size))
	{
		DEBUGMSG1("Could not read Quake3 BSP header for file '%s'", filename);
		goto return_failure;
	}

	// check for valid header info
	if (q3file.header.magic[0] != 'I' || q3file.header.magic[1] != 'B' || 
		q3file.header.magic[2] != 'S' || q3file.header.magic[3] != 'P' ||
		q3file.header.version != 0x2e)
	{
		DEBUGMSG1("File is not a valid Quake3 BSP file '%s'", filename);
		goto return_failure;
	}
	
	// read each of the lumps that we want
	// for each lump, we jump to the specified file position, and then read all the data into an array
	for (counter = q3lump_Entities; counter <= q3lump_MaxLumps; ((long&)counter)++)
	{
		size = q3file.header.direntries[counter].length;
		switch (counter)
		{
		default:
			break;
		case q3lump_Textures:
			q3file.textures = new q3_texture[size/sizeof(q3_texture)];
			if (0 > File->SetPosition(q3file.header.direntries[counter].offset, deFile_Seek_Start))
			{
				DEBUGMSG2("Could not read Quake3 BSP data segment %d for file '%s'", counter, filename);
				goto return_failure;
			}
			if (size != File->Read(q3file.textures, size))
			{
				DEBUGMSG1("Could not read Quake3 BSP texture data for file '%s'", filename);
				goto return_failure;
			}
			break;
		case q3lump_Faces:
			q3file.faces = new q3_face[size/sizeof(q3_face)];
			if (0 > File->SetPosition(q3file.header.direntries[counter].offset, deFile_Seek_Start))
			{
				DEBUGMSG2("Could not read Quake3 BSP data segment %d for file '%s'", counter, filename);
				goto return_failure;
			}
			if (size != File->Read(q3file.faces, size))
			{
				DEBUGMSG1("Could not read Quake3 BSP face data for file '%s'", filename);
				goto return_failure;
			}
			break;
		case q3lump_Vertices:
			q3file.vertices = new q3_vertex[size/sizeof(q3_vertex)];
			if (0 > File->SetPosition(q3file.header.direntries[counter].offset, deFile_Seek_Start))
			{
				DEBUGMSG2("Could not read Quake3 BSP data segment %d for file '%s'", counter, filename);
				goto return_failure;
			}
			if (size != File->Read(q3file.vertices, size))
			{
				DEBUGMSG1("Could not read Quake3 BSP vertex data for file '%s'", filename);
				goto return_failure;
			}
			break;
		case q3lump_Lightmaps:
			q3file.lightmaps = new q3_lightmap[size/sizeof(q3_lightmap)];
			if (0 > File->SetPosition(q3file.header.direntries[counter].offset, deFile_Seek_Start))
			{
				DEBUGMSG2("Could not read Quake3 BSP data segment %d for file '%s'", counter, filename);
				goto return_failure;
			}
			if (size != File->Read(q3file.lightmaps, size))
			{
				DEBUGMSG1("Could not read Quake3 BSP lightmap data for file '%s'", filename);
				goto return_failure;
			}
			break;
		case q3lump_MeshVerts:
			q3file.meshverts = new q3_meshvert[size/sizeof(q3_meshvert)];
			if (0 > File->SetPosition(q3file.header.direntries[counter].offset, deFile_Seek_Start))
			{
				DEBUGMSG2("Could not read Quake3 BSP data segment %d for file '%s'", counter, filename);
				goto return_failure;
			}
			if (size != File->Read(q3file.meshverts, size))
			{
				DEBUGMSG1("Could not read Quake3 BSP meshvert data for file '%s'", filename);
				goto return_failure;
			}
			break;
		}
	}
	
	// can only get here if we haven't bailed out while reading the file
	File->Release();
	FS->Release();
	return deTRUE;
	
return_failure:
	// something went wrong, so release all our mem and return false
	if (File)
		File->Release();
	if (FS)
		FS->Release();
	delete[] q3file.textures;
	delete[] q3file.lightmaps;
	delete[] q3file.vertices;
	delete[] q3file.faces;
	delete[] q3file.meshverts;
	
	return deFALSE;
}

#define USE_ALL_STREAMS (0) // set to 1 if you REALLY want normals and lightmap coords
#define USE_MESHVERTS   (1) // leave this as 1 or some levels will look totally garbled
#define USE_PATCHES     (1) // parse and compute the geometry for curved surface patches
#define REFINE_PATCHES  (1) // actually create curved surfaces instead of just filling in the holes
#define PATCH_DETAIL    (8) // default 3 - it's the # of vertices in X and Y for each patch

#if REFINE_PATCHES
#include "deLine_priv.hpp" // need the BezierLine class
#endif

// ONLY FOR TESTING - DO NOT DISTRIBUTE CONTENT WITHOUT PROPER AUTHORIZATION!
// THIS MEANS YOU CANT DISTRIBUTE CONTENT MADE WITH Q3RADIANT
DEBRUSH_API deBoolean IdeBrush_LoadFromQ3BSP(const char * filename, IdeLargeStaticBrush* pBrush, deDouble Scale)
{
	//deWorldObjectSmartPtr<IdeVertexBuffer>	VertexBuffer;
	//deWorldObjectSmartPtr<IdeVertexBuffer>	IndexBuffer;
	IdeVertexBuffer*	VertexBuffer = NULL;
	IdeVertexBuffer*	IndexBuffer = NULL;
	q3_bspdata			q3data;
	int					i;
	int					NumTextures, NumFaces, NumVerts, PatchVerts;
	int					PatchVertsUsed;
	deBoolean			Needs32Bit;
	deTransform			scaletform, normaltform;

	{
		// Quake3 levels are stored like a 3DS file... Z is up or down or something
		// we use Y as up, so we need to flip it around
		deVec3d tempvec;
		scaletform.ApplyScaling(Scale);
		// convert Y to Z
		scaletform.RotateX(-DE_PI/2);
		scaletform.SqueezeVectors();
		// switch from right-handed to left-handed
		scaletform.GetRight(tempvec);
		tempvec = -tempvec;
		scaletform.SetRight(tempvec);
		// get a normal-transforming one (can't do scaling, just rotation)
		normaltform = scaletform;
		normaltform.OrthoNormalize();
	}

	if (!ReadQ3BSPFile(filename, q3data))
		return deFALSE;

	DEBUGMSG0("Loading a Quake 3 BSP file - do not violate license restrictions on tools such as Q3Radiant");

	// compute some handy values from the data sizes
	NumTextures = (q3data.header.direntries[q3lump_Textures].length) / sizeof(q3_texture);
	NumFaces = (q3data.header.direntries[q3lump_Faces].length) / sizeof(q3_face);
	NumVerts = (q3data.header.direntries[q3lump_Vertices].length) / sizeof(q3_vertex);
	// initialize our patch vertex counters
	PatchVerts = 0;
	PatchVertsUsed = 0;

	// add each texture to the brush
	{
		IdeRenderTexture* textureref; // used so we can just create 1 and then copy it a lot
		IdeRenderTexture* texture;
		IdeResourceBase* rsc;
		IdeBitmapProxy* bmp;
		char texname[256];

		// set up our texture blending method - just doing "color * diffuse"
		textureref = IdeRenderState_CreateRenderTexture();
		textureref->Lock();
		textureref->SetColorArg(0, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		textureref->SetColorArg(0, 2, IdeRenderTexture::TEXTUREARG_DIFFUSE);
		textureref->SetColorOperation(0, IdeRenderTexture::TEXTUREOP_MODULATE);
		textureref->SetAlphaArg(0, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		textureref->SetAlphaArg(0, 2, IdeRenderTexture::TEXTUREARG_DIFFUSE);
		textureref->SetAlphaOperation(0, IdeRenderTexture::TEXTUREOP_MODULATE);
		
		textureref->SetColorArg(1, 1, IdeRenderTexture::TEXTUREARG_PREVIOUS);
		textureref->SetColorArg(1, 2, IdeRenderTexture::TEXTUREARG_TEXTURE);
		textureref->SetColorOperation(1, IdeRenderTexture::TEXTUREOP_SELECTARG1); // change to MODULATE if lightmaps work
		textureref->SetAlphaOperation(1, IdeRenderTexture::TEXTUREOP_DISABLE);

		textureref->SetColorOperation(2, IdeRenderTexture::TEXTUREOP_DISABLE);
		textureref->SetAlphaOperation(2, IdeRenderTexture::TEXTUREOP_DISABLE);
		textureref->Unlock();

		// tell the brush how many textures we need
		pBrush->SetNumTextures(NumTextures);
		// now go through and set all those textures properly
		for (i = 0; i < NumTextures; ++i)
		{
			// first try loading the .bmp file - notice, WE DO NOT LOAD JPEG's, so convert the textures to .bmp
			strcpy(texname, q3data.textures[i].filename);
			strcat(texname, ".bmp");
			rsc = IdeResourceManager_GetManager()->GetResource(texname, NULL);
			if (!rsc)
			{
				// if the .bmp wasn't found, try looking for a .tga
				strcpy(texname, q3data.textures[i].filename);
				strcat(texname, ".tga");
				rsc = IdeResourceManager_GetManager()->GetResource(texname, NULL);
			}
			bmp = IdeBitmap_CreateProxy(rsc);
			texture = IdeRenderState_CreateRenderTexture();
			texture->Lock();
			textureref->CopyStageSettings(texture, 0, 0);
			textureref->CopyStageSettings(texture, 1, 1);
			textureref->CopyStageSettings(texture, 2, 2);
			texture->SetBitmap(0, bmp);
//			texture->SetBitmap(1, lightmap); // no lightmaps here, sorry
			texture->Unlock();
			pBrush->SetTexture(i, texture);
			texture->Release();
		}
		textureref->Release();
	}

#if REFINE_PATCHES
	BezierLine CSplines[3];					// control splines
	BezierLine PatchSplines[PATCH_DETAIL];	// interpolation splines
	{
#if (PATCH_DETAIL <= 1 || PATCH_DETAIL > 30)
		DontUseThePatchDetailYouHave();		// hehe, should generate an error if you use crazy values
#endif
		// just set up some default values and degrees for the splines - all quadratic
		int x, y, temp;
		deVec3d tempvec = deZeroVec;
		for (x = 0; x < 3; ++x)
		{
			CSplines[x].AddCV(tempvec);
			CSplines[x].AddCV(tempvec);
			CSplines[x].AddCV(tempvec);
		}
		for (x = 0; x < PATCH_DETAIL; ++x)
		{
			PatchSplines[x].AddCV(tempvec);
			PatchSplines[x].AddCV(tempvec);
			PatchSplines[x].AddCV(tempvec);
		}
		for (i = 0; i < NumFaces; ++i)
		{
			if (q3data.faces[i].type == 2)
			{
				// this computes the number of vertices needed for the selected detail level
				// vertices are not shared between patches although they potentially could be
				x = (q3data.faces[i].patch_dim[0]-1)/2;
				y = (q3data.faces[i].patch_dim[1]-1)/2;
				x *= PATCH_DETAIL;
				y *= PATCH_DETAIL;
				temp = x*y;
				PatchVerts += temp;
			}
		}
	}
#endif

	// create our master vertex and index buffers
	{
		VertexBuffer = IdeVertexBuffer_CreateVBuffer();
		IndexBuffer = IdeVertexBuffer_CreateVBuffer();
		
		if (!VertexBuffer || !IndexBuffer)
		{
			if (VertexBuffer)
				VertexBuffer->Release();
			if (IndexBuffer)
				IndexBuffer->Release();
			DEBUGRETURN(deFALSE, "LoadFromQ3BSP: Error creating master vertex buffer");
		}

		// just create our bogstandard data streams
		VertexBuffer->Lock();
		VertexBuffer->CreateBuffer(IdeVertexBuffer::BUFFER_POSITION, deFALSE);
		VertexBuffer->CreateBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0, deFALSE);
		VertexBuffer->CreateBuffer(IdeVertexBuffer::BUFFER_DIFFUSE, deFALSE);
#if USE_ALL_STREAMS
		VertexBuffer->CreateBuffer(IdeVertexBuffer::BUFFER_NORMAL, deFALSE);
		VertexBuffer->CreateBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS1, deFALSE);
#endif
		// this creates enough vertices to hold all the ones from the file plus the patch verts we will compute
		VertexBuffer->SetBufferSize(NumVerts + PatchVerts);
		VertexBuffer->Unlock();
		
		// since we're using actual separate buffers (which isn't necessary, it's just a test of the feature)
		// we have to bind the "data stream buffer" to the "index buffer"
		// this can be useful when you have multiple index buffers that treat the same vertex data differently
		IndexBuffer->Lock();
		IndexBuffer->BindDataBuffer(VertexBuffer);
		VertexBuffer->Release();
		// check if we need 32-bit indices (which may not work on cards like a GeForce2)
		Needs32Bit = (NumVerts + PatchVerts > 65535);
		if (Needs32Bit)
			IndexBuffer->CreateBuffer(IdeVertexBuffer::BUFFER_INDICES32, deFALSE);
		else
			IndexBuffer->CreateBuffer(IdeVertexBuffer::BUFFER_INDICES16, deFALSE);
		IndexBuffer->Unlock();
	}

	// put all vertices into the vertex buffer
	{
		q3_vertex*	Vertices;
		deVertex*	Positions;
		deVertex*	Normals;
		deTexCoord*	Textures0;
		deTexCoord*	Textures1;
		deARGB*		Diffuses;
		deVertex	TempVert;
		int len;
		len = (q3data.header.direntries[q3lump_Vertices].length)/sizeof(q3_vertex);
		Vertices = q3data.vertices;

		VertexBuffer->Lock();
		Positions = (deVertex*)  VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
		Textures0 = (deTexCoord*)VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);
		Diffuses =  (deARGB*)    VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
#if USE_ALL_STREAMS
		Normals =   (deVertex*)  VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_NORMAL);
		Textures1 = (deTexCoord*)VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS1);
#else
		Normals = NULL;
		Textures1 = NULL;
#endif
		// loop through all the data contained in the file and copy it into our native streams
		for (i = 0; i < len; ++i)
		{
			TempVert = *((deVertex*)Vertices[i].position);
			scaletform.Transform(TempVert, Positions[i]);
#if USE_ALL_STREAMS
			TempVert = *((deVertex*)Vertices[i].normal);
			normaltform.Transform(TempVert, Normals[i]);
			Textures1[i] = *((deTexCoord*)Vertices[i].texcoord[1]);
#endif
			Textures0[i] = *((deTexCoord*)Vertices[i].texcoord[0]);
			Diffuses[i] =	(Vertices[i].color & 0xff00ff00) | ((Vertices[i].color >> 16) & 0x000000ff) | ((Vertices[i].color << 16) & 0x00ff0000);
			Diffuses[i] |= 0xff808080; // this should brighten up those dull colors, although it can look kinda funky
		}
		VertexBuffer->Unlock();
	}

	// time to make some triangles!
	{
		IdeVertexBuffer* SubIndexBuffer;
		// count how many indices are needed for each texture
		int* NumNeeded = new int[NumTextures];
		for (i = 0; i < NumTextures; ++i)
		{
			// initialize to 0
			NumNeeded[i] = 0;
		}
		// for each face, add the number of indices needed to the NumNeeded entry corresponding to the face's texture
		for (i = 0; i < NumFaces; ++i)
		{
			// make sure we have a valid texture
			DE_ASSERT(q3data.faces[i].texture >= 0 && q3data.faces[i].texture < NumTextures);
			if (q3data.faces[i].type == 1) // triangle fan
			{
				// making lists from fans, for optimal rendering speed through batching
#if USE_MESHVERTS
				NumNeeded[q3data.faces[i].texture] += (q3data.faces[i].n_meshverts);
#else
				NumNeeded[q3data.faces[i].texture] += (q3data.faces[i].n_vertexes-2)*3;
#endif // USE_MESHVERTS
			}
#if USE_PATCHES
			else if (q3data.faces[i].type == 2) // bezier patch
			{
				int x,y, temp;
				x = (q3data.faces[i].patch_dim[0]-1)/2; // number of patches in X direction
				y = (q3data.faces[i].patch_dim[1]-1)/2; // number of patches in Y direction
#if REFINE_PATCHES
				// compute the detailed version
				temp = 3*2*x*(PATCH_DETAIL-1)*y*(PATCH_DETAIL-1);
#else
				// just create a hull to fill in the gaps
				temp = 3*8*x*y;
#endif
				NumNeeded[q3data.faces[i].texture] += temp;
			}
#endif // USE_PATCHES
		}
		for (i = 0; i < NumTextures; ++i)
		{
			// for each texture, make a sub-VB from the index buffer
			// these Sub-VB's will be used to batch all the same-texture triangles together to render with 1 API call
			SubIndexBuffer = IdeVertexBuffer_CreateSubVBuffer(IndexBuffer);
			pBrush->SetVertexBuffer(i, SubIndexBuffer);
			SubIndexBuffer->Lock();
			SubIndexBuffer->SetRenderType(IdeVertexBuffer::RENDER_TRIANGLELIST);
			SubIndexBuffer->SetBufferSize(NumNeeded[i], deTRUE);
			SubIndexBuffer->Unlock();
			SubIndexBuffer->Release();
		}
		// we have lots of claims on it now from all the child buffers
		IndexBuffer->Release();

		// for each face with this texture, put its indices into the sub-IB
		int texindex, k;
		int* NumFilled = new int[NumTextures];
		for (i = 0; i < NumTextures; ++i)
		{
			// initialize to 0
			NumFilled[i] = 0;
		}
		for (i = 0; i < NumFaces; ++i)
		{
			// if it's not a triangle fan or bezier patch, just carry on
			if (q3data.faces[i].type > 2)
				continue;
			texindex = q3data.faces[i].texture;
			IndexBuffer = pBrush->GetVertexBuffer(texindex);
			IndexBuffer->Lock();
			if (q3data.faces[i].type == 1) // triangle fan, but we convert to a list
			{
				if (Needs32Bit)
				{
					u32* Indices = (u32*)IndexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_INDICES32);
#if !USE_MESHVERTS
					// just treat the vertices as a triangle fan - can look totally mangled
					for (k = 0; k < q3data.faces[i].n_vertexes-2; ++k)
					{
						Indices[NumFilled[texindex] + k*3  ] = (u32)(q3data.faces[i].vertex);
						Indices[NumFilled[texindex] + k*3+1] = (u32)(q3data.faces[i].vertex + k+1);
						Indices[NumFilled[texindex] + k*3+2] = (u32)(q3data.faces[i].vertex + k+2);
						
						// make sure we have valid indices
						DE_ASSERT(Indices[NumFilled[texindex] + k*3  ] < NumVerts && Indices[NumFilled[texindex] + k*3  ] < 65536);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3+1] < NumVerts && Indices[NumFilled[texindex] + k*3+1] < 65536);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3+2] < NumVerts && Indices[NumFilled[texindex] + k*3+2] < 65536);
						
						// don't need any degenerates
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+1]);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+2]);
					}
#else
					// this should work on all maps, especially ones where the fan method doesn't
					u32 baseindex, index;
					baseindex = q3data.faces[i].vertex;
					for (k = 0; k < q3data.faces[i].n_meshverts/3; ++k)
					{
						// compute the indices from the 'meshvert' information
						index = (u32)(baseindex + q3data.meshverts[q3data.faces[i].meshvert + k*3].offset);
						DE_ASSERT(index < (u32)NumVerts && index < 16777216);
						Indices[NumFilled[texindex] + k*3] = index;
						index = (u32)(baseindex + q3data.meshverts[q3data.faces[i].meshvert + k*3+1].offset);
						DE_ASSERT(index < (u32)NumVerts && index < 16777216);
						Indices[NumFilled[texindex] + k*3+1] = index;
						index = (u32)(baseindex + q3data.meshverts[q3data.faces[i].meshvert + k*3+2].offset);
						DE_ASSERT(index < (u32)NumVerts && index < 16777216);
						Indices[NumFilled[texindex] + k*3+2] = index;
						// don't need any degenerates
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+1]);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+2]);
					}
#endif // USE_MESHVERTS
				}
				else
				{
					// for some reason, the 2 variants of this loop are life and death different
					// the first is the gametutorials.com method, and it looks all garbled
					// the second is the method of some stanford student, and it causes a BSOD
					u16* Indices = (u16*)IndexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_INDICES16);
#if !USE_MESHVERTS
					// just treat the vertices as a triangle fan - can look totally mangled
					for (k = 0; k < q3data.faces[i].n_vertexes-2; ++k)
					{
						Indices[NumFilled[texindex] + k*3  ] = (u16)(q3data.faces[i].vertex);
						Indices[NumFilled[texindex] + k*3+1] = (u16)(q3data.faces[i].vertex + k+2);
						Indices[NumFilled[texindex] + k*3+2] = (u16)(q3data.faces[i].vertex + k+1);
						
						// make sure we have valid indices
						DE_ASSERT(Indices[NumFilled[texindex] + k*3  ] < NumVerts && Indices[NumFilled[texindex] + k*3  ] < 65536);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3+1] < NumVerts && Indices[NumFilled[texindex] + k*3+1] < 65536);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3+2] < NumVerts && Indices[NumFilled[texindex] + k*3+2] < 65536);
						
						// don't need any degenerates
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+1]);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+2]);
					}
#else
					// this should work on all maps, especially ones where the fan method doesn't
					int baseindex, index;
					baseindex = q3data.faces[i].vertex;
					for (k = 0; k < q3data.faces[i].n_meshverts/3; ++k)
					{
						// compute the indices from the 'meshvert' information
						index = (baseindex + q3data.meshverts[q3data.faces[i].meshvert + k*3].offset);
						DE_ASSERT(index < NumVerts && index < 65536);
						Indices[NumFilled[texindex] + k*3] = (u16)index;
						index = (baseindex + q3data.meshverts[q3data.faces[i].meshvert + k*3+2].offset);
						DE_ASSERT(index < NumVerts && index < 65536);
						Indices[NumFilled[texindex] + k*3+1] = (u16)index;
						index = (baseindex + q3data.meshverts[q3data.faces[i].meshvert + k*3+1].offset);
						DE_ASSERT(index < NumVerts && index < 65536);
						Indices[NumFilled[texindex] + k*3+2] = (u16)index;
						// don't need any degenerates
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+1]);
						DE_ASSERT(Indices[NumFilled[texindex] + k*3] != Indices[NumFilled[texindex] + k*3+2]);
					}
#endif // USE_MESHVERTS
				}
#if USE_MESHVERTS
				NumFilled[texindex] += (q3data.faces[i].n_meshverts);
#else
				NumFilled[texindex] += (q3data.faces[i].n_vertexes-2)*3;
#endif
			}
#if USE_PATCHES
			else if (q3data.faces[i].type == 2) // bezier patch
			{
				int x,y,xsize,ysize, baseindex;
				// for control verts, each patch gets 3 verts and adjacent patches share verts
				// 3 = 1 patch, 5 = 2 patches, 7 = 3 patches, etc
				xsize = q3data.faces[i].patch_dim[0]; // number of control vertices in X
				ysize = q3data.faces[i].patch_dim[1]; // number of control vertices in Y
				int temp = 3*2*(xsize-1)*(ysize-1);   // number of vertices needed for this set of patches
#if REFINE_PATCHES
				{
					// need to write new information into the vertex buffer
					VertexBuffer->Lock();
					deVertex* positions = (deVertex*)VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
					deTexCoord* Textures0 = (deTexCoord*)VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);
					deARGB* colors = (deARGB*)VertexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
					deVec3d tempvec;
					deDouble xfraction, yfraction, xtemp, ytemp;
					int xpatch, ypatch;
					// loop through all the patches in this set
					for (ypatch = 0; ypatch < (ysize-1)/2; ++ypatch)
					{
						for (xpatch = 0; xpatch < (xsize-1)/2; ++xpatch)
						{
							// for each patch, we fill in the control spline data
							for (y = 2*ypatch; y < 2*ypatch+3; ++y)
							{
								baseindex = q3data.faces[i].vertex + y*xsize + 2*xpatch;
								tempvec = deMake_Vec3d(positions[baseindex]);
								CSplines[y-2*ypatch].SetCV(0, tempvec);
								tempvec = deMake_Vec3d(positions[baseindex+1]);
								CSplines[y-2*ypatch].SetCV(1, tempvec);
								tempvec = deMake_Vec3d(positions[baseindex+2]);
								CSplines[y-2*ypatch].SetCV(2, tempvec);
							}
							// base index we will be reading from (control vertex 0)
							baseindex = q3data.faces[i].vertex;
							// cycle through the X dimension of the patch
							for (k = 0; k < PATCH_DETAIL; ++k)
							{
								// interpolate the control splines to generate a spline perpendicular to them
								// this perpendicular interpolated spline makes the actual geometry
								xfraction = k * (1.0 / (PATCH_DETAIL-1));
								CSplines[0].GetPointOnLine(xfraction, tempvec);
								PatchSplines[k].SetCV(0, tempvec);
								CSplines[1].GetPointOnLine(xfraction, tempvec);
								PatchSplines[k].SetCV(1, tempvec);
								CSplines[2].GetPointOnLine(xfraction, tempvec);
								PatchSplines[k].SetCV(2, tempvec);

								// cycle through the interpolated spline
								for (y = 0; y < PATCH_DETAIL; ++y)
								{
									yfraction = y * (1.0 / (PATCH_DETAIL-1));
									// calculate the precise position for this vertex
									PatchSplines[k].GetPointOnLine(yfraction, tempvec);
									// figure out where to put the vertex in the vertex buffer
									temp = NumVerts + PatchVertsUsed + y*PATCH_DETAIL*(xsize-1)/2 + xpatch*PATCH_DETAIL + k;
									DE_ASSERT(temp < NumVerts + PatchVerts);
									// put it there
									positions[temp] = deMake_Vertex(tempvec);
									// just copy the diffuse color of the first control vertex
									colors[temp] = colors[baseindex];
									// now the fun stuff: computing texture coordinates!
									Textures0[temp].u = 0;
									Textures0[temp].v = 0;
									// each quadrant has to interpolate a different set of 4 control vertices
									if (xfraction < 0.5)
									{
										xtemp = xfraction*2;
										if (yfraction < 0.5)
										{
											ytemp = yfraction*2;
											Textures0[temp].u += (deFloat)((1.0-ytemp)*(xtemp*Textures0[baseindex+1].u + (1-xtemp)*Textures0[baseindex].u));
											Textures0[temp].u += (deFloat)((    ytemp)*(xtemp*Textures0[baseindex+xsize+1].u + (1-xtemp)*Textures0[baseindex+xsize].u));
											Textures0[temp].v += (deFloat)((1.0-xtemp)*(ytemp*Textures0[baseindex+xsize].v + (1-ytemp)*Textures0[baseindex].v));
											Textures0[temp].v += (deFloat)((    xtemp)*(ytemp*Textures0[baseindex+xsize+1].v + (1-ytemp)*Textures0[baseindex+1].v));
										}
										else
										{
											ytemp = (yfraction-0.5)*2;
											Textures0[temp].u += (deFloat)((1.0-ytemp)*(xtemp*Textures0[baseindex+xsize+1].u + (1-xtemp)*Textures0[baseindex+xsize].u));
											Textures0[temp].u += (deFloat)((    ytemp)*(xtemp*Textures0[baseindex+2*xsize+1].u + (1-xtemp)*Textures0[baseindex+2*xsize].u));
											Textures0[temp].v += (deFloat)((1.0-xtemp)*(ytemp*Textures0[baseindex+2*xsize].v + (1-ytemp)*Textures0[baseindex+xsize].v));
											Textures0[temp].v += (deFloat)((    xtemp)*(ytemp*Textures0[baseindex+2*xsize+1].v + (1-ytemp)*Textures0[baseindex+xsize+1].v));
										}
									}
									else
									{
										xtemp = (xfraction-0.5)*2;
										if (yfraction < 0.5)
										{
											ytemp = yfraction*2;
											Textures0[temp].u += (deFloat)((1.0-ytemp)*(xtemp*Textures0[baseindex+2].u + (1-xtemp)*Textures0[baseindex+1].u));
											Textures0[temp].u += (deFloat)((    ytemp)*(xtemp*Textures0[baseindex+xsize+2].u + (1-xtemp)*Textures0[baseindex+xsize+1].u));
											Textures0[temp].v += (deFloat)((1.0-xtemp)*(ytemp*Textures0[baseindex+xsize+1].v + (1-ytemp)*Textures0[baseindex+1].v));
											Textures0[temp].v += (deFloat)((    xtemp)*(ytemp*Textures0[baseindex+xsize+2].v + (1-ytemp)*Textures0[baseindex+2].v));
										}
										else
										{
											ytemp = (yfraction-0.5)*2;
											Textures0[temp].u += (deFloat)((1.0-ytemp)*(xtemp*Textures0[baseindex+xsize+2].u + (1-xtemp)*Textures0[baseindex+xsize+1].u));
											Textures0[temp].u += (deFloat)((    ytemp)*(xtemp*Textures0[baseindex+2*xsize+2].u + (1-xtemp)*Textures0[baseindex+2*xsize+1].u));
											Textures0[temp].v += (deFloat)((1.0-xtemp)*(ytemp*Textures0[baseindex+2*xsize+1].v + (1-ytemp)*Textures0[baseindex+xsize+1].v));
											Textures0[temp].v += (deFloat)((    xtemp)*(ytemp*Textures0[baseindex+2*xsize+2].v + (1-ytemp)*Textures0[baseindex+xsize+2].v));
										}
									}
								}
							}
						}
					}
					VertexBuffer->Unlock();
				}
				// baseindex is the starting index for the triangles
				baseindex = NumVerts + PatchVertsUsed;
				xsize = (q3data.faces[i].patch_dim[0]-1)/2;
				ysize = (q3data.faces[i].patch_dim[1]-1)/2;
				PatchVertsUsed += xsize*PATCH_DETAIL*ysize*PATCH_DETAIL;
				// temp is used to signify the number of indices stored
				temp = 3*2*xsize*(PATCH_DETAIL-1)*ysize*(PATCH_DETAIL-1);
				// number of vertices in patch
				xsize = xsize*PATCH_DETAIL;
				ysize = ysize*PATCH_DETAIL;
#else
				baseindex = q3data.faces[i].vertex;
#endif // REFINE_PATCHES
				if (Needs32Bit)
				{
					u32* Indices = (u32*)IndexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_INDICES32);
					k = 0;
					for (y = 0; y < ysize-1; ++y)
					{
						// skip the unshared vertices
						if (y % PATCH_DETAIL == PATCH_DETAIL-1 && y != ysize-2)
							++y;
						for (x = 0; x < xsize-1; ++x)
						{
							// skip the unshared vertices
							if (x % PATCH_DETAIL == PATCH_DETAIL-1 && x != xsize-2)
								++x;
							// triangle in top left
							Indices[NumFilled[texindex] + k*6  ] = (u32)(baseindex + (y  )*xsize + x  );
							Indices[NumFilled[texindex] + k*6+1] = (u32)(baseindex + (y  )*xsize + x+1);
							Indices[NumFilled[texindex] + k*6+2] = (u32)(baseindex + (y+1)*xsize + x  );
							
							// triangle in bottom right
							Indices[NumFilled[texindex] + k*6+3] = (u32)(baseindex + (y+1)*xsize + x  );
							Indices[NumFilled[texindex] + k*6+4] = (u32)(baseindex + (y  )*xsize + x+1);
							Indices[NumFilled[texindex] + k*6+5] = (u32)(baseindex + (y+1)*xsize + x+1);
							
							// make sure we have valid indices
							DE_ASSERT(Indices[NumFilled[texindex] + k*6  ] < (u32)(NumVerts+PatchVertsUsed));
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+1] < (u32)(NumVerts+PatchVertsUsed));
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+2] < (u32)(NumVerts+PatchVertsUsed));
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+3] < (u32)(NumVerts+PatchVertsUsed));
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+4] < (u32)(NumVerts+PatchVertsUsed));
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+5] < (u32)(NumVerts+PatchVertsUsed));
							
							// check for degenerate tri's
							DE_ASSERT(Indices[NumFilled[texindex] + k*6  ] != Indices[NumFilled[texindex] + k*6+1]);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6  ] != Indices[NumFilled[texindex] + k*6+2]);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+3] != Indices[NumFilled[texindex] + k*6+4]);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+3] != Indices[NumFilled[texindex] + k*6+5]);
							
							// make sure we haven't extended past the end of the index buffer
							DE_ASSERT(k*6+5 <= temp);
							
							++k;
						}
					}
				}
				else
				{
					// for some reason, the 2 variants of this loop are life and death different
					// the first is the gametutorials.com method, and it looks all garbled
					// the second is the method of some stanford student, and it causes a BSOD
					u16* Indices = (u16*)IndexBuffer->GetBuffer(IdeVertexBuffer::BUFFER_INDICES16);
					k = 0;
					for (y = 0; y < ysize-1; ++y)
					{
						// skip the unshared vertices
						if (y % PATCH_DETAIL == PATCH_DETAIL-1 && y != ysize-2)
							++y;
						for (x = 0; x < xsize-1; ++x)
						{
							// skip the unshared vertices
							if (x % PATCH_DETAIL == PATCH_DETAIL-1 && x != xsize-2)
								++x;
							// triangle in top left
							Indices[NumFilled[texindex] + k*6  ] = (u16)(baseindex + (y  )*xsize + x  );
							Indices[NumFilled[texindex] + k*6+1] = (u16)(baseindex + (y  )*xsize + x+1);
							Indices[NumFilled[texindex] + k*6+2] = (u16)(baseindex + (y+1)*xsize + x  );
							
							// triangle in bottom right
							Indices[NumFilled[texindex] + k*6+3] = (u16)(baseindex + (y+1)*xsize + x  );
							Indices[NumFilled[texindex] + k*6+4] = (u16)(baseindex + (y  )*xsize + x+1);
							Indices[NumFilled[texindex] + k*6+5] = (u16)(baseindex + (y+1)*xsize + x+1);
							
							// make sure we have valid indices
							DE_ASSERT(Indices[NumFilled[texindex] + k*6  ] < NumVerts+PatchVertsUsed);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+1] < NumVerts+PatchVertsUsed);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+2] < NumVerts+PatchVertsUsed);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+3] < NumVerts+PatchVertsUsed);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+4] < NumVerts+PatchVertsUsed);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+5] < NumVerts+PatchVertsUsed);
							
							// check for degenerate tri's
							DE_ASSERT(Indices[NumFilled[texindex] + k*6  ] != Indices[NumFilled[texindex] + k*6+1]);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6  ] != Indices[NumFilled[texindex] + k*6+2]);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+3] != Indices[NumFilled[texindex] + k*6+4]);
							DE_ASSERT(Indices[NumFilled[texindex] + k*6+3] != Indices[NumFilled[texindex] + k*6+5]);

							// make sure we haven't extended past the end of the index buffer
							DE_ASSERT(k*6+5 <= temp);
							
							++k;
						}
					}
				}
				NumFilled[texindex] += temp;
			}
#endif // USE_PATCHES
			IndexBuffer->Unlock();
		}
		int TotalFilled = 0;
		for (i = 0; i < NumTextures; ++i)
		{
			// check that this buffer is exactly the right size
			TotalFilled += NumFilled[i];
			DE_ASSERT(NumFilled[i] == NumNeeded[i]);
		}
		// check that we exactly filled up our index buffer
		DE_ASSERT(TotalFilled == IndexBuffer->GetParentBuffer()->GetIndexCount());
		// release the memory
		delete[] NumNeeded;
		delete[] NumFilled;
	}

	// release all the memory
	delete[] q3data.textures;
	delete[] q3data.lightmaps;
	delete[] q3data.vertices;
	delete[] q3data.faces;
	delete[] q3data.meshverts;

	// return true for success
	return deTRUE;
}
