code:
#include "common.h"

#include "SpecialFX.h"
#include "RenderBuffer.h"
#include "Timer.h"
#include "Sprite.h"
#include "Font.h"
#include "Text.h"
#include "TxdStore.h"
#include "FileMgr.h"
#include "FileLoader.h"
#include "Timecycle.h"
#include "Lights.h"
#include "ModelIndices.h"
#include "VisibilityPlugins.h"
#include "World.h"
#include "PlayerPed.h"
#include "Particle.h"
#include "Shadows.h"
#include "General.h"
#include "Camera.h"
#include "Shadows.h"
#include "main.h"

RwIm3DVertex StreakVertices[4];
RwImVertexIndex StreakIndexList[12];

RwIm3DVertex TraceVertices[6];
RwImVertexIndex TraceIndexList[12];

void
CSpecialFX::Init(void)
{
	CBulletTraces::Init();

	RwIm3DVertexSetU(&StreakVertices[0], 0.0f);
	RwIm3DVertexSetV(&StreakVertices[0], 0.0f);
	RwIm3DVertexSetU(&StreakVertices[1], 1.0f);
	RwIm3DVertexSetV(&StreakVertices[1], 0.0f);
	RwIm3DVertexSetU(&StreakVertices[2], 0.0f);
	RwIm3DVertexSetV(&StreakVertices[2], 0.0f);
	RwIm3DVertexSetU(&StreakVertices[3], 1.0f);
	RwIm3DVertexSetV(&StreakVertices[3], 0.0f);

	StreakIndexList[0] = 0;
	StreakIndexList[1] = 1;
	StreakIndexList[2] = 2;
	StreakIndexList[3] = 1;
	StreakIndexList[4] = 3;
	StreakIndexList[5] = 2;
	StreakIndexList[6] = 0;
	StreakIndexList[7] = 2;
	StreakIndexList[8] = 1;
	StreakIndexList[9] = 1;
	StreakIndexList[10] = 2;
	StreakIndexList[11] = 3;

	RwIm3DVertexSetRGBA(&TraceVertices[0], 20, 20, 20, 255);
	RwIm3DVertexSetRGBA(&TraceVertices[1], 20, 20, 20, 255);
	RwIm3DVertexSetRGBA(&TraceVertices[2], 70, 70, 70, 255);
	RwIm3DVertexSetRGBA(&TraceVertices[3], 70, 70, 70, 255);
	RwIm3DVertexSetRGBA(&TraceVertices[4], 10, 10, 10, 255);
	RwIm3DVertexSetRGBA(&TraceVertices[5], 10, 10, 10, 255);
	RwIm3DVertexSetU(&TraceVertices[0], 0.0);
	RwIm3DVertexSetV(&TraceVertices[0], 0.0);
	RwIm3DVertexSetU(&TraceVertices[1], 1.0);
	RwIm3DVertexSetV(&TraceVertices[1], 0.0);
	RwIm3DVertexSetU(&TraceVertices[2], 0.0);
	RwIm3DVertexSetV(&TraceVertices[2], 0.5);
	RwIm3DVertexSetU(&TraceVertices[3], 1.0);
	RwIm3DVertexSetV(&TraceVertices[3], 0.5);
	RwIm3DVertexSetU(&TraceVertices[4], 0.0);
	RwIm3DVertexSetV(&TraceVertices[4], 1.0);
	RwIm3DVertexSetU(&TraceVertices[5], 1.0);
	RwIm3DVertexSetV(&TraceVertices[5], 1.0);

	TraceIndexList[0] = 0;
	TraceIndexList[1] = 2;
	TraceIndexList[2] = 1;
	TraceIndexList[3] = 1;
	TraceIndexList[4] = 2;
	TraceIndexList[5] = 3;
	TraceIndexList[6] = 2;
	TraceIndexList[7] = 4;
	TraceIndexList[8] = 3;
	TraceIndexList[9] = 3;
	TraceIndexList[10] = 4;
	TraceIndexList[11] = 5;

	CMotionBlurStreaks::Init();
	CBrightLights::Init();
	CShinyTexts::Init();
	CMoneyMessages::Init();
	C3dMarkers::Init();
}

RwObject*
LookForBatCB(RwObject *object, void *data)
{
	static CMatrix MatLTM;

	if(CVisibilityPlugins::GetAtomicModelInfo((RpAtomic*)object) == (CSimpleModelInfo*)data){
		MatLTM = CMatrix(RwFrameGetLTM(RpAtomicGetFrame((RpAtomic*)object)));
		CVector p1 = MatLTM * CVector(0.02f, 0.05f, 0.07f);
		CVector p2 = MatLTM * CVector(0.246f, 0.0325f, 0.796f);
		CMotionBlurStreaks::RegisterStreak((uintptr)object, 100, 100, 100, p1, p2);
	}
	return nil;
}

void
CSpecialFX::Update(void)
{
	CMotionBlurStreaks::Update();
	CBulletTraces::Update();

	if(FindPlayerPed() &&
	   FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT &&
	   FindPlayerPed()->GetWeapon()->m_eWeaponState == WEAPONSTATE_FIRING){
#ifdef PED_SKIN
		if(IsClumpSkinned(FindPlayerPed()->GetClump())){
			LookForBatCB((RwObject*)FindPlayerPed()->m_pWeaponModel, CModelInfo::GetModelInfo(MI_BASEBALL_BAT));
		}else
#endif
			RwFrameForAllObjects(FindPlayerPed()->m_pFrames[PED_HANDR]->frame, LookForBatCB, CModelInfo::GetModelInfo(MI_BASEBALL_BAT));
	}
}

void
CSpecialFX::Shutdown(void)
{
	C3dMarkers::Shutdown();
}

void
CSpecialFX::Render(void)
{
	PUSH_RENDERGROUP("CSpecialFX::Render");
	CMotionBlurStreaks::Render();
	CBulletTraces::Render();
	CBrightLights::Render();
	CShinyTexts::Render();
	CMoneyMessages::Render();
#ifdef NEW_RENDERER
	if(!(gbNewRenderer && FredIsInFirstPersonCam()))
#endif
	C3dMarkers::Render();
	POP_RENDERGROUP();
}

CRegisteredMotionBlurStreak CMotionBlurStreaks::aStreaks[NUMMBLURSTREAKS];

void
CRegisteredMotionBlurStreak::Update(void)
{
	int i;
	bool wasUpdated;
	bool lastWasUpdated = false;
	for(i = 2; i > 0; i--){
		m_pos1[i] = m_pos1[i-1];
		m_pos2[i] = m_pos2[i-1];
		m_isValid[i] = m_isValid[i-1];
		wasUpdated = true;
		if(!lastWasUpdated && !m_isValid[i])
			wasUpdated = false;
		lastWasUpdated = wasUpdated;
	}
	m_isValid[0] = false;
	if(!wasUpdated)
		m_id = 0;
}

void
CRegisteredMotionBlurStreak::Render(void)
{
	int i;
	int a1, a2;
	for(i = 0; i < 2; i++)
		if(m_isValid[i] && m_isValid[i+1]){
			a1 = (255/3)*(3-i)/3;
			RwIm3DVertexSetRGBA(&StreakVertices[0], m_red, m_green, m_blue, a1);
			RwIm3DVertexSetRGBA(&StreakVertices[1], m_red, m_green, m_blue, a1);
			a2 = (255/3)*(3-(i+1))/3;
			RwIm3DVertexSetRGBA(&StreakVertices[2], m_red, m_green, m_blue, a2);
			RwIm3DVertexSetRGBA(&StreakVertices[3], m_red, m_green, m_blue, a2);
			RwIm3DVertexSetPos(&StreakVertices[0], m_pos1[i].x, m_pos1[i].y, m_pos1[i].z);
			RwIm3DVertexSetPos(&StreakVertices[1], m_pos2[i].x, m_pos2[i].y, m_pos2[i].z);
			RwIm3DVertexSetPos(&StreakVertices[2], m_pos1[i+1].x, m_pos1[i+1].y, m_pos1[i+1].z);
			RwIm3DVertexSetPos(&StreakVertices[3], m_pos2[i+1].x, m_pos2[i+1].y, m_pos2[i+1].z);
			LittleTest();
			if(RwIm3DTransform(StreakVertices, 4, nil, rwIM3D_VERTEXUV)){
				RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, StreakIndexList, 12);
				RwIm3DEnd();
			}
		}
}

void
CMotionBlurStreaks::Init(void)
{
	int i;
	for(i = 0; i < NUMMBLURSTREAKS; i++)
		aStreaks[i].m_id = 0;
}

void
CMotionBlurStreaks::Update(void)
{
	int i;
	for(i = 0; i < NUMMBLURSTREAKS; i++)
		if(aStreaks[i].m_id != 0)
			aStreaks[i].Update();
}

void
CMotionBlurStreaks::RegisterStreak(uintptr id, uint8 r, uint8 g, uint8 b, CVector p1, CVector p2)
{
	int i;
	for(i = 0; i < NUMMBLURSTREAKS; i++){
		if(aStreaks[i].m_id == id){
			// Found a streak from last frame, update
			aStreaks[i].m_red = r;
			aStreaks[i].m_green = g;
			aStreaks[i].m_blue = b;
			aStreaks[i].m_pos1[0] = p1;
			aStreaks[i].m_pos2[0] = p2;
			aStreaks[i].m_isValid[0] = true;
			return;
		}
	}
	// Find free slot
	for(i = 0; aStreaks[i].m_id != 0; i++)
		if(i == NUMMBLURSTREAKS-1)
			return;
	// Create a new streak
	aStreaks[i].m_id = id;
	aStreaks[i].m_red = r;
	aStreaks[i].m_green = g;
	aStreaks[i].m_blue = b;
	aStreaks[i].m_pos1[0] = p1;
	aStreaks[i].m_pos2[0] = p2;
	aStreaks[i].m_isValid[0] = true;
	aStreaks[i].m_isValid[1] = false;
	aStreaks[i].m_isValid[2] = false;
}

void
CMotionBlurStreaks::Render(void)
{
	bool setRenderStates = false;
	int i;
	for(i = 0; i < NUMMBLURSTREAKS; i++)
		if(aStreaks[i].m_id != 0){
			if(!setRenderStates){
				RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
				RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
				RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE);
				RwRenderStateSet(rwRENDERSTATEFOGCOLOR,
					(void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255));
				RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
				RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
				RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)FALSE);
 				setRenderStates = true;
			}
			aStreaks[i].Render();
		}
	if(setRenderStates){
		RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
		RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
		RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE);
	}
}

CBulletTrace CBulletTraces::aTraces[NUMBULLETTRACES];

void CBulletTraces::Init(void)
{
	for (int i = 0; i < NUMBULLETTRACES; i++)
		aTraces[i].m_bInUse = false;
}

void CBulletTraces::AddTrace(CVector* vecStart, CVector* vecTarget)
{
	int index;
	for (index = 0; index < NUMBULLETTRACES; index++) {
		if (!aTraces[index].m_bInUse)
			break;
	}
	if (index == NUMBULLETTRACES)
		return;
	aTraces[index].m_vecCurrentPos = *vecStart;
	aTraces[index].m_vecTargetPos = *vecTarget;
	aTraces[index].m_bInUse = true;
	aTraces[index].m_framesInUse = 0;
	aTraces[index].m_lifeTime = 25 + CGeneral::GetRandomNumber() % 32;
}

void CBulletTraces::Render(void)
{
	for (int i = 0; i < NUMBULLETTRACES; i++) {
		if (!aTraces[i].m_bInUse)
			continue;
		RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
#ifdef FIX_BUGS
		// Raster has no transparent pixels so it relies on the raster format having alpha
		// to turn on blending. librw image conversion might get rid of it right now so let's
		// just force it on.
		RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
#endif
		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
		RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpShadowExplosionTex));
		CVector inf = aTraces[i].m_vecCurrentPos;
		CVector sup = aTraces[i].m_vecTargetPos;
		CVector center = (inf + sup) / 2;
		CVector width = CrossProduct(TheCamera.GetForward(), (sup - inf));
		width.Normalise();
		width /= 20;
		uint8 intensity = aTraces[i].m_lifeTime;
		for (int i = 0; i < ARRAY_SIZE(TraceVertices); i++)
			RwIm3DVertexSetRGBA(&TraceVertices[i], intensity, intensity, intensity, 0xFF);
		RwIm3DVertexSetPos(&TraceVertices[0], inf.x + width.x, inf.y + width.y, inf.z + width.z);
		RwIm3DVertexSetPos(&TraceVertices[1], inf.x - width.x, inf.y - width.y, inf.z - width.z);
		RwIm3DVertexSetPos(&TraceVertices[2], center.x + width.x, center.y + width.y, center.z + width.z);
		RwIm3DVertexSetPos(&TraceVertices[3], center.x - width.x, center.y - width.y, center.z - width.z);
		RwIm3DVertexSetPos(&TraceVertices[4], sup.x + width.x, sup.y + width.y, sup.z + width.z);
		RwIm3DVertexSetPos(&TraceVertices[5], sup.x - width.x, sup.y - width.y, sup.z - width.z);
		LittleTest();
		if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, rwIM3D_VERTEXUV)) {
			RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList));
			RwIm3DEnd();
		}
	}
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
}

void CBulletTraces::Update(void)
{
	for (int i = 0; i < NUMBULLETTRACES; i++) {
		if (aTraces[i].m_bInUse)
			aTraces[i].Update();
	}
}

void CBulletTrace::Update(void)
{
	if (m_framesInUse == 0) {
		m_framesInUse++;
		return;
	}
	if (m_framesInUse > 60) {
		m_bInUse = false;
		return;
	}
	CVector diff = m_vecCurrentPos - m_vecTargetPos;
	float remaining = diff.Magnitude();
	if (remaining > 0.8f)
		m_vecCurrentPos = m_vecTargetPos + (remaining - 0.8f) / remaining * diff;
	else
		m_bInUse = false;
	if (--m_lifeTime == 0)
		m_bInUse = false;
	m_framesInUse++;
}

RpAtomic *
MarkerAtomicCB(RpAtomic *atomic, void *data)
{
	*(RpAtomic**)data = atomic;
	return atomic;
}

