/****************************************************************************************/

/*  SoftDrv.C                                                                           */

/*                                                                                      */

/*  Author: Mike Sandige                                                                */

/*  Description:  This is the API layer for the Jet3D software driver.                  */

/*                                                                                      */

/*  Copyright (c) 1996-1999, Eclipse Entertainment, L.L.C.                              */

/*                                                                                      */

/*  The contents of this file are subject to the Jet3D Public License                   */

/*  Version 1.02 (the "License"); you may not use this file except in                   */

/*  compliance with the License. You may obtain a copy of the License at                */

/*  http://www.jet3d.com                                                                */

/*                                                                                      */

/*  Software distributed under the License is distributed on an "AS IS"                 */

/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */

/*  the License for the specific language governing rights and limitations              */

/*  under the License.                                                                  */

/*                                                                                      */

/*  The Original Code is Jet3D, released December 12, 1999.                             */

/*  Copyright (C) 1996-1999 Eclipse Entertainment, L.L.C. All Rights Reserved           */

/*                                                                                      */

/****************************************************************************************/

#ifdef WIN32

#pragma warning(disable : 4201 4214 4115)

#include <windows.h>

#pragma warning(default : 4201 4214 4115; disable : 4514 4244)

#endif



#ifdef BUILD_BE

#include <stdlib.h> // qsort

#include <memory.h>

#endif



#include <stdio.h> 

#include <assert.h>



#include "SoftDrv.h"

#include "CPUInfo.h"

#include "Dcommon.h"

#include "SpanBuffer.h"		

#include "Span.h"

#include "Ram.h"



#include "SWTHandle.h"

#include "Display.h"

#include "TRaster.h"

#include "DrawDecal.h"

#include "Errorlog.h"



//#define SOFTDRV_MAX_SPANS 10000

#define SOFTDRV_MAX_AVG_SPANS_PER_LINE (22)	





#define SOFTDRV_RENDER_FLAG_TMAP  (1<<11)	

#define SOFTDRV_RENDER_FLAG_LMAP  (1<<12)	

#define SOFTDRV_ROP_XLATE_TABLE  (4096*2) //10 bits from JE_RENDER_FLAG, 1 bit for lightmap, 1 bit for texturemap



#define SOFTDRV_DESCRIPTION_LENGTH 256

typedef struct 

{

	char				Description[SOFTDRV_DESCRIPTION_LENGTH];

	Display_Type		DisplayType;

	DisplayModeInfo		*Info;

} SoftDrv_DisplayInfo;



typedef struct SoftDrv

{

	int						RefCount;

	int						DisplayCount;

	int						CurrentDisplayIndex;

	uint16					*ZBufferPtr;

	SoftDrv_DisplayInfo		Display[DISPLAY_COUNT];

	uint8				    Rop_Translation[SOFTDRV_ROP_XLATE_TABLE];	

} SoftDrv;



static SoftDrv SoftDrv_Internals={JE_FALSE};





int32				 RenderMode = RENDER_NONE;

DRV_Window			 ClientWindow	={ 0 };

Display				*SD_Display = NULL;

jeBoolean            SD_ProcessorHas3DNow;

jeBoolean            SD_ProcessorHasMMX;

jeBoolean			 SD_DIBDisplayMode = JE_FALSE;

jeBoolean			 SD_Active = false;

DRV_EngineSettings	 SD_EngineSettings=

							{   // to conform to DRV_Driver structure.

								/*CanSupportFlags*/ (DRV_SUPPORT_ALPHA|DRV_SUPPORT_COLORKEY),

								/*PreferenceFlags*/ (DRV_PREFERENCE_NO_MIRRORS | DRV_PREFERENCE_DRAW_WALPHA_IN_BSP)

							};



DRV_CacheInfo		SoftDrv_CacheInfo;



S32					LastError;

char				LastErrorStr[200];



//========================================================================================

//	Internal Poly Mgr to SoftDrv.... (Used for sorting transparent/color-keyed polys

//========================================================================================

#define MAX_BATCHED_POLYS				256		// Max polys before an auto-flush is performed

#define MAX_BATCHED_VERTS				256		// Max verts before an auto-flush is performed



#define Type_Invalid			0

#define	Type_Gouraud			1

#define	Type_Misc				2

#define	Type_World				3



typedef struct Batch_Poly

{

	uint8				Type;



	int32				NumVerts;

	jeTLVertex			*Verts;



	jeFloat				CenterZ;



	jeRDriver_Layer		Layers[2];

	int32				NumLayers;



	void				*LMapCBContext;



	uint32				Flags;

} Batch_Poly;



typedef struct Batch

{

	int32			TotalPolys;			// Num polys in batch

	int32			TotalVerts;			// Num verts of all polys



	Batch_Poly		Polys[MAX_BATCHED_POLYS];

	jeTLVertex		Verts[MAX_BATCHED_VERTS];



	Batch_Poly		*SortedPolys[MAX_BATCHED_POLYS];

} Batch;



#define		MAX_BATCHES	16



static Batch			g_PolyMgr[MAX_BATCHES];			// Local Global Poly Batch Mgr

static int32			BatchCounter;

static int32			CurrentBatch;



static void BatchPoly(	Batch					*Mgr,

						const jeTLVertex		*Verts, 

						int32					NumVerts, 

						jeRDriver_Layer			*Layers,

						int32					NumLayers,

						void					*LMapCBContext,

						uint32					Flags,

						uint8					Type);

static void SortPolys(Batch *Mgr);

static void Batch_FlushBatch(Batch *Mgr);

static jeBoolean DRIVERCC RenderGouraudPoly(jeTLVertex *Pnts, int32 NumPoints, uint32 Flags);

static jeBoolean DRIVERCC RenderWorldPoly(	jeTLVertex			*Pnts, 

											int32				NumPoints, 

											jeRDriver_Layer		*Layers,

											int32				NumLayers,

											void				*LMapCBContext,

											uint32				Flags);

static jeBoolean DRIVERCC RenderMiscTexturePoly(jeTLVertex *Pnts, int32 NumPoints, jeRDriver_Layer *Layers, int32 NumLayers, uint32 Flags);





jeBoolean DRIVERCC SoftDrv_BeginBatch(void);

jeBoolean DRIVERCC SoftDrv_EndBatch(void);

void SoftDrv_LightMapSetupCallback(TRaster_Lightmap *LM);



void SoftDrv_ClearZBuffer(DRV_Window *Window)

{

	int32	ZBSize;



	ZBSize = (Window->Width*Window->Height)<<1;



	memset(SoftDrv_Internals.ZBufferPtr, 0xFF, ZBSize);

}





static void SoftDrv_DisplayInfoTable_Destroy( SoftDrv *S )

{

	int i;



	S->RefCount--;

	if (S->RefCount>0)

		return;

	

	for (i=0; i<S->DisplayCount; i++)

		{

			if (S->Display[i].Info != NULL)

				{

					DisplayModeInfo_Destroy( &(S->Display[i].Info) );

					S->Display[i].Info=NULL;

				}

		}

	S->DisplayCount = 0;

}

	

static jeBoolean SoftDrv_DisplayInfoTable_Create( SoftDrv *S, jeBoolean FillOutModes)

{

	char VersionString[SOFTDRV_DESCRIPTION_LENGTH]="v"DRV_VMAJS"."DRV_VMINS".";

	int i;

	FillOutModes;		// avoid unreference parameter warning

	

	assert( S != NULL );

	if (S->RefCount>0)

		{

			S->RefCount++;

			return JE_TRUE;

		}

	else

		S->RefCount=1;

	

			

	S->DisplayCount = 0;

	for (i=0; i<DISPLAY_COUNT; i++)

		{

			S->Display[i].Info = DisplayModeInfo_Create();

			if (S->Display[i].Info == NULL)

				{

					SoftDrv_DisplayInfoTable_Destroy( S );

					jeErrorLog_AddString(JE_ERR_MEMORY_RESOURCE,"SoftDrv_DisplayInfoTableCreate: unable to create table",NULL);

					return JE_FALSE;

				}

			if (Display_GetDisplayInfo(		(Display_Type)i,

											S->Display[i].Description, 

											SOFTDRV_DESCRIPTION_LENGTH-strlen(VersionString),

											S->Display[i].Info) == JE_FALSE)

				{

					DisplayModeInfo_Destroy( &(S->Display[i].Info) );

					jeErrorLog_AddString(JE_ERR_MEMORY_RESOURCE,"SoftDrv_DisplayInfoTableCreate: problem filling table. (continuing)",NULL);

					jeErrorLog_Clear();  // is this ok?

					S->Display[i].Info=NULL;

					//return JE_FALSE;

				}

			else

				{

					strcat(S->Display[i].Description,VersionString);

					S->Display[i].DisplayType = (Display_Type)i;

					S->DisplayCount++;

				}

		}

	return JE_TRUE;

}



