#include "pch.hpp"
//===========================================================================//
// File:	 VertexBuffer.cpp												 //
// Project:  GameOS					                                         //
// Contents: Functions for the manipulation of vertex buffers				 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 12/29/99  AG		Created								 					 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "3DRasterizer.hpp"
#include "DXRasterizer.hpp"
#include "DirectX.hpp"
#include "Debugger.hpp"

extern DWORD gLanguageDLL;
extern HGOSHEAP DefaultHeap;

DWORD VertexBuffersLocked=0;			// Number of vertex buffers locked during scene

VertexBuffer* pVertexBuffers=0;			// Pointer to linked list of vertex buffer

#define VERTEX_BATCH 30					// Multiple of 3!

#define MAX_VERTEXFORMATS 9				// Formats +1


#define Vertex1UV D3DFVF_TLVERTEX
#define Vertex2UV (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2)


DWORD vType[]={
				0,
				D3DFVF_TLVERTEX,													// gos_VERTEX
				D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2,		// gos_VERTEX_2UV
				D3DFVF_LVERTEX,														// gosD3DLVERTEX
				D3DFVF_XYZ    | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2,		// gosD3DLVERTEX_2UV
				D3DFVF_VERTEX,														// gosD3DVERTEX
				D3DFVF_XYZ    | D3DFVF_NORMAL  | D3DFVF_TEX2,						// gosD3DVERTEX_2UV
				D3DFVF_XYZB1  | D3DFVF_NORMAL  | D3DFVF_TEX1,						// gosD3DBLENDVERTEX
				D3DFVF_XYZB1  | D3DFVF_NORMAL  | D3DFVF_TEX2,						// gosD3DBLENDVERTEX_2UV
				};

DWORD vBladeSupport[]={
				0,
				1,								// gos_VERTEX
				0,								// gos_VERTEX_2UV
				1,								// gosD3DLVERTEX
				0,								// gosD3DLVERTEX_2UV
				1,								// gosD3DVERTEX
				0,								// gosD3DVERTEX_2UV
				0,								// gosD3DBLENDVERTEX
				0,								// gosD3DBLENDVERTEX_2UV
				};

DWORD vTypeSize[]={
				0,
				sizeof(gos_VERTEX),				// gos_VERTEX
				sizeof(gos_VERTEX_2UV),			// gos_VERTEX_2UV
				sizeof(gosD3DLVERTEX),			// gosD3DLVERTEX
				sizeof(gosD3DLVERTEX_2UV),		// gosD3DLVERTEX_2UV
				sizeof(gosD3DVERTEX),			// gosD3DVERTEX
				sizeof(gosD3DVERTEX_2UV),		// gosD3DVERTEX_2UV
				sizeof(gosD3DBLENDVERTEX),		// gosD3DBLENDVERTEX
				sizeof(gosD3DBLENDVERTEX_2UV),	// gosD3DBLENDVERTEX_2UV
				};

DWORD vLightPresent[]={
				0,
				0,								// gos_VERTEX
				0,								// gos_VERTEX_2UV
				0,								// gosD3DLVERTEX
				0,								// gosD3DLVERTEX_2UV
				1,								// gosD3DVERTEX
				1,								// gosD3DVERTEX_2UV
				1,								// gosD3DBLENDVERTEX
				1,								// gosD3DBLENDVERTEX_2UV
				};

DWORD vTwoUVPresent[]={
				0,
				0,								// gos_VERTEX
				1,								// gos_VERTEX_2UV
				0,								// gosD3DLVERTEX
				1,								// gosD3DLVERTEX_2UV
				0,								// gosD3DVERTEX
				1,								// gosD3DVERTEX_2UV
				0,								// gosD3DBLENDVERTEX
				1,								// gosD3DBLENDVERTEX_2UV
				};

VertexBuffer* vDebugBuffer[MAX_VERTEXFORMATS];	// Pointers to vertex buffers for debug data