bool
C3dMarker::AddMarker(uint32 identifier, uint16 type, float fSize, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate)
{
	m_nIdentifier = identifier;

	m_Matrix.SetUnity();

	RpAtomic *origAtomic;
	origAtomic = nil;
	RpClumpForAllAtomics(C3dMarkers::m_pRpClumpArray[type], MarkerAtomicCB, &origAtomic);

	RpAtomic *atomic = RpAtomicClone(origAtomic);
	RwFrame *frame = RwFrameCreate();
	RpAtomicSetFrame(atomic, frame);
	CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);

	RpGeometry *geometry = RpAtomicGetGeometry(atomic);
	RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR);

	m_pAtomic = atomic;
	m_Matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic)));
	m_pMaterial = RpGeometryGetMaterial(geometry, 0);
	m_fSize = fSize;
	m_fStdSize = m_fSize;
	m_Color.red = r;
	m_Color.green = g;
	m_Color.blue = b;
	m_Color.alpha = a;
	m_nPulsePeriod = pulsePeriod;
	m_fPulseFraction = pulseFraction;
	m_nRotateRate = rotateRate;
	m_nStartTime = CTimer::GetTimeInMilliseconds();
	m_nType = type;
	return m_pAtomic != nil;
}

void
C3dMarker::DeleteMarkerObject()
{
	RwFrame *frame;

	m_nIdentifier = 0;
	m_nStartTime = 0;
	m_bIsUsed = false;
	m_nType = MARKERTYPE_INVALID;

	frame = RpAtomicGetFrame(m_pAtomic);
	RpAtomicDestroy(m_pAtomic);
	RwFrameDestroy(frame);
	m_pAtomic = nil;
}

void
C3dMarker::Render()
{
	if (m_pAtomic == nil) return;

	RpMaterialSetColor(m_pMaterial, &m_Color);

	m_Matrix.UpdateRW();

	CMatrix matrix;
	matrix.Attach(m_Matrix.m_attachment);
	matrix.Scale(m_fSize);
	matrix.UpdateRW();

	RwFrameUpdateObjects(RpAtomicGetFrame(m_pAtomic));
	SetBrightMarkerColours(m_fBrightness);
	if (m_nType != MARKERTYPE_ARROW)
		RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
	RpAtomicRender(m_pAtomic);
	if (m_nType != MARKERTYPE_ARROW)
		RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	ReSetAmbientAndDirectionalColours();
}

C3dMarker C3dMarkers::m_aMarkerArray[NUM3DMARKERS];
int32 C3dMarkers::NumActiveMarkers;
RpClump* C3dMarkers::m_pRpClumpArray[NUMMARKERTYPES];

void
C3dMarkers::Init()
{
	for (int i = 0; i < NUM3DMARKERS; i++) {
		m_aMarkerArray[i].m_pAtomic = nil;
		m_aMarkerArray[i].m_nType = MARKERTYPE_INVALID;
		m_aMarkerArray[i].m_bIsUsed = false;
		m_aMarkerArray[i].m_nIdentifier = 0;
		m_aMarkerArray[i].m_Color.red = 255;
		m_aMarkerArray[i].m_Color.green = 255;
		m_aMarkerArray[i].m_Color.blue = 255;
		m_aMarkerArray[i].m_Color.alpha = 255;
		m_aMarkerArray[i].m_nPulsePeriod = 1024;
		m_aMarkerArray[i].m_nRotateRate = 5;
		m_aMarkerArray[i].m_nStartTime = 0;
		m_aMarkerArray[i].m_fPulseFraction = 0.25f;
		m_aMarkerArray[i].m_fStdSize = 1.0f;
		m_aMarkerArray[i].m_fSize = 1.0f;
		m_aMarkerArray[i].m_fBrightness = 1.0f;
		m_aMarkerArray[i].m_fCameraRange = 0.0f;
	}
	NumActiveMarkers = 0;
	int txdSlot = CTxdStore::FindTxdSlot("particle");
	CTxdStore::PushCurrentTxd();
	CTxdStore::SetCurrentTxd(txdSlot);
	CFileMgr::ChangeDir("\\");
	m_pRpClumpArray[MARKERTYPE_ARROW] = CFileLoader::LoadAtomicFile2Return("models/generic/arrow.dff");
	m_pRpClumpArray[MARKERTYPE_CYLINDER] = CFileLoader::LoadAtomicFile2Return("models/generic/zonecylb.dff");
	CTxdStore::PopCurrentTxd();
}

void
C3dMarkers::Shutdown()
{
	for (int i = 0; i < NUM3DMARKERS; i++) {
		if (m_aMarkerArray[i].m_pAtomic != nil)
			m_aMarkerArray[i].DeleteMarkerObject();
	}

	for (int i = 0; i < NUMMARKERTYPES; i++) {
		if (m_pRpClumpArray[i] != nil)
			RpClumpDestroy(m_pRpClumpArray[i]);
	}
}

void
C3dMarkers::Render()
{
	NumActiveMarkers = 0;
	ActivateDirectional();
	for (int i = 0; i < NUM3DMARKERS; i++) {
		if (m_aMarkerArray[i].m_bIsUsed) {
			if (m_aMarkerArray[i].m_fCameraRange < 120.0f)
				m_aMarkerArray[i].Render();
			NumActiveMarkers++;
			m_aMarkerArray[i].m_bIsUsed = false;
		} else if (m_aMarkerArray[i].m_pAtomic != nil) {
			m_aMarkerArray[i].DeleteMarkerObject();
		}
	}
}

C3dMarker *
C3dMarkers::PlaceMarker(uint32 identifier, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate)
{
	C3dMarker *pMarker;

	pMarker = nil;
	float dist = Sqrt((pos.x - FindPlayerCentreOfWorld(0).x) * (pos.x - FindPlayerCentreOfWorld(0).x) + (pos.y - FindPlayerCentreOfWorld(0).y) * (pos.y - FindPlayerCentreOfWorld(0).y));

	if (type != MARKERTYPE_ARROW && type != MARKERTYPE_CYLINDER) return nil;

	for (int i = 0; i < NUM3DMARKERS; i++) {
		if (!m_aMarkerArray[i].m_bIsUsed && m_aMarkerArray[i].m_nIdentifier == identifier) {
			pMarker = &m_aMarkerArray[i];
			break;
		}
	}

	if (pMarker == nil) {
		for (int i = 0; i < NUM3DMARKERS; i++) {
			if (m_aMarkerArray[i].m_nType == MARKERTYPE_INVALID) {
				pMarker = &m_aMarkerArray[i];
				break;
			}
		}
	}

	if (pMarker == nil && type == MARKERTYPE_ARROW) {
		for (int i = 0; i < NUM3DMARKERS; i++) {
			if (dist < m_aMarkerArray[i].m_fCameraRange && m_aMarkerArray[i].m_nType == MARKERTYPE_ARROW && (pMarker == nil || m_aMarkerArray[i].m_fCameraRange > pMarker->m_fCameraRange)) {
				pMarker = &m_aMarkerArray[i];
				break;
			}
		}

		if (pMarker != nil)
			pMarker->m_nType = MARKERTYPE_INVALID;
	}

	if (pMarker == nil) return pMarker;

	pMarker->m_fCameraRange = dist;
	if (pMarker->m_nIdentifier == identifier && pMarker->m_nType == type) {
		if (type == MARKERTYPE_ARROW) {
			if (dist < 25.0f) {
				if (dist > 5.0f)
					pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f;
				else
					pMarker->m_fStdSize = size - 0.3f * size;
			} else {
				pMarker->m_fStdSize = size;
			}
		} else if (type == MARKERTYPE_CYLINDER) {
			if (dist < size + 12.0f) {
				if (dist > size + 1.0f)
					pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a;
				else
					pMarker->m_Color.alpha = (float)a * 0.3f;
			} else {
				pMarker->m_Color.alpha = a;
			}
		}
		float someSin = Sin(TWOPI * (float)((pMarker->m_nPulsePeriod - 1) & (CTimer::GetTimeInMilliseconds() - pMarker->m_nStartTime)) / (float)pMarker->m_nPulsePeriod);
		pMarker->m_fSize = pMarker->m_fStdSize - pulseFraction * pMarker->m_fStdSize * someSin;

		if (type == MARKERTYPE_ARROW) {
			pos.z += 0.25f * pMarker->m_fStdSize * someSin;
		} else if (type == MARKERTYPE_0) {
			if (someSin > 0.0f)
				pMarker->m_Color.alpha = (float)a * 0.7f * someSin + a;
			else
				pMarker->m_Color.alpha = (float)a * 0.4f * someSin + a;
		}
		if (pMarker->m_nRotateRate) {
			CVector pos = pMarker->m_Matrix.GetPosition();
			pMarker->m_Matrix.RotateZ(DEGTORAD(pMarker->m_nRotateRate * CTimer::GetTimeStep()));
			pMarker->m_Matrix.GetPosition() = pos;
		}
		if (type == MARKERTYPE_ARROW)
			pMarker->m_Matrix.GetPosition() = pos;
		pMarker->m_bIsUsed = true;
		return pMarker;
	}

	if (pMarker->m_nIdentifier != 0)
		pMarker->DeleteMarkerObject();

	pMarker->AddMarker(identifier, type, size, r, g, b, a, pulsePeriod, pulseFraction, rotateRate);
	if (type == MARKERTYPE_CYLINDER || type == MARKERTYPE_0 || type == MARKERTYPE_2) {
		float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 1.0f, nil);
		if (z != 0.0f)
			pos.z = z - 0.05f * size;
	}
	pMarker->m_Matrix.SetTranslate(pos.x, pos.y, pos.z);
	if (type == MARKERTYPE_2) {
		pMarker->m_Matrix.RotateX(PI);
		pMarker->m_Matrix.GetPosition() = pos;
	}
	pMarker->m_Matrix.UpdateRW();
	if (type == MARKERTYPE_ARROW) {
		if (dist < 25.0f) {
			if (dist > 5.0f)
				pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f;
			else
				pMarker->m_fStdSize = size - 0.3f * size;
		} else {
			pMarker->m_fStdSize = size;
		}
	} else if (type == MARKERTYPE_CYLINDER) {
		if (dist < size + 12.0f) {
			if (dist > size + 1.0f)
				pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a;
			else
				pMarker->m_Color.alpha = (float)a * 0.3f;
		} else {
			pMarker->m_Color.alpha = a;
		}
	}
	pMarker->m_bIsUsed = true;
	return pMarker;
}

void
C3dMarkers::PlaceMarkerSet(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate)
{
	PlaceMarker(id, type, pos, size, r, g, b, a, pulsePeriod, pulseFraction, 1);
	PlaceMarker(id, type, pos, size * 0.93f, r, g, b, a, pulsePeriod, pulseFraction, 2);
	PlaceMarker(id, type, pos, size * 0.86f, r, g, b, a, pulsePeriod, pulseFraction, -1);
}

void
C3dMarkers::Update()
{
}

#define BRIGHTLIGHTS_MAX_DIST (60.0f)	// invisible beyond this
#define BRIGHTLIGHTS_FADE_DIST (45.0f)	// strongest between these two
#define CARLIGHTS_MAX_DIST (30.0f)
#define CARLIGHTS_FADE_DIST (15.0f)	// 31 for close lights

int CBrightLights::NumBrightLights;
CBrightLight CBrightLights::aBrightLights[NUMBRIGHTLIGHTS];

void
CBrightLights::Init(void)
{
	NumBrightLights = 0;
}

void
CBrightLights::RegisterOne(CVector pos, CVector up, CVector side, CVector front,
	uint8 type, uint8 red, uint8 green, uint8 blue)
{
	if(NumBrightLights >= NUMBRIGHTLIGHTS)
		return;

	aBrightLights[NumBrightLights].m_camDist = (pos - TheCamera.GetPosition()).Magnitude();
	if(aBrightLights[NumBrightLights].m_camDist > BRIGHTLIGHTS_MAX_DIST)
		return;

	aBrightLights[NumBrightLights].m_pos = pos;
	aBrightLights[NumBrightLights].m_up = up;
	aBrightLights[NumBrightLights].m_side = side;
	aBrightLights[NumBrightLights].m_front = front;
	aBrightLights[NumBrightLights].m_type = type;
	aBrightLights[NumBrightLights].m_red = red;
	aBrightLights[NumBrightLights].m_green = green;
	aBrightLights[NumBrightLights].m_blue = blue;

	NumBrightLights++;
}

static float TrafficLightsSide[6] = { -0.09f, 0.09f, 0.162f, 0.09f, -0.09f, -0.162f };
static float TrafficLightsUp[6] = { 0.162f, 0.162f, 0.0f, -0.162f, -0.162f, 0.0f };
static float LongCarHeadLightsSide[8] = { -0.2f, 0.2f, -0.2f, 0.2f, -0.2f, 0.2f, -0.2f, 0.2f };
static float LongCarHeadLightsFront[8] = { 0.1f, 0.1f, -0.1f, -0.1f, 0.1f, 0.1f, -0.1f, -0.1f };
static float LongCarHeadLightsUp[8] = { 0.1f, 0.1f, 0.1f, 0.1f, -0.1f, -0.1f, -0.1f, -0.1f };
static float SmallCarHeadLightsSide[8] = { -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f };
static float SmallCarHeadLightsFront[8] = { 0.08f, 0.08f, -0.08f, -0.08f, 0.08f, 0.08f, -0.08f, -0.08f };
static float SmallCarHeadLightsUp[8] = { 0.08f, 0.08f, 0.08f, 0.08f, -0.08f, -0.08f, -0.08f, -0.08f };
static float BigCarHeadLightsSide[8] = { -0.15f, 0.15f, -0.15f, 0.15f, -0.15f, 0.15f, -0.15f, 0.15f };
static float BigCarHeadLightsFront[8] = { 0.15f, 0.15f, -0.15f, -0.15f, 0.15f, 0.15f, -0.15f, -0.15f };
static float BigCarHeadLightsUp[8] = { 0.15f, 0.15f, 0.15f, 0.15f, -0.15f, -0.15f, -0.15f, -0.15f };
static float TallCarHeadLightsSide[8] = { -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f };
static float TallCarHeadLightsFront[8] = { 0.08f, 0.08f, -0.08f, -0.08f, 0.08f, 0.08f, -0.08f, -0.08f };
static float TallCarHeadLightsUp[8] = { 0.2f, 0.2f, 0.2f, 0.2f, -0.2f, -0.2f, -0.2f, -0.2f };
static float SirenLightsSide[6] = { -0.04f, 0.04f, 0.06f, 0.04f, -0.04f, -0.06f };
static float SirenLightsUp[6] = { 0.06f, 0.06f, 0.0f, -0.06f, -0.06f, 0.0f };
static RwImVertexIndex TrafficLightIndices[4*3] = { 0, 1, 5,  1, 2, 3,  1, 3, 4,  1, 4, 5 };
static RwImVertexIndex CubeIndices[12*3] = {
	0, 2, 1,  1, 2, 3,  3, 5, 1,  3, 7, 5,
	2, 7, 3,  2, 6, 7,  4, 0, 1,  4, 1, 5,
	6, 0, 4,  6, 2, 0,  6, 5, 7,  6, 4, 5
};