jeBoolean DRIVERCC SoftDrv_SetActive(jeBoolean Active)

{

	SD_Active = Active;

	Display_SetActive(SD_Display,Active);

	return true;

}







jeBoolean DRIVERCC SoftDrv_Init(DRV_DriverHook *Hook)

{

	// hook->width, hook->height are ignored...

	uint16	*ZBuffer=NULL;



	#pragma message ("fix to:") //jeBoolean DRIVERCC SoftDrv_Init(Hwnd, char *DriverString, int Height, int Width, int BitsPerPixel, uint32 Flags)



	if (SoftDrv_DisplayInfoTable_Create(&(SoftDrv_Internals), JE_TRUE)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_Init: failed to get mode info",NULL);

			return false;

		}



	if ((Hook->Driver<0) || (Hook->Driver>=SoftDrv_Internals.DisplayCount))

		{

			jeErrorLog_AddString(JE_ERR_BAD_PARAMETER,"SoftDrv_Init: bad driver index",NULL);

			SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

			return false;

		}



	

//	VInfo		=&(SoftDrv->VideoModeInfo[Hook->Driver]);

	//VInfo->bpp	=16;



	SD_ProcessorHas3DNow = CPUInfo_TestFor3DNow();

	SD_ProcessorHasMMX   = CPUInfo_TestForMMX();

  

	{

		int Height, Width, BitsPerPixel;

		uint32 Flags;

		

		if (DisplayModeInfo_GetNth(	SoftDrv_Internals.Display[Hook->Driver].Info, 

									Hook->Mode,

									&Width,

									&Height,

									&BitsPerPixel,

									&Flags ) == JE_FALSE)

			{

				jeErrorLog_AddString(JE_ERR_BAD_PARAMETER,"SoftDrv_Init: unable to get mode info: bad mode index",NULL);

				SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

				return false;

			}



    SD_Display = Display_Create(	(G3DView *)Hook->hWnd,

										SoftDrv_Internals.Display[Hook->Driver].DisplayType,

										Width,

										Height,

										BitsPerPixel,

										Flags);

		if (SD_Display == NULL)

			{

				jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE, "SoftDrv_Init: Could not initialize display",NULL);

				SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

				return	false;

			}						

	

    if( 1 )

		{

			int32 BitsPerPixel;

			Display_Type DisplayType;

			uint32 Flags;			

		

			Display_GetDisplayFormat(	SD_Display, 

										&DisplayType, 

										&(ClientWindow.Width),

										&(ClientWindow.Height),

										&BitsPerPixel,

										&Flags);



		}



	}



	if (SpanBuffer_Create(ClientWindow.Width,ClientWindow.Height, ClientWindow.Height * SOFTDRV_MAX_AVG_SPANS_PER_LINE)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE, "SoftDrv_Init:  Could not create span buffer",NULL);

			Display_Destroy(&SD_Display);

			SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

			return	false;

		}



	if (Display_GetPixelFormat(	SD_Display,

								//&ClientWindow.PixelPitch,

								&ClientWindow.BytesPerPixel,

								&ClientWindow.R_shift,

								&ClientWindow.R_mask,

								&ClientWindow.R_width,

								&ClientWindow.G_shift,

								&ClientWindow.G_mask,

								&ClientWindow.G_width,

								&ClientWindow.B_shift,

								&ClientWindow.B_mask,

								&ClientWindow.B_width)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE, "SoftDrv_Init:  Could not get display pixel format",NULL);

			Display_Destroy(&SD_Display);

			SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

			return	false;

		}



  {	

  jeSpan_DestinationFormat DestFormat;

  switch( ClientWindow.R_width + ClientWindow.G_width + ClientWindow.B_width )

    {

    case 15: DestFormat = JE_SPAN_DESTINATION_FORMAT_555;

             break;

    case 16: DestFormat = JE_SPAN_DESTINATION_FORMAT_565;

             break;

    default: 

			jeErrorLog_AddString(JE_ERR_BAD_PARAMETER,"SoftDrv_Init: unsupported destination format",NULL);

			SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

			return false;

    }



	if (Span_SetOutputMode( DestFormat, JE_SPAN_HARDWARE_INTEL ) == JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_Init: unable to set span drawing mode",NULL);

			SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

			return false;

		}

  }





	{

		U32	OldFlags	=0;



		SoftDrv_Internals.ZBufferPtr=(U16 *)jeRam_Allocate(ClientWindow.Width * ClientWindow.Height * 2);

		

		if(!SoftDrv_Internals.ZBufferPtr)

		{

			jeErrorLog_AddString(JE_ERR_MEMORY_RESOURCE,"SoftDrv_Init:  Not enought memory for ZBuffer",NULL);

			Display_Destroy(&SD_Display);

			SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

			return false;

		}



// This may be faster if we used an area under the BeOS

#ifdef WIN32

		if(!VirtualProtect((U8 *)SoftDrv_Internals.ZBufferPtr,

			(ClientWindow.Width * ClientWindow.Height)*2,

			PAGE_READWRITE | PAGE_NOCACHE,

			&OldFlags))

		{

			// nothing to do on failure...

		}

#endif

	}



	{

		int i;

		for (i=0; i<SOFTDRV_ROP_XLATE_TABLE; i++)	

		{

			SoftDrv_Internals.Rop_Translation[i] = JE_ROP_END;

		}

	}



#define ZSET (JE_RENDER_FLAG_NO_ZTEST)

#define ZTEST (JE_RENDER_FLAG_NO_ZWRITE)

#define NOZ  (ZSET | ZTEST)

#define ZTESTSET (0)

#define LSHADE (0)

#define TMAP SOFTDRV_RENDER_FLAG_TMAP		//set!

#define LMAP SOFTDRV_RENDER_FLAG_LMAP		//set!

#define AFLAT (JE_RENDER_FLAG_ALPHA)

#define SBUF  (JE_RENDER_FLAG_STEST | JE_RENDER_FLAG_SWRITE)

#define AMAP (JE_RENDER_FLAG_COLORKEY)		//set!

#define BFILT (JE_RENDER_FLAG_BILINEAR_FILTER)

