#include "MLRHeaders.hpp"

#ifdef CalDraw
	GOSVertexPool *ToBeDrawnPrimitive::allVerticesToDraw;
#endif

extern DWORD gShowBucketColors;

MLRSorter::ClassData*
	MLRSorter::DefaultData = NULL;

int MLRSorter::lastUsedAlphaVertex, MLRSorter::lastUsedAlphaBumpVertex;
int MLRSorter::maxUsedAlphaVertex, MLRSorter::maxUsedAlphaBumpVertex;

Stuff::DynamicArrayOf<GOSVertex>
	*MLRSorter::alphaVertices;
Stuff::DynamicArrayOf<GOSVertex3UV>
	*MLRSorter::alphaBumpVertices;

Stuff::DynamicArrayOf<SortData> *MLRSorter::rawDrawData; // Max_Number_Primitives_Per_Frame
int MLRSorter::lastUsedRaw, MLRSorter::maxUsedRaw;
int MLRSorter::lastUsedInBucketNotDrawn[MLRState::PriorityCount];

#ifdef CalDraw
int MLRSorter::lastUsedDraw, MLRSorter::maxUsedDraw, MLRSorter::maxUsedInBucket;
Stuff::DynamicArrayOf<ToBeDrawnPrimitive> *MLRSorter::drawData; // Max_Number_Primitives_Per_Frame
Stuff::DynamicArrayOf<ToBeDrawnPrimitiveIndex>  //, Max_Number_Primitives_Per_Frame + Max_Number_ScreenQuads_Per_Frame
	*MLRSorter::priorityBucketsNotDrawn[MLRState::PriorityCount];
#endif

bool dontSeeMe = true;

SortData::DrawFunc SortData::Draw[LastMode]	= 
{
	&SortData::DrawTriList,
	&SortData::DrawTriIndexedList,
	&SortData::DrawPointCloud,
	&SortData::DrawQuads,
	&SortData::DrawLineCloud
};