void
CBrightLights::Render(void)
{
	int i, j;
	CVector pos;

	if(NumBrightLights == 0)
		return;

	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);

	TempBufferVerticesStored = 0;
	TempBufferIndicesStored = 0;

	for(i = 0; i < NumBrightLights; i++){
		if(TempBufferIndicesStored > TEMPBUFFERINDEXSIZE-40 || TempBufferVerticesStored > TEMPBUFFERVERTSIZE-40)
			RenderOutGeometryBuffer();

		int r, g, b, a;
		float flicker = (CGeneral::GetRandomNumber()&0xFF) * 0.2f;
		switch(aBrightLights[i].m_type){
		case BRIGHTLIGHT_TRAFFIC_GREEN:
			r = flicker; g = 255; b = flicker;
			break;
		case BRIGHTLIGHT_TRAFFIC_YELLOW:
			r = 255; g = 128; b = flicker;
			break;
		case BRIGHTLIGHT_TRAFFIC_RED:
			r = 255; g = flicker; b = flicker;
			break;

		case BRIGHTLIGHT_FRONT_LONG:
		case BRIGHTLIGHT_FRONT_SMALL:
		case BRIGHTLIGHT_FRONT_BIG:
		case BRIGHTLIGHT_FRONT_TALL:
			r = 255; g = 255; b = 255;
			break;

		case BRIGHTLIGHT_REAR_LONG:
		case BRIGHTLIGHT_REAR_SMALL:
		case BRIGHTLIGHT_REAR_BIG:
		case BRIGHTLIGHT_REAR_TALL:
			r = 255; g = flicker; b = flicker;
			break;

		case BRIGHTLIGHT_SIREN:
			r = aBrightLights[i].m_red;
			g = aBrightLights[i].m_green;
			b = aBrightLights[i].m_blue;
			break;
		}

		if(aBrightLights[i].m_camDist < BRIGHTLIGHTS_FADE_DIST)
			a = 255;
		else
			a = 255*(1.0f - (aBrightLights[i].m_camDist-BRIGHTLIGHTS_FADE_DIST)/(BRIGHTLIGHTS_MAX_DIST-BRIGHTLIGHTS_FADE_DIST));
		// fade car lights down to 31 as they come near
		if(aBrightLights[i].m_type >= BRIGHTLIGHT_FRONT_LONG && aBrightLights[i].m_type <= BRIGHTLIGHT_REAR_TALL){
			if(aBrightLights[i].m_camDist < CARLIGHTS_FADE_DIST)
				a = 31;
			else if(aBrightLights[i].m_camDist < CARLIGHTS_MAX_DIST)
				a = 31 + (255-31)*((aBrightLights[i].m_camDist-CARLIGHTS_FADE_DIST)/(CARLIGHTS_MAX_DIST-CARLIGHTS_FADE_DIST));
		}

		switch(aBrightLights[i].m_type){
		case BRIGHTLIGHT_TRAFFIC_GREEN:
		case BRIGHTLIGHT_TRAFFIC_YELLOW:
		case BRIGHTLIGHT_TRAFFIC_RED:
			for(j = 0; j < 6; j++){
				pos = TrafficLightsSide[j]*aBrightLights[i].m_side +
					TrafficLightsUp[j]*aBrightLights[i].m_up +
					aBrightLights[i].m_pos;
				RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a);
				RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z);
			}
			for(j = 0; j < 4*3; j++)
				TempBufferRenderIndexList[TempBufferIndicesStored+j] = TrafficLightIndices[j] + TempBufferVerticesStored;
			TempBufferVerticesStored += 6;
			TempBufferIndicesStored += 4*3;
			break;

		case BRIGHTLIGHT_FRONT_LONG:
		case BRIGHTLIGHT_REAR_LONG:
			for(j = 0; j < 8; j++){
				pos = LongCarHeadLightsSide[j]*aBrightLights[i].m_side +
					LongCarHeadLightsUp[j]*aBrightLights[i].m_up +
					LongCarHeadLightsFront[j]*aBrightLights[i].m_front +
					aBrightLights[i].m_pos;
				RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a);
				RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z);
			}
			for(j = 0; j < 12*3; j++)
				TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored;
			TempBufferVerticesStored += 8;
			TempBufferIndicesStored += 12*3;
			break;

		case BRIGHTLIGHT_FRONT_SMALL:
		case BRIGHTLIGHT_REAR_SMALL:
			for(j = 0; j < 8; j++){
				pos = SmallCarHeadLightsSide[j]*aBrightLights[i].m_side +
					SmallCarHeadLightsUp[j]*aBrightLights[i].m_up +
					SmallCarHeadLightsFront[j]*aBrightLights[i].m_front +
					aBrightLights[i].m_pos;
				RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a);
				RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z);
			}
			for(j = 0; j < 12*3; j++)
				TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored;
			TempBufferVerticesStored += 8;
			TempBufferIndicesStored += 12*3;
			break;

		case BRIGHTLIGHT_FRONT_BIG:
		case BRIGHTLIGHT_REAR_BIG:
			for (j = 0; j < 8; j++) {
			pos = BigCarHeadLightsSide[j] * aBrightLights[i].m_side +
				BigCarHeadLightsUp[j] * aBrightLights[i].m_up +
				BigCarHeadLightsFront[j] * aBrightLights[i].m_front +
				aBrightLights[i].m_pos;
			RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + j], r, g, b, a);
			RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + j], pos.x, pos.y, pos.z);
		}
		for (j = 0; j < 12 * 3; j++)
			TempBufferRenderIndexList[TempBufferIndicesStored + j] = CubeIndices[j] + TempBufferVerticesStored;
		TempBufferVerticesStored += 8;
		TempBufferIndicesStored += 12 * 3;
		break;

		case BRIGHTLIGHT_FRONT_TALL:
		case BRIGHTLIGHT_REAR_TALL:
			for(j = 0; j < 8; j++){
				pos = TallCarHeadLightsSide[j]*aBrightLights[i].m_side +
					TallCarHeadLightsUp[j]*aBrightLights[i].m_up +
					TallCarHeadLightsFront[j]*aBrightLights[i].m_front +
					aBrightLights[i].m_pos;
				RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a);
				RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z);
			}
			for(j = 0; j < 12*3; j++)
				TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored;
			TempBufferVerticesStored += 8;
			TempBufferIndicesStored += 12*3;
			break;

		case BRIGHTLIGHT_SIREN:
			for(j = 0; j < 6; j++){
				pos = SirenLightsSide[j] * TheCamera.GetRight() +
					SirenLightsUp[j] * TheCamera.GetUp() +
					aBrightLights[i].m_pos;
				RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a);
				RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z);
			}
			for(j = 0; j < 4*3; j++)
				TempBufferRenderIndexList[TempBufferIndicesStored+j] = TrafficLightIndices[j] + TempBufferVerticesStored;
			TempBufferVerticesStored += 6;
			TempBufferIndicesStored += 4*3;
			break;

		}
	}

	RenderOutGeometryBuffer();
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
	NumBrightLights = 0;
}

void
CBrightLights::RenderOutGeometryBuffer(void)
{
	if(TempBufferIndicesStored != 0){
		LittleTest();
		if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){
			RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored);
			RwIm3DEnd();
		}
		TempBufferVerticesStored = 0;
		TempBufferIndicesStored = 0;
	}
}

int CShinyTexts::NumShinyTexts;
CShinyText CShinyTexts::aShinyTexts[NUMSHINYTEXTS];

void
CShinyTexts::Init(void)
{
	NumShinyTexts = 0;
}

void
CShinyTexts::RegisterOne(CVector p0, CVector p1, CVector p2, CVector p3,
	float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3,
	uint8 type, uint8 red, uint8 green, uint8 blue, float maxDist)
{
	if(NumShinyTexts >= NUMSHINYTEXTS)
		return;

	aShinyTexts[NumShinyTexts].m_camDist = (p0 - TheCamera.GetPosition()).Magnitude();
	if(aShinyTexts[NumShinyTexts].m_camDist > maxDist)
		return;
	aShinyTexts[NumShinyTexts].m_verts[0] = p0;
	aShinyTexts[NumShinyTexts].m_verts[1] = p1;
	aShinyTexts[NumShinyTexts].m_verts[2] = p2;
	aShinyTexts[NumShinyTexts].m_verts[3] = p3;
	aShinyTexts[NumShinyTexts].m_texCoords[0].x = u0;
	aShinyTexts[NumShinyTexts].m_texCoords[0].y = v0;
	aShinyTexts[NumShinyTexts].m_texCoords[1].x = u1;
	aShinyTexts[NumShinyTexts].m_texCoords[1].y = v1;
	aShinyTexts[NumShinyTexts].m_texCoords[2].x = u2;
	aShinyTexts[NumShinyTexts].m_texCoords[2].y = v2;
	aShinyTexts[NumShinyTexts].m_texCoords[3].x = u3;
	aShinyTexts[NumShinyTexts].m_texCoords[3].y = v3;
	aShinyTexts[NumShinyTexts].m_type = type;
	aShinyTexts[NumShinyTexts].m_red = red;
	aShinyTexts[NumShinyTexts].m_green = green;
	aShinyTexts[NumShinyTexts].m_blue = blue;
	// Fade out at half the max dist
	float halfDist = maxDist*0.5f;
	if(aShinyTexts[NumShinyTexts].m_camDist > halfDist){
		float f = 1.0f - (aShinyTexts[NumShinyTexts].m_camDist - halfDist)/halfDist;
		aShinyTexts[NumShinyTexts].m_red *= f;
		aShinyTexts[NumShinyTexts].m_green *= f;
		aShinyTexts[NumShinyTexts].m_blue *= f;
	}

	NumShinyTexts++;
}

void
CShinyTexts::Render(void)
{
	int i, ix, v;
	RwTexture *lastTex = nil;

	if(NumShinyTexts == 0)
		return;

	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);

	TempBufferVerticesStored = 0;
	TempBufferIndicesStored = 0;

	for(i = 0; i < NumShinyTexts; i++){
		if(TempBufferIndicesStored > TEMPBUFFERINDEXSIZE-64 || TempBufferVerticesStored > TEMPBUFFERVERTSIZE-62)
			RenderOutGeometryBuffer();

		uint8 r = aShinyTexts[i].m_red;
		uint8 g = aShinyTexts[i].m_green;
		uint8 b = aShinyTexts[i].m_blue;

		switch(aShinyTexts[i].m_type){
		case SHINYTEXT_WALK:
			if(lastTex != gpWalkDontTex){
				RenderOutGeometryBuffer();
				RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpWalkDontTex));
				lastTex = gpWalkDontTex;
			}
	quad:
			v = TempBufferVerticesStored;
			RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+0], r, g, b, 255);
			RwIm3DVertexSetPos(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_verts[0].x, aShinyTexts[i].m_verts[0].y, aShinyTexts[i].m_verts[0].z);
			RwIm3DVertexSetU(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].x);
			RwIm3DVertexSetV(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].y);
			RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+1], r, g, b, 255);
			RwIm3DVertexSetPos(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_verts[1].x, aShinyTexts[i].m_verts[1].y, aShinyTexts[i].m_verts[1].z);
			RwIm3DVertexSetU(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].x);
			RwIm3DVertexSetV(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].y);
			RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+2], r, g, b, 255);
			RwIm3DVertexSetPos(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_verts[2].x, aShinyTexts[i].m_verts[2].y, aShinyTexts[i].m_verts[2].z);
			RwIm3DVertexSetU(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].x);
			RwIm3DVertexSetV(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].y);
			RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+3], r, g, b, 255);
			RwIm3DVertexSetPos(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_verts[3].x, aShinyTexts[i].m_verts[3].y, aShinyTexts[i].m_verts[3].z);
			RwIm3DVertexSetU(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].x);
			RwIm3DVertexSetV(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].y);
			ix = TempBufferIndicesStored;
			TempBufferRenderIndexList[ix+0] = 0 + TempBufferVerticesStored;
			TempBufferRenderIndexList[ix+1] = 1 + TempBufferVerticesStored;
			TempBufferRenderIndexList[ix+2] = 2 + TempBufferVerticesStored;
			TempBufferRenderIndexList[ix+3] = 2 + TempBufferVerticesStored;
			TempBufferRenderIndexList[ix+4] = 1 + TempBufferVerticesStored;
			TempBufferRenderIndexList[ix+5] = 3 + TempBufferVerticesStored;
			TempBufferVerticesStored += 4;
			TempBufferIndicesStored += 6;
			break;

		case SHINYTEXT_FLAT:
			if(lastTex != nil){
				RenderOutGeometryBuffer();
				RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
				lastTex = nil;
			}
			goto quad;
		}
	}

	RenderOutGeometryBuffer();
	NumShinyTexts = 0;

	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
}

void
CShinyTexts::RenderOutGeometryBuffer(void)
{
	if(TempBufferIndicesStored != 0){
		LittleTest();
		if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){
			RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored);
			RwIm3DEnd();
		}
		TempBufferVerticesStored = 0;
		TempBufferIndicesStored = 0;
	}
}

#define MONEY_MESSAGE_LIFETIME_MS 2000

CMoneyMessage CMoneyMessages::aMoneyMessages[NUMMONEYMESSAGES];