#define CLAMP (JE_RENDER_FLAG_CLAMP_UV)



	//gouraud

	SoftDrv_Internals.Rop_Translation[LSHADE | NOZ]=JE_ROP_LSHADE;

	SoftDrv_Internals.Rop_Translation[LSHADE | ZSET]=JE_ROP_LSHADE_ZSET;

	SoftDrv_Internals.Rop_Translation[LSHADE | ZTEST]=JE_ROP_LSHADE_ZTEST;

	SoftDrv_Internals.Rop_Translation[LSHADE | ZTESTSET]=JE_ROP_LSHADE_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[LSHADE | AFLAT | NOZ]=JE_ROP_LSHADE_AFLAT;

	SoftDrv_Internals.Rop_Translation[LSHADE | AFLAT | ZSET]=JE_ROP_LSHADE_AFLAT_ZSET;

	SoftDrv_Internals.Rop_Translation[LSHADE | AFLAT | ZTEST]=JE_ROP_LSHADE_AFLAT_ZTEST;

	SoftDrv_Internals.Rop_Translation[LSHADE | AFLAT | ZTESTSET]=JE_ROP_LSHADE_AFLAT_ZTESTSET;

	//tmap

	SoftDrv_Internals.Rop_Translation[TMAP | NOZ]=JE_ROP_TMAP_LSHADE;

	SoftDrv_Internals.Rop_Translation[TMAP | ZSET]=JE_ROP_TMAP_LSHADE_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | ZTEST]=JE_ROP_TMAP_LSHADE_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | ZTESTSET]=JE_ROP_TMAP_LSHADE_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LMAP | ZSET | SBUF]=JE_ROP_TMAP_LMAP_ZSET_SBUF;

		SoftDrv_Internals.Rop_Translation[TMAP | LMAP | ZTESTSET | SBUF]=JE_ROP_TMAP_LMAP_ZSET_SBUF;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | ZSET | SBUF]=JE_ROP_TMAP_LSHADE_ZSET_SBUF;

		SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | ZTESTSET | SBUF]=JE_ROP_TMAP_LSHADE_ZSET_SBUF;

	SoftDrv_Internals.Rop_Translation[TMAP | LMAP | ZTESTSET]=JE_ROP_TMAP_LSHADE_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AFLAT | NOZ]=JE_ROP_TMAP_LSHADE_AFLAT;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AFLAT | ZSET]=JE_ROP_TMAP_LSHADE_AFLAT_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AFLAT | ZTEST]=JE_ROP_TMAP_LSHADE_AFLAT_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AFLAT | ZTESTSET]=JE_ROP_TMAP_LSHADE_AFLAT_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | NOZ]=JE_ROP_TMAP_LSHADE_AMAP;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | ZSET]=JE_ROP_TMAP_LSHADE_AMAP_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | ZTEST]=JE_ROP_TMAP_LSHADE_AMAP_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | ZTESTSET]=JE_ROP_TMAP_LSHADE_AMAP_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LMAP | AMAP | NOZ]=JE_ROP_TMAP_LMAP_AMAP;

	SoftDrv_Internals.Rop_Translation[TMAP | LMAP | AMAP | ZSET]=JE_ROP_TMAP_LMAP_AMAP_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LMAP | AMAP | ZTEST]=JE_ROP_TMAP_LMAP_AMAP_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | LMAP | AMAP | ZTESTSET]=JE_ROP_TMAP_LMAP_AMAP_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LMAP | AFLAT | ZTESTSET]=JE_ROP_TMAP_LMAP_AFLAT_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | AFLAT | NOZ]=JE_ROP_TMAP_LSHADE_AMAP_AFLAT;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | AFLAT | ZSET]=JE_ROP_TMAP_LSHADE_AMAP_AFLAT_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | AFLAT | ZTEST]=JE_ROP_TMAP_LSHADE_AMAP_AFLAT_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | LSHADE | AMAP | AFLAT | ZTESTSET]=JE_ROP_TMAP_LSHADE_AMAP_AFLAT_ZTESTSET;



	//tmap filter

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | NOZ]=JE_ROP_TMAP_BFILT_LSHADE;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | ZSET]=JE_ROP_TMAP_BFILT_LSHADE_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | ZTEST]=JE_ROP_TMAP_BFILT_LSHADE_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | ZTESTSET]=JE_ROP_TMAP_BFILT_LSHADE_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | ZSET | SBUF]=JE_ROP_TMAP_BFILT_LMAP_ZSET_SBUF;

		SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | ZTESTSET | SBUF]=JE_ROP_TMAP_BFILT_LMAP_ZSET_SBUF;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | ZSET | SBUF]=JE_ROP_TMAP_BFILT_LSHADE_ZSET_SBUF;

		SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | ZTESTSET | SBUF]=JE_ROP_TMAP_BFILT_LSHADE_ZSET_SBUF;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | ZTESTSET]=JE_ROP_TMAP_BFILT_LSHADE_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AFLAT | NOZ]=JE_ROP_TMAP_BFILT_LSHADE_AFLAT;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AFLAT | ZSET]=JE_ROP_TMAP_BFILT_LSHADE_AFLAT_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AFLAT | ZTEST]=JE_ROP_TMAP_BFILT_LSHADE_AFLAT_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AFLAT | ZTESTSET]=JE_ROP_TMAP_BFILT_LSHADE_AFLAT_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | NOZ]=JE_ROP_TMAP_BFILT_LSHADE_AMAP;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | ZSET]=JE_ROP_TMAP_BFILT_LSHADE_AMAP_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | ZTEST]=JE_ROP_TMAP_BFILT_LSHADE_AMAP_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | ZTESTSET]=JE_ROP_TMAP_BFILT_LSHADE_AMAP_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | AMAP | NOZ]=JE_ROP_TMAP_BFILT_LMAP_AMAP;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | AMAP | ZSET]=JE_ROP_TMAP_BFILT_LMAP_AMAP_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | AMAP | ZTEST]=JE_ROP_TMAP_BFILT_LMAP_AMAP_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | AMAP | ZTESTSET]=JE_ROP_TMAP_BFILT_LMAP_AMAP_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LMAP | AFLAT | ZTESTSET]=JE_ROP_TMAP_BFILT_LMAP_AFLAT_ZTESTSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | AFLAT | NOZ]=JE_ROP_TMAP_BFILT_LSHADE_AMAP_AFLAT;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | AFLAT | ZSET]=JE_ROP_TMAP_BFILT_LSHADE_AMAP_AFLAT_ZSET;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | AFLAT | ZTEST]=JE_ROP_TMAP_BFILT_LSHADE_AMAP_AFLAT_ZTEST;

	SoftDrv_Internals.Rop_Translation[TMAP | BFILT | LSHADE | AMAP | AFLAT | ZTESTSET]=JE_ROP_TMAP_BFILT_LSHADE_AMAP_AFLAT_ZTESTSET;



	// assume the window is active:

	SoftDrv_SetActive(JE_TRUE);

	return	true;

}



jeBoolean DRIVERCC SoftDrv_Shutdown(void)

{

	SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

	

	if (SD_Display)

		Display_Destroy(&SD_Display);

	SD_Display = NULL;



	if(SoftDrv_Internals.ZBufferPtr!=NULL)

		jeRam_Free(SoftDrv_Internals.ZBufferPtr);

	SoftDrv_Internals.ZBufferPtr=NULL;

	SpanBuffer_Destroy();

	return	true;

}



jeBoolean DRIVERCC SoftDrv_SetGamma(float Gamma)

{

	Gamma;

	return true;

}



jeBoolean DRIVERCC SoftDrv_GetGamma(float *Gamma)

{

	assert(Gamma);



	*Gamma = 1.0f;



	return true;

}





jeBoolean	DRIVERCC SoftDrv_UpdateWindow(void)

{

	int32 BitsPerPixel;

	Display_Type DisplayType;

	uint32 Flags;			



	SpanBuffer_Destroy();

	if(SoftDrv_Internals.ZBufferPtr!=NULL)

		jeRam_Free(SoftDrv_Internals.ZBufferPtr);

	SoftDrv_Internals.ZBufferPtr=NULL;



	if (Display_UpdateWindow(SD_Display)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE, "SoftDrv_UpdateWindow:  Could not create new window buffer",NULL);

			Display_Destroy(&SD_Display);

			return	false;

		}

		

	Display_GetDisplayFormat(	SD_Display, 

								&DisplayType, 

								&(ClientWindow.Width),

								&(ClientWindow.Height),

								&BitsPerPixel,

								&Flags);



	if (SpanBuffer_Create(ClientWindow.Width,ClientWindow.Height, ClientWindow.Height * SOFTDRV_MAX_AVG_SPANS_PER_LINE)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE, "SoftDrv_UpdateWindow:  Could not create span buffer",NULL);

			Display_Destroy(&SD_Display);

			return	false;

		}



	{

		U32	OldFlags	=0;



		SoftDrv_Internals.ZBufferPtr=(U16 *)jeRam_Allocate(ClientWindow.Width * ClientWindow.Height * 2);

		

		if(!SoftDrv_Internals.ZBufferPtr)

		{

			jeErrorLog_AddString(JE_ERR_MEMORY_RESOURCE,"SoftDrv_UpdateWindow:  Not enought memory for ZBuffer",NULL);

			Display_Destroy(&SD_Display);

			return false;

		}

#ifdef WIN32

		if(!VirtualProtect((U8 *)SoftDrv_Internals.ZBufferPtr,

			(ClientWindow.Width * ClientWindow.Height)*2,

			PAGE_READWRITE | PAGE_NOCACHE,

			&OldFlags))

		{

			// nothing to do on failure...

		}

#endif



	}



	return	JE_TRUE;

}





jeBoolean DRIVERCC SoftDrv_BeginScene(jeBoolean Clear, jeBoolean ClearZ, RECT *pWorldRect)