//
// Internal function to create / recreate vertex bufers
//
void CreateVB( VertexBuffer* pNew, DWORD NumberVertices, bool Optimized, gosVERTEXTYPE VertexType )
{
//
// Setup caps for best performance
//
	D3DVERTEXBUFFERDESC Desc;
	memset( &Desc, 0, sizeof(Desc) );
	Desc.dwSize=sizeof(Desc);
	Desc.dwCaps=D3DVBCAPS_WRITEONLY | D3DVBCAPS_DONOTCLIP | (HasTLHAL ? 0 : D3DVBCAPS_SYSTEMMEMORY);
	Desc.dwFVF=vType[VertexType];
	Desc.dwNumVertices=NumberVertices;
//
// Add to list of current vertex buffers
//
	VertexBuffer* pTemp=pNew->pNext;
	memset( pNew, 0, sizeof(VertexBuffer) );
	pNew->Magic=0x12348888;
	pNew->NumberVertices=NumberVertices;
	pNew->WantOptimize=Optimized;
	pNew->VertexType=VertexType;
	pNew->pNext=pTemp;
//
// Using blade and unsupported type?
//
	if( Environment.Renderer==3 && 0==vBladeSupport[VertexType] )
	{
		return;
	}
//
// Create vertex buffer
//
	wCreateVertexBuffer( d3d7, &Desc, &pNew->vBuffer, 0 );
//
// Create a debug TLVERTEX vertex copy of vertex buffer
//
	#ifdef LAB_ONLY
		memset( &Desc, 0, sizeof(Desc) );
		Desc.dwSize=sizeof(Desc);
		Desc.dwCaps=D3DVBCAPS_DONOTCLIP | D3DVBCAPS_SYSTEMMEMORY;
		Desc.dwFVF=vTwoUVPresent[VertexType] ? vType[VERTEX_gos_VERTEX_2UV] : vType[VERTEX_gos_VERTEX];
		Desc.dwNumVertices=NumberVertices;
		wCreateVertexBuffer( d3d7, &Desc, &pNew->DebugvBuffer, 0 );
//
// If running Hardware T&L, need to create a copy of the untransformed vertices as well
//
		if( HasTLHAL )
		{
			memset( &Desc, 0, sizeof(Desc) );
			Desc.dwSize=sizeof(Desc);
			Desc.dwCaps=D3DVBCAPS_DONOTCLIP | D3DVBCAPS_SYSTEMMEMORY;
			Desc.dwFVF=vType[VertexType];
			Desc.dwNumVertices=NumberVertices;
			wCreateVertexBuffer( d3d7, &Desc, &pNew->CopyvBuffer, 0 );
		}
	#endif
}




//
// Called when mode changes and all vertex buffers are now invalid
//
void InvalidateVertexBuffers()
{
	VertexBuffer* pThis=pVertexBuffers;
	while( pThis )
	{
		if( pThis->vBuffer )
			wRelease( pThis->vBuffer );
		pThis->vBuffer=0;
		#ifdef LAB_ONLY
			if( pThis->CopyvBuffer )
				wRelease( pThis->CopyvBuffer );
			pThis->CopyvBuffer=0;
			if( pThis->DebugvBuffer )
				wRelease( pThis->DebugvBuffer );
			pThis->DebugvBuffer=0;
		#endif
		pThis->Lost=1;
		pThis=pThis->pNext;
	}
}


//
// Returns a handle to a vertex buffer
//
// If the Optimized flag is set, when the Vertex Buffer is locked and unlocked the first time it will be optimized. It cannot be locked again after this.
//
DWORD __stdcall gos_CreateVertexBuffer( DWORD NumberVertices, bool Optimized, gosVERTEXTYPE VertexType )
{
	gosASSERT( VertexType!=VERTEX_gos_VERTEX && VertexType!=VERTEX_gos_VERTEX_2UV );		// Temp limitation
	gosASSERT( VertexType>=VERTEX_gos_VERTEX && VertexType<=VERTEX_gosD3DBLENDVERTEX_2UV );
	gosASSERT( !InsideBeginScene && NumberVertices<D3DMAXNUMVERTICES );

	VertexBuffer* pNew=(VertexBuffer*)gos_Malloc(sizeof(VertexBuffer), DefaultHeap );
//
// Create new vertex buffer
//
	CreateVB( pNew, NumberVertices, Optimized, VertexType );
//
// Add to list of vertex buffers
//
	pNew->pNext=pVertexBuffers;
	pVertexBuffers=pNew;
//
// Return pointer to internal structure
//
	return (DWORD)pNew;
}


//
// Called when new mode is setup to recreate all previous vertex buffers
//
void ReCreateVertexBuffers()
{
	VertexBuffer* pThis=pVertexBuffers;
	while( pThis )
	{
		VertexBuffer *pNext=pThis->pNext;
		CreateVB( pThis, pThis->NumberVertices, pThis->WantOptimize, pThis->VertexType );
		pThis->Lost=1;
		pThis->pNext = pNext;
		pThis=pNext;
	}
//
// Debugging vertex buffers exist? - One for each vertex type that needs processing (except 1st 2 untransformed vertex types)
//
	#ifdef LAB_ONLY
		int OldTL=HasTLHAL;		// Make sure created in system memory
		HasTLHAL=0;
		for( int t0=VERTEX_gosD3DLVERTEX; t0<MAX_VERTEXFORMATS; t0++ )
		{
			if( !vDebugBuffer[t0] )
				vDebugBuffer[t0]=(VertexBuffer*)gos_CreateVertexBuffer( VERTEX_BATCH, 0, (gosVERTEXTYPE)t0 );
		}
		HasTLHAL=OldTL;
	#endif
}