void
CMoneyMessage::Render()
{
	const float MAX_SCALE = 4.0f;
	uint32 nLifeTime = CTimer::GetTimeInMilliseconds() - m_nTimeRegistered;
	if (nLifeTime >= MONEY_MESSAGE_LIFETIME_MS) m_nTimeRegistered = 0;
	else {
		float fLifeTime = (float)nLifeTime / MONEY_MESSAGE_LIFETIME_MS;
		RwV3d vecOut;
		float fDistX, fDistY;
		if (CSprite::CalcScreenCoors(m_vecPosition + CVector(0.0f, 0.0f, fLifeTime), &vecOut, &fDistX, &fDistY, true)) {
			fDistX *= (0.7f * fLifeTime + 2.0f) * m_fSize;
			fDistY *= (0.7f * fLifeTime + 2.0f) * m_fSize;
			CFont::SetPropOn();
			CFont::SetBackgroundOff();

			float fScaleY = Min(fDistY / 100.0f, MAX_SCALE);
			float fScaleX = Min(fDistX / 100.0f, MAX_SCALE);

#ifdef FIX_BUGS
			CFont::SetScale(SCREEN_SCALE_X(fScaleX), SCREEN_SCALE_Y(fScaleY));
#else
			CFont::SetScale(fScaleX, fScaleY);
#endif
			CFont::SetCentreOn();
			CFont::SetCentreSize(SCREEN_WIDTH);
			CFont::SetJustifyOff();
			CFont::SetColor(CRGBA(m_Colour.r, m_Colour.g, m_Colour.b, (255.0f - 255.0f * fLifeTime) * m_fOpacity));
			CFont::SetBackGroundOnlyTextOff();
			CFont::SetFontStyle(FONT_BANK);
			CFont::PrintString(vecOut.x, vecOut.y, m_aText);
		}
	}
}

void
CMoneyMessages::Init()
{
	for (int32 i = 0; i < NUMMONEYMESSAGES; i++)
		aMoneyMessages[i].m_nTimeRegistered = 0;
}

void
CMoneyMessages::Render()
{
	for (int32 i = 0; i < NUMMONEYMESSAGES; i++) {
		if (aMoneyMessages[i].m_nTimeRegistered != 0)
			aMoneyMessages[i].Render();
	}
}

void
CMoneyMessages::RegisterOne(CVector vecPos, const char *pText, uint8 bRed, uint8 bGreen, uint8 bBlue, float fSize, float fOpacity)
{
	uint32 i;
#ifdef FIX_BUGS
	for(i = 0; i < NUMMONEYMESSAGES && aMoneyMessages[i].m_nTimeRegistered != 0; i++);
#else
	for(i = 0; aMoneyMessages[i].m_nTimeRegistered != 0 && i < NUMMONEYMESSAGES; i++);
#endif

	if(i < NUMMONEYMESSAGES) {
		// Add data of this money message to the array
		AsciiToUnicode(pText, aMoneyMessages[i].m_aText);

		aMoneyMessages[i].m_nTimeRegistered = CTimer::GetTimeInMilliseconds();
		aMoneyMessages[i].m_vecPosition = vecPos;
		aMoneyMessages[i].m_Colour.red = bRed;
		aMoneyMessages[i].m_Colour.green = bGreen;
		aMoneyMessages[i].m_Colour.blue = bBlue;
		aMoneyMessages[i].m_fSize = fSize;
		aMoneyMessages[i].m_fOpacity = fOpacity;
	}
}

CRGBA FoamColour(255, 255, 255, 255);
uint32 CSpecialParticleStuff::BoatFromStart;

void
CSpecialParticleStuff::CreateFoamAroundObject(CMatrix* pMatrix, float innerFw, float innerRg, float innerUp, int32 particles)
{
	float outerFw = innerFw + 5.0f;
	float outerRg = innerRg + 5.0f;
	float outerUp = innerUp + 5.0f;
	for (int attempts = 0; particles > 0 && attempts < 1000; attempts++) {
		CVector pos;
		int rnd = CGeneral::GetRandomNumber();
		pos.x = (int8)(rnd - 128) * innerFw / 110.0f;
		pos.y = (int8)((rnd >> 8) - 128) * innerFw / 110.0f;
		pos.z = 0.0f;
		if (DotProduct2D(pos, TheCamera.GetForward()) >= 0)
			continue;
		// was there any point in adding it here?
		pos += pMatrix->GetPosition();
		pos.z = 2.0f;
		float fw = Abs(DotProduct(pMatrix->GetForward(), pos - pMatrix->GetPosition()));
		if (fw >= outerFw)
			continue;
		float rg = Abs(DotProduct(pMatrix->GetRight(), pos - pMatrix->GetPosition()));
		if (rg >= outerRg)
			continue;
		float up = Abs(DotProduct(pMatrix->GetUp(), pos - pMatrix->GetPosition()));
		if (up >= outerUp)
			continue;
		if (fw > innerFw || rg > innerRg || up > innerUp) {
			CParticle::AddParticle(PARTICLE_STEAM2, pos, CVector(0.0f, 0.0f, 0.0f), nil, 4.0f, FoamColour, 1, 0, 0, 0);
			particles--;
		}
	}
}

void
CSpecialParticleStuff::StartBoatFoamAnimation()
{
	BoatFromStart = CTimer::GetTimeInMilliseconds();
}

void
CSpecialParticleStuff::UpdateBoatFoamAnimation(CMatrix* pMatrix)
{
	static int32 FrameInAnimation = 0;
	static float X, Y, Z, dX, dY, dZ;
	CreateFoamAroundObject(pMatrix, 107.0f, 24.1f, 30.5f, 2);
	uint32 prev = CTimer::GetPreviousTimeInMilliseconds();
	uint32 cur = CTimer::GetTimeInMilliseconds();
	if (FrameInAnimation != 0) {
		X += dX;
		Y += dY;
		Z += dZ;
		CVector pos = *pMatrix * CVector(X, Y, Z);
		CParticle::AddParticle(PARTICLE_STEAM_NY, pos, CVector(0.0f, 0.0f, 0.0f),
			nil, FrameInAnimation * 0.5f + 2.0f, FoamColour, 1, 0, 0, 0);
		if (++FrameInAnimation > 15)
			FrameInAnimation = 0;
	}
	if ((cur & 0x3FF) < (prev & 0x3FF)) {
		FrameInAnimation = 1;
		int rnd = CGeneral::GetRandomNumber();
		X = (int8)(rnd - 128) * 0.2f;
		Y = (int8)((rnd >> 8) - 128) * 0.2f;
		Z = 10.0f;
		rnd = CGeneral::GetRandomNumber();
		dX = (int8)(rnd - 128) * 0.02f;
		dY = (int8)((rnd >> 8) - 128) * 0.02f;
		dZ = 2.0f;
	}
}


code:
#pragma once

class CSpecialFX
{
public:
	static void Render(void);
	static void Update(void);
	static void Init(void);
	static void Shutdown(void);
};

class CRegisteredMotionBlurStreak
{
public:
	uintptr m_id;
	uint8 m_red;
	uint8 m_green;
	uint8 m_blue;
	CVector m_pos1[3];
	CVector m_pos2[3];
	bool m_isValid[3];

	void Update(void);
	void Render(void);
};

class CMotionBlurStreaks
{
	static CRegisteredMotionBlurStreak aStreaks[NUMMBLURSTREAKS];
public:
	static void Init(void);
	static void Update(void);
	static void RegisterStreak(uintptr id, uint8 r, uint8 g, uint8 b, CVector p1, CVector p2);
	static void Render(void);
};

struct CBulletTrace
{
	CVector m_vecCurrentPos;
	CVector m_vecTargetPos;
	bool m_bInUse;
	uint8 m_framesInUse;
	uint8 m_lifeTime;

	void Update(void);
};

class CBulletTraces
{
public:
	static CBulletTrace aTraces[NUMBULLETTRACES];

	static void Init(void);
	static void AddTrace(CVector*, CVector*);
	static void Render(void);
	static void Update(void);
};

enum
{
	MARKERTYPE_0 = 0,
	MARKERTYPE_ARROW,
	MARKERTYPE_2,
	MARKERTYPE_3,
	MARKERTYPE_CYLINDER,
	NUMMARKERTYPES,

	MARKERTYPE_INVALID = 0x101
};

class C3dMarker
{
public:
	CMatrix m_Matrix;
	RpAtomic *m_pAtomic;
	RpMaterial *m_pMaterial;
	uint16 m_nType;
	bool m_bIsUsed;
	uint32 m_nIdentifier;
	RwRGBA m_Color;
	uint16 m_nPulsePeriod;
	int16 m_nRotateRate;
	uint32 m_nStartTime;
	float m_fPulseFraction;
	float m_fStdSize;
	float m_fSize;
	float m_fBrightness;
	float m_fCameraRange;

	bool AddMarker(uint32 identifier, uint16 type, float fSize, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate);
	void DeleteMarkerObject();
	void Render();
};

class C3dMarkers
{
public:
	static void Init();
	static void Shutdown();
	static C3dMarker *PlaceMarker(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate);
	static void PlaceMarkerSet(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate);
	static void Render();
	static void Update();

	static C3dMarker m_aMarkerArray[NUM3DMARKERS];
	static int32 NumActiveMarkers;
	static RpClump* m_pRpClumpArray[NUMMARKERTYPES];
};

enum
{
	BRIGHTLIGHT_INVALID,
	BRIGHTLIGHT_TRAFFIC_GREEN,
	BRIGHTLIGHT_TRAFFIC_YELLOW,
	BRIGHTLIGHT_TRAFFIC_RED,

	// white
	BRIGHTLIGHT_FRONT_LONG,
	BRIGHTLIGHT_FRONT_SMALL,
	BRIGHTLIGHT_FRONT_BIG,
	BRIGHTLIGHT_FRONT_TALL,

	// red
	BRIGHTLIGHT_REAR_LONG,
	BRIGHTLIGHT_REAR_SMALL,
	BRIGHTLIGHT_REAR_BIG,
	BRIGHTLIGHT_REAR_TALL,

	BRIGHTLIGHT_SIREN,	// unused

	BRIGHTLIGHT_FRONT = BRIGHTLIGHT_FRONT_LONG,
	BRIGHTLIGHT_REAR = BRIGHTLIGHT_REAR_LONG,
};

class CBrightLight
{
public:
	CVector m_pos;
	CVector m_up;
	CVector m_side;
	CVector m_front;
	float m_camDist;
	uint8 m_type;
	uint8 m_red;
	uint8 m_green;
	uint8 m_blue;
};

class CBrightLights
{
	static int NumBrightLights;
	static CBrightLight aBrightLights[NUMBRIGHTLIGHTS];
public:
	static void Init(void);
	static void RegisterOne(CVector pos, CVector up, CVector side, CVector front,
		uint8 type, uint8 red = 0, uint8 green = 0, uint8 blue = 0);
	static void Render(void);
	static void RenderOutGeometryBuffer(void);
};

enum
{
	SHINYTEXT_WALK = 1,
	SHINYTEXT_FLAT
};

class CShinyText
{
public:
	CVector m_verts[4];
	CVector2D m_texCoords[4];
	float m_camDist;
	uint8 m_type;
	uint8 m_red;
	uint8 m_green;
	uint8 m_blue;
};

class CShinyTexts
{
	static int NumShinyTexts;
	static CShinyText aShinyTexts[NUMSHINYTEXTS];
public:
	static void Init(void);
	static void RegisterOne(CVector p0, CVector p1, CVector p2, CVector p3,
		float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3,
		uint8 type, uint8 red, uint8 green, uint8 blue, float maxDist);
	static void Render(void);
	static void RenderOutGeometryBuffer(void);
};

class CMoneyMessage
{
	friend class CMoneyMessages;

	uint32	m_nTimeRegistered;
	CVector	m_vecPosition;
	wchar	m_aText[16];
	CRGBA	m_Colour;
	float	m_fSize;
	float	m_fOpacity;
public:
	void Render();
};

class CMoneyMessages
{
	static CMoneyMessage aMoneyMessages[NUMMONEYMESSAGES];
public:
	static void Init();
	static void Render();
	static void	RegisterOne(CVector vecPos, const char *pText, uint8 bRed, uint8 bGreen, uint8 bBlue, float fSize, float fOpacity);
};

class CSpecialParticleStuff
{
	static uint32 BoatFromStart;
public:
	static void CreateFoamAroundObject(CMatrix*, float, float, float, int32);
	static void StartBoatFoamAnimation();
	static void UpdateBoatFoamAnimation(CMatrix*);
};

license: mit


code:
#include "common.h"

#include "main.h"
#include "Draw.h"
#include "Camera.h"
#include "Sprite.h"

#ifdef ASPECT_RATIO_SCALE
#include "Frontend.h"
#endif

float CSprite::m_f2DNearScreenZ;
float CSprite::m_f2DFarScreenZ;
float CSprite::m_fRecipNearClipPlane;
int32 CSprite::m_bFlushSpriteBufferSwitchZTest;

float
CSprite::CalcHorizonCoors(void)
{
	CVector p = TheCamera.GetPosition() + CVector(TheCamera.CamFrontXNorm, TheCamera.CamFrontYNorm, 0.0f)*3000.0f;
	p.z = 0.0f;
	p = TheCamera.m_viewMatrix * p;
	return p.y * SCREEN_HEIGHT / p.z;
}

bool
CSprite::CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip)
{
	CVector viewvec = TheCamera.m_viewMatrix * in;
	*out = viewvec;
	if(out->z <= CDraw::GetNearClipZ() + 1.0f) return false;
	if(out->z >= CDraw::GetFarClipZ() && farclip) return false;
	float recip = 1.0f/out->z;
	out->x *= SCREEN_WIDTH * recip;
	out->y *= SCREEN_HEIGHT * recip;
	const float fov = DefaultFOV;
	// this is used to scale correctly if you zoom in with sniper rifle
	float fovScale = fov / CDraw::GetFOV();

#ifdef FIX_SPRITES
	*outw = CDraw::ms_bFixSprites ? (fovScale * recip * SCREEN_HEIGHT) : (fovScale * SCREEN_SCALE_AR(recip) * SCREEN_WIDTH);
#else
	*outw = fovScale * SCREEN_SCALE_AR(recip) * SCREEN_WIDTH;
#endif
	*outh = fovScale * recip * SCREEN_HEIGHT;

	return true;
}