{



	pWorldRect;		// unused.



	if (RenderMode != RENDER_NONE)

		{

			jeErrorLog_AddString(JE_ERR_INTERNAL_RESOURCE,"SoftDrv_BeginScene: still in a render mode",jeErrorLog_IntToString(RenderMode));

			return false;

		}



	memset(&SoftDrv_CacheInfo, 0, sizeof(DRV_CacheInfo));



	// Clear the span buffer all the time

	SpanBuffer_Clear();



	if (ClearZ)

		{

			SoftDrv_ClearZBuffer(&ClientWindow);

		}

	

	

	if(!Display_Lock(SD_Display,&(ClientWindow.Buffer), &(ClientWindow.PixelPitch)))

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE ,"SoftDrv_BeginScene: failed to lock display buffer",NULL );

			return	false;

		}



	TRaster_Setup(32,ClientWindow.Width, (uint16 *)ClientWindow.Buffer,SoftDrv_Internals.ZBufferPtr,SoftDrv_LightMapSetupCallback);



	if (Clear)

		{

			if (!Display_Wipe(SD_Display,0))

				{

					jeErrorLog_AddString( JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_BeginScene: failed to wipe display buffer",NULL );

					return	false;

				}

		}



	if (!SoftDrv_BeginBatch())

		return JE_FALSE;



	return true;

}





jeBoolean DRIVERCC SoftDrv_EndScene(void)