//
// Remove a vertex buffer from the chain of vertex buffers
//
void RemoveVBFromChain( VertexBuffer* pThis )
{
	if( pVertexBuffers==pThis )
	{
		pVertexBuffers=pVertexBuffers->pNext;
		gos_Free( pThis );
	}
	else
	{
		VertexBuffer* pTemp=pVertexBuffers;
		while( pTemp )
		{
			if( pTemp->pNext==pThis )
			{
				pTemp->pNext=pThis->pNext;
				gos_Free( pThis );
				break;
			}
			pTemp=pTemp->pNext;
		}
	}
}

//
// Deletes the vertex buffer
//
void __stdcall gos_DeleteVertexBuffer( DWORD VertexBufferHandle )
{
	VertexBuffer* pThis=(VertexBuffer*)VertexBufferHandle;

	gosASSERT( !InsideBeginScene && pThis->Magic==0x12348888 && !pThis->Locked );

	if( !pThis->Lost )
	{
		if( pThis->vBuffer )
			wRelease( pThis->vBuffer );
		#ifdef LAB_ONLY
			if( pThis->CopyvBuffer )
				wRelease( pThis->CopyvBuffer );
			if( pThis->DebugvBuffer )
				wRelease( pThis->DebugvBuffer );
		#endif
	}

	RemoveVBFromChain( pThis );
}	



//
// Init all vertex buffer variables
//
void InitVertexBuffers()
{
	memset( vDebugBuffer, 0, sizeof(vDebugBuffer) );
}


	
//
// Close all vertex buffers
//
void DestroyVextexBuffers( bool Silent )
{
//
// Get rid of debugging vertex buffers
//
	#ifdef LAB_ONLY
		for( int t0=0; t0<sizeof(vDebugBuffer)/sizeof(vDebugBuffer[0]); t0++ )
		{
			if( vDebugBuffer[t0] )
			{
				if( vDebugBuffer[t0]->vBuffer )
					wRelease( vDebugBuffer[t0]->vBuffer );
				if(  vDebugBuffer[t0]->DebugvBuffer )
					wRelease( vDebugBuffer[t0]->DebugvBuffer );
				RemoveVBFromChain( vDebugBuffer[t0] );
			}
			vDebugBuffer[t0]=0;
		}
	#endif
//
// Release all user vertex buffers
//
	VertexBuffer* pThis=pVertexBuffers;
	while( pThis )
	{
		if( !Silent )
			PAUSE(( "Vertex buffer not destoryed, %d vertices.", pThis->NumberVertices ));
		VertexBuffer* pTemp=pThis;
		pThis=pThis->pNext;
		if( pTemp->vBuffer )
			wRelease( pTemp->vBuffer );
		#ifdef LAB_ONLY
			if( pTemp->CopyvBuffer )
				wRelease( pTemp->CopyvBuffer );
			if( pTemp->DebugvBuffer )
				wRelease( pTemp->DebugvBuffer );
		#endif
		gos_Free( pTemp );
	}
	pVertexBuffers=0;
}




//
// Application should check this at the start of a frame. If the user has changed modes or video cards, this will be true.
// All vertex buffers will be marked INVALID and cannot be locked or rendered. They must be deleted and re-created.
//
bool __stdcall gos_VertexBuffersLost( DWORD VertexBufferHandle )
{
	if( VertexBufferHandle==0 )
		return ModeChanged;
	else
	{
		VertexBuffer* pThis=(VertexBuffer*)VertexBufferHandle;
		return pThis->Lost;
	}
}


//
// Locks a vertex buffer and returns a pointer
//
void* __stdcall gos_LockVertexBuffer( DWORD VertexBufferHandle, bool DiscardContents, bool NoOverwrite )
{
	VertexBuffer* pThis=(VertexBuffer*)VertexBufferHandle;

	gosASSERT( pThis->Magic==0x12348888 && !pThis->Locked && !pThis->Optimized );

	pThis->Locked=true;

	VertexBuffersLocked++;
	
	wLock( pThis->vBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY | (DiscardContents ? DDLOCK_DISCARDCONTENTS : 0) | (NoOverwrite ? DDLOCK_NOOVERWRITE : 0), &pThis->Pointer, NULL );
	
	return pThis->Pointer;
}