#define SPRITEBUFFERSIZE 64
static int32 nSpriteBufferIndex;
static RwIm2DVertex SpriteBufferVerts[SPRITEBUFFERSIZE*6];
static RwIm2DVertex verts[4];

void
CSprite::InitSpriteBuffer(void)
{
	m_f2DNearScreenZ = RwIm2DGetNearScreenZ();
	m_f2DFarScreenZ = RwIm2DGetFarScreenZ();
}

void
CSprite::InitSpriteBuffer2D(void)
{
	m_fRecipNearClipPlane = 1.0f / RwCameraGetNearClipPlane(Scene.camera);
	InitSpriteBuffer();
}

void
CSprite::FlushSpriteBuffer(void)
{
	if(nSpriteBufferIndex > 0){
		if(m_bFlushSpriteBufferSwitchZTest){
			RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
			RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6);
			RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
		}else
			RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6);
		nSpriteBufferIndex = 0;
	}
}

void
CSprite::RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a)
{
	static short indices[] = { 0, 1, 2, 3 };
	// 0---3
	// |   |
	// 1---2
	float xs[4];
	float ys[4];
	float us[4];
	float vs[4];
	int i;

	xs[0] = x-w;	us[0] = 0.0f;
	xs[1] = x-w;	us[1] = 0.0f;
	xs[2] = x+w;	us[2] = 1.0f;
	xs[3] = x+w;	us[3] = 1.0f;

	ys[0] = y-h;	vs[0] = 0.0f;
	ys[1] = y+h;	vs[1] = 1.0f;
	ys[2] = y+h;	vs[2] = 1.0f;
	ys[3] = y-h;	vs[3] = 0.0f;

	// clip
	for(i = 0; i < 4; i++){
		if(xs[i] < 0.0f){
			us[i] = -xs[i] / (2.0f*w);
			xs[i] = 0.0f;
		}
		if(xs[i] > SCREEN_WIDTH){
			us[i] = 1.0f - (xs[i]-SCREEN_WIDTH) / (2.0f*w);
			xs[i] = SCREEN_WIDTH;
		}
		if(ys[i] < 0.0f){
			vs[i] = -ys[i] / (2.0f*h);
			ys[i] = 0.0f;
		}
		if(ys[i] > SCREEN_HEIGHT){
			vs[i] = 1.0f - (ys[i]-SCREEN_HEIGHT) / (2.0f*h);
			ys[i] = SCREEN_HEIGHT;
		}
	}

	// (DrawZ - DrawNear)/(DrawFar - DrawNear) = (SpriteZ-SpriteNear)/(SpriteFar-SpriteNear)
	// So to calculate SpriteZ:
	float screenz = m_f2DNearScreenZ +
		(z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
		((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);

	for(i = 0; i < 4; i++){
		RwIm2DVertexSetScreenX(&verts[i], xs[i]);
		RwIm2DVertexSetScreenY(&verts[i], ys[i]);
		RwIm2DVertexSetScreenZ(&verts[i], screenz);
		RwIm2DVertexSetCameraZ(&verts[i], z);
		RwIm2DVertexSetRecipCameraZ(&verts[i], recipz);
		RwIm2DVertexSetIntRGBA(&verts[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
		RwIm2DVertexSetU(&verts[i], us[i], recipz);
		RwIm2DVertexSetV(&verts[i], vs[i], recipz);
	}
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, verts, 4);
}

void
CSprite::RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a)
{
	float c = Cos(rotation);
	float s = Sin(rotation);

	float xs[4];
	float ys[4];
	float us[4];
	float vs[4];
	int i;

	// Fade out when too near
	// why not in buffered version?
	if(z < 3.0f){
		if(z < 1.5f)
			return;
		int f = (z - 1.5f)/1.5f * 255;
		r = f*r >> 8;
		g = f*g >> 8;
		b = f*b >> 8;
		intens = f*intens >> 8;
	}

	xs[0] = x + w*(-c-s);	us[0] = 0.0f;
	xs[1] = x + w*(-c+s);	us[1] = 0.0f;
	xs[2] = x + w*(+c+s);	us[2] = 1.0f;
	xs[3] = x + w*(+c-s);	us[3] = 1.0f;

	ys[0] = y + h*(-c+s);	vs[0] = 0.0f;
	ys[1] = y + h*(+c+s);	vs[1] = 1.0f;
	ys[2] = y + h*(+c-s);	vs[2] = 1.0f;
	ys[3] = y + h*(-c-s);	vs[3] = 0.0f;

	// No clipping, just culling
	if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
	if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
	if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH &&
	   xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return;
	if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT &&
	   ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return;

	float screenz = m_f2DNearScreenZ +
		(z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
		((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);

	for(i = 0; i < 4; i++){
		RwIm2DVertexSetScreenX(&verts[i], xs[i]);
		RwIm2DVertexSetScreenY(&verts[i], ys[i]);
		RwIm2DVertexSetScreenZ(&verts[i], screenz);
		RwIm2DVertexSetCameraZ(&verts[i], z);
		RwIm2DVertexSetRecipCameraZ(&verts[i], recipz);
		RwIm2DVertexSetIntRGBA(&verts[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
		RwIm2DVertexSetU(&verts[i], us[i], recipz);
		RwIm2DVertexSetV(&verts[i], vs[i], recipz);
	}
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, verts, 4);
}

void
CSprite::RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a)
{
	m_bFlushSpriteBufferSwitchZTest = 0;

	// 0---3
	// |   |
	// 1---2
	float xs[4];
	float ys[4];
	float us[4];
	float vs[4];
	int i;

	xs[0] = x-w;	us[0] = 0.0f;
	xs[1] = x-w;	us[1] = 0.0f;
	xs[2] = x+w;	us[2] = 1.0f;
	xs[3] = x+w;	us[3] = 1.0f;

	ys[0] = y-h;	vs[0] = 0.0f;
	ys[1] = y+h;	vs[1] = 1.0f;
	ys[2] = y+h;	vs[2] = 1.0f;
	ys[3] = y-h;	vs[3] = 0.0f;

	// clip
	for(i = 0; i < 4; i++){
		if(xs[i] < 0.0f){
			us[i] = -xs[i] / (2.0f*w);
			xs[i] = 0.0f;
		}
		if(xs[i] > SCREEN_WIDTH){
			us[i] = 1.0f - (xs[i]-SCREEN_WIDTH) / (2.0f*w);
			xs[i] = SCREEN_WIDTH;
		}
		if(ys[i] < 0.0f){
			vs[i] = -ys[i] / (2.0f*h);
			ys[i] = 0.0f;
		}
		if(ys[i] > SCREEN_HEIGHT){
			vs[i] = 1.0f - (ys[i]-SCREEN_HEIGHT) / (2.0f*h);
			ys[i] = SCREEN_HEIGHT;
		}
	}

	float screenz = m_f2DNearScreenZ +
		(z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
		((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);

	RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
	static int indices[6] = { 0, 1, 2, 3, 0, 2 };
	for(i = 0; i < 6; i++){
		RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
		RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
		RwIm2DVertexSetScreenZ(&vert[i], screenz);
		RwIm2DVertexSetCameraZ(&vert[i], z);
		RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
		RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
		RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
		RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
	}
	nSpriteBufferIndex++;
	if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
		FlushSpriteBuffer();
}

void
CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a)
{
	m_bFlushSpriteBufferSwitchZTest = 0;
	// TODO: replace with lookup
	float c = Cos(DEGTORAD(rotation));
	float s = Sin(DEGTORAD(rotation));

	float xs[4];
	float ys[4];
	float us[4];
	float vs[4];
	int i;

	xs[0] = x - c*w - s*h;	us[0] = 0.0f;
	xs[1] = x - c*w + s*h;	us[1] = 0.0f;
	xs[2] = x + c*w + s*h;	us[2] = 1.0f;
	xs[3] = x + c*w - s*h;	us[3] = 1.0f;

	ys[0] = y - c*h + s*w;	vs[0] = 0.0f;
	ys[1] = y + c*h + s*w;	vs[1] = 1.0f;
	ys[2] = y + c*h - s*w;	vs[2] = 1.0f;
	ys[3] = y - c*h - s*w;	vs[3] = 0.0f;

	// No clipping, just culling
	if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
	if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
	if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH &&
	   xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return;
	if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT &&
	   ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return;

	float screenz = m_f2DNearScreenZ +
		(z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
		((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);

	RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
	static int indices[6] = { 0, 1, 2, 3, 0, 2 };
	for(i = 0; i < 6; i++){
		RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
		RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
		RwIm2DVertexSetScreenZ(&vert[i], screenz);
		RwIm2DVertexSetCameraZ(&vert[i], z);
		RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
		RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
		RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
		RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
	}
	nSpriteBufferIndex++;
	if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
		FlushSpriteBuffer();
}

void
CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a)
{
	m_bFlushSpriteBufferSwitchZTest = 0;
	float c = Cos(rotation);
	float s = Sin(rotation);

	float xs[4];
	float ys[4];
	float us[4];
	float vs[4];
	int i;

	xs[0] = x + w*(-c-s);	us[0] = 0.0f;
	xs[1] = x + w*(-c+s);	us[1] = 0.0f;
	xs[2] = x + w*(+c+s);	us[2] = 1.0f;
	xs[3] = x + w*(+c-s);	us[3] = 1.0f;

	ys[0] = y + h*(-c+s);	vs[0] = 0.0f;
	ys[1] = y + h*(+c+s);	vs[1] = 1.0f;
	ys[2] = y + h*(+c-s);	vs[2] = 1.0f;
	ys[3] = y + h*(-c-s);	vs[3] = 0.0f;

	// No clipping, just culling
	if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
	if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
	if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH &&
	   xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return;
	if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT &&
	   ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return;

	float screenz = m_f2DNearScreenZ +
		(z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
		((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);

	RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
	static int indices[6] = { 0, 1, 2, 3, 0, 2 };
	for(i = 0; i < 6; i++){
		RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
		RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
		RwIm2DVertexSetScreenZ(&vert[i], screenz);
		RwIm2DVertexSetCameraZ(&vert[i], z);
		RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
		RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
		RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
		RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
	}
	nSpriteBufferIndex++;
	if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
		FlushSpriteBuffer();
}

void
CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a)
{
	m_bFlushSpriteBufferSwitchZTest = 0;
	float c = Cos(rotation);
	float s = Sin(rotation);

	float xs[4];
	float ys[4];
	float us[4];
	float vs[4];
	float cf[4];
	int i;

	xs[0] = x + w*(-c-s);	us[0] = 0.0f;
	xs[1] = x + w*(-c+s);	us[1] = 0.0f;
	xs[2] = x + w*(+c+s);	us[2] = 1.0f;
	xs[3] = x + w*(+c-s);	us[3] = 1.0f;

	ys[0] = y + h*(-c+s);	vs[0] = 0.0f;
	ys[1] = y + h*(+c+s);	vs[1] = 1.0f;
	ys[2] = y + h*(+c-s);	vs[2] = 1.0f;
	ys[3] = y + h*(-c-s);	vs[3] = 0.0f;

	// No clipping, just culling
	if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
	if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
	if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH &&
	   xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return;
	if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT &&
	   ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return;

	// Colour factors, cx/y is the direction in which colours change from rgb1 to rgb2
	cf[0] = (cx*(-c-s) + cy*(-c+s))*0.5f + 0.5f;
	cf[0] = Clamp(cf[0], 0.0f, 1.0f);
	cf[1] = (cx*(-c+s) + cy*( c+s))*0.5f + 0.5f;
	cf[1] = Clamp(cf[1], 0.0f, 1.0f);
	cf[2] = (cx*( c+s) + cy*( c-s))*0.5f + 0.5f;
	cf[2] = Clamp(cf[2], 0.0f, 1.0f);
	cf[3] = (cx*( c-s) + cy*(-c-s))*0.5f + 0.5f;
	cf[3] = Clamp(cf[3], 0.0f, 1.0f);

	float screenz = m_f2DNearScreenZ +
		(z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
		((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);

	RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
	static int indices[6] = { 0, 1, 2, 3, 0, 2 };
	for(i = 0; i < 6; i++){
		RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
		RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
		RwIm2DVertexSetScreenZ(&vert[i], screenz);
		RwIm2DVertexSetCameraZ(&vert[i], z);
		RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
		RwIm2DVertexSetIntRGBA(&vert[i],
			r1*cf[indices[i]] + r2*(1.0f - cf[indices[i]]),
			g1*cf[indices[i]] + g2*(1.0f - cf[indices[i]]),
			b1*cf[indices[i]] + b2*(1.0f - cf[indices[i]]),
			a);
		RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
		RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
	}
	nSpriteBufferIndex++;
	if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
		FlushSpriteBuffer();
}

void
CSprite::Set6Vertices2D(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
{
	float screenz, recipz;
	float z = RwCameraGetNearClipPlane(Scene.camera);	// not done by game

	screenz = m_f2DNearScreenZ;
	recipz = m_fRecipNearClipPlane;

	RwIm2DVertexSetScreenX(&verts[0], r.left);
	RwIm2DVertexSetScreenY(&verts[0], r.top);
	RwIm2DVertexSetScreenZ(&verts[0], screenz);
	RwIm2DVertexSetCameraZ(&verts[0], z);
	RwIm2DVertexSetRecipCameraZ(&verts[0], recipz);
	RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&verts[0], 0.0f, recipz);
	RwIm2DVertexSetV(&verts[0], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[1], r.right);
	RwIm2DVertexSetScreenY(&verts[1], r.top);
	RwIm2DVertexSetScreenZ(&verts[1], screenz);
	RwIm2DVertexSetCameraZ(&verts[1], z);
	RwIm2DVertexSetRecipCameraZ(&verts[1], recipz);
	RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a);
	RwIm2DVertexSetU(&verts[1], 1.0f, recipz);
	RwIm2DVertexSetV(&verts[1], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[2], r.right);
	RwIm2DVertexSetScreenY(&verts[2], r.bottom);
	RwIm2DVertexSetScreenZ(&verts[2], screenz);
	RwIm2DVertexSetCameraZ(&verts[2], z);
	RwIm2DVertexSetRecipCameraZ(&verts[2], recipz);
	RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&verts[2], 1.0f, recipz);
	RwIm2DVertexSetV(&verts[2], 1.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[3], r.left);
	RwIm2DVertexSetScreenY(&verts[3], r.bottom);
	RwIm2DVertexSetScreenZ(&verts[3], screenz);
	RwIm2DVertexSetCameraZ(&verts[3], z);
	RwIm2DVertexSetRecipCameraZ(&verts[3], recipz);
	RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a);
	RwIm2DVertexSetU(&verts[3], 0.0f, recipz);
	RwIm2DVertexSetV(&verts[3], 1.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[4], r.left);
	RwIm2DVertexSetScreenY(&verts[4], r.top);
	RwIm2DVertexSetScreenZ(&verts[4], screenz);
	RwIm2DVertexSetCameraZ(&verts[4], z);
	RwIm2DVertexSetRecipCameraZ(&verts[4], recipz);
	RwIm2DVertexSetIntRGBA(&verts[4], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&verts[4], 0.0f, recipz);
	RwIm2DVertexSetV(&verts[4], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[5], r.right);
	RwIm2DVertexSetScreenY(&verts[5], r.bottom);
	RwIm2DVertexSetScreenZ(&verts[5], screenz);
	RwIm2DVertexSetCameraZ(&verts[5], z);
	RwIm2DVertexSetRecipCameraZ(&verts[5], recipz);
	RwIm2DVertexSetIntRGBA(&verts[5], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&verts[5], 1.0f, recipz);
	RwIm2DVertexSetV(&verts[5], 1.0f, recipz);
}

void
CSprite::Set6Vertices2D(RwIm2DVertex *verts, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
		const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
{
	float screenz, recipz;
	float z = RwCameraGetNearClipPlane(Scene.camera);	// not done by game

	screenz = m_f2DNearScreenZ;
	recipz = m_fRecipNearClipPlane;

	RwIm2DVertexSetScreenX(&verts[0], x3);
	RwIm2DVertexSetScreenY(&verts[0], y3);
	RwIm2DVertexSetScreenZ(&verts[0], screenz);
	RwIm2DVertexSetCameraZ(&verts[0], z);
	RwIm2DVertexSetRecipCameraZ(&verts[0], recipz);
	RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&verts[0], 0.0f, recipz);
	RwIm2DVertexSetV(&verts[0], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[1], x4);
	RwIm2DVertexSetScreenY(&verts[1], y4);
	RwIm2DVertexSetScreenZ(&verts[1], screenz);
	RwIm2DVertexSetCameraZ(&verts[1], z);
	RwIm2DVertexSetRecipCameraZ(&verts[1], recipz);
	RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a);
	RwIm2DVertexSetU(&verts[1], 1.0f, recipz);
	RwIm2DVertexSetV(&verts[1], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[2], x2);
	RwIm2DVertexSetScreenY(&verts[2], y2);
	RwIm2DVertexSetScreenZ(&verts[2], screenz);
	RwIm2DVertexSetCameraZ(&verts[2], z);
	RwIm2DVertexSetRecipCameraZ(&verts[2], recipz);
	RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&verts[2], 1.0f, recipz);
	RwIm2DVertexSetV(&verts[2], 1.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[3], x1);
	RwIm2DVertexSetScreenY(&verts[3], y1);
	RwIm2DVertexSetScreenZ(&verts[3], screenz);
	RwIm2DVertexSetCameraZ(&verts[3], z);
	RwIm2DVertexSetRecipCameraZ(&verts[3], recipz);
	RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a);
	RwIm2DVertexSetU(&verts[3], 0.0f, recipz);
	RwIm2DVertexSetV(&verts[3], 1.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[4], x3);
	RwIm2DVertexSetScreenY(&verts[4], y3);
	RwIm2DVertexSetScreenZ(&verts[4], screenz);
	RwIm2DVertexSetCameraZ(&verts[4], z);
	RwIm2DVertexSetRecipCameraZ(&verts[4], recipz);
	RwIm2DVertexSetIntRGBA(&verts[4], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&verts[4], 0.0f, recipz);
	RwIm2DVertexSetV(&verts[4], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&verts[5], x2);
	RwIm2DVertexSetScreenY(&verts[5], y2);
	RwIm2DVertexSetScreenZ(&verts[5], screenz);
	RwIm2DVertexSetCameraZ(&verts[5], z);
	RwIm2DVertexSetRecipCameraZ(&verts[5], recipz);
	RwIm2DVertexSetIntRGBA(&verts[5], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&verts[5], 1.0f, recipz);
	RwIm2DVertexSetV(&verts[5], 1.0f, recipz);
}

void
CSprite::RenderBufferedOneXLUSprite2D(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, uint8 alpha)
{
	m_bFlushSpriteBufferSwitchZTest = 1;
	CRGBA col(intens * colour.red >> 8, intens * colour.green >> 8, intens * colour.blue >> 8, alpha);
	CRect rect(x - w, y - h, x + h, y + h);
	Set6Vertices2D(&SpriteBufferVerts[6 * nSpriteBufferIndex], rect, col, col, col, col);
	nSpriteBufferIndex++;
	if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
		FlushSpriteBuffer();
}

void
CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, float rotation, uint8 alpha)
{
	m_bFlushSpriteBufferSwitchZTest = 1;
	CRGBA col(intens * colour.red >> 8, intens * colour.green >> 8, intens * colour.blue >> 8, alpha);
	float c = Cos(DEGTORAD(rotation));
	float s = Sin(DEGTORAD(rotation));

	Set6Vertices2D(&SpriteBufferVerts[6 * nSpriteBufferIndex],
		x + c*w - s*h,
		y - c*h - s*w,
		x + c*w + s*h,
		y + c*h - s*w,
		x - c*w - s*h,
		y - c*h + s*w,
		x - c*w + s*h,
		y + c*h + s*w,
		col, col, col, col);
	nSpriteBufferIndex++;
	if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
		FlushSpriteBuffer();
}


code:
#pragma once

class CSprite
{
	static float m_f2DNearScreenZ;
	static float m_f2DFarScreenZ;
	static float m_fRecipNearClipPlane;
	static int32 m_bFlushSpriteBufferSwitchZTest;
public:
	static float CalcHorizonCoors(void);
	static bool CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip);
	static void InitSpriteBuffer(void);
	static void InitSpriteBuffer2D(void);
	static void FlushSpriteBuffer(void);
	static void RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a);
	static void RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
	static void RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a);
	static void RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
	static void RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
	// cx/y is the direction in which the colour changes
	static void RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a);
	static void Set6Vertices2D(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);
	static void Set6Vertices2D(RwIm2DVertex *verts, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
		const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);
	static void RenderBufferedOneXLUSprite2D(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, uint8 alpha);
	static void RenderBufferedOneXLUSprite2D_Rotate_Dimension(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, float rotation, uint8 alpha);

};