{

	assert( RenderMode == RENDER_NONE );



	SoftDrv_EndBatch();



	#if 0

		// useful to examine the zbuffer

		{

			int i;

			unsigned short *b,*z;

			b = (unsigned short *)ClientWindow.Buffer;

			z = (unsigned short *)ZBuffer;

			for (i=ClientWindow.Height * ClientWindow.Width; i>0; i--,b++,z++)

				{

					*b = *z;

				}

		}

	#endif



	if (!Display_Unlock(SD_Display))

		{

			jeErrorLog_AddString( JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_EndScene: failed to unlock display buffer",NULL );

			return	false;

		}

	if (!Display_Blit(SD_Display))

		{

			jeErrorLog_AddString( JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_EndScene: failed to blit display buffer",NULL );

			return	false;

		}

		

	//	SOFTDRV.NumWorldSpans = RegPixels;

	//	SOFTDRV.NumRenderedPolys = RGBPixels;



	return true;

}



// this is exported so that this driver source can double as a dll or statically linked.

DllExport jeBoolean DriverHook(DRV_Driver **Driver)

{



	*Driver = &SOFTDRV;



	// Make sure the error string ptr is not null, or invalid!!!

	SOFTDRV.LastErrorStr = LastErrorStr;





	return true;

}



jeBoolean DRIVERCC SoftDrv_BeginBatch(void)

{

	assert(BatchCounter >= 0);



	if (BatchCounter >= MAX_BATCHES)

		return JE_FALSE;



	BatchCounter++;

	CurrentBatch = BatchCounter-1;



	return JE_TRUE;

}



jeBoolean DRIVERCC SoftDrv_EndBatch(void)

{

	assert(BatchCounter > 0);



	Batch_FlushBatch(&g_PolyMgr[CurrentBatch]);



	BatchCounter--;

	CurrentBatch = BatchCounter-1;



	return JE_TRUE;

}



#ifdef __cplusplus

extern "C" {

#endif



JETAPI	void * jeEngine_SoftwareDriver(void)

{

	return (void *)DriverHook;

}



#ifdef __cplusplus

}

#endif



jeBoolean DRIVERCC SoftDrv_ScreenShot(const char *Name)

{

	Name;

	return false;

}



jeBoolean DRIVERCC SoftDrv_EnumSubDrivers(DRV_ENUM_DRV_CB *Cb, void *Context)

{

	int i;

	

	if (SoftDrv_DisplayInfoTable_Create(&(SoftDrv_Internals), JE_FALSE)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_EnumSubDrivers: failed to get mode info",NULL);

			return false;

		}



	for (i=0; i<SoftDrv_Internals.DisplayCount; i++)

		{

			if(!Cb(i, SoftDrv_Internals.Display[i].Description, Context))

			{

				break;

			}

		}



	SoftDrv_DisplayInfoTable_Destroy(&SoftDrv_Internals);

	return	true;

}



	

jeBoolean DRIVERCC SoftDrv_EnumModes(S32 Driver, char *DriverName, DRV_ENUM_MODES_CB *Cb, void *Context)

{

	int		i,Count;

	DriverName;		// avoid unused parameter;



	if (SoftDrv_DisplayInfoTable_Create(&(SoftDrv_Internals), JE_TRUE)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_EnumModes: failed to get mode info",NULL);

			return false;

		}

	

	if ((Driver < 0) || (Driver>=SoftDrv_Internals.DisplayCount))

		{

			jeErrorLog_AddString(JE_ERR_BAD_PARAMETER,"SoftDrv_EnumModes: bad driver index",NULL);

			SoftDrv_DisplayInfoTable_Destroy(&(SoftDrv_Internals));

			return false;

		}

	



	Count = DisplayModeInfo_GetModeCount( SoftDrv_Internals.Display[Driver].Info );



	for (i=0; i<Count; i++)

		{

			int Width,Height,BitsPerPixel;

			uint32 Flags;



			if (DisplayModeInfo_GetNth(SoftDrv_Internals.Display[Driver].Info,i,

									&Width,&Height,&BitsPerPixel,&Flags) != JE_FALSE)

				{

					char Description[256];

					if (Width<0 && Height<0)

						{

							sprintf(Description,"Window Mode");

						}

					else

						{

							sprintf(Description,"%dx%dx%d",Width, Height, BitsPerPixel);

						}

					if(Flags & MODEXMODE)

						strcat(Description," ModeX");

					if (SD_ProcessorHas3DNow)

						strcat(Description," 3DNow!");

		

					Cb(i,Description,Width,Height,BitsPerPixel,Context);

				}

		}

	SoftDrv_DisplayInfoTable_Destroy(&SoftDrv_Internals);

	return true;

}





static int SoftDrv_ComputeMipLevel(

	const jeTLVertex 	*Pnts,	

	float ScaleU,float ScaleV,

	int MipCount)

{

	float	du, dv, dx, dy, MipScale;

	int MipLevel;



	du	=Pnts[1].u - Pnts[0].u;

	dv	=Pnts[1].v - Pnts[0].v;

	dx	=Pnts[1].x - Pnts[0].x;

	dy	=Pnts[1].y - Pnts[0].y;



	du	/= ScaleU;

	dv	/= ScaleV;



	MipScale	=((du*du)+(dv*dv)) / ((dx*dx)+(dy*dy));



	if(MipScale <= 5)		// 2, 6, 12

		{

			MipLevel	=0;

		}

	else if(MipScale <= 20)

		{

			MipLevel	=1;

		}

	else if(MipScale <= 45)

		{

			MipLevel	=2;

		}

	else

		{

			MipLevel	=3;

		}

	if(MipLevel >= MipCount)

		{

			MipLevel	=MipCount-1;

		}

	return MipLevel;

}

	



jeBoolean DRIVERCC SoftDrv_RenderGouraudPoly(jeTLVertex *Pnts, int32 NumPoints, uint32 Flags)

{

	if(!SD_Active)

		return	JE_TRUE;



	if (Flags & JE_RENDER_FLAG_ALPHA)

	{

		BatchPoly(&g_PolyMgr[CurrentBatch], Pnts, NumPoints, NULL, 0, NULL, Flags, Type_Gouraud);

		return JE_TRUE;

	}



	if (!RenderGouraudPoly(Pnts, NumPoints, Flags))

		return JE_FALSE;



	if (Flags & JE_RENDER_FLAG_FLUSHBATCH)

		Batch_FlushBatch(&g_PolyMgr[CurrentBatch]);



	return JE_TRUE;

}



	// Clean this up:

jeRDriver_Layer		*SoftDrv_TempLayers;

int32				SoftDrv_TempNumLayers;

void				*SoftDrv_TempLMapCBContext;



void SoftDrv_LightMapSetupCallback(TRaster_Lightmap *LM)

{

	jeRDriver_LMapCBInfo	LMapCBInfo;



	assert(SoftDrv_TempNumLayers == 2);



	// Setup the jeRDriver_LMapCBInfo structure

	SOFTDRV.SetupLightmap(&LMapCBInfo, SoftDrv_TempLMapCBContext);



	{

		float					MipScale;

		float					ShiftU,ShiftV;

		float					ScaleU,ScaleV;

		float					LightMapShiftU, LightMapShiftV;

		jeRDriver_Layer			*TexLayer, *LMapLayer;

		jeRDriver_THandleInfo	Info;



		TexLayer = &SoftDrv_TempLayers[0];

		LMapLayer = &SoftDrv_TempLayers[1];

	

		ShiftU = TexLayer->ShiftU;

		ShiftV = TexLayer->ShiftV;

		MipScale = (float)( 1<<LM->MipIndex);

		ScaleU = (1.0f/TexLayer->ScaleU);

		ScaleV = (1.0f/TexLayer->ScaleV);

		

		LightMapShiftU = ((float)(LMapLayer->ShiftU));

		LightMapShiftV = ((float)(LMapLayer->ShiftV));



		LM->LightMapShiftU = (ShiftU+LightMapShiftU*ScaleU)/MipScale;

		LM->LightMapScaleU = (1.0f/(LMapLayer->ScaleU * ScaleU )) * MipScale;

		

		LM->LightMapShiftV = (ShiftV+LightMapShiftV*ScaleV)/MipScale;

		LM->LightMapScaleV = (1.0f/(LMapLayer->ScaleV * ScaleV )) * MipScale;



		LM->BitPtr = (unsigned short *)LMapCBInfo.RGBLight[0];



		SWTHandle_GetInfo(LMapLayer->THandle, 0, &Info);



		LM->Width = Info.Width;

		LM->Height = Info.Height;

	}

}



//#define PROFILE



#ifdef PROFILE

#include "rdtsc.h"

#define AVERAGE_ACROSS 10

double AverageCount[AVERAGE_ACROSS]={

	0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};

	#if 0

	,

	0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

	0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

	0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

	0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

	0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};

	#endif

int AverageIndex = 0;

rdtsc_timer_type RDTSCStart,RDTSCEnd;

	

#endif



jeBoolean DRIVERCC SoftDrv_RenderWorldPoly(	jeTLVertex			*Pnts, 

											int32				NumPoints, 

											jeRDriver_Layer		*Layers,

											int32				NumLayers,

											void				*LMapCBContext,

											uint32				Flags)

{

	jeRDriver_THandleInfo	Info;



	if(!SD_Active)

		return	JE_TRUE;



	#ifdef PROFILE

	rdtsc_read(&RDTSCStart);

    rdtsc_zero(&RDTSCEnd);

	#endif



	SWTHandle_GetInfo(Layers->THandle,0, &Info);



	if (Flags & (JE_RENDER_FLAG_ALPHA|JE_RENDER_FLAG_COLORKEY) || (Info.PixelFormat.Flags & RDRIVER_PF_ALPHA))

	{

		BatchPoly(&g_PolyMgr[CurrentBatch], Pnts, NumPoints, Layers, NumLayers, LMapCBContext, Flags, Type_World);

		return JE_TRUE;

	}



	if (!RenderWorldPoly(Pnts, NumPoints, Layers, NumLayers, LMapCBContext, Flags))

		return JE_FALSE;



	if (Flags & JE_RENDER_FLAG_FLUSHBATCH)

		Batch_FlushBatch(&g_PolyMgr[CurrentBatch]);





	#ifdef PROFILE

	{

		double Count=0.0;

		int i;

		char s[200];



		rdtsc_read(&RDTSCEnd);

		rdtsc_delta(&RDTSCStart,&RDTSCEnd,&RDTSCEnd);

		//jeEngine_Printf(Engine, 320,10,"Render Time=%f",(double)(rdtsc_cycles(&RDTSCEnd)/200000000.0));

		//jeEngine_Printf(Engine, 320,30,"Render Cycles=%f",(double)(rdtsc_cycles(&RDTSCEnd)));

		AverageCount[(AverageIndex++)%AVERAGE_ACROSS] = rdtsc_cycles(&RDTSCEnd);

		for (i=0; i<AVERAGE_ACROSS; i++)

			{	

				Count+=AverageCount[i];

			}

		Count /= (double)AVERAGE_ACROSS;



		if ((AverageIndex%AVERAGE_ACROSS)==0)

			{	

				sprintf(s,"Avg Sec=%f.   (%dx%d)\n",(double)(Count/200000000.0),ClientWindow.Width,ClientWindow.Height);

		//jeEngine_Printf(Engine, 320,60,"AVG Render Time=%f",(double)(Count/200000000.0));

		//jeEngine_Printf(Engine, 320,90,"AVG Render Cycles=%f",(double)(Count));

				OutputDebugString(s);

			}

				

	}

	#endif



	return JE_TRUE;

}



jeBoolean DRIVERCC SoftDrv_RenderMiscTexturePoly(jeTLVertex *Pnts, int32 NumPoints, jeRDriver_Layer *Layers, int32 NumLayers, uint32 Flags)

{

	jeRDriver_THandleInfo	Info;



	if(!SD_Active)

		return	JE_TRUE;



	SWTHandle_GetInfo(Layers->THandle,0, &Info);



	if (Flags & (JE_RENDER_FLAG_ALPHA|JE_RENDER_FLAG_COLORKEY) || (Info.PixelFormat.Flags & RDRIVER_PF_ALPHA))

	{

		BatchPoly(&g_PolyMgr[CurrentBatch], Pnts, NumPoints, Layers, NumLayers, NULL, Flags, Type_Misc);

		return JE_TRUE;

	}



	if (!RenderMiscTexturePoly(Pnts, NumPoints, Layers, NumLayers, Flags))

		return JE_FALSE;



	if (Flags & JE_RENDER_FLAG_FLUSHBATCH)

		Batch_FlushBatch(&g_PolyMgr[CurrentBatch]);



	return JE_TRUE;

}





jeBoolean	DRIVERCC	SoftDrv_ResetAll(void)

{

	return SWTHandle_FreeAllTextureHandles();

}





static jeBoolean DRIVERCC SoftDrv_GetDeviceCaps(jeDeviceCaps *DeviceCaps)

{

	DeviceCaps->SuggestedDefaultRenderFlags = 0;//JE_RENDER_FLAG_BILINEAR_FILTER;

	DeviceCaps->CanChangeRenderFlags = 0xFFFFFFFF;



	return JE_TRUE;

}



DRV_Driver SOFTDRV = 

{

	"Software driver. v"DRV_VMAJS"."DRV_VMINS". Copyright 1998-1999, Eclipse Entertainment; All Rights Reserved.",

	DRV_VERSION_MAJOR,

	DRV_VERSION_MINOR,



	DRV_ERROR_NONE,

	NULL,

	

	SoftDrv_EnumSubDrivers,

	SoftDrv_EnumModes,

	SWTHandle_EnumPixelFormats,



	SoftDrv_GetDeviceCaps, 



	SoftDrv_Init,

	SoftDrv_Shutdown,

	SoftDrv_ResetAll,

	SoftDrv_UpdateWindow,

	SoftDrv_SetActive,



	SWTHandle_CreateTexture,

	SWTHandle_DestroyTexture,



	SWTHandle_LockTextureHandle,

	SWTHandle_UnLockTextureHandle,



	SWTHandle_SetPalette,

	SWTHandle_GetPalette,

	SWTHandle_SetAlpha,

	SWTHandle_GetAlpha,



	SWTHandle_GetInfo,



	SoftDrv_BeginScene,

	SoftDrv_EndScene,



	SoftDrv_BeginBatch,

	SoftDrv_EndBatch,



	SoftDrv_RenderGouraudPoly,

	SoftDrv_RenderWorldPoly,

	SoftDrv_RenderMiscTexturePoly,



	DrawDecal,



	0,0,0,

	

	&SoftDrv_CacheInfo,



	SoftDrv_ScreenShot,



	SoftDrv_SetGamma,

	SoftDrv_GetGamma,



	&SD_EngineSettings,

	NULL,								// Init to NULL, engine SHOULD set this (SetupLightmap)

};



//========================================================================================

//	Internal Poly Mgr to SoftDrv.... (Used for sorting transparent/color-keyed polys

//========================================================================================



//========================================================================================

//	BatchPoly

//========================================================================================

static void BatchPoly(	Batch					*Mgr,

						const jeTLVertex		*Verts, 

						int32					NumVerts, 

						jeRDriver_Layer			*Layers,

						int32					NumLayers,

						void					*LMapCBContext,

						uint32					Flags,

						uint8					Type)

{

	jeTLVertex		*pVert;

	int32			i;

	Batch_Poly		*pPoly;



	if (Mgr->TotalPolys+1 > MAX_BATCHED_POLYS)

		Batch_FlushBatch(Mgr);

	else if (Mgr->TotalVerts+NumVerts > MAX_BATCHED_VERTS)

		Batch_FlushBatch(Mgr);



	pPoly = &Mgr->Polys[Mgr->TotalPolys];



	pPoly->NumVerts = NumVerts;

	pPoly->Verts = &Mgr->Verts[Mgr->TotalVerts];

	pPoly->LMapCBContext = LMapCBContext;

	pPoly->Flags = Flags;

	pPoly->NumLayers = NumLayers;

	pPoly->Type = Type;



	for (i=0; i< NumLayers; i++)

		pPoly->Layers[i] = Layers[i];



	assert(LMapCBContext || NumLayers <= 1);



	pVert = pPoly->Verts;



	// Find the center of the poly to use for sorting

	pPoly->CenterZ = 0.0f;



	for (i=0; i< NumVerts; i++, pVert++, Verts++)

	{

		*pVert = *Verts;

	

		pPoly->CenterZ += pVert->z;

	}



	pPoly->CenterZ /= NumVerts;



	// Update counts

	Mgr->TotalPolys++;

	Mgr->TotalVerts += NumVerts;

}



//====================================================================================

//====================================================================================

static int PolyComp(const void *a, const void *b)

{

	Batch_Poly	*pPoly1, *pPoly2;



	pPoly1 = (*(Batch_Poly**)a);

	pPoly2 = (*(Batch_Poly**)b);



	// We should be working in 1/Z space

	if (pPoly1->CenterZ == pPoly2->CenterZ)		

		return 0;



	if (pPoly1->CenterZ > pPoly2->CenterZ)

		return -1;



	return 1;

}



//====================================================================================

//====================================================================================

static void SortPolys(Batch *Mgr)

{

	Batch_Poly		*pPoly;

	int32			i;



	pPoly = Mgr->Polys;



	for (i=0; i<Mgr->TotalPolys; i++, pPoly++)

		Mgr->SortedPolys[i] = pPoly;

	

	// Sort the polys

	qsort(&Mgr->SortedPolys, Mgr->TotalPolys, sizeof(Mgr->SortedPolys[0]), PolyComp);

}



//========================================================================================

//	Batch_FlushBatch

//========================================================================================

static void Batch_FlushBatch(Batch *Mgr)

{

	int32			i;



	SortPolys(Mgr);



	for (i=0; i< Mgr->TotalPolys; i++)

	{

		Batch_Poly			*pPoly;

		uint32				Flags;



		pPoly = Mgr->SortedPolys[i];



		Flags = pPoly->Flags;



		switch (pPoly->Type)

		{

			case Type_Gouraud:

			{

				assert(pPoly->NumLayers == 0);

				assert(!pPoly->LMapCBContext);



				RenderGouraudPoly((jeTLVertex*)pPoly->Verts, pPoly->NumVerts, Flags);

				break;

			}



			case Type_Misc:

			{

				assert(pPoly->NumLayers == 1);

				assert(!pPoly->LMapCBContext);

				RenderMiscTexturePoly((jeTLVertex*)pPoly->Verts, pPoly->NumVerts, pPoly->Layers, 1, Flags);

				break;

			}



			case Type_World:

			{

				if (pPoly->NumLayers == 2)

				{

					assert(pPoly->LMapCBContext);

					RenderWorldPoly((jeTLVertex*)pPoly->Verts, pPoly->NumVerts, pPoly->Layers, 2, pPoly->LMapCBContext, Flags);

				}

				else

				{

					assert(!pPoly->LMapCBContext);

					RenderWorldPoly((jeTLVertex*)pPoly->Verts, pPoly->NumVerts, pPoly->Layers, 1, NULL, Flags);

				}



				break;

			}



			default:

				assert(0);

		}

	}



	// Update counts

	Mgr->TotalPolys = 0;

	Mgr->TotalVerts = 0;

}



//====================================================================================

//====================================================================================

static jeBoolean DRIVERCC RenderGouraudPoly(jeTLVertex *Pnts, int32 NumPoints, uint32 Flags)

{

	int				i;

	jeROP			ROP;

#ifndef DO_CLOCKWISE_CHECK	

	int32			b, c;

#endif

	jeTLVertex		Pnts2[3];



	assert(Pnts != NULL);

	assert(NumPoints > 2);



	assert( Flags < SOFTDRV_ROP_XLATE_TABLE );

	{

		int F = Flags&~(JE_RENDER_FLAG_FLUSHBATCH | JE_RENDER_FLAG_COUNTER_CLOCKWISE | JE_RENDER_FLAG_SPECULAR | JE_RENDER_FLAG_CLAMP_UV | JE_RENDER_FLAG_BILINEAR_FILTER );

		assert(SoftDrv_Internals.Rop_Translation[F]!=JE_ROP_END);

		ROP = (jeROP)SoftDrv_Internals.Rop_Translation[F];

		TRaster_SetClamping(Flags&JE_RENDER_FLAG_CLAMP_UV);

	}



	// note : rop cracking logic removed.  Copied to end of file for reference.





#ifndef DO_CLOCKWISE_CHECK	

	if (Flags & JE_RENDER_FLAG_COUNTER_CLOCKWISE)

	{

		b = 2;

		c = 1;

	}

	else

	{

		b = 1;

		c = 2;

	}

#endif



	#ifdef _DEBUG

	{

		// Sanity check 

		for(i=0;i < NumPoints;i++)

		{

			assert( Pnts[i].x >= 0 ) ;

			assert( Pnts[i].y >= 0 ) ;

		

			assert( Pnts[i].x < ClientWindow.Width ) ;

			assert( Pnts[i].y < ClientWindow.Height ) ;

		}

	}

	#endif



	// Pnts2[0] will remain the same the entire fanning process

	Pnts2[0] = Pnts[0];



	for(i=0;i < NumPoints-2;i++)

	{



	#ifdef DO_CLOCKWISE_CHECK	

		if (  (((Pnts[i+1].x-Pnts[0].x) * (Pnts[i+2].y-Pnts[0].y)) - ((Pnts[i+1].y-Pnts[0].y)*(Pnts[i+2].x-Pnts[0].x)))<0.0f)

		{

			Pnts2[0] = Pnts[i+2];

			Pnts2[1] = Pnts[i+1];

			Pnts2[2] = Pnts[0];

		}

		else

		{

			Pnts2[0] = Pnts[0];

			Pnts2[1] = Pnts[i+1];

			Pnts2[2] = Pnts[i+2];

		}

	#else

		Pnts2[b] = Pnts[i+1];

		Pnts2[c] = Pnts[i+2];

	#endif



		if (TRaster_Rasterize( ROP ,NULL, 0, Pnts2 )==JE_FALSE)

			{

				jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_RenderGouradPoly: failed to rasterize",NULL);

				return JE_FALSE;

			}



	}

	

 	return JE_TRUE;

}



//====================================================================================

//====================================================================================

static jeBoolean DRIVERCC RenderWorldPoly(	jeTLVertex			*Pnts, 

											int32				NumPoints, 

											jeRDriver_Layer		*Layers,

											int32				NumLayers,

											void				*LMapCBContext,

											uint32				Flags)

{

	int32					i;

	jeROP					ROP;

	int						MipLevel;

	int						MipCount;

	jeRDriver_THandleInfo	THandleInfo;

	jeRDriver_THandle		*THandle;



	assert(Pnts != NULL);

	assert(NumPoints > 2);

	assert(Layers);

	assert(NumLayers > 0);



	THandle = Layers->THandle;		// Grab the main texture handle (Layer 0)

	assert(THandle != NULL);



	SWTHandle_GetInfo(THandle,0,&THandleInfo);

	MipCount = SWTHandle_GetMipCount(THandle);



	// note : rop cracking logic removed.  Copied to end of file for reference.



	//if (THandle)

		Flags |= SOFTDRV_RENDER_FLAG_TMAP;

	if (THandleInfo.PixelFormat.PixelFormat==JE_PIXELFORMAT_16BIT_4444_ARGB)

		Flags |= JE_RENDER_FLAG_COLORKEY;

	if (NumLayers > 1)

		Flags |= SOFTDRV_RENDER_FLAG_LMAP;



	assert( Flags < SOFTDRV_ROP_XLATE_TABLE );

	{

		int F = Flags&~(JE_RENDER_FLAG_FLUSHBATCH | JE_RENDER_FLAG_COUNTER_CLOCKWISE | JE_RENDER_FLAG_SPECULAR | JE_RENDER_FLAG_CLAMP_UV);

		assert(SoftDrv_Internals.Rop_Translation[F]!=JE_ROP_END);

		ROP = (jeROP)SoftDrv_Internals.Rop_Translation[F];

		TRaster_SetClamping(Flags&JE_RENDER_FLAG_CLAMP_UV);

	}



	{

		jeTLVertex	Pnts2[3];

		float		OOW,OOH;

		float		ShiftU,ShiftV,ScaleU,ScaleV;

		int32		b, c;



		// this scaling work can be done once at texture setup time

		ShiftU = Layers->ShiftU;

		ShiftV = Layers->ShiftV;

		ScaleU = 1.0f/Layers->ScaleU;

		ScaleV = 1.0f/Layers->ScaleV;

		OOW = 1.0f / (float)THandleInfo.Width;

		OOH = 1.0f / (float)THandleInfo.Height;

		

		SoftDrv_TempLayers = Layers;

		SoftDrv_TempNumLayers = NumLayers;

		SoftDrv_TempLMapCBContext = LMapCBContext;



		Pnts2[0] = Pnts[0];

		Pnts2[0].u = (Pnts2[0].u*ScaleU+ShiftU)*OOW;

		Pnts2[0].v = (Pnts2[0].v*ScaleV+ShiftV)*OOH;



		MipLevel = SoftDrv_ComputeMipLevel(Pnts, Layers->ScaleU, Layers->ScaleV, MipCount);



		if (Flags & JE_RENDER_FLAG_COUNTER_CLOCKWISE)

		{

			b = 2;

			c = 1;

		}

		else

		{

			b = 1;

			c = 2;

		}



		#ifdef _DEBUG

		{

			// Sanity check 

			for(i=0;i < NumPoints;i++)

			{

				assert( Pnts[i].x >= 0 ) ;

				assert( Pnts[i].y >= 0 ) ;

		

				assert( Pnts[i].x < ClientWindow.Width ) ;

				assert( Pnts[i].y < ClientWindow.Height ) ;

			}

		}

		#endif



		for(i=0;i < NumPoints-2;i++)

			{

				Pnts2[b] = Pnts[i+1];

				Pnts2[c] = Pnts[i+2];



				Pnts2[b].u = (Pnts2[b].u*ScaleU+ShiftU)*OOW;

				Pnts2[b].v = (Pnts2[b].v*ScaleV+ShiftV)*OOH;

				Pnts2[c].u = (Pnts2[c].u*ScaleU+ShiftU)*OOW;

				Pnts2[c].v = (Pnts2[c].v*ScaleV+ShiftV)*OOH;



				if (TRaster_Rasterize(  ROP, THandle, MipLevel, Pnts2 )==JE_FALSE)

					{

						jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_RenderWorldPoly: rasterize failed",NULL);

						return JE_FALSE;

					}



			}

	}

 	return JE_TRUE;

}



//====================================================================================

//====================================================================================

static jeBoolean DRIVERCC RenderMiscTexturePoly(jeTLVertex *Pnts, int32 NumPoints, jeRDriver_Layer *Layers, int32 NumLayers, uint32 Flags)

{

	int					i;

	jeROP				ROP;

	int					MipLevel;

	jeRDriver_THandle	*THandle;

#ifndef DO_CLOCKWISE_CHECK	

	int32				b, c;

#endif

	jeTLVertex	Pnts2[3];



	assert(Pnts != NULL);

	assert(NumPoints > 2);

	assert(Layers);

	assert(NumLayers == 1);



	THandle = Layers->THandle;

	assert(THandle);





	{

		jeRDriver_THandleInfo THandleInfo;

		//if (THandle)

			{

				SWTHandle_GetInfo(THandle,0,&THandleInfo);

				Flags |= SOFTDRV_RENDER_FLAG_TMAP;

			}

		if (THandleInfo.PixelFormat.PixelFormat==JE_PIXELFORMAT_16BIT_4444_ARGB)

			Flags |= JE_RENDER_FLAG_COLORKEY;

		assert( Flags < SOFTDRV_ROP_XLATE_TABLE );

		{

			int F = Flags&~(JE_RENDER_FLAG_FLUSHBATCH | JE_RENDER_FLAG_COUNTER_CLOCKWISE | JE_RENDER_FLAG_SPECULAR | JE_RENDER_FLAG_CLAMP_UV);

			assert(SoftDrv_Internals.Rop_Translation[F]!=JE_ROP_END);

			ROP = (jeROP)SoftDrv_Internals.Rop_Translation[F];

			TRaster_SetClamping(Flags&JE_RENDER_FLAG_CLAMP_UV);

		}

	}



	// note : rop cracking logic removed.  Copied to end of file for reference.



	// message ("Automatically make a 4444 from other formats?")



#ifndef DO_CLOCKWISE_CHECK	

	if (Flags & JE_RENDER_FLAG_COUNTER_CLOCKWISE)

	{

		b = 2;

		c = 1;

	}

	else

	{

		b = 1;

		c = 2;

	}

#endif



	#ifdef _DEBUG

	{

		// Sanity check 

		for(i=0;i < NumPoints;i++)

		{

			assert( Pnts[i].x >= 0 ) ;

			assert( Pnts[i].y >= 0 ) ;

		

			assert( Pnts[i].x < ClientWindow.Width ) ;

			assert( Pnts[i].y < ClientWindow.Height ) ;

		}

	}

	#endif



	// Pnts2[0] will remain the same the entire fanning process

	Pnts2[0] = Pnts[0];



	MipLevel = 0;



	if (THandle)

		{

			int MipCount = SWTHandle_GetMipCount(THandle);

			if (MipCount>1)

				MipLevel = SoftDrv_ComputeMipLevel(Pnts,1.0f,1.0f,MipCount);

		}



	for(i=0;i < NumPoints-2;i++)

	{

	#ifdef DO_CLOCKWISE_CHECK	

		if (  (((Pnts[i+1].x-Pnts[0].x) * (Pnts[i+2].y-Pnts[0].y)) - ((Pnts[i+1].y-Pnts[0].y)*(Pnts[i+2].x-Pnts[0].x)))<0.0f)

		{

			Pnts2[0] = Pnts[i+2];

			Pnts2[1] = Pnts[i+1];

			Pnts2[2] = Pnts[0];

		}

		else

		{

			Pnts2[0] = Pnts[0];

			Pnts2[1] = Pnts[i+1];

			Pnts2[2] = Pnts[i+2];

		}

	#else

		Pnts2[b] = Pnts[i+1];

		Pnts2[c] = Pnts[i+2];

	#endif



		if (TRaster_Rasterize(  ROP,THandle,MipLevel, Pnts2 )==JE_FALSE)

			{

				jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"SoftDrv_RenderMiscTexturePoly: rasterize failed",NULL);

				return JE_FALSE;

			}

	}

	

 	return JE_TRUE;

}