//
// Unlocks a vertex buffer
//
void __stdcall gos_UnLockVertexBuffer( DWORD VertexBufferHandle )
{
	VertexBuffer* pThis=(VertexBuffer*)VertexBufferHandle;

	gosASSERT( pThis->Magic==0x12348888 && pThis->Locked );

	pThis->Locked=false;
	pThis->Lost=false;
//
// If there is a debugging system memory copy of D3DVERTEX data, copy all vertices into it
//
	#ifdef LAB_ONLY
		if( pThis->CopyvBuffer )
		{
			void* pDest;
			wLock( pThis->CopyvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY | DDLOCK_DISCARDCONTENTS, &pDest, NULL );

			memcpy( pDest, pThis->Pointer, pThis->NumberVertices*vTypeSize[pThis->VertexType] );

			wUnlock( pThis->CopyvBuffer );
		}
	#endif

	VertexBuffersLocked--;

	wUnlock( pThis->vBuffer );
//
// If the vertex buffer was created with the optimize flag, optimize it after the 1st unlock
//
	if( pThis->WantOptimize )
	{
		wOptimize( pThis->vBuffer, d3dDevice7, 0 );
		pThis->Optimized=true;
	}
}











//
// Transforms and renders a vertex buffer
//
void __stdcall gos_RenderVertexBuffer( DWORD VertexBufferHandle, DWORD StartVertex, DWORD NumberVertices, WORD* lpwIndices, DWORD NumberIndices, gosPRIMITIVETYPE PrimitiveType )
{
	VertexBuffer* pThis=(VertexBuffer*)VertexBufferHandle;

	gosASSERT( pThis->Magic==0x12348888 && !pThis->Locked && !pThis->Lost && StartVertex<pThis->NumberVertices && StartVertex+NumberVertices<=pThis->NumberVertices && InsideBeginScene && NumberVertices>0 );

	if( DirtyStates )
		FlushRenderStates();

	#ifdef _ARMOR
		for( int t0=0; t0<NumberIndices; t0++ )
		{
			gosASSERT( lpwIndices[t0] < NumberVertices );	// Check only the indices are valid (vertices are not transformed yet)
		}
	#endif

	switch( PrimitiveType )
	{
		case PRIMITIVE_POINTLIST:
		{
			#ifdef LAB_ONLY
				if( gDisableLinesPoints && !InDebugger )
					return;

				PrimitivesRendered+=NumberVertices;
				PointsRendered+=NumberVertices;

				if( ZoomMode )
				{
					SafeFPU();
					wProcessVertices( pThis->DebugvBuffer, ((vLightPresent[pThis->VertexType] && RenderStates[gos_State_Lighting]) ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, NumberVertices, (pThis->CopyvBuffer ? pThis->CopyvBuffer : pThis->vBuffer), 0, d3dDevice7, 0 );
					GameOSFPU();

					if( vTwoUVPresent[pThis->VertexType] )
					{			
						gos_VERTEX_2UV* pVertices;

						wLock( pThis->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, (void**)&pVertices, NULL );

						for( int t0=0; t0<NumberVertices; t0++ )
						{
							pVertices[t0].x-=(DBMouseX-pVertices[t0].x)*ZoomMode;
							pVertices[t0].y-=(DBMouseY-pVertices[t0].y)*ZoomMode;
						}

						wUnlock( pThis->DebugvBuffer );
					}
					else
					{			
						gos_VERTEX* pVertices;

						wLock( pThis->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, (void**)&pVertices, NULL );

						for( int t0=0; t0<NumberVertices; t0++ )
						{
							pVertices[t0].x-=(DBMouseX-pVertices[t0].x)*ZoomMode;
							pVertices[t0].y-=(DBMouseY-pVertices[t0].y)*ZoomMode;
						}

						wUnlock( pThis->DebugvBuffer );
					}
				}
			#endif

			SafeFPU();
			wDrawPrimitiveVB( d3dDevice7, D3DPT_POINTLIST, pThis->vBuffer, StartVertex, NumberVertices, 0 );
			GameOSFPU();
			return;
		}

		case PRIMITIVE_LINELIST:
		{
			gosASSERT( NumberIndices>0 && (NumberIndices&1)==0 );

			#ifdef LAB_ONLY
				if( gDisableLinesPoints && !InDebugger )
					return;

				PrimitivesRendered+=NumberVertices>>1;
				LinesRendered+=NumberVertices>>1;

				if( ZoomMode )
				{
					SafeFPU();
					wProcessVertices( pThis->DebugvBuffer, ((vLightPresent[pThis->VertexType] && RenderStates[gos_State_Lighting]) ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, NumberVertices, (pThis->CopyvBuffer ? pThis->CopyvBuffer : pThis->vBuffer), 0, d3dDevice7, 0 );
					GameOSFPU();

					if( vTwoUVPresent[pThis->VertexType] )
					{			
						gos_VERTEX_2UV* pVertices;

						wLock( pThis->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, (void**)&pVertices, NULL );

						for( int t0=0; t0<NumberVertices; t0++ )
						{
							pVertices[t0].x-=(DBMouseX-pVertices[t0].x)*ZoomMode;
							pVertices[t0].y-=(DBMouseY-pVertices[t0].y)*ZoomMode;
						}

						wUnlock( pThis->DebugvBuffer );
					}
					else
					{			
						gos_VERTEX* pVertices;

						wLock( pThis->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, (void**)&pVertices, NULL );

						for( int t0=0; t0<NumberVertices; t0++ )
						{
							pVertices[t0].x-=(DBMouseX-pVertices[t0].x)*ZoomMode;
							pVertices[t0].y-=(DBMouseY-pVertices[t0].y)*ZoomMode;
						}

						wUnlock( pThis->DebugvBuffer );
					}

					SafeFPU();
					wDrawIndexedPrimitiveVB( d3dDevice7, D3DPT_LINELIST, pThis->DebugvBuffer, StartVertex, NumberVertices, lpwIndices, NumberIndices, 0 );
					GameOSFPU();

					return;
				}
			#endif

			SafeFPU();
			wDrawIndexedPrimitiveVB( d3dDevice7, D3DPT_LINELIST, pThis->vBuffer, StartVertex, NumberVertices, lpwIndices, NumberIndices, 0 );
			GameOSFPU();
			return;
		}

		case PRIMITIVE_TRIANGLELIST:
		{
			gosASSERT( NumberIndices>0 && (NumberIndices%3)==0 );
	
			#ifdef LAB_ONLY
				if( gDisablePrimitives && !InDebugger )
					return;
	
				PrimitivesRendered+=NumberIndices/3;
				IndexedVBTriangleLength+=NumberIndices/3;
				IndexedVBTriangleCalls++;
	
				if( RenderMode )
				{
					SafeFPU();
					wProcessVertices( pThis->DebugvBuffer, ((vLightPresent[pThis->VertexType] && RenderStates[gos_State_Lighting]) ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, NumberVertices, (pThis->CopyvBuffer ? pThis->CopyvBuffer : pThis->vBuffer), 0, d3dDevice7, 0 );
					GameOSFPU();

					if( vTwoUVPresent[pThis->VertexType] )
					{			
						gos_VERTEX_2UV* pVertices;
						wLock( pThis->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&pVertices, NULL );

						for( int t0=0; t0<NumberIndices; t0+=3 )
						{
							DebugTriangle_2UV( &pVertices[lpwIndices[t0]], &pVertices[lpwIndices[t0+1]], &pVertices[lpwIndices[t0+2]] );
						}

						wUnlock( pThis->DebugvBuffer );
					}
					else
					{			
						gos_VERTEX* pVertices;
						wLock( pThis->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&pVertices, NULL );

						for( int t0=0; t0<NumberIndices; t0+=3 )
						{
							DebugTriangle( &pVertices[lpwIndices[t0]], &pVertices[lpwIndices[t0+1]], &pVertices[lpwIndices[t0+2]] );
						}

						wUnlock( pThis->DebugvBuffer );
					}

					return;
				}
			#endif

			SafeFPU();
			wDrawIndexedPrimitiveVB( d3dDevice7, D3DPT_TRIANGLELIST, pThis->vBuffer, StartVertex, NumberVertices, lpwIndices, NumberIndices, 0 );
			GameOSFPU();
			return;
		}

		default:
			STOP(( "Unknown primitive type" ));
	}
}






//
// Transforms and renders an array of vertices
//
void __stdcall gos_RenderIndexedArray( void* pVertexArray, DWORD NumberVertices, WORD* lpwIndices, DWORD NumberIndices, gosVERTEXTYPE VertexType, gosPRIMITIVETYPE PrimitiveType )
{
	gosASSERT( VertexType!=VERTEX_gos_VERTEX && VertexType!=VERTEX_gos_VERTEX_2UV );		// Temp limitation
	gosASSERT( VertexType>=VERTEX_gos_VERTEX && VertexType<=VERTEX_gosD3DBLENDVERTEX_2UV );
	gosASSERT( InsideBeginScene && NumberVertices>0 );

	if( DirtyStates )
		FlushRenderStates();

	#ifdef _ARMOR
		for( int t0=0; t0<NumberIndices; t0++ )
		{
			gosASSERT( lpwIndices[t0] < NumberVertices );		// Check the indices are valid (vertices are not transformed yet)
		}
	#endif

	switch( PrimitiveType )
	{
		case PRIMITIVE_POINTLIST:
		{
			#ifdef LAB_ONLY
				if( gDisableLinesPoints && !InDebugger )
					return;

				PrimitivesRendered+=NumberVertices;
				PointsRendered+=NumberVertices;
				if( RenderMode )
				{
//
// Process batches of VERTEX_BATCH vertices at a time (to speed up debugging!)
//
					int HighestIndex=0;
					BYTE* pDestVertices;
					BYTE* pSourceVertices=(BYTE*)pVertexArray;
					DWORD SizeOfVertices=vTypeSize[VertexType];

					while( HighestIndex+VERTEX_BATCH <= NumberVertices )
					{
						wLock( vDebugBuffer[VertexType]->vBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, (void**)&pDestVertices, NULL );
						memcpy( pDestVertices, pSourceVertices+SizeOfVertices*HighestIndex, SizeOfVertices*VERTEX_BATCH );
						wUnlock( vDebugBuffer[VertexType]->vBuffer );

						SafeFPU();
						wProcessVertices( vDebugBuffer[VertexType]->DebugvBuffer, (RenderStates[gos_State_Lighting] ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, VERTEX_BATCH, vDebugBuffer[VertexType]->vBuffer, 0, d3dDevice7, 0 );
						GameOSFPU();

						if( vTwoUVPresent[VertexType] )
						{
							gos_VERTEX_2UV* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							for( int t0=0; t0<VERTEX_BATCH; t0++ )
							{
								TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
								TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
							}

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

							SafeFPU();
							wDrawPrimitive( d3dDevice7, D3DPT_POINTLIST, Vertex2UV, TransformedVertices, VERTEX_BATCH, 0 );
							GameOSFPU();
						}
						else
						{
							gos_VERTEX* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							for( int t0=0; t0<VERTEX_BATCH; t0++ )
							{
								TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
								TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
							}

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

							SafeFPU();
							wDrawPrimitive( d3dDevice7, D3DPT_POINTLIST, Vertex1UV, TransformedVertices, VERTEX_BATCH, 0 );
							GameOSFPU();
						}

						HighestIndex+=VERTEX_BATCH;
					}
//
// Now render remaining indices
//
					if( NumberVertices==HighestIndex )
						return;

					wLock( vDebugBuffer[VertexType]->vBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, (void**)&pDestVertices, NULL );
					memcpy( pDestVertices, pSourceVertices+SizeOfVertices*HighestIndex, SizeOfVertices*(NumberVertices-HighestIndex) );
					wUnlock( vDebugBuffer[VertexType]->vBuffer );

					SafeFPU();
					wProcessVertices( vDebugBuffer[VertexType]->DebugvBuffer, (RenderStates[gos_State_Lighting] ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, NumberVertices-HighestIndex, vDebugBuffer[VertexType]->vBuffer, 0, d3dDevice7, 0 );
					GameOSFPU();

					if( vTwoUVPresent[VertexType] )
					{
						gos_VERTEX_2UV* TransformedVertices;
						wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

						for( int t0=0; t0<NumberVertices-HighestIndex; t0++ )
						{
							TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
							TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
						}

						wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

						SafeFPU();
						wDrawPrimitive( d3dDevice7, D3DPT_POINTLIST, Vertex2UV, TransformedVertices, NumberVertices-HighestIndex, 0 );
						GameOSFPU();
					}
					else
					{
						gos_VERTEX* TransformedVertices;
						wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

						for( int t0=0; t0<NumberVertices-HighestIndex; t0++ )
						{
							TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
							TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
						}

						wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

						SafeFPU();
						wDrawPrimitive( d3dDevice7, D3DPT_POINTLIST, Vertex1UV, TransformedVertices, NumberVertices-HighestIndex, 0 );
						GameOSFPU();
					}

					return;
				}
			#endif

			SafeFPU();
			wDrawPrimitive( d3dDevice7, D3DPT_POINTLIST, vType[VertexType], pVertexArray, NumberVertices, 0 );
			GameOSFPU();

			return;
		}

		case PRIMITIVE_LINELIST:
		{
			gosASSERT( NumberIndices>0 && (NumberIndices&1)==0 );

			#ifdef LAB_ONLY
				if( gDisableLinesPoints && !InDebugger )
					return;

				PrimitivesRendered+=NumberVertices>>1;
				LinesRendered+=NumberVertices>>1;
				if( RenderMode )
				{
//
// Process batches of VERTEX_BATCH vertices at a time (to speed up debugging!)
//
					int HighestIndex=0;
					BYTE* pDestVertices;
					BYTE* pSourceVertices=(BYTE*)pVertexArray;
					DWORD SizeOfVertices=vTypeSize[VertexType];

					while( HighestIndex+VERTEX_BATCH <= NumberIndices )
					{
						wLock( vDebugBuffer[VertexType]->vBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, (void**)&pDestVertices, NULL );

						for( int t0=0; t0<VERTEX_BATCH; t0++ )
							memcpy( pDestVertices+SizeOfVertices*t0, pSourceVertices+SizeOfVertices*lpwIndices[HighestIndex+t0], SizeOfVertices );
						
						wUnlock( vDebugBuffer[VertexType]->vBuffer );

						SafeFPU();
						wProcessVertices( vDebugBuffer[VertexType]->DebugvBuffer, (RenderStates[gos_State_Lighting] ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, VERTEX_BATCH, vDebugBuffer[VertexType]->vBuffer, 0, d3dDevice7, 0 );
						GameOSFPU();

						if( vTwoUVPresent[VertexType] )
						{
							gos_VERTEX_2UV* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							for( int t0=0; t0<VERTEX_BATCH; t0++ )
							{
								TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
								TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
							}

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

							SafeFPU();
							wDrawPrimitive( d3dDevice7, D3DPT_LINELIST, Vertex2UV, TransformedVertices, VERTEX_BATCH, 0 );
							GameOSFPU();
						}
						else
						{
							gos_VERTEX* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							for( int t0=0; t0<VERTEX_BATCH; t0++ )
							{
								TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
								TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
							}

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

							SafeFPU();
							wDrawPrimitive( d3dDevice7, D3DPT_LINELIST, Vertex1UV, TransformedVertices, VERTEX_BATCH, 0 );
							GameOSFPU();
						}

						HighestIndex+=VERTEX_BATCH;
					}
//
// Now render remaining indices
//
					if( NumberIndices==HighestIndex )
						return;

					wLock( vDebugBuffer[VertexType]->vBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, (void**)&pDestVertices, NULL );

					for( int t0=HighestIndex; t0<NumberIndices; t0++ )
					{
						memcpy( pDestVertices+SizeOfVertices*(t0-HighestIndex), pSourceVertices+SizeOfVertices*lpwIndices[t0], SizeOfVertices );
					}					

					wUnlock( vDebugBuffer[VertexType]->vBuffer );

					SafeFPU();
					wProcessVertices( vDebugBuffer[VertexType]->DebugvBuffer, (RenderStates[gos_State_Lighting] ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, NumberIndices-HighestIndex, vDebugBuffer[VertexType]->vBuffer, 0, d3dDevice7, 0 );
					GameOSFPU();

					if( vTwoUVPresent[VertexType] )
					{
						gos_VERTEX_2UV* TransformedVertices;
						wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

						for( int t0=0; t0<NumberIndices-HighestIndex; t0++ )
						{
							TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
							TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
						}

						wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

						SafeFPU();
						wDrawPrimitive( d3dDevice7, D3DPT_LINELIST, Vertex2UV, TransformedVertices, NumberIndices-HighestIndex, 0 );
						GameOSFPU();
					}
					else
					{
						gos_VERTEX* TransformedVertices;
						wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

						for( int t0=0; t0<NumberIndices-HighestIndex; t0++ )
						{
							TransformedVertices[t0].x-=(DBMouseX-TransformedVertices[t0].x)*ZoomMode;
							TransformedVertices[t0].y-=(DBMouseY-TransformedVertices[t0].y)*ZoomMode;
						}

						wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );

						SafeFPU();
						wDrawPrimitive( d3dDevice7, D3DPT_LINELIST, Vertex1UV, TransformedVertices, NumberIndices-HighestIndex, 0 );
						GameOSFPU();
					}

					return;
				}
			#endif

			SafeFPU();
			wDrawIndexedPrimitive( d3dDevice7, D3DPT_LINELIST, vType[VertexType], pVertexArray, NumberVertices, lpwIndices, NumberIndices, 0 );
			GameOSFPU();

			return;
		}

		case PRIMITIVE_TRIANGLELIST:
		{
			gosASSERT( NumberIndices>0 && (NumberIndices%3)==0 );
	
			#ifdef LAB_ONLY
				if( gDisablePrimitives && !InDebugger )
					return;
				PrimitivesRendered+=NumberIndices/3;
				IndexedTriangleLength+=NumberIndices/3;
				IndexedTriangleCalls++;
				if( RenderMode )
				{
//
// Process batches of VERTEX_BATCH vertices at a time (to speed up debugging!)
//
					int HighestIndex=0;
					BYTE* pDestVertices;
					BYTE* pSourceVertices=(BYTE*)pVertexArray;
					DWORD SizeOfVertices=vTypeSize[VertexType];

					while( HighestIndex+VERTEX_BATCH <= NumberIndices )
					{
						wLock( vDebugBuffer[VertexType]->vBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, (void**)&pDestVertices, NULL );

						for( int t0=0; t0<VERTEX_BATCH; t0++ )
							memcpy( pDestVertices+SizeOfVertices*t0, pSourceVertices+SizeOfVertices*lpwIndices[HighestIndex+t0], SizeOfVertices );
						
						wUnlock( vDebugBuffer[VertexType]->vBuffer );

						SafeFPU();
						wProcessVertices( vDebugBuffer[VertexType]->DebugvBuffer, (RenderStates[gos_State_Lighting] ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, VERTEX_BATCH, vDebugBuffer[VertexType]->vBuffer, 0, d3dDevice7, 0 );
						GameOSFPU();

						if( vTwoUVPresent[VertexType] )
						{
							gos_VERTEX_2UV* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							for( t0=0; t0<VERTEX_BATCH; t0+=3 )
								DebugTriangle_2UV( &TransformedVertices[t0], &TransformedVertices[t0+1], &TransformedVertices[t0+2] );

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );
						}
						else
						{
							gos_VERTEX* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							for( t0=0; t0<VERTEX_BATCH; t0+=3 )
								DebugTriangle( &TransformedVertices[t0], &TransformedVertices[t0+1], &TransformedVertices[t0+2] );

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );
						}

						HighestIndex+=VERTEX_BATCH;
					}
//
// Now render remaining indices
//
					for( int t0=HighestIndex; t0<NumberIndices; t0+=3 )
					{
						wLock( vDebugBuffer[VertexType]->vBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, (void**)&pDestVertices, NULL );

						memcpy( pDestVertices+SizeOfVertices*0, pSourceVertices+SizeOfVertices*lpwIndices[t0+0], SizeOfVertices );
						memcpy( pDestVertices+SizeOfVertices*1, pSourceVertices+SizeOfVertices*lpwIndices[t0+1], SizeOfVertices );
						memcpy( pDestVertices+SizeOfVertices*2, pSourceVertices+SizeOfVertices*lpwIndices[t0+2], SizeOfVertices );

						wUnlock( vDebugBuffer[VertexType]->vBuffer );

						SafeFPU();
						wProcessVertices( vDebugBuffer[VertexType]->DebugvBuffer, (RenderStates[gos_State_Lighting] ? D3DVOP_LIGHT : 0) | D3DVOP_TRANSFORM, 0, 3, vDebugBuffer[VertexType]->vBuffer, 0, d3dDevice7, 0 );
						GameOSFPU();

						if( vTwoUVPresent[VertexType] )
						{
							gos_VERTEX_2UV* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							DebugTriangle_2UV( &TransformedVertices[0], &TransformedVertices[1], &TransformedVertices[2] );

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );
						}
						else
						{
							gos_VERTEX* TransformedVertices;
							wLock( vDebugBuffer[VertexType]->DebugvBuffer, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, (void**)&TransformedVertices, NULL );

							DebugTriangle( &TransformedVertices[0], &TransformedVertices[1], &TransformedVertices[2] );

							wUnlock( vDebugBuffer[VertexType]->DebugvBuffer );
						}
					}
					return;
				}
			#endif

			SafeFPU();
			wDrawIndexedPrimitive( d3dDevice7, D3DPT_TRIANGLELIST, vType[VertexType], pVertexArray, NumberVertices, lpwIndices, NumberIndices, 0 );
			GameOSFPU();

			return;
		}

		default:
			STOP(( "Unknown primitive type" ));
	}
}