code:
#include "common.h"

#include "main.h"
#include "Draw.h"
#include "Camera.h"
#include "Sprite2d.h"
#include "Font.h"

RwIm2DVertex CSprite2d::maVertices[8];
float CSprite2d::RecipNearClip;
int32 CSprite2d::mCurrentBank;
RwTexture *CSprite2d::mpBankTextures[10];
int32 CSprite2d::mCurrentSprite[10];
int32 CSprite2d::mBankStart[10];
RwIm2DVertex CSprite2d::maBankVertices[500];

void
CSprite2d::SetRecipNearClip(void)
{
	RecipNearClip = 1.0f / RwCameraGetNearClipPlane(Scene.camera);
}

void
CSprite2d::InitPerFrame(void)
{
	int i;

	mCurrentBank = 0;
	for(i = 0; i < 10; i++)
		mCurrentSprite[i] = 0;
#ifndef SQUEEZE_PERFORMANCE
	for(i = 0; i < 10; i++)
		mpBankTextures[i] = nil;
#endif
}

int32
CSprite2d::GetBank(int32 n, RwTexture *tex)
{
#ifndef SQUEEZE_PERFORMANCE
	mpBankTextures[mCurrentBank] = tex;
#endif
	mCurrentSprite[mCurrentBank] = 0;
	mBankStart[mCurrentBank+1] = mBankStart[mCurrentBank] + n;
	return mCurrentBank++;
}

void
CSprite2d::AddSpriteToBank(int32 bank, const CRect &rect, const CRGBA &col,
	float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2)
{
	SetVertices(&maBankVertices[6 * (mCurrentSprite[bank] + mBankStart[bank])],
		rect, col, col, col, col,
		u0, v0, u1, v1, u2, v2, u3, v3);
	mCurrentSprite[bank]++;
	if(mCurrentSprite[bank] + mBankStart[bank] >= mBankStart[bank+1]){
		DrawBank(bank);
		mCurrentSprite[bank] = 0;
	}
}

void
CSprite2d::DrawBank(int32 bank)
{
	if(mCurrentSprite[bank] == 0)
		return;
#ifndef SQUEEZE_PERFORMANCE
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER,
		mpBankTextures[bank] ? RwTextureGetRaster(mpBankTextures[bank]) : nil);
#else
	CFont::Sprite[bank].SetRenderState();
#endif
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
	RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, &maBankVertices[6*mBankStart[bank]], 6*mCurrentSprite[bank]);
	mCurrentSprite[bank] = 0;
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
}

void
CSprite2d::Delete(void)
{
	if(m_pTexture){
		RwTextureDestroy(m_pTexture);
		m_pTexture = nil;
	}
}

void
CSprite2d::SetTexture(const char *name)
{
	Delete();
	if(name)
		m_pTexture = RwTextureRead(name, nil);
}

void
CSprite2d::SetTexture(const char *name, const char *mask)
{
	Delete();
	if(name)
		m_pTexture = RwTextureRead(name, mask);
}

void
CSprite2d::SetAddressing(RwTextureAddressMode addr)
{
	if(m_pTexture)
		RwTextureSetAddressing(m_pTexture, addr);
}

void
CSprite2d::SetRenderState(void)
{
	if(m_pTexture)
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(m_pTexture));
	else
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
}

void
CSprite2d::Draw(float x, float y, float w, float h, const CRGBA &col)
{
	SetVertices(CRect(x, y, x + w, y + h), col, col, col, col, 0);
	SetRenderState();
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
}

void
CSprite2d::Draw(const CRect &rect, const CRGBA &col)
{
	SetVertices(rect, col, col, col, col, 0);
	SetRenderState();
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
}

void
CSprite2d::Draw(const CRect &rect, const CRGBA &col,
	float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2)
{
	SetVertices(rect, col, col, col, col, u0, v0, u1, v1, u3, v3, u2, v2);
	SetRenderState();
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
}

void
CSprite2d::Draw(const CRect &rect, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
{
	SetVertices(rect, c0, c1, c2, c3, 0);
	SetRenderState();
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
}

void
CSprite2d::Draw(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &col)
{
	SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, col, col, col, col);
	SetRenderState();
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
}

// Arguments:
// 2---3
// |   |
// 0---1
void
CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, uint32 far)
{
	float screenz, z, recipz;

	if(far){
		screenz = RwIm2DGetFarScreenZ();
		z = RwCameraGetFarClipPlane(Scene.camera);
	}else{
		screenz = RwIm2DGetNearScreenZ();
		z = 1.0f/RecipNearClip;
	}
	recipz = 1.0f/z;
	float offset = 1.0f/1024.0f;

	// This is what we draw:
	// 0---1
	// | / |
	// 3---2
	RwIm2DVertexSetScreenX(&maVertices[0], r.left);
	RwIm2DVertexSetScreenY(&maVertices[0], r.top);
	RwIm2DVertexSetScreenZ(&maVertices[0], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[0], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[0], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&maVertices[0], 0.0f+offset, recipz);
	RwIm2DVertexSetV(&maVertices[0], 0.0f+offset, recipz);

	RwIm2DVertexSetScreenX(&maVertices[1], r.right);
	RwIm2DVertexSetScreenY(&maVertices[1], r.top);
	RwIm2DVertexSetScreenZ(&maVertices[1], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[1], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[1], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a);
	RwIm2DVertexSetU(&maVertices[1], 1.0f+offset, recipz);
	RwIm2DVertexSetV(&maVertices[1], 0.0f+offset, recipz);

	RwIm2DVertexSetScreenX(&maVertices[2], r.right);
	RwIm2DVertexSetScreenY(&maVertices[2], r.bottom);
	RwIm2DVertexSetScreenZ(&maVertices[2], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[2], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[2], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&maVertices[2], 1.0f+offset, recipz);
	RwIm2DVertexSetV(&maVertices[2], 1.0f+offset, recipz);

	RwIm2DVertexSetScreenX(&maVertices[3], r.left);
	RwIm2DVertexSetScreenY(&maVertices[3], r.bottom);
	RwIm2DVertexSetScreenZ(&maVertices[3], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[3], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[3], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a);
	RwIm2DVertexSetU(&maVertices[3], 0.0f+offset, recipz);
	RwIm2DVertexSetV(&maVertices[3], 1.0f+offset, recipz);
}

void
CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3,
	float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2)
{
	float screenz, z, recipz;

	screenz = RwIm2DGetNearScreenZ();
	z = 1.0f/RecipNearClip;
	recipz = 1.0f/z;

	// This is what we draw:
	// 0---1
	// | / |
	// 3---2
	RwIm2DVertexSetScreenX(&maVertices[0], r.left);
	RwIm2DVertexSetScreenY(&maVertices[0], r.top);
	RwIm2DVertexSetScreenZ(&maVertices[0], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[0], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[0], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&maVertices[0], u0, recipz);
	RwIm2DVertexSetV(&maVertices[0], v0, recipz);

	RwIm2DVertexSetScreenX(&maVertices[1], r.right);
	RwIm2DVertexSetScreenY(&maVertices[1], r.top);
	RwIm2DVertexSetScreenZ(&maVertices[1], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[1], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[1], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a);
	RwIm2DVertexSetU(&maVertices[1], u1, recipz);
	RwIm2DVertexSetV(&maVertices[1], v1, recipz);

	RwIm2DVertexSetScreenX(&maVertices[2], r.right);
	RwIm2DVertexSetScreenY(&maVertices[2], r.bottom);
	RwIm2DVertexSetScreenZ(&maVertices[2], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[2], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[2], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&maVertices[2], u2, recipz);
	RwIm2DVertexSetV(&maVertices[2], v2, recipz);

	RwIm2DVertexSetScreenX(&maVertices[3], r.left);
	RwIm2DVertexSetScreenY(&maVertices[3], r.bottom);
	RwIm2DVertexSetScreenZ(&maVertices[3], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[3], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[3], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a);
	RwIm2DVertexSetU(&maVertices[3], u3, recipz);
	RwIm2DVertexSetV(&maVertices[3], v3, recipz);
}