#if 0



world poly rop:-------



#ifdef USE_RENDER_STATES

	// figure out which rop.  Based on lightmap, pixel format, alpha, and RenderMode.  Ick.

	if (RenderMode==RENDER_WORLD)

		{

			// always z set but not test.

			assert( !(Flags & DRV_RENDER_ALPHA) );

			assert( !(THandle->PixelFormat.PixelFormat ==JE_PIXELFORMAT_16BIT_4444_ARGB));



			if (LInfo)

				{

					ROP = JE_ROP_TMAP_LMAP_ZSET_SBUF;

				}

			else

				{

					ROP = JE_ROP_TMAP_LSHADE_ZSET_SBUF;	

				}

		}

	else

		{

			if (LInfo)

				{

					if (THandle->PixelFormat.PixelFormat ==JE_PIXELFORMAT_16BIT_4444_ARGB)

						{

							ROP = JE_ROP_TMAP_LMAP_AMAP_ZTESTSET;

						}

					else if (Flags & DRV_RENDER_ALPHA)

						{

							ROP = JE_ROP_TMAP_LMAP_AFLAT_ZTESTSET;

						}

					else 

						{	

							// assert( Pnts.a == 255.0f );

							ROP = JE_ROP_TMAP_LMAP_ZTESTSET;

						}

				}

			else

				{

					if (THandle->PixelFormat.PixelFormat ==JE_PIXELFORMAT_16BIT_4444_ARGB)

						{

							ROP = JE_ROP_TMAP_LSHADE_AMAP_ZTESTSET;	

						}

					else if (Flags & DRV_RENDER_ALPHA)

						{

							ROP = JE_ROP_TMAP_LSHADE_AFLAT_ZTESTSET;

						}

					else 

						{	

							// assert( Pnts.a == 255.0f );

							ROP = JE_ROP_TMAP_LSHADE_ZTESTSET;

						}

				}

		}