SortData::LoadSortAlphaFunc SortData::LoadSortAlpha[LastMode]	= 
{
	&SortData::LoadAlphaFromTriList,
	&SortData::LoadAlphaFromTriIndexedList,
	&SortData::LoadAlphaFromPointCloud,
	&SortData::LoadAlphaFromQuads,
	&SortData::LoadAlphaFromLineCloud
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SortData::DrawTriList()
{
	MLR_RENDER("GOS Draw::DrawTriList");
	Start_Timer(GOS_Draw_Time);

#ifdef LAB_ONLY
	if(dontSeeMe == true)
#endif
	{
		if(texture_2_3==0)
		{
			gos_DrawTriangles( (GOSVertex *)vertices, numVertices);
		}
		else
		{
			STOP(("GOS doesnt suppert gos_DrawTriangles for gos_VERTEX_2UV yet."));
//			gos_DrawTriangles( (GOSVertex2UV *)vertices, numVertices);
		}
	}
	Stop_Timer(GOS_Draw_Time);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SortData::DrawTriIndexedList()
{
	MLR_RENDER("GOS Draw::DrawTriIndexedList");
	Start_Timer(GOS_Draw_Time);

#ifdef LAB_ONLY
	if(dontSeeMe == true)
#endif
	{
		if(texture_2_3==0)
		{
			gos_RenderIndexedArray( (GOSVertex *)vertices, numVertices, indices, numIndices);
		}
		else
		{
			if(state.GetBumpMapMode())
			{
				gos_RenderIndexedArray( (GOSVertex3UV *)vertices, numVertices, indices, numIndices);
			}
			else
			{
				gos_RenderIndexedArray( (GOSVertex2UV *)vertices, numVertices, indices, numIndices);
			}
		}
	}
	Stop_Timer(GOS_Draw_Time);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SortData::DrawPointCloud()
{
	MLR_RENDER("GOS Draw::DrawPointCloud");
	Verify(texture_2_3==0);
	Start_Timer(GOS_Draw_Time);

#ifdef LAB_ONLY
	if(dontSeeMe == true)
#endif
	{

		GOSVertex pArray[32*3];

		float size = (float)numIndices;
		
		if( size == 0 )
		{
			size = 2.4f;
		}
		else
		{
			size *= 2.4f;
		}

		int Triangle = 0, Vertex = 0;
	//
	// Warning! - These points need clipping!
	//
		for( int i=numVertices; i; i-- )
		{
			pArray[Triangle+0] = *((GOSVertex *)vertices + Vertex);
			pArray[Triangle+1] = *((GOSVertex *)vertices + Vertex);
			pArray[Triangle+2] = *((GOSVertex *)vertices + Vertex);

			pArray[Triangle+1].x += size;
			pArray[Triangle+2].y += size;

			Triangle +=3;
			Vertex++;

			if( Triangle==32*3 || i==1)
			{
				gos_DrawTriangles( pArray, Triangle );
				Triangle=0;
			}
		}
	}
	Stop_Timer(GOS_Draw_Time);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SortData::DrawQuads()
{
	MLR_RENDER("GOS Draw::DrawQuads");
	Start_Timer(GOS_Draw_Time);

#ifdef LAB_ONLY
	if(dontSeeMe == true)
#endif
	{
		if(texture_2_3==0)
		{
//			gos_DrawTriangles( (GOSVertex *)vertices, numVertices);
			gos_DrawQuads( (GOSVertex *)vertices, numVertices);
		}
		else
		{
			STOP(("GOS doesnt suppert gos_DrawQuads for gos_VERTEX_2UV yet."));
//			gos_DrawQuads( (GOSVertex2UV *)vertices, numVertices);
		}

	}
	Stop_Timer(GOS_Draw_Time);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SortData::DrawLineCloud()
{
	Verify(texture_2_3==0);
	MLR_RENDER("GOS Draw::DrawLineCloud");
	Start_Timer(GOS_Draw_Time);

	for(int i=0;i<numVertices;i++)
	{
		if(((GOSVertex *)vertices)[i].x > Environment.screenWidth-1)
		{
			((GOSVertex *)vertices)[i].x = static_cast<Scalar>(Environment.screenWidth-1);
		}
		if(((GOSVertex *)vertices)[i].y > Environment.screenHeight-1)
		{
			((GOSVertex *)vertices)[i].y = static_cast<Scalar>(Environment.screenHeight-1);
		}
	}

#ifdef LAB_ONLY
	if(dontSeeMe == true)
#endif
	{
		gos_DrawLines( (GOSVertex *)vertices, numVertices);
	}
	Stop_Timer(GOS_Draw_Time);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	SortData::LoadAlphaFromTriList(MLRSortByOrder* sorter, int alphaBaseIndex, Scalar offset)
{
	MLR_RENDER("Alpha Sorting::TriList");
	Start_Timer(Alpha_Sorting_Time);
	int i, index = 0, end = (int)(numVertices*0.333333333333333333333334);
	Verify(texture_2_3==0);

	if(state.GetBumpMapMode())
	{
		if(MLRSorter::lastUsedAlphaBumpVertex >= MLRSorter::maxUsedAlphaBumpVertex)
		{
			return 0;
		}
		int length = MLRSorter::alphaBumpVertices->GetLength();

		for(i=0;i<end;i++)
		{
			SortAlpha* alpha = sorter->GetSortAlpha(alphaBaseIndex + i);
			alpha->render_state = state.GetRenderStateFlags();
			alpha->tex_2_3 = texture_2_3;

			GOSVertex3UV *dvertices = &(*MLRSorter::alphaBumpVertices)[MLRSorter::lastUsedAlphaBumpVertex];
			dvertices[0] = ((GOSVertex3UV *)vertices)[index++];
			alpha->distance = dvertices[0].z;
			dvertices[1] = ((GOSVertex3UV *)vertices)[index++];
			alpha->distance += dvertices[1].z;
			dvertices[2] = ((GOSVertex3UV *)vertices)[index++];
			alpha->distance += (dvertices[2].z - offset);

			alpha->triangle = MLRSorter::lastUsedAlphaBumpVertex; // dvertices;
			
			MLRSorter::lastUsedAlphaBumpVertex += 3;

			if(MLRSorter::lastUsedAlphaBumpVertex >= MLRSorter::maxUsedAlphaBumpVertex)
			{
				return i;
			}

			if (MLRSorter::lastUsedAlphaBumpVertex >= length && length < MLRSorter::maxUsedAlphaBumpVertex)
			{
				gos_PushCurrentHeap(StaticHeap);
				length *= 2;
				MLRSorter::alphaBumpVertices->SetLength(length);
				gos_PopCurrentHeap();
			}
		}
	}
	else
	{
		if (MLRSorter::lastUsedAlphaVertex >= MLRSorter::maxUsedAlphaVertex)
		{
			return 0;
		}
		int length = MLRSorter::alphaVertices->GetLength();

		for(i=0;i<end;i++)
		{
			SortAlpha* alpha = sorter->GetSortAlpha(alphaBaseIndex + i);
			alpha->render_state = state.GetRenderStateFlags();
			alpha->tex_2_3 = texture_2_3;

			GOSVertex *dvertices = &(*MLRSorter::alphaVertices)[MLRSorter::lastUsedAlphaVertex];
			dvertices[0] = ((GOSVertex *)vertices)[index++];
			alpha->distance = dvertices[0].z;
			dvertices[1] = ((GOSVertex *)vertices)[index++];
			alpha->distance += dvertices[1].z;
			dvertices[2] = ((GOSVertex *)vertices)[index++];
			alpha->distance += (dvertices[2].z - offset);

			alpha->triangle = MLRSorter::lastUsedAlphaVertex; // dvertices;
			MLRSorter::lastUsedAlphaVertex += 3;

			if(MLRSorter::lastUsedAlphaVertex >= MLRSorter::maxUsedAlphaVertex)
			{
				return i;
			}

			if (MLRSorter::lastUsedAlphaVertex >= length && length < MLRSorter::maxUsedAlphaVertex)
			{
				gos_PushCurrentHeap(StaticHeap);
				length *= 2;
				MLRSorter::alphaVertices->SetLength(length);
				gos_PopCurrentHeap();
			}
		}
	}

	Stop_Timer(Alpha_Sorting_Time);

	return i;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	SortData::LoadAlphaFromTriIndexedList(MLRSortByOrder* sorter, int alphaBaseIndex, Scalar offset)
{
	MLR_RENDER("Alpha Sorting::TriIndexedList");
	Start_Timer(Alpha_Sorting_Time);
	int i, index = 0, end = (int)(numIndices*0.333333333333333333333334);
//	Verify(texture_2_3==0);

	if(state.GetBumpMapMode())
	{
		if(MLRSorter::lastUsedAlphaBumpVertex >= MLRSorter::maxUsedAlphaBumpVertex)
		{
			return 0;
		}
		int length = MLRSorter::alphaBumpVertices->GetLength();

		for(i=0;i<end;i++)
		{
			SortAlpha* alpha = sorter->GetSortAlpha(alphaBaseIndex + i);
			alpha->render_state = state.GetRenderStateFlags();
			alpha->tex_2_3 = texture_2_3;

			GOSVertex3UV *dvertices = &(*MLRSorter::alphaBumpVertices)[MLRSorter::lastUsedAlphaBumpVertex];

			dvertices[0] = ((GOSVertex3UV *)vertices)[indices[index++]];
			alpha->distance = dvertices[0].z;
			
			dvertices[1] = ((GOSVertex3UV *)vertices)[indices[index++]];
			alpha->distance += dvertices[1].z;
			
			dvertices[2] = ((GOSVertex3UV *)vertices)[indices[index++]];
			alpha->distance += (dvertices[2].z - offset);

			alpha->triangle = MLRSorter::lastUsedAlphaBumpVertex; //dvertices;

			MLRSorter::lastUsedAlphaBumpVertex += 3;
			if(MLRSorter::lastUsedAlphaBumpVertex >= MLRSorter::maxUsedAlphaBumpVertex)
			{
				return i;
			}

			if (MLRSorter::lastUsedAlphaBumpVertex >= length && length < MLRSorter::maxUsedAlphaBumpVertex)
			{
				gos_PushCurrentHeap(StaticHeap);
				length *= 2;
				MLRSorter::alphaBumpVertices->SetLength(length);
				gos_PopCurrentHeap();
			}
		}
	}
	else
	{
		if (MLRSorter::lastUsedAlphaVertex >= MLRSorter::maxUsedAlphaVertex)
		{
			return 0;
		}
		int length = MLRSorter::alphaVertices->GetLength();

		for(i=0;i<end;i++)
		{
			SortAlpha* alpha = sorter->GetSortAlpha(alphaBaseIndex + i);
			alpha->render_state = state.GetRenderStateFlags();
			alpha->tex_2_3 = texture_2_3;

			GOSVertex *dvertices = &(*MLRSorter::alphaVertices)[MLRSorter::lastUsedAlphaVertex];

			dvertices[0] = ((GOSVertex *)vertices)[indices[index++]];
			alpha->distance = dvertices[0].z;

			dvertices[1] = ((GOSVertex *)vertices)[indices[index++]];
			alpha->distance += dvertices[1].z;

			dvertices[2] = ((GOSVertex *)vertices)[indices[index++]];
			alpha->distance += (dvertices[2].z - offset);

			alpha->triangle = MLRSorter::lastUsedAlphaVertex; //dvertices;

			MLRSorter::lastUsedAlphaVertex += 3;
			if(MLRSorter::lastUsedAlphaVertex >= MLRSorter::maxUsedAlphaVertex)
			{
				return i;
			}

			if (MLRSorter::lastUsedAlphaVertex >= length && length < MLRSorter::maxUsedAlphaVertex)
			{
				gos_PushCurrentHeap(StaticHeap);
				length *= 2;
				MLRSorter::alphaVertices->SetLength(length);
				gos_PopCurrentHeap();
			}
		}
	}
	Stop_Timer(Alpha_Sorting_Time);

	return i;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	SortData::LoadAlphaFromPointCloud(MLRSortByOrder*, int, Scalar)
{
	Start_Timer(Alpha_Sorting_Time);
	STOP(("Not implemented"));
	Stop_Timer(Alpha_Sorting_Time);

	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	SortData::LoadAlphaFromQuads(MLRSortByOrder*, int, Scalar)
{
	Start_Timer(Alpha_Sorting_Time);
	STOP(("Not implemented"));
	Stop_Timer(Alpha_Sorting_Time);

	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	SortData::LoadAlphaFromLineCloud(MLRSortByOrder*, int, Scalar)
{
	Start_Timer(Alpha_Sorting_Time);
	STOP(("Not implemented"));
	Stop_Timer(Alpha_Sorting_Time);

	return 0;
}


#ifdef CalDraw
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ToBeDrawnPrimitive::ToBeDrawnPrimitive()
{
	primitive = NULL;

	cameraPosition = Stuff::LinearMatrix4D::Identity;

	UnitVector3D v;
	Stuff::LinearMatrix4D::Identity.GetLocalForwardInWorld(&v);
	cameraDirection.Normalize(v);

	shapeToClipMatrix = Stuff::LinearMatrix4D::Identity;

	for(int i=0;i<Limits::Max_Number_Of_Lights_Per_Primitive;i++)
	{
		activeLights[i] = NULL;
	}
	nrOfActiveLights = 0;
}
#endif

//#############################################################################
//############################    MLRSorter    ################################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRSorter::InitializeClass()
{
	Verify(!DefaultData);
	Verify(gos_GetCurrentHeap() == StaticHeap);
	DefaultData =
		new ClassData(
			MLRSorterClassID,
			"MidLevelRenderer::MLRSorter",
			RegisteredClass::DefaultData
		);

	
	maxUsedAlphaVertex = 6*Limits::Max_Number_Vertices_Per_Frame;
	alphaVertices = new Stuff::DynamicArrayOf<GOSVertex> (3);

	maxUsedAlphaBumpVertex = 6*Limits::Max_Number_Vertices_Per_Frame;
	alphaBumpVertices = new Stuff::DynamicArrayOf<GOSVertex3UV> (3);

	maxUsedRaw = Limits::Max_Number_Primitives_Per_Frame;
	rawDrawData = new Stuff::DynamicArrayOf<SortData> (1); // Limits::Max_Number_Primitives_Per_Frame);

#ifdef CalDraw
	for(int i=0;i<MLRState::PriorityCount;i++)
	{
		lastUsedInBucketNotDrawn[i] = 0;
		priorityBucketsNotDrawn[i] = new Stuff::DynamicArrayOf<ToBeDrawnPrimitiveIndex> (1); // Limits::Max_Number_Primitives_Per_Frame + Limits::Max_Number_ScreenQuads_Per_Frame);
	}
	maxUsedInBucket = Limits::Max_Number_Primitives_Per_Frame + Limits::Max_Number_ScreenQuads_Per_Frame;
	maxUsedDraw = Limits::Max_Number_Primitives_Per_Frame;
	drawData = new Stuff::DynamicArrayOf<ToBeDrawnPrimitive> (1); // Limits::Max_Number_Primitives_Per_Frame
#endif


}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRSorter::TerminateClass()
{
#ifdef CalDraw
	drawData->SetLength(0);
	delete drawData;

	for(int i=0;i<MLRState::PriorityCount;i++)
	{
		priorityBucketsNotDrawn[i]->SetLength(0);
		delete priorityBucketsNotDrawn[i];
	}
#endif

	rawDrawData->SetLength(0);
	delete rawDrawData;

	alphaVertices->SetLength(0);
	delete alphaVertices;

	alphaBumpVertices->SetLength(0);
	delete alphaBumpVertices;

	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRSorter::MLRSorter(ClassData *class_data, MLRTexturePool *tp):
	RegisteredClass(class_data)
{
	Verify(gos_GetCurrentHeap() == SorterHeap);
	texturePool = tp;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRSorter::~MLRSorter()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRSorter::StartDraw(const MLRState &default_state)
{
	theCurrentState = default_state.renderState;

	MLRState state;
	state = default_state;
	state.SetRendererState(texturePool);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRSorter::Reset ()
{
	lastUsedRaw = 0;

#ifdef CalDraw
	for(int i=0;i<MLRState::PriorityCount;i++)
	{
		lastUsedInBucketNotDrawn[i] = 0;
	}

	lastUsedDraw = 0;
#endif
	lastUsedAlphaVertex = 0;
	lastUsedAlphaBumpVertex = 0;

	SetCurrentState();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRSorter::DrawPrimitive(MLRPrimitiveBase *pt, int pass)
{
	Check_Object(this); 
	Check_Object(pt);

	SortData *sd = NULL;

	switch(pt->GetSortDataMode())
	{
		case SortData::TriList:
		case SortData::TriIndexedList:
		{
			int index = SetRawData(pt, pass);
			if(index<0)
			{
				break;
			}
			sd = &(*rawDrawData)[index];

#ifdef LAB_ONLY
			if(gShowBucketColors>1)
			{
				sd->state.SetTextureHandle(0);
			}
#endif
			if(theCurrentState != sd->state.renderState)
			{
				SetDifferences(sd->state, sd->texture_2_3 & 0xffff, sd->texture_2_3>>16);
			}

			if(sd->texture_2_3>0)
			{
				if(sd->state.GetBumpMapMode())
				{
				}
				else
				{
					gos_SetRenderState( 
						gos_State_Texture2, 
						(*texturePool)[sd->texture_2_3 & 0xffff]->GetImage(NULL)->imageHandle
					);
					switch(sd->state.GetMultiTextureMode())
					{
						case MLRState::MultiTextureLightmapMode:
							gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_LightMap );
						break;
						case MLRState::MultiTextureSpecularMode:
							gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_SpecularMap );
						break;
						case MLRState::MultiTextureDetailMode:
							gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_DetailTexture );
						break;
					}
				}
			}
			else
			{
			}

			SortData::DrawFunc drawFunc = sd->Draw[sd->type];

			(sd->*drawFunc)();

			if(sd->texture_2_3>0)
			{
				gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_None );
			}
		}
		break;
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MLRSorter::SetRawData
		(
			void *vertices, 
			int numVertices, 
			const MLRStateBase& state,
			const int& mode,
			int tex_2_3
		)
{
	Check_Object(this);

	Verify (vertices != NULL);
	Verify (numVertices > 0);

	if (lastUsedRaw >= maxUsedRaw)
	{
		return -1;
//		STOP(("Maximum number of raw primitives exceeded"));
	}

	int length = rawDrawData->GetLength();
	if (lastUsedRaw >= length)
	{
		gos_PushCurrentHeap(StaticHeap);
		length *= 2;
		rawDrawData->SetLength(length);
		gos_PopCurrentHeap();
	}

	SortData *sd = rawDrawData->GetData() + lastUsedRaw;

	sd->vertices = vertices;
	sd->indices = 0;

	sd->state = state;

	sd->numVertices = numVertices;
	sd->numIndices = 0;

	sd->type = mode;
	sd->texture_2_3 = tex_2_3;

	lastUsedRaw++;

	return lastUsedRaw-1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MLRSorter::SetRawIndexedData
		(
			void *vertices, 
			int numVertices, 
			WORD *indices, 
			int numIndices, 
			const MLRStateBase& state,
			const int& mode,
			int tex_2_3
		)
{
	Check_Object(this);

	Verify (vertices != NULL);
	Verify (numVertices > 0);

	if (lastUsedRaw >= maxUsedRaw)
	{
		return -1;
//		STOP(("Maximum number of raw primitives exceeded"));
	}

	int length = rawDrawData->GetLength();
	if (lastUsedRaw >= length)
	{
		gos_PushCurrentHeap(StaticHeap);
		length *= 2;
		rawDrawData->SetLength(length);
		gos_PopCurrentHeap();
	}

	SortData *sd = rawDrawData->GetData() + lastUsedRaw;

	sd->vertices = vertices;
	sd->indices = indices;

	sd->state = state;

	sd->numVertices = numVertices;
	sd->numIndices = numIndices;

	sd->type = mode;
	sd->texture_2_3 = tex_2_3;
		
	lastUsedRaw++;

	return lastUsedRaw-1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MLRSorter::SetRawData ( MLRPrimitiveBase *pt, int pass)
{
	Check_Object(this);
	Check_Object(pt);
	
	int drawMode = pt->GetSortDataMode();
	
	switch(drawMode)
	{
		case SortData::TriIndexedList:
		{
			MLRIndexedPrimitiveBase *ipt = static_cast<MLRIndexedPrimitiveBase *>(pt);

			int tex_2_3 = 0;
			void *vertices = ipt->GetGOSVertices(pass);
			int vertexCount = ipt->GetNumGOSVertices();

			if(pt->GetCurrentState(pass).GetMultiTextureMode()!=MLRState::MultiTextureOffMode && MLRState::GetMultitextureLightMap())
			{
				Verify(pass==0);
				tex_2_3 = pt->GetCurrentState(1).GetTextureHandle();
				vertices = pt->GetGOSVertices2UV();
			}

			if(pt->GetCurrentState(pass).GetBumpMapMode())
			{
				Verify(pass==0);
				tex_2_3 = pt->GetTexture2() | (pt->GetTexture3()<<16);
				vertices = pt->GetGOSVertices3UV();
			}

			return SetRawIndexedData (
				vertices,
				vertexCount, 
				ipt->GetGOSIndices(pass),
				ipt->GetNumGOSIndices(), 
				ipt->GetCurrentState(pass),
				drawMode,
				tex_2_3
			);
		}
		case SortData::TriList:
			return SetRawData (
				pt->GetGOSVertices(pass),
				pt->GetNumGOSVertices(), 
				pt->GetCurrentState(pass),
				drawMode
			);
	}

	return -1;
}

#ifdef CalDraw
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRSorter::IncreaseTBDPCounter()
{
	Check_Object(this);
	
	if (lastUsedDraw < maxUsedDraw)
	{
		int priority = (*drawData)[lastUsedDraw].state.GetPriority();

		(*priorityBucketsNotDrawn[priority])[lastUsedInBucketNotDrawn[priority]].tbdpIndex = lastUsedDraw;
		(*priorityBucketsNotDrawn[priority])[lastUsedInBucketNotDrawn[priority]].handle = (*drawData)[lastUsedDraw].state.GetTextureHandle();

		lastUsedInBucketNotDrawn[priority]++;

		if (lastUsedInBucketNotDrawn[priority] >= maxUsedInBucket)
		{
			STOP(("Maximum number of bucket primitives exceeded"));
		}

		int length = priorityBucketsNotDrawn[priority]->GetLength();
		if (lastUsedInBucketNotDrawn[priority] >= length)
		{
			gos_PushCurrentHeap(StaticHeap);
			length *= 2;
			priorityBucketsNotDrawn[priority]->SetLength(length);
			gos_PopCurrentHeap();
		}

		lastUsedDraw++;

		length = drawData->GetLength();

		if (lastUsedDraw >= length && lastUsedDraw < maxUsedDraw)
		{
			gos_PushCurrentHeap(StaticHeap);
			length *= 2;
			if(length > maxUsedDraw)
			{
				length = maxUsedDraw;
			}
			drawData->SetLength(length);
			gos_PopCurrentHeap();
		}
	}
	#if defined(LAB_ONLY)
		else
		{
			PAUSE(("Maximum number of draw primitives exceeded"));
		}
	#endif
}
#endif


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MLRSorter::SetDifferences(const DWORD original_renderState, const DWORD newer_renderState, DWORD tex2, DWORD tex3)
{
	int changed = (original_renderState ^ newer_renderState);

	if( (changed & MLRState::BumpMapMask) && ((newer_renderState & MLRState::BumpMapMask)==0))
	{
		gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_None );
		gos_SetRenderState( gos_State_Texture2, 0 );
		gos_SetRenderState( gos_State_Texture3, 0 );

		MLRTexture *tex_p = NULL;
		if( (newer_renderState & MLRState::TextureMask) && (NULL!=(tex_p=(*texturePool)[newer_renderState & MLRState::TextureMask])) )
			gos_SetRenderState( 
				gos_State_Texture, 
				tex_p->GetImage(NULL)->imageHandle
			);
		else
			gos_SetRenderState( gos_State_Texture, 0 );

		switch( newer_renderState&MLRState::AlphaMask )
		{
			case MLRState::OneZeroMode:
				gos_SetRenderState(gos_State_AlphaTest, 0);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneZero);
				break;

			case MLRState::KeyedAlphaMode:
				gos_SetRenderState(gos_State_AlphaTest, 1);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneZero);
				break;

			case MLRState::OneOneMode:
				gos_SetRenderState(gos_State_AlphaTest, 0);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneOne);
				break;

			case MLRState::AlphaOneMode:
				gos_SetRenderState(gos_State_AlphaTest, 1);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_AlphaOne);
				break;

			case MLRState::OneAlphaMode:
				SPEW((0, "MLRState::OneAlphaMode not available anymore"));
				break;

			case MLRState::AlphaInvAlphaMode:
				gos_SetRenderState(gos_State_AlphaTest, 1);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha);
				break;

			case MLRState::OneInvAlphaMode:
				gos_SetRenderState(gos_State_AlphaTest, 0);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneInvAlpha);
				break;

			default:
				STOP(("Unknown alpha state"));
		}

		switch( newer_renderState&MLRState::FilterMask )
		{
			case MLRState::NoFilterMode:
				gos_SetRenderState(gos_State_Filter, gos_FilterNone);
				break;

			case MLRState::BiLinearFilterMode:
				gos_SetRenderState(gos_State_Filter, gos_FilterBiLinear);
				break;

			case MLRState::TriLinearFilterMode:
				gos_SetRenderState(gos_State_Filter, gos_FilterTriLinear);
				break;

			default:
				STOP(("Unknown filter state"));
		}

		gos_SetRenderState(	gos_State_Dither, newer_renderState & MLRState::DitherOnMode );

		if( newer_renderState & MLRState::WireFrameOnlyMode )
		{
			DWORD wfColor=0xffffff;
			gos_SetRenderState(	gos_State_WireframeMode, (DWORD)&wfColor );
		}
		else
		{
			gos_SetRenderState(	gos_State_WireframeMode, 0 );
		}

		gos_SetRenderState(	gos_State_Perspective, newer_renderState & MLRState::TextureCorrectionOnMode );

		if (newer_renderState & MLRState::FogMask)
		{
			gos_SetRenderState(	gos_State_Fog, (int)&MLRState::fogColor);
		}
		else
		{
			gos_SetRenderState(	gos_State_Fog, 0);
		}

		gos_SetRenderState(
			gos_State_ShadeMode,
			(newer_renderState & MLRState::FlatColoringMask) ? gos_ShadeFlat : gos_ShadeGouraud
		);

		gos_SetRenderState(
			gos_State_ZWrite,
			(newer_renderState & MLRState::ZBufferWriteMask) ? 1 : 0
		);

		gos_SetRenderState(	gos_State_Specular, newer_renderState & MLRState::SpecularOnMode );

		gos_SetRenderState(	
			gos_State_TextureAddress, 
			(newer_renderState & MLRState::TextureClamp) ? gos_TextureClamp : gos_TextureWrap
		);

		gos_SetRenderState( gos_State_ZCompare,(newer_renderState & MLRState::ZBufferCompareMask) ? 1 : 0 );

		return true;
	}

	if(changed)
	{
		MLRTexture *tex_p = NULL;
		Check_Object(texturePool);
		if(changed & MLRState::TextureMask)
		{
			if( (newer_renderState & MLRState::TextureMask) && (NULL!=(tex_p=(*texturePool)[newer_renderState & MLRState::TextureMask])))
			{
				gos_SetRenderState( 
					gos_State_Texture, 
					tex_p->GetImage(NULL)->imageHandle
				);
			}
			else
				gos_SetRenderState(gos_State_Texture, 0);
		}

		if(changed & MLRState::AlphaMask)
		{
			switch( newer_renderState&MLRState::AlphaMask )
			{
			case MLRState::OneZeroMode:
				gos_SetRenderState(gos_State_AlphaTest, 0);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneZero);
				break;

			case MLRState::KeyedAlphaMode:
				gos_SetRenderState(gos_State_AlphaTest, 1);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneZero);
				break;

			case MLRState::OneOneMode:
				gos_SetRenderState(gos_State_AlphaTest, 0);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneOne);
				break;

			case MLRState::AlphaOneMode:
				gos_SetRenderState(gos_State_AlphaTest, 1);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_AlphaOne);
				break;

			case MLRState::OneAlphaMode:
				SPEW((0, "MLRState::OneAlphaMode not available anymore"));
				break;

			case MLRState::AlphaInvAlphaMode:
				gos_SetRenderState(gos_State_AlphaTest, 1);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha);
				break;

			case MLRState::OneInvAlphaMode:
				gos_SetRenderState(gos_State_AlphaTest, 0);
				gos_SetRenderState(gos_State_AlphaMode, gos_Alpha_OneInvAlpha);
				break;

			default:
				STOP(("Unknown alpha renderState"));
			}
		}

		if (changed & MLRState::FilterMask)
		{
			switch( newer_renderState&MLRState::FilterMask )
			{
				case MLRState::NoFilterMode:
					gos_SetRenderState(gos_State_Filter, gos_FilterNone);
					break;

				case MLRState::BiLinearFilterMode:
					gos_SetRenderState(gos_State_Filter, gos_FilterBiLinear);
					break;

				case MLRState::TriLinearFilterMode:
					gos_SetRenderState(gos_State_Filter, gos_FilterTriLinear);
					break;

				default:
					STOP(("Unknown filter state"));
			}
		}

		if(changed & MLRState::FogMask)
		{
			if(newer_renderState & MLRState::FogMask)
			{
				gos_SetRenderState(	gos_State_Fog, (int)&MLRState::fogColor);
			}
			else
			{
				gos_SetRenderState(	gos_State_Fog, 0);
			}
		}

		if(changed & MLRState::SpecularMask)
		{
			gos_SetRenderState(	gos_State_Specular, newer_renderState & MLRState::SpecularOnMode );
		}

		if(changed & MLRState::TextureWrapMask)
		{
			gos_SetRenderState(	
				gos_State_TextureAddress, 
				(newer_renderState & MLRState::TextureClamp) ? gos_TextureClamp : gos_TextureWrap
			);
		}

		if(changed & MLRState::DitherOnMode)
		{
			gos_SetRenderState(	gos_State_Dither, newer_renderState & MLRState::DitherOnMode );
		}

		if(changed & MLRState::TextureCorrectionOnMode)
		{
			gos_SetRenderState(	gos_State_Perspective, newer_renderState & MLRState::TextureCorrectionOnMode );
		}

		if(changed & MLRState::WireFrameMask)
		{
			if( newer_renderState & MLRState::WireFrameOnlyMode )
			{
				DWORD wfColor=0xffffff;
				gos_SetRenderState(	gos_State_WireframeMode, (DWORD)&wfColor );
			}
			else
			{
				gos_SetRenderState(	gos_State_WireframeMode, 0 );
			}
		}

		if(changed & MLRState::FlatColoringMask)
		{
			gos_SetRenderState(gos_State_ShadeMode, (newer_renderState & MLRState::FlatColoringMask) ? gos_ShadeFlat : gos_ShadeGouraud);
		}

		if(changed & MLRState::ZBufferWriteMask)
		{
			gos_SetRenderState(gos_State_ZWrite, (newer_renderState & MLRState::ZBufferWriteMask) ? 1 : 0);
		}

		if(changed & MLRState::ZBufferCompareMask)
		{
			gos_SetRenderState( gos_State_ZCompare, (newer_renderState & MLRState::ZBufferCompareMask) ? 1 : 0);
		}
	}

	if(changed & MLRState::BumpMapMask)
	{
		MLRTexture *tex_p = NULL;
		MLRTexture *tex2_p = NULL;
		MLRTexture *tex3_p = NULL;

		if(	(newer_renderState & MLRState::BumpMapMask) &&
			(NULL!=(tex_p=(*texturePool)[newer_renderState & MLRState::TextureMask])) &&
			(NULL!=(tex2_p=(*texturePool)[tex2 & MLRState::TextureMask]))
		)
		{
			if(MLRState::GetCanBumpEnvMap() && (NULL!=(tex3_p=(*texturePool)[tex3 & MLRState::TextureMask])))
			{
				gos_SetRenderState( 
					gos_State_Texture, 
					(*texturePool)[newer_renderState & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
				gos_SetRenderState(
					gos_State_Texture2,
					(*texturePool)[tex2 & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
				gos_SetRenderState(
					gos_State_Texture3,
					(*texturePool)[tex3 & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
				gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_BumpEnvMap );
			}
			else if(MLRState::GetCanBumpDotMap())
			{
				gos_SetRenderState(
					gos_State_Texture,
					(*texturePool)[tex2 & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
				gos_SetRenderState( 
					gos_State_Texture2, 
					(*texturePool)[newer_renderState & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
				gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_BumpDotMap );
			}
		}
		else
		{
			gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_None );
			gos_SetRenderState( gos_State_Texture, 0 );
			gos_SetRenderState( gos_State_Texture2, 0 );
			gos_SetRenderState( gos_State_Texture3, 0 );
		}
	}
	else
	{
		if(newer_renderState & MLRState::BumpMapMask)
		{
			if(MLRState::GetCanBumpEnvMap())
			{
				gos_SetRenderState(
					gos_State_Texture2,
					(*texturePool)[tex2 & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
				gos_SetRenderState(
					gos_State_Texture3,
					(*texturePool)[tex3 & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
			}
			else if(MLRState::GetCanBumpDotMap())
			{
				gos_SetRenderState(
					gos_State_Texture,
					(*texturePool)[tex2 & MLRState::TextureMask]->GetImage(NULL)->imageHandle
				);
			}
		}
	}

	return true;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRSorter::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