void
CSprite2d::SetVertices(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
		const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
{
	float screenz, recipz;
	float z = RwCameraGetNearClipPlane(Scene.camera);	// not done by game

	screenz = RwIm2DGetNearScreenZ();
	recipz = RecipNearClip;

	RwIm2DVertexSetScreenX(&maVertices[0], x3);
	RwIm2DVertexSetScreenY(&maVertices[0], y3);
	RwIm2DVertexSetScreenZ(&maVertices[0], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[0], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[0], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&maVertices[0], 0.0f, recipz);
	RwIm2DVertexSetV(&maVertices[0], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&maVertices[1], x4);
	RwIm2DVertexSetScreenY(&maVertices[1], y4);
	RwIm2DVertexSetScreenZ(&maVertices[1], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[1], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[1], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a);
	RwIm2DVertexSetU(&maVertices[1], 1.0f, recipz);
	RwIm2DVertexSetV(&maVertices[1], 0.0f, recipz);

	RwIm2DVertexSetScreenX(&maVertices[2], x2);
	RwIm2DVertexSetScreenY(&maVertices[2], y2);
	RwIm2DVertexSetScreenZ(&maVertices[2], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[2], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[2], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&maVertices[2], 1.0f, recipz);
	RwIm2DVertexSetV(&maVertices[2], 1.0f, recipz);

	RwIm2DVertexSetScreenX(&maVertices[3], x1);
	RwIm2DVertexSetScreenY(&maVertices[3], y1);
	RwIm2DVertexSetScreenZ(&maVertices[3], screenz);
	RwIm2DVertexSetCameraZ(&maVertices[3], z);
	RwIm2DVertexSetRecipCameraZ(&maVertices[3], recipz);
	RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a);
	RwIm2DVertexSetU(&maVertices[3], 0.0f, recipz);
	RwIm2DVertexSetV(&maVertices[3], 1.0f, recipz);
}

void
CSprite2d::SetVertices(int n, float *positions, float *uvs, const CRGBA &col)
{
	int i;
	float screenz, recipz, z;

	screenz = RwIm2DGetNearScreenZ();
	recipz = RecipNearClip;
	z = RwCameraGetNearClipPlane(Scene.camera);	// not done by game

	for(i = 0; i < n; i++){
		RwIm2DVertexSetScreenX(&maVertices[i], positions[i*2 + 0]);
		RwIm2DVertexSetScreenY(&maVertices[i], positions[i*2 + 1]);
		RwIm2DVertexSetScreenZ(&maVertices[i], screenz + 0.0001f);
		RwIm2DVertexSetCameraZ(&maVertices[i], z);
		RwIm2DVertexSetRecipCameraZ(&maVertices[i], recipz);
		RwIm2DVertexSetIntRGBA(&maVertices[i], col.r, col.g, col.b, col.a);
		RwIm2DVertexSetU(&maVertices[i], uvs[i*2 + 0], recipz);
		RwIm2DVertexSetV(&maVertices[i], uvs[i*2 + 1], recipz);
	}
}

void
CSprite2d::SetMaskVertices(int n, float *positions)
{
	int i;
	float screenz, recipz, z;

	screenz = RwIm2DGetNearScreenZ();
	recipz = RecipNearClip;
	z = RwCameraGetNearClipPlane(Scene.camera);	// not done by game

	for(i = 0; i < n; i++){
		RwIm2DVertexSetScreenX(&maVertices[i], positions[i*2 + 0]);
		RwIm2DVertexSetScreenY(&maVertices[i], positions[i*2 + 1]);
		RwIm2DVertexSetScreenZ(&maVertices[i], screenz);
		RwIm2DVertexSetCameraZ(&maVertices[i], z);
		RwIm2DVertexSetRecipCameraZ(&maVertices[i], recipz);
#if !defined(GTA_PS2_STUFF) && defined(RWLIBS)
		RwIm2DVertexSetIntRGBA(&maVertices[i], 0, 0, 0, 0);
#else
		RwIm2DVertexSetIntRGBA(&maVertices[i], 255, 255, 255, 255);
#endif
	}
}

void
CSprite2d::SetVertices(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3,
		float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2)
{
	float screenz, recipz, z;

	screenz = RwIm2DGetNearScreenZ();
	recipz = RecipNearClip;
	z = RwCameraGetNearClipPlane(Scene.camera);	// not done by game

	RwIm2DVertexSetScreenX(&verts[0], r.left);
	RwIm2DVertexSetScreenY(&verts[0], r.top);
	RwIm2DVertexSetScreenZ(&verts[0], screenz);
	RwIm2DVertexSetCameraZ(&verts[0], z);
	RwIm2DVertexSetRecipCameraZ(&verts[0], recipz);
	RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&verts[0], u0, recipz);
	RwIm2DVertexSetV(&verts[0], v0, recipz);

	RwIm2DVertexSetScreenX(&verts[1], r.left);
	RwIm2DVertexSetScreenY(&verts[1], r.bottom);
	RwIm2DVertexSetScreenZ(&verts[1], screenz);
	RwIm2DVertexSetCameraZ(&verts[1], z);
	RwIm2DVertexSetRecipCameraZ(&verts[1], recipz);
	RwIm2DVertexSetIntRGBA(&verts[1], c0.r, c0.g, c0.b, c0.a);
	RwIm2DVertexSetU(&verts[1], u2, recipz);
	RwIm2DVertexSetV(&verts[1], v2, recipz);

	RwIm2DVertexSetScreenX(&verts[2], r.right);
	RwIm2DVertexSetScreenY(&verts[2], r.bottom);
	RwIm2DVertexSetScreenZ(&verts[2], screenz);
	RwIm2DVertexSetCameraZ(&verts[2], z);
	RwIm2DVertexSetRecipCameraZ(&verts[2], recipz);
	RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&verts[2], u3, recipz);
	RwIm2DVertexSetV(&verts[2], v3, recipz);

	RwIm2DVertexSetScreenX(&verts[3], r.left);
	RwIm2DVertexSetScreenY(&verts[3], r.top);
	RwIm2DVertexSetScreenZ(&verts[3], screenz);
	RwIm2DVertexSetCameraZ(&verts[3], z);
	RwIm2DVertexSetRecipCameraZ(&verts[3], recipz);
	RwIm2DVertexSetIntRGBA(&verts[3], c2.r, c2.g, c2.b, c2.a);
	RwIm2DVertexSetU(&verts[3], u0, recipz);
	RwIm2DVertexSetV(&verts[3], v0, recipz);

	RwIm2DVertexSetScreenX(&verts[4], r.right);
	RwIm2DVertexSetScreenY(&verts[4], r.bottom);
	RwIm2DVertexSetScreenZ(&verts[4], screenz);
	RwIm2DVertexSetCameraZ(&verts[4], z);
	RwIm2DVertexSetRecipCameraZ(&verts[4], recipz);
	RwIm2DVertexSetIntRGBA(&verts[4], c1.r, c1.g, c1.b, c1.a);
	RwIm2DVertexSetU(&verts[4], u3, recipz);
	RwIm2DVertexSetV(&verts[4], v3, recipz);

	RwIm2DVertexSetScreenX(&verts[5], r.right);
	RwIm2DVertexSetScreenY(&verts[5], r.top);
	RwIm2DVertexSetScreenZ(&verts[5], screenz);
	RwIm2DVertexSetCameraZ(&verts[5], z);
	RwIm2DVertexSetRecipCameraZ(&verts[5], recipz);
	RwIm2DVertexSetIntRGBA(&verts[5], c3.r, c3.g, c3.b, c3.a);
	RwIm2DVertexSetU(&verts[5], u1, recipz);
	RwIm2DVertexSetV(&verts[5], v1, recipz);

}

void
CSprite2d::DrawRect(const CRect &r, const CRGBA &col)
{
	SetVertices(r, col, col, col, col, false);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
	RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(col.a != 255));
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD);
}

void
CSprite2d::DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
{
	SetVertices(r, c0, c1, c2, c3, false);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
}

void
CSprite2d::DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
{
	SetVertices(r, c0, c1, c2, c3, false);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
}

void CSprite2d::Draw2DPolygon(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &color)
{
	SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, color, color, color, color);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, 0);
	RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(color.a != 255));
	RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD);
}


code:
#pragma once

class CSprite2d
{
	static float RecipNearClip;
	static int32 mCurrentBank;
	static RwTexture *mpBankTextures[10];
	static int32 mCurrentSprite[10];
	static int32 mBankStart[10];
	static RwIm2DVertex maBankVertices[500];
	static RwIm2DVertex maVertices[8];
public:
	RwTexture *m_pTexture;

	static void SetRecipNearClip(void);
	static void InitPerFrame(void);
	static int32 GetBank(int32 n, RwTexture *tex);
	static void AddSpriteToBank(int32 bank, const CRect &rect, const CRGBA &col,
		float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2);
	static void DrawBank(int32 bank);

	CSprite2d(void) : m_pTexture(nil) {};
	~CSprite2d(void) { Delete(); };
	void Delete(void);
	void SetRenderState(void);
	void SetTexture(const char *name);
	void SetTexture(const char *name, const char *mask);
	void SetAddressing(RwTextureAddressMode addr);
	void Draw(float x, float y, float w, float h, const CRGBA &col);
	void Draw(const CRect &rect, const CRGBA &col);
	void Draw(const CRect &rect, const CRGBA &col,
		float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2);
	void Draw(const CRect &rect, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);
	void Draw(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &col);

	static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, uint32 far);
	static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3,
		float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2);
	static void SetVertices(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
		const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);
	static void SetVertices(int n, float *positions, float *uvs, const CRGBA &col);
	static void SetMaskVertices(int n, float *positions);
	static void SetVertices(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3,
		float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2);

	static void DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);
	static void DrawRect(const CRect &r, const CRGBA &col);
	static void DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);

	static void Draw2DPolygon(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &color);

	static RwIm2DVertex* GetVertices() { return maVertices; };
};


code:
#include "common.h"
#include "TexList.h"
#include "rtbmp.h"
#include "FileMgr.h"

bool CTexList::ms_nTexUsed[MAX_TEXUSED];

void
CTexList::Initialise()
{}

void
CTexList::Shutdown()
{}

RwTexture *
CTexList::SetTexture(int32 slot, char *name)
{
	return nil;
}

int32
CTexList::GetFirstFreeTexture()
{
	for (int32 i = 0; i < MAX_TEXUSED; i++)
		if (!ms_nTexUsed[i])
			return i;
	return -1;
}

RwTexture *
CTexList::LoadFileNameTexture(char *name)
{
	return SetTexture(GetFirstFreeTexture(), name);
}

void
CTexList::LoadGlobalTextureList()
{
	CFileMgr::SetDir("TEXTURES");
}


code:
#pragma once

class CTexList
{
	enum { MAX_TEXUSED = 400, };
	static bool ms_nTexUsed[MAX_TEXUSED];
public:
	static void Initialise();
	static void Shutdown();
	static RwTexture *SetTexture(int32 slot, char *name);
	static int32 GetFirstFreeTexture();
	static RwTexture *LoadFileNameTexture(char *name);
	static void LoadGlobalTextureList();
};


code:
#include "common.h"

#include "main.h"
#include "Clock.h"
#include "Weather.h"
#include "Camera.h"
#include "Shadows.h"
#include "ZoneCull.h"
#include "CutsceneMgr.h"
#include "FileMgr.h"
#include "Timecycle.h"