#else

//Trial:

#if 0

	// Figure out which rop.  

	if (Flags & JE_RENDER_FLAG_SWRITE)				// Span Writes

	{

		assert(Flags & JE_RENDER_FLAG_STEST);		// We currently only support both SWRITE and SMASK flags together

		assert(!(Flags & JE_RENDER_FLAG_ALPHA));	// We don't support Alpha with spans (They are expected to be F2B which doesn't make sense to support alpha!)

		assert(!(THandleInfo.PixelFormat.PixelFormat == JE_PIXELFORMAT_16BIT_4444_ARGB));



		// Always z set but not test.

		if (NumLayers > 1)

			ROP = JE_ROP_TMAP_LMAP_ZSET_SBUF;

		else

			ROP = JE_ROP_TMAP_LSHADE_ZSET_SBUF;	

	}

	else

	{

		if (NumLayers > 1)

		{

			if (THandleInfo.PixelFormat.PixelFormat ==JE_PIXELFORMAT_16BIT_4444_ARGB)

				ROP = JE_ROP_TMAP_LMAP_AMAP_ZTESTSET;

			else if (Flags & JE_RENDER_FLAG_ALPHA)

				ROP = JE_ROP_TMAP_LMAP_AFLAT_ZTESTSET;

			else 

				ROP = JE_ROP_TMAP_LMAP_ZTESTSET;

		}

		else

		{

			if (THandleInfo.PixelFormat.PixelFormat ==JE_PIXELFORMAT_16BIT_4444_ARGB)

				ROP = JE_ROP_TMAP_LSHADE_AMAP_ZTESTSET;	

			else if (Flags & JE_RENDER_FLAG_ALPHA)

				ROP = JE_ROP_TMAP_LSHADE_AFLAT_ZTESTSET;

			else 

				ROP = JE_ROP_TMAP_LSHADE_ZTESTSET;

		}

	}

#endif

#endif

---------------

gouraud

jeROP SoftDrv_GouraudFlagsToRop[16] = 

{

	JE_ROP_LSHADE_ZTESTSET,			//		ZCHK	ZWRITE

	JE_ROP_LSHADE_AFLAT_ZTESTSET,	//alpha	ZCHK	ZWRITE

	JE_ROP_LSHADE_ZTESTSET,			//		ZCHK	ZWRITE

	JE_ROP_LSHADE_AFLAT_ZTESTSET,	//alpha	ZCHK	ZWRITE

	JE_ROP_LSHADE_ZSET,				//		nozchk	ZWRITE

	JE_ROP_LSHADE_AFLAT_ZSET,		//alpha nozchk	ZWRITE

	JE_ROP_LSHADE_ZSET,				//		nozchk	ZWRITE

	JE_ROP_LSHADE_AFLAT_ZSET,		//alpha nozchk	ZWRITE

	JE_ROP_LSHADE_ZTEST,			//		ZCHK	nozwrite

	JE_ROP_LSHADE_AFLAT_ZTEST,		//alpha	ZCHK	nozwrite

	JE_ROP_LSHADE_ZTEST,			//		ZCHK	nozwrite

	JE_ROP_LSHADE_AFLAT_ZTEST,		//alpha	ZCHK	nozwrite

	JE_ROP_LSHADE,					//		nozchk  nozwrite

	JE_ROP_LSHADE_AFLAT,			//alpha nozchk  nozwrite

	JE_ROP_LSHADE,					//		nozchk	nozwrite

	JE_ROP_LSHADE_AFLAT,			//alpha	nozchk	nozwrite

};



	ROP = SoftDrv_GouraudFlagsToRop[Flags & 0xF];



---------------

misc



	ROP = GetRopFromRenderFlags(THandle, Flags);





static jeROP GetRopFromRenderFlags(jeRDriver_THandle *THandle, uint32 Flags)

{

	jeRDriver_THandleInfo THandleInfo;

	if (THandle)	

		{

			SWTHandle_GetInfo(THandle,0,&THandleInfo);

		}

	else

		{

			THandleInfo.PixelFormat.PixelFormat = JE_PIXELFORMAT_NO_DATA;

		}



	//Trial:

	if (THandleInfo.PixelFormat.PixelFormat==JE_PIXELFORMAT_16BIT_4444_ARGB)

		Flags |= JE_RENDER_FLAG_COLORKEY;

	if (THandle)

		Flags |= SOFTDRV_RENDER_FLAG_TMAP;

	assert( Flags < SOFTDRV_ROP_XLATE_TABLE );

	{

		int F = Flags&~(JE_RENDER_FLAG_FLUSHBATCH | JE_RENDER_FLAG_COUNTER_CLOCKWISE | JE_RENDER_FLAG_SPECULAR | JE_RENDER_FLAG_CLAMP_UV);

		assert(SoftDrv_Internals.Rop_Translation[F]!=JE_ROP_END);

		TRaster_SetClamping(Flags&JE_RENDER_FLAG_CLAMP_UV);

		return SoftDrv_Internals.Rop_Translation[F];

	}



	#if 0

		

	if (THandleInfo.PixelFormat.PixelFormat==JE_PIXELFORMAT_16BIT_4444_ARGB)

	{

		// Combine with Rops that support texture alpha (AMAP)

		if (Flags & JE_RENDER_FLAG_ALPHA)

		{

			if (!(Flags & JE_RENDER_FLAG_NO_ZTEST))

			{

				if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

					return JE_ROP_TMAP_LSHADE_AMAP_AFLAT_ZTESTSET;

				else

					return JE_ROP_TMAP_LSHADE_AMAP_AFLAT_ZTEST;

			}

			else if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

				return JE_ROP_TMAP_LSHADE_AMAP_AFLAT_ZSET;

			else

				return JE_ROP_TMAP_LSHADE_AMAP_AFLAT;

		}

		else 

		{

			if (!(Flags & JE_RENDER_FLAG_NO_ZTEST))

			{

				if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

					return JE_ROP_TMAP_LSHADE_AMAP_ZTESTSET;

				else

					return JE_ROP_TMAP_LSHADE_AMAP_ZTEST;

			}

			else if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

				return JE_ROP_TMAP_LSHADE_AMAP_ZSET;

			else

				return JE_ROP_TMAP_LSHADE_AMAP;

		}

	}

	else

	{

		if (Flags & JE_RENDER_FLAG_ALPHA)

		{

			if (!(Flags & JE_RENDER_FLAG_NO_ZTEST))

			{

				if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

					return JE_ROP_TMAP_LSHADE_AFLAT_ZTESTSET;

				else

					return JE_ROP_TMAP_LSHADE_AFLAT_ZTEST;

			}

			else if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

				return JE_ROP_TMAP_LSHADE_AFLAT_ZSET;

			else

				return JE_ROP_TMAP_LSHADE_AFLAT;

		}

		else 

		{

			if (!(Flags & JE_RENDER_FLAG_NO_ZTEST))

			{

				if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

					return JE_ROP_TMAP_LSHADE_ZTESTSET;

				else

					return JE_ROP_TMAP_LSHADE_ZTEST;

			}

			else if (!(Flags & JE_RENDER_FLAG_NO_ZWRITE))

				return JE_ROP_TMAP_LSHADE_ZSET;

			else

				return JE_ROP_TMAP_LSHADE;

		}

	}



	assert(0);

	return 0;

#endif

}







#endif