int32 CTimeCycle::m_nAmbientRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nAmbientGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nAmbientBlue[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nDirectionalRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nDirectionalGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nDirectionalBlue[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSkyTopRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSkyTopGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSkyTopBlue[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSkyBottomRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSkyBottomGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSkyBottomBlue[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSunCoreRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSunCoreGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSunCoreBlue[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSunCoronaRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSunCoronaGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nSunCoronaBlue[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fSunSize[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fSpriteSize[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fSpriteBrightness[NUMHOURS][NUMWEATHERS];
int16 CTimeCycle::m_nShadowStrength[NUMHOURS][NUMWEATHERS];
int16 CTimeCycle::m_nLightShadowStrength[NUMHOURS][NUMWEATHERS];
int16 CTimeCycle::m_nTreeShadowStrength[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fFogStart[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fFarClip[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fLightsOnGroundBrightness[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nLowCloudsRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nLowCloudsGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nLowCloudsBlue[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nFluffyCloudsTopRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nFluffyCloudsTopGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nFluffyCloudsTopBlue[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nFluffyCloudsBottomRed[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nFluffyCloudsBottomGreen[NUMHOURS][NUMWEATHERS];
int32 CTimeCycle::m_nFluffyCloudsBottomBlue[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fBlurRed[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fBlurGreen[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fBlurBlue[NUMHOURS][NUMWEATHERS];
float CTimeCycle::m_fBlurAlpha[NUMHOURS][NUMWEATHERS];

float CTimeCycle::m_fCurrentAmbientRed;
float CTimeCycle::m_fCurrentAmbientGreen;
float CTimeCycle::m_fCurrentAmbientBlue;
float CTimeCycle::m_fCurrentDirectionalRed;
float CTimeCycle::m_fCurrentDirectionalGreen;
float CTimeCycle::m_fCurrentDirectionalBlue;
int32 CTimeCycle::m_nCurrentSkyTopRed;
int32 CTimeCycle::m_nCurrentSkyTopGreen;
int32 CTimeCycle::m_nCurrentSkyTopBlue;
int32 CTimeCycle::m_nCurrentSkyBottomRed;
int32 CTimeCycle::m_nCurrentSkyBottomGreen;
int32 CTimeCycle::m_nCurrentSkyBottomBlue;
int32 CTimeCycle::m_nCurrentSunCoreRed;
int32 CTimeCycle::m_nCurrentSunCoreGreen;
int32 CTimeCycle::m_nCurrentSunCoreBlue;
int32 CTimeCycle::m_nCurrentSunCoronaRed;
int32 CTimeCycle::m_nCurrentSunCoronaGreen;
int32 CTimeCycle::m_nCurrentSunCoronaBlue;
float CTimeCycle::m_fCurrentSunSize;
float CTimeCycle::m_fCurrentSpriteSize;
float CTimeCycle::m_fCurrentSpriteBrightness;
int32 CTimeCycle::m_nCurrentShadowStrength;
int32 CTimeCycle::m_nCurrentLightShadowStrength;
int32 CTimeCycle::m_nCurrentTreeShadowStrength;
float CTimeCycle::m_fCurrentFogStart;
float CTimeCycle::m_fCurrentFarClip;
float CTimeCycle::m_fCurrentLightsOnGroundBrightness;
int32 CTimeCycle::m_nCurrentLowCloudsRed;
int32 CTimeCycle::m_nCurrentLowCloudsGreen;
int32 CTimeCycle::m_nCurrentLowCloudsBlue;
int32 CTimeCycle::m_nCurrentFluffyCloudsTopRed;
int32 CTimeCycle::m_nCurrentFluffyCloudsTopGreen;
int32 CTimeCycle::m_nCurrentFluffyCloudsTopBlue;
int32 CTimeCycle::m_nCurrentFluffyCloudsBottomRed;
int32 CTimeCycle::m_nCurrentFluffyCloudsBottomGreen;
int32 CTimeCycle::m_nCurrentFluffyCloudsBottomBlue;
float CTimeCycle::m_fCurrentBlurRed;
float CTimeCycle::m_fCurrentBlurGreen;
float CTimeCycle::m_fCurrentBlurBlue;
float CTimeCycle::m_fCurrentBlurAlpha;
int32 CTimeCycle::m_nCurrentFogColourRed;
int32 CTimeCycle::m_nCurrentFogColourGreen;
int32 CTimeCycle::m_nCurrentFogColourBlue;

int32 CTimeCycle::m_FogReduction;

int32 CTimeCycle::m_CurrentStoredValue;
CVector CTimeCycle::m_VectorToSun[16];
float CTimeCycle::m_fShadowFrontX[16];
float CTimeCycle::m_fShadowFrontY[16];
float CTimeCycle::m_fShadowSideX[16];
float CTimeCycle::m_fShadowSideY[16];
float CTimeCycle::m_fShadowDisplacementX[16];
float CTimeCycle::m_fShadowDisplacementY[16];

void
CTimeCycle::Initialise(void)
{
	int w, h;
	int li, bi;
	char line[1040];

	int ambR, ambG, ambB;
	int dirR, dirG, dirB;
	int skyTopR, skyTopG, skyTopB;
	int skyBotR, skyBotG, skyBotB;
	int sunCoreR, sunCoreG, sunCoreB;
	int sunCoronaR, sunCoronaG, sunCoronaB;
	float sunSz, sprSz, sprBght;
	int shad, lightShad, treeShad;
	float farClp, fogSt, lightGnd;
	int cloudR, cloudG, cloudB;
	int fluffyTopR, fluffyTopG, fluffyTopB;
	int fluffyBotR, fluffyBotG, fluffyBotB;
	float blurR, blurG, blurB, blurA;

	debug("Intialising CTimeCycle...\n");

	CFileMgr::SetDir("DATA");
	CFileMgr::LoadFile("TIMECYC.DAT", work_buff, sizeof(work_buff), "rb");
	CFileMgr::SetDir("");

	line[0] = '\0';
	bi = 0;
	for(w = 0; w < NUMWEATHERS; w++)
		for(h = 0; h < NUMHOURS; h++){
			li = 0;
			while(work_buff[bi] == '/'){
				while(work_buff[bi] != '\n')
					bi++;
				bi++;
			}
			while(work_buff[bi] != '\n')
				line[li++] = work_buff[bi++];
			line[li] = '\0';
			bi++;

			sscanf(line, "%d %d %d  %d %d %d  %d %d %d  %d %d %d "
			             "%d %d %d  %d %d %d  %f %f %f %d %d %d %f %f %f "
			             "%d %d %d  %d %d %d  %d %d %d  %f %f %f %f",
				&ambR, &ambG, &ambB,
				 &dirR, &dirG, &dirB,
				 &skyTopR, &skyTopG, &skyTopB,
				 &skyBotR, &skyBotG, &skyBotB,
				&sunCoreR, &sunCoreG, &sunCoreB,
				 &sunCoronaR, &sunCoronaG, &sunCoronaB,
				 &sunSz, &sprSz, &sprBght,
				 &shad, &lightShad, &treeShad,
				 &farClp, &fogSt, &lightGnd,
				&cloudR, &cloudG, &cloudB,
				 &fluffyTopR, &fluffyTopG, &fluffyTopB,
				 &fluffyBotR, &fluffyBotG, &fluffyBotB,
				 &blurR, &blurG, &blurB, &blurA);

			m_nAmbientRed[h][w] = ambR;
			m_nAmbientGreen[h][w] = ambG;
			m_nAmbientBlue[h][w] = ambB;
			m_nDirectionalRed[h][w] = dirR;
			m_nDirectionalGreen[h][w] = dirG;
			m_nDirectionalBlue[h][w] = dirB;
			m_nSkyTopRed[h][w] = skyTopR;
			m_nSkyTopGreen[h][w] = skyTopG;
			m_nSkyTopBlue[h][w] = skyTopB;
			m_nSkyBottomRed[h][w] = skyBotR;
			m_nSkyBottomGreen[h][w] = skyBotG;
			m_nSkyBottomBlue[h][w] = skyBotB;
			m_nSunCoreRed[h][w] = sunCoreR;
			m_nSunCoreGreen[h][w] = sunCoreG;
			m_nSunCoreBlue[h][w] = sunCoreB;
			m_nSunCoronaRed[h][w] = sunCoronaR;
			m_nSunCoronaGreen[h][w] = sunCoronaG;
			m_nSunCoronaBlue[h][w] = sunCoronaB;
			m_fSunSize[h][w] = sunSz;
			m_fSpriteSize[h][w] = sprSz;
			m_fSpriteBrightness[h][w] = sprBght;
			m_nShadowStrength[h][w] = shad;
			m_nLightShadowStrength[h][w] = lightShad;
			m_nTreeShadowStrength[h][w] = treeShad;
			m_fFarClip[h][w] = farClp;
			m_fFogStart[h][w] = fogSt;
			m_fLightsOnGroundBrightness[h][w] = lightGnd;
			m_nLowCloudsRed[h][w] = cloudR;
			m_nLowCloudsGreen[h][w] = cloudG;
			m_nLowCloudsBlue[h][w] = cloudB;
			m_nFluffyCloudsTopRed[h][w] = fluffyTopR;
			m_nFluffyCloudsTopGreen[h][w] = fluffyTopG;
			m_nFluffyCloudsTopBlue[h][w] = fluffyTopB;
			m_nFluffyCloudsBottomRed[h][w] = fluffyBotR;
			m_nFluffyCloudsBottomGreen[h][w] = fluffyBotG;
			m_nFluffyCloudsBottomBlue[h][w] = fluffyBotB;
			m_fBlurRed[h][w] = blurR;
			m_fBlurGreen[h][w] = blurG;
			m_fBlurBlue[h][w] = blurB;
			m_fBlurAlpha[h][w] = blurA;
		}

	m_FogReduction = 0;

	debug("CTimeCycle ready\n");
}

void
CTimeCycle::Update(void)
{
	int h1 = CClock::GetHours();
	int h2 = (h1+1)%24;
	int w1 = CWeather::OldWeatherType;
	int w2 = CWeather::NewWeatherType;
	float timeInterp = CClock::GetMinutes()/60.0f;
	// coefficients for a bilinear interpolation
	float c0 = (1.0f-timeInterp) * (1.0f-CWeather::InterpolationValue);
	float c1 = timeInterp * (1.0f-CWeather::InterpolationValue);
	float c2 = (1.0f-timeInterp) * CWeather::InterpolationValue;
	float c3 = timeInterp * CWeather::InterpolationValue;

#define INTERP(v) v[h1][w1]*c0 + v[h2][w1]*c1 + v[h1][w2]*c2 + v[h2][w2]*c3

	m_nCurrentSkyTopRed = INTERP(m_nSkyTopRed);
	m_nCurrentSkyTopGreen = INTERP(m_nSkyTopGreen);
	m_nCurrentSkyTopBlue = INTERP(m_nSkyTopBlue);

	m_nCurrentSkyBottomRed = INTERP(m_nSkyBottomRed);
	m_nCurrentSkyBottomGreen = INTERP(m_nSkyBottomGreen);
	m_nCurrentSkyBottomBlue = INTERP(m_nSkyBottomBlue);

	m_fCurrentAmbientRed = INTERP(m_nAmbientRed);
	m_fCurrentAmbientGreen = INTERP(m_nAmbientGreen);
	m_fCurrentAmbientBlue = INTERP(m_nAmbientBlue);
	m_fCurrentAmbientRed /= 255.0f;
	m_fCurrentAmbientGreen /= 255.0f;
	m_fCurrentAmbientBlue /= 255.0f;

	m_fCurrentDirectionalRed = INTERP(m_nDirectionalRed);
	m_fCurrentDirectionalGreen = INTERP(m_nDirectionalGreen);
	m_fCurrentDirectionalBlue = INTERP(m_nDirectionalBlue);
	m_fCurrentDirectionalRed /= 255.0f;
	m_fCurrentDirectionalGreen /= 255.0f;
	m_fCurrentDirectionalBlue /= 255.0f;

	m_nCurrentSunCoreRed = INTERP(m_nSunCoreRed);
	m_nCurrentSunCoreGreen = INTERP(m_nSunCoreGreen);
	m_nCurrentSunCoreBlue = INTERP(m_nSunCoreBlue);

	m_nCurrentSunCoronaRed = INTERP(m_nSunCoronaRed);
	m_nCurrentSunCoronaGreen = INTERP(m_nSunCoronaGreen);
	m_nCurrentSunCoronaBlue = INTERP(m_nSunCoronaBlue);

	m_fCurrentSunSize = INTERP(m_fSunSize);
	m_fCurrentSpriteSize = INTERP(m_fSpriteSize);
	m_fCurrentSpriteBrightness = INTERP(m_fSpriteBrightness);
	m_nCurrentShadowStrength = INTERP(m_nShadowStrength);
	m_nCurrentLightShadowStrength = INTERP(m_nLightShadowStrength);
	m_nCurrentTreeShadowStrength = INTERP(m_nTreeShadowStrength);
	m_fCurrentFarClip = INTERP(m_fFarClip);
	m_fCurrentFogStart = INTERP(m_fFogStart);
	m_fCurrentLightsOnGroundBrightness = INTERP(m_fLightsOnGroundBrightness);

	m_nCurrentLowCloudsRed = INTERP(m_nLowCloudsRed);
	m_nCurrentLowCloudsGreen = INTERP(m_nLowCloudsGreen);
	m_nCurrentLowCloudsBlue = INTERP(m_nLowCloudsBlue);

	m_nCurrentFluffyCloudsTopRed = INTERP(m_nFluffyCloudsTopRed);
	m_nCurrentFluffyCloudsTopGreen = INTERP(m_nFluffyCloudsTopGreen);
	m_nCurrentFluffyCloudsTopBlue = INTERP(m_nFluffyCloudsTopBlue);

	m_nCurrentFluffyCloudsBottomRed = INTERP(m_nFluffyCloudsBottomRed);
	m_nCurrentFluffyCloudsBottomGreen = INTERP(m_nFluffyCloudsBottomGreen);
	m_nCurrentFluffyCloudsBottomBlue = INTERP(m_nFluffyCloudsBottomBlue);

	m_fCurrentBlurRed = INTERP(m_fBlurRed);
	m_fCurrentBlurGreen = INTERP(m_fBlurGreen);
	m_fCurrentBlurBlue = INTERP(m_fBlurBlue);
	m_fCurrentBlurAlpha = INTERP(m_fBlurAlpha);

	if(TheCamera.m_BlurType == MOTION_BLUR_NONE || TheCamera.m_BlurType == MOTION_BLUR_LIGHT_SCENE)
		TheCamera.SetMotionBlur(m_fCurrentBlurRed, m_fCurrentBlurGreen, m_fCurrentBlurBlue, m_fCurrentBlurAlpha, MOTION_BLUR_LIGHT_SCENE);

	if(m_FogReduction != 0)
		m_fCurrentFarClip = Max(m_fCurrentFarClip, m_FogReduction/64.0f * 650.0f);
	m_nCurrentFogColourRed = (m_nCurrentSkyTopRed + 2*m_nCurrentSkyBottomRed) / 3;
	m_nCurrentFogColourGreen = (m_nCurrentSkyTopGreen + 2*m_nCurrentSkyBottomGreen) / 3;
	m_nCurrentFogColourBlue = (m_nCurrentSkyTopBlue + 2*m_nCurrentSkyBottomBlue) / 3;

	m_CurrentStoredValue = (m_CurrentStoredValue+1)&0xF;

	float sunAngle = 2*PI*(CClock::GetMinutes() + CClock::GetHours()*60)/(24*60);
	CVector &sunPos = GetSunDirection();
	sunPos.x = Sin(sunAngle);
	sunPos.y = 1.0f;
	sunPos.z = 0.2f - Cos(sunAngle);
	sunPos.Normalise();

	CShadows::CalcPedShadowValues(sunPos,
		&m_fShadowFrontX[m_CurrentStoredValue], &m_fShadowFrontY[m_CurrentStoredValue],
		&m_fShadowSideX[m_CurrentStoredValue], &m_fShadowSideY[m_CurrentStoredValue],
		&m_fShadowDisplacementX[m_CurrentStoredValue], &m_fShadowDisplacementY[m_CurrentStoredValue]);

	if(TheCamera.GetForward().z < -0.9f ||
	   !CWeather::bScriptsForceRain && (CCullZones::PlayerNoRain() || CCullZones::CamNoRain() || CCutsceneMgr::IsRunning()))
		m_FogReduction = Min(m_FogReduction+1, 64);
	else
		m_FogReduction = Max(m_FogReduction-1, 0);
}