summaryrefslogtreecommitdiff
path: root/src/rw
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
committerclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
commit9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686 (patch)
treea84756b82513739a2672db3a1f0ec579db6d18ff /src/rw
downloadre3-9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686.tar.gz
re3-9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686.zip
Import Cai1Hsu/re3 @ miami (reVC / GTA:VC decompilation)HEADmiami
Snapshot import (no upstream history) into git.ancap.in.ua/claude, per @lzcnt. Source: https://github.com/Cai1Hsu/re3 branch miami.
Diffstat (limited to 'src/rw')
-rw-r--r--src/rw/ClumpRead.cpp223
-rw-r--r--src/rw/Lights.cpp389
-rw-r--r--src/rw/Lights.h27
-rw-r--r--src/rw/MemoryHeap.cpp499
-rw-r--r--src/rw/MemoryHeap.h204
-rw-r--r--src/rw/MemoryMgr.cpp130
-rw-r--r--src/rw/MemoryMgr.h12
-rw-r--r--src/rw/NodeName.cpp77
-rw-r--r--src/rw/NodeName.h4
-rw-r--r--src/rw/RwHelper.cpp800
-rw-r--r--src/rw/RwHelper.h64
-rw-r--r--src/rw/RwMatFX.cpp312
-rw-r--r--src/rw/RwPS2AlphaTest.cpp247
-rw-r--r--src/rw/TexRead.cpp487
-rw-r--r--src/rw/TexturePools.cpp221
-rw-r--r--src/rw/TexturePools.h42
-rw-r--r--src/rw/TxdStore.cpp183
-rw-r--r--src/rw/TxdStore.h44
-rw-r--r--src/rw/VisibilityPlugins.cpp966
-rw-r--r--src/rw/VisibilityPlugins.h149
20 files changed, 5080 insertions, 0 deletions
diff --git a/src/rw/ClumpRead.cpp b/src/rw/ClumpRead.cpp
new file mode 100644
index 0000000..9c027dc
--- /dev/null
+++ b/src/rw/ClumpRead.cpp
@@ -0,0 +1,223 @@
+#include "common.h"
+
+struct rpGeometryList
+{
+ RpGeometry **geometries;
+ int32 numGeoms;
+};
+
+struct rpAtomicBinary
+{
+ RwInt32 frameIndex;
+ RwInt32 geomIndex;
+ RwInt32 flags;
+ RwInt32 unused;
+};
+
+static int32 numberGeometrys;
+static int32 streamPosition;
+static rpGeometryList gGeomList;
+static rwFrameList gFrameList;
+static RpClumpChunkInfo gClumpInfo;
+
+rpGeometryList*
+GeometryListStreamRead1(RwStream *stream, rpGeometryList *geomlist)
+{
+ int i;
+ RwUInt32 size, version;
+ RwInt32 numGeoms;
+
+ numberGeometrys = 0;
+ if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version))
+ return nil;
+ assert(size == 4);
+ if(RwStreamRead(stream, &numGeoms, 4) != 4)
+ return nil;
+
+ numberGeometrys = numGeoms/2;
+ geomlist->numGeoms = numGeoms;
+ if(geomlist->numGeoms > 0){
+ geomlist->geometries = (RpGeometry**)RwMalloc(geomlist->numGeoms * sizeof(RpGeometry*));
+ if(geomlist->geometries == nil)
+ return nil;
+ memset(geomlist->geometries, 0, geomlist->numGeoms * sizeof(RpGeometry*));
+ }else
+ geomlist->geometries = nil;
+
+ for(i = 0; i < numberGeometrys; i++){
+ if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version))
+ return nil;
+ geomlist->geometries[i] = RpGeometryStreamRead(stream);
+ if(geomlist->geometries[i] == nil)
+ return nil;
+ }
+
+ return geomlist;
+}
+
+rpGeometryList*
+GeometryListStreamRead2(RwStream *stream, rpGeometryList *geomlist)
+{
+ int i;
+ RwUInt32 version;
+
+ for(i = numberGeometrys; i < geomlist->numGeoms; i++){
+ if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version))
+ return nil;
+ geomlist->geometries[i] = RpGeometryStreamRead(stream);
+ if(geomlist->geometries[i] == nil)
+ return nil;
+ }
+
+ return geomlist;
+}
+
+void
+GeometryListDeinitialize(rpGeometryList *geomlist)
+{
+ int i;
+
+ for(i = 0; i < geomlist->numGeoms; i++)
+ if(geomlist->geometries[i])
+ RpGeometryDestroy(geomlist->geometries[i]);
+
+ if(geomlist->numGeoms){
+ RwFree(geomlist->geometries);
+ geomlist->numGeoms = 0;
+ }
+}
+
+RpAtomic*
+ClumpAtomicStreamRead(RwStream *stream, rwFrameList *frmList, rpGeometryList *geomList)
+{
+ RwUInt32 size, version;
+ rpAtomicBinary a;
+ RpAtomic *atomic;
+
+ numberGeometrys = 0;
+ if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version))
+ return nil;
+ assert(size <= sizeof(rpAtomicBinary));
+ if(RwStreamRead(stream, &a, size) != size)
+ return nil;
+
+ atomic = RpAtomicCreate();
+ if(atomic == nil)
+ return nil;
+
+ RpAtomicSetFlags(atomic, a.flags);
+
+ if(frmList->numFrames){
+ assert(a.frameIndex < frmList->numFrames);
+ RpAtomicSetFrame(atomic, frmList->frames[a.frameIndex]);
+ }
+
+ if(geomList->numGeoms){
+ assert(a.geomIndex < geomList->numGeoms);
+ RpAtomicSetGeometry(atomic, geomList->geometries[a.geomIndex], 0);
+ }else{
+ RpGeometry *geom;
+ if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version)){
+ RpAtomicDestroy(atomic);
+ return nil;
+ }
+ geom = RpGeometryStreamRead(stream);
+ if(geom == nil){
+ RpAtomicDestroy(atomic);
+ return nil;
+ }
+ RpAtomicSetGeometry(atomic, geom, 0);
+ RpGeometryDestroy(geom);
+ }
+
+ return atomic;
+}
+
+bool
+RpClumpGtaStreamRead1(RwStream *stream)
+{
+ RwUInt32 size, version;
+
+ if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version))
+ return false;
+ if(version >= 0x33000){
+ assert(size == 12);
+ if(RwStreamRead(stream, &gClumpInfo, 12) != 12)
+ return false;
+ }else{
+ assert(size == 4);
+ if(RwStreamRead(stream, &gClumpInfo, 4) != 4)
+ return false;
+ }
+
+ if(!RwStreamFindChunk(stream, rwID_FRAMELIST, nil, &version))
+ return false;
+ if(rwFrameListStreamRead(stream, &gFrameList) == nil)
+ return false;
+
+ if(!RwStreamFindChunk(stream, rwID_GEOMETRYLIST, nil, &version)){
+ rwFrameListDeinitialize(&gFrameList);
+ return false;
+ }
+ if(GeometryListStreamRead1(stream, &gGeomList) == nil){
+ rwFrameListDeinitialize(&gFrameList);
+ return false;
+ }
+ streamPosition = STREAMPOS(stream);
+ return true;
+}
+
+RpClump*
+RpClumpGtaStreamRead2(RwStream *stream)
+{
+ int i;
+ RwUInt32 version;
+ RpAtomic *atomic;
+ RpClump *clump;
+
+ clump = RpClumpCreate();
+ if(clump == nil)
+ return nil;
+
+ RwStreamSkip(stream, streamPosition - STREAMPOS(stream));
+
+ if(GeometryListStreamRead2(stream, &gGeomList) == nil){
+ GeometryListDeinitialize(&gGeomList);
+ rwFrameListDeinitialize(&gFrameList);
+ RpClumpDestroy(clump);
+ return nil;
+ }
+
+ RpClumpSetFrame(clump, gFrameList.frames[0]);
+
+ for(i = 0; i < gClumpInfo.numAtomics; i++){
+ if(!RwStreamFindChunk(stream, rwID_ATOMIC, nil, &version)){
+ GeometryListDeinitialize(&gGeomList);
+ rwFrameListDeinitialize(&gFrameList);
+ RpClumpDestroy(clump);
+ return nil;
+ }
+
+ atomic = ClumpAtomicStreamRead(stream, &gFrameList, &gGeomList);
+ if(atomic == nil){
+ GeometryListDeinitialize(&gGeomList);
+ rwFrameListDeinitialize(&gFrameList);
+ RpClumpDestroy(clump);
+ return nil;
+ }
+
+ RpClumpAddAtomic(clump, atomic);
+ }
+
+ GeometryListDeinitialize(&gGeomList);
+ rwFrameListDeinitialize(&gFrameList);
+ return clump;
+}
+
+void
+RpClumpGtaCancelStream(void)
+{
+ GeometryListDeinitialize(&gGeomList);
+ rwFrameListDeinitialize(&gFrameList);
+ gFrameList.numFrames = 0;
+}
diff --git a/src/rw/Lights.cpp b/src/rw/Lights.cpp
new file mode 100644
index 0000000..772e196
--- /dev/null
+++ b/src/rw/Lights.cpp
@@ -0,0 +1,389 @@
+#include "common.h"
+#include <rwcore.h>
+#include <rpworld.h>
+
+#include "Lights.h"
+#include "Timer.h"
+#include "Timecycle.h"
+#include "Coronas.h"
+#include "Weather.h"
+#include "ZoneCull.h"
+#include "Frontend.h"
+#include "MBlur.h"
+
+RpLight *pAmbient;
+RpLight *pDirect;
+RpLight *pExtraDirectionals[4] = { nil };
+int LightStrengths[4];
+int NumExtraDirLightsInWorld;
+
+RwRGBAReal AmbientLightColourForFrame;
+RwRGBAReal AmbientLightColourForFrame_PedsCarsAndObjects;
+RwRGBAReal DirectionalLightColourForFrame;
+
+RwRGBAReal AmbientLightColour;
+RwRGBAReal DirectionalLightColour;
+
+#ifdef EXTENDED_COLOURFILTER
+#include "postfx.h"
+#define USEBLURCOLORS CPostFX::UseBlurColours()
+#else
+#define USEBLURCOLORS CMBlur::BlurOn
+#endif
+
+void
+SetLightsWithTimeOfDayColour(RpWorld *)
+{
+ CVector vec1, vec2, vecsun;
+ RwMatrix mat;
+
+ if(pAmbient){
+ if(USEBLURCOLORS){
+ AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed_Bl() * CCoronas::LightsMult;
+ AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen_Bl() * CCoronas::LightsMult;
+ AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue_Bl() * CCoronas::LightsMult;
+ }else{
+ AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed() * CCoronas::LightsMult;
+ AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen() * CCoronas::LightsMult;
+ AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue() * CCoronas::LightsMult;
+ }
+
+ if(USEBLURCOLORS){
+ AmbientLightColourForFrame_PedsCarsAndObjects.red = CTimeCycle::GetAmbientRed_Obj_Bl() * CCoronas::LightsMult;
+ AmbientLightColourForFrame_PedsCarsAndObjects.green = CTimeCycle::GetAmbientGreen_Obj_Bl() * CCoronas::LightsMult;
+ AmbientLightColourForFrame_PedsCarsAndObjects.blue = CTimeCycle::GetAmbientBlue_Obj_Bl() * CCoronas::LightsMult;
+ }else{
+ AmbientLightColourForFrame_PedsCarsAndObjects.red = CTimeCycle::GetAmbientRed_Obj() * CCoronas::LightsMult;
+ AmbientLightColourForFrame_PedsCarsAndObjects.green = CTimeCycle::GetAmbientGreen_Obj() * CCoronas::LightsMult;
+ AmbientLightColourForFrame_PedsCarsAndObjects.blue = CTimeCycle::GetAmbientBlue_Obj() * CCoronas::LightsMult;
+ }
+
+ if(CWeather::LightningFlash && !CCullZones::CamNoRain()){
+ AmbientLightColourForFrame.red = 1.0f;
+ AmbientLightColourForFrame.green = 1.0f;
+ AmbientLightColourForFrame.blue = 1.0f;
+
+ AmbientLightColourForFrame_PedsCarsAndObjects.red = 1.0f;
+ AmbientLightColourForFrame_PedsCarsAndObjects.green = 1.0f;
+ AmbientLightColourForFrame_PedsCarsAndObjects.blue = 1.0f;
+ }
+ RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
+ }
+
+ if(pDirect){
+ DirectionalLightColourForFrame.red = CTimeCycle::GetDirectionalRed() * CCoronas::LightsMult;
+ DirectionalLightColourForFrame.green = CTimeCycle::GetDirectionalGreen() * CCoronas::LightsMult;
+ DirectionalLightColourForFrame.blue = CTimeCycle::GetDirectionalBlue() * CCoronas::LightsMult;
+ RpLightSetColor(pDirect, &DirectionalLightColourForFrame);
+
+ vecsun = CTimeCycle::m_VectorToSun[CTimeCycle::m_CurrentStoredValue];
+ vec1 = CVector(0.0f, 0.0f, 1.0f);
+ vec2 = CrossProduct(vec1, vecsun);
+ vec2.Normalise();
+ vec1 = CrossProduct(vec2, vecsun);
+ mat.at.x = -vecsun.x;
+ mat.at.y = -vecsun.y;
+ mat.at.z = -vecsun.z;
+ mat.right.x = vec1.x;
+ mat.right.y = vec1.y;
+ mat.right.z = vec1.z;
+ mat.up.x = vec2.x;
+ mat.up.y = vec2.y;
+ mat.up.z = vec2.z;
+ RwFrameTransform(RpLightGetFrame(pDirect), &mat, rwCOMBINEREPLACE);
+ }
+
+ if(FrontEndMenuManager.m_PrefsBrightness > 256){
+ float f1 = 2.0f * (FrontEndMenuManager.m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f;
+ float f2 = 3.0f * (FrontEndMenuManager.m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f;
+
+ AmbientLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f2);
+ AmbientLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f2);
+ AmbientLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f2);
+ AmbientLightColourForFrame_PedsCarsAndObjects.red = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.red * f1);
+ AmbientLightColourForFrame_PedsCarsAndObjects.green = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.green * f1);
+ AmbientLightColourForFrame_PedsCarsAndObjects.blue = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.blue * f1);
+#ifdef FIX_BUGS
+ DirectionalLightColourForFrame.red = Min(1.0f, DirectionalLightColourForFrame.red * f1);
+ DirectionalLightColourForFrame.green = Min(1.0f, DirectionalLightColourForFrame.green * f1);
+ DirectionalLightColourForFrame.blue = Min(1.0f, DirectionalLightColourForFrame.blue * f1);
+#else
+ DirectionalLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f1);
+ DirectionalLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f1);
+ DirectionalLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f1);
+#endif
+ }
+}
+
+RpWorld*
+LightsCreate(RpWorld *world)
+{
+ int i;
+ RwRGBAReal color;
+ RwFrame *frame;
+
+ if(world == nil)
+ return nil;
+
+ pAmbient = RpLightCreate(rpLIGHTAMBIENT);
+ RpLightSetFlags(pAmbient, rpLIGHTLIGHTATOMICS);
+ color.red = 0.25f;
+ color.green = 0.25f;
+ color.blue = 0.2f;
+ RpLightSetColor(pAmbient, &color);
+
+ pDirect = RpLightCreate(rpLIGHTDIRECTIONAL);
+ RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
+ color.red = 1.0f;
+ color.green = 0.85f;
+ color.blue = 0.45f;
+ RpLightSetColor(pDirect, &color);
+ RpLightSetRadius(pDirect, 2.0f);
+ frame = RwFrameCreate();
+ RpLightSetFrame(pDirect, frame);
+ RwV3d axis = { 1.0f, 1.0f, 0.0f };
+ RwFrameRotate(frame, &axis, 160.0f, rwCOMBINEPRECONCAT);
+
+ RpWorldAddLight(world, pAmbient);
+ RpWorldAddLight(world, pDirect);
+
+ for(i = 0; i < NUMEXTRADIRECTIONALS; i++){
+ pExtraDirectionals[i] = RpLightCreate(rpLIGHTDIRECTIONAL);
+ RpLightSetFlags(pExtraDirectionals[i], 0);
+ color.red = 1.0f;
+ color.green = 0.5f;
+ color.blue = 0.0f;
+ RpLightSetColor(pExtraDirectionals[i], &color);
+ RpLightSetRadius(pExtraDirectionals[i], 2.0f);
+ frame = RwFrameCreate();
+ RpLightSetFrame(pExtraDirectionals[i], frame);
+ RpWorldAddLight(world, pExtraDirectionals[i]);
+ }
+
+ return world;
+}
+
+void
+LightsDestroy(RpWorld *world)
+{
+ int i;
+
+ if(world == nil)
+ return;
+
+ if(pAmbient){
+ RpWorldRemoveLight(world, pAmbient);
+ RpLightDestroy(pAmbient);
+ pAmbient = nil;
+ }
+
+ if(pDirect){
+ RpWorldRemoveLight(world, pDirect);
+ RwFrameDestroy(RpLightGetFrame(pDirect));
+ RpLightDestroy(pDirect);
+ pDirect = nil;
+ }
+
+ for(i = 0; i < NUMEXTRADIRECTIONALS; i++)
+ if(pExtraDirectionals[i]){
+ RpWorldRemoveLight(world, pExtraDirectionals[i]);
+ RwFrameDestroy(RpLightGetFrame(pExtraDirectionals[i]));
+ RpLightDestroy(pExtraDirectionals[i]);
+ pExtraDirectionals[i] = nil;
+ }
+}
+
+void
+WorldReplaceNormalLightsWithScorched(RpWorld *world, float l)
+{
+ RwRGBAReal color;
+ color.red = l;
+ color.green = l;
+ color.blue = l;
+ RpLightSetColor(pAmbient, &color);
+ RpLightSetFlags(pDirect, 0);
+}
+
+void
+WorldReplaceScorchedLightsWithNormal(RpWorld *world)
+{
+ RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
+ RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
+}
+
+void
+AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue)
+{
+ float strength;
+ int weakest;
+ int i, n;
+ RwRGBAReal color;
+ RwV3d *dir;
+
+ strength = Max(Max(red, green), blue);
+ n = -1;
+ if(NumExtraDirLightsInWorld < NUMEXTRADIRECTIONALS)
+ n = NumExtraDirLightsInWorld;
+ else{
+ weakest = strength;
+ for(i = 0; i < NUMEXTRADIRECTIONALS; i++)
+ if(LightStrengths[i] < weakest){
+ weakest = LightStrengths[i];
+ n = i;
+ }
+ }
+
+ if(n < 0)
+ return;
+
+ color.red = red;
+ color.green = green;
+ color.blue = blue;
+ RpLightSetColor(pExtraDirectionals[n], &color);
+ dir = RwMatrixGetAt(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n])));
+ dir->x = -dirx;
+ dir->y = -diry;
+ dir->z = -dirz;
+ RwMatrixUpdate(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n])));
+ RwFrameUpdateObjects(RpLightGetFrame(pExtraDirectionals[n]));
+ RpLightSetFlags(pExtraDirectionals[n], rpLIGHTLIGHTATOMICS);
+ LightStrengths[n] = strength;
+ NumExtraDirLightsInWorld = Min(NumExtraDirLightsInWorld+1, NUMEXTRADIRECTIONALS);
+}
+
+void
+RemoveExtraDirectionalLights(RpWorld *world)
+{
+ int i;
+ for(i = 0; i < NumExtraDirLightsInWorld; i++)
+ RpLightSetFlags(pExtraDirectionals[i], 0);
+ NumExtraDirLightsInWorld = 0;
+}
+
+void
+SetAmbientAndDirectionalColours(float f)
+{
+ AmbientLightColour.red = AmbientLightColourForFrame.red * f;
+ AmbientLightColour.green = AmbientLightColourForFrame.green * f;
+ AmbientLightColour.blue = AmbientLightColourForFrame.blue * f;
+
+ DirectionalLightColour.red = DirectionalLightColourForFrame.red * f;
+ DirectionalLightColour.green = DirectionalLightColourForFrame.green * f;
+ DirectionalLightColour.blue = DirectionalLightColourForFrame.blue * f;
+
+ RpLightSetColor(pAmbient, &AmbientLightColour);
+ RpLightSetColor(pDirect, &DirectionalLightColour);
+}
+
+// unused
+void
+SetFlashyColours(float f)
+{
+ if(CTimer::GetTimeInMilliseconds() & 0x100){
+ AmbientLightColour.red = 1.0f;
+ AmbientLightColour.green = 1.0f;
+ AmbientLightColour.blue = 1.0f;
+
+ DirectionalLightColour.red = DirectionalLightColourForFrame.red;
+ DirectionalLightColour.green = DirectionalLightColourForFrame.green;
+ DirectionalLightColour.blue = DirectionalLightColourForFrame.blue;
+
+ RpLightSetColor(pAmbient, &AmbientLightColour);
+ RpLightSetColor(pDirect, &DirectionalLightColour);
+ }else{
+ SetAmbientAndDirectionalColours(f * 0.75f);
+ }
+}
+
+// unused
+void
+SetFlashyColours_Mild(float f)
+{
+ if(CTimer::GetTimeInMilliseconds() & 0x100){
+ AmbientLightColour.red = 0.65f;
+ AmbientLightColour.green = 0.65f;
+ AmbientLightColour.blue = 0.65f;
+
+ DirectionalLightColour.red = DirectionalLightColourForFrame.red;
+ DirectionalLightColour.green = DirectionalLightColourForFrame.green;
+ DirectionalLightColour.blue = DirectionalLightColourForFrame.blue;
+
+ RpLightSetColor(pAmbient, &AmbientLightColour);
+ RpLightSetColor(pDirect, &DirectionalLightColour);
+ }else{
+ SetAmbientAndDirectionalColours(f * 0.9f);
+ }
+}
+
+void
+SetBrightMarkerColours(float f)
+{
+ AmbientLightColour.red = 0.6f;
+ AmbientLightColour.green = 0.6f;
+ AmbientLightColour.blue = 0.6f;
+
+ DirectionalLightColour.red = (1.0f - DirectionalLightColourForFrame.red) * 0.4f + DirectionalLightColourForFrame.red;
+ DirectionalLightColour.green = (1.0f - DirectionalLightColourForFrame.green) * 0.4f + DirectionalLightColourForFrame.green;
+ DirectionalLightColour.blue = (1.0f - DirectionalLightColourForFrame.blue) * 0.4f + DirectionalLightColourForFrame.blue;
+
+ RpLightSetColor(pAmbient, &AmbientLightColour);
+ RpLightSetColor(pDirect, &DirectionalLightColour);
+}
+
+void
+ReSetAmbientAndDirectionalColours(void)
+{
+ RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
+ RpLightSetColor(pDirect, &DirectionalLightColourForFrame);
+}
+
+void
+DeActivateDirectional(void)
+{
+ RpLightSetFlags(pDirect, 0);
+}
+
+void
+ActivateDirectional(void)
+{
+ RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
+}
+
+RwRGBAReal FullLight = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+void
+SetFullAmbient(void)
+{
+ RpLightSetColor(pAmbient, &FullLight);
+}
+
+void
+SetAmbientColours(void)
+{
+ RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
+}
+
+void
+SetAmbientColoursForPedsCarsAndObjects(void)
+{
+ RpLightSetColor(pAmbient, &AmbientLightColourForFrame_PedsCarsAndObjects);
+}
+
+uint8 IndicateR[] = { 0, 255, 0, 0, 255, 255, 0 };
+uint8 IndicateG[] = { 0, 0, 255, 0, 255, 0, 255 };
+uint8 IndicateB[] = { 0, 0, 0, 255, 0, 255, 255 };
+
+void
+SetAmbientColoursToIndicateRoadGroup(int i)
+{
+ AmbientLightColour.red = IndicateR[i%7]/255.0f;
+ AmbientLightColour.green = IndicateG[i%7]/255.0f;
+ AmbientLightColour.blue = IndicateB[i%7]/255.0f;
+ RpLightSetColor(pAmbient, &AmbientLightColour);
+}
+
+void
+SetAmbientColours(RwRGBAReal *color)
+{
+ RpLightSetColor(pAmbient, color);
+}
diff --git a/src/rw/Lights.h b/src/rw/Lights.h
new file mode 100644
index 0000000..ad355ad
--- /dev/null
+++ b/src/rw/Lights.h
@@ -0,0 +1,27 @@
+#pragma once
+
+extern RpLight *pAmbient;
+extern RpLight *pDirect;
+extern RpLight *pExtraDirectionals[4];
+extern int LightStrengths[4];
+extern int NumExtraDirLightsInWorld;
+
+void SetLightsWithTimeOfDayColour(RpWorld *);
+RpWorld *LightsCreate(RpWorld *world);
+void LightsDestroy(RpWorld *world);
+void WorldReplaceNormalLightsWithScorched(RpWorld *world, float l);
+void WorldReplaceScorchedLightsWithNormal(RpWorld *world);
+void AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue);
+void RemoveExtraDirectionalLights(RpWorld *world);
+void SetAmbientAndDirectionalColours(float f);
+void SetFlashyColours(float f);
+void SetFlashyColours_Mild(float f);
+void SetBrightMarkerColours(float f);
+void ReSetAmbientAndDirectionalColours(void);
+void DeActivateDirectional(void);
+void ActivateDirectional(void);
+void SetAmbientColours(void);
+void SetAmbientColoursForPedsCarsAndObjects(void);
+void SetAmbientColoursToIndicateRoadGroup(int i);
+void SetAmbientColours(RwRGBAReal *color);
+void SetFullAmbient(void);
diff --git a/src/rw/MemoryHeap.cpp b/src/rw/MemoryHeap.cpp
new file mode 100644
index 0000000..285a7c7
--- /dev/null
+++ b/src/rw/MemoryHeap.cpp
@@ -0,0 +1,499 @@
+#include "common.h"
+#include "main.h"
+#include "FileMgr.h"
+#include "Timer.h"
+#include "ModelInfo.h"
+#include "Streaming.h"
+#include "FileLoader.h"
+#include "MemoryHeap.h"
+
+// TODO(MIAMI)
+
+#ifdef USE_CUSTOM_ALLOCATOR
+
+//#define MEMORYHEAP_ASSERT(cond) { if (!(cond)) { printf("ASSERT File:%s Line:%d\n", __FILE__, __LINE__); exit(1); } }
+//#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) { if (!(cond)) { printf("ASSERT File:%s Line:%d:\n\t%s\n", __FILE__, __LINE__, message); exit(1); } }
+
+#define MEMORYHEAP_ASSERT(cond) assert(cond)
+#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) assert(cond)
+
+// registered pointers that we keep track of
+void **gPtrList[4000];
+int32 numPtrs;
+int32 gPosnInList;
+// indices into the ptr list in here are free
+CStack<int32, 4000> m_ptrListIndexStack;
+// how much memory we've moved
+uint32 memMoved;
+
+CMemoryHeap gMainHeap;
+
+void
+CMemoryHeap::Init(uint32 total)
+{
+ MEMORYHEAP_ASSERT((total != 0xF) != 0);
+
+ m_totalMemUsed = 0;
+ m_memUsed = nil;
+ m_currentMemID = MEMID_FREE;
+ m_blocksUsed = nil;
+ m_totalBlocksUsed = 0;
+ m_unkMemId = -1;
+
+ uint8 *mem = (uint8*)malloc(total);
+ assert(((uintptr)mem & 0xF) == 0);
+ m_start = (HeapBlockDesc*)mem;
+ m_end = (HeapBlockDesc*)(mem + total - sizeof(HeapBlockDesc));
+ m_start->m_memId = MEMID_FREE;
+ m_start->m_size = total - 2*sizeof(HeapBlockDesc);
+ m_end->m_memId = MEMID_GAME;
+ m_end->m_size = 0;
+
+ m_freeList.m_last.m_size = INT_MAX;
+ m_freeList.Init();
+ m_freeList.Insert(m_start);
+
+ // TODO: figure out what these are and use sizeof
+ m_fixedSize[0].Init(0x10);
+ m_fixedSize[1].Init(0x20);
+ m_fixedSize[2].Init(0xE0);
+ m_fixedSize[3].Init(0x60);
+ m_fixedSize[4].Init(0x1C0);
+ m_fixedSize[5].Init(0x50);
+
+ m_currentMemID = MEMID_FREE; // disable registration
+ m_memUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32));
+ m_blocksUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32));
+ RegisterMalloc(GetDescFromHeapPointer(m_memUsed));
+ RegisterMalloc(GetDescFromHeapPointer(m_blocksUsed));
+
+ m_currentMemID = MEMID_GAME;
+ for(int i = 0; i < NUM_MEMIDS; i++){
+ m_memUsed[i] = 0;
+ m_blocksUsed[i] = 0;
+ }
+}
+
+void
+CMemoryHeap::RegisterMalloc(HeapBlockDesc *block)
+{
+ block->m_memId = m_currentMemID;
+ if(m_currentMemID == MEMID_FREE)
+ return;
+ m_totalMemUsed += block->m_size + sizeof(HeapBlockDesc);
+ m_memUsed[m_currentMemID] += block->m_size + sizeof(HeapBlockDesc);
+ m_blocksUsed[m_currentMemID]++;
+ m_totalBlocksUsed++;
+}
+
+void
+CMemoryHeap::RegisterFree(HeapBlockDesc *block)
+{
+ if(block->m_memId == MEMID_FREE)
+ return;
+ m_totalMemUsed -= block->m_size + sizeof(HeapBlockDesc);
+ m_memUsed[block->m_memId] -= block->m_size + sizeof(HeapBlockDesc);
+ m_blocksUsed[block->m_memId]--;
+ m_totalBlocksUsed--;
+}
+
+void*
+CMemoryHeap::Malloc(uint32 size)
+{
+ static int recursion = 0;
+
+ // weird way to round up
+ if((size & 0xF) != 0)
+ size = (size&~0xF) + 0x10;
+
+ recursion++;
+
+ // See if we can allocate from one of the fixed-size lists
+ for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){
+ CommonSize *list = &m_fixedSize[i];
+ if(m_fixedSize[i].m_size == size){
+ HeapBlockDesc *block = list->Malloc();
+ if(block){
+ RegisterMalloc(block);
+ recursion--;
+ return block->GetDataPointer();
+ }
+ break;
+ }
+ }
+
+ // now try the normal free list
+ HeapBlockDesc *next;
+ for(HeapBlockDesc *block = m_freeList.m_first.m_next;
+ block != &m_freeList.m_last;
+ block = next){
+ MEMORYHEAP_ASSERT(block->m_memId == MEMID_FREE);
+ MEMORYHEAP_ASSERT_MESSAGE(block >= m_start && block <= m_end, "Block outside of memory");
+
+ // make sure block has maximum size
+ uint32 initialsize = block->m_size;
+ uint32 blocksize = CombineFreeBlocks(block);
+#ifdef FIX_BUGS
+ // has to be done here because block can be moved
+ next = block->m_next;
+#endif
+ if(initialsize != blocksize){
+ block->RemoveHeapFreeBlock();
+ HeapBlockDesc *pos = block->m_prev->FindSmallestFreeBlock(block->m_size);
+ block->InsertHeapFreeBlock(pos->m_prev);
+ }
+ if(block->m_size >= size){
+ // got space to allocate from!
+ block->RemoveHeapFreeBlock();
+ FillInBlockData(block, block->GetNextConsecutive(), size);
+ recursion--;
+ return block->GetDataPointer();
+ }
+#ifndef FIX_BUGS
+ next = block->m_next;
+#endif
+ }
+
+ // oh no, we're losing, try to free some stuff
+ static bool removeCollision = false;
+ static bool removeIslands = false;
+ static bool removeBigBuildings = false;
+ size_t initialMemoryUsed = CStreaming::ms_memoryUsed;
+ CStreaming::MakeSpaceFor(0xCFE800 - CStreaming::ms_memoryUsed);
+ if (recursion > 10)
+ CGame::TidyUpMemory(true, false);
+ else if (recursion > 6)
+ CGame::TidyUpMemory(false, true);
+ if (initialMemoryUsed == CStreaming::ms_memoryUsed && recursion > 11) {
+ if (!removeCollision && !CGame::playingIntro) {
+ CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC);
+ removeCollision = true;
+ }
+ else if (!removeIslands && !CGame::playingIntro) {
+ CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL);
+ CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL);
+ CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN);
+ removeIslands = true;
+ }
+ else if (!removeBigBuildings) {
+ CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL);
+ CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL);
+ CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN);
+ }
+ else {
+ LoadingScreen("NO MORE MEMORY", nil, nil);
+ LoadingScreen("NO MORE MEMORY", nil, nil);
+ }
+ CGame::TidyUpMemory(true, false);
+ }
+ void *mem = Malloc(size);
+ if (removeCollision) {
+ CTimer::Stop();
+ // TODO: different on PS2
+ CFileLoader::LoadCollisionFromDatFile(CCollision::ms_collisionInMemory);
+ removeCollision = false;
+ CTimer::Update();
+ }
+ if (removeBigBuildings || removeIslands) {
+ CTimer::Stop();
+ if (!CGame::playingIntro)
+ CStreaming::RequestBigBuildings(CGame::currLevel);
+ CStreaming::LoadAllRequestedModels(true);
+ removeBigBuildings = false;
+ removeIslands = false;
+ CTimer::Update();
+ }
+ recursion--;
+ return mem;
+}
+
+void*
+CMemoryHeap::Realloc(void *ptr, uint32 size)
+{
+ if(ptr == nil)
+ return Malloc(size);
+
+ // weird way to round up
+ if((size & 0xF) != 0)
+ size = (size&~0xF) + 0x10;
+
+ HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
+
+#ifdef FIX_BUGS
+ // better handling of size < block->m_size
+ if(size == 0){
+ Free(ptr);
+ return nil;
+ }
+ if(block->m_size >= size){
+ // shrink allocated block
+ RegisterFree(block);
+ PushMemId(block->m_memId);
+ FillInBlockData(block, block->GetNextConsecutive(), size);
+ PopMemId();
+ return ptr;
+ }
+#else
+ // not growing. just returning here is a bit cheap though
+ if(block->m_size >= size)
+ return ptr;
+#endif
+
+ // have to grow allocated block
+ HeapBlockDesc *next = block->GetNextConsecutive();
+ MEMORYHEAP_ASSERT_MESSAGE(next >= m_start && next <= m_end, "Block outside of memory");
+ if(next->m_memId == MEMID_FREE){
+ // try to grow the current block
+ // make sure the next free block has maximum size
+ uint32 freespace = CombineFreeBlocks(next);
+ HeapBlockDesc *end = next->GetNextConsecutive();
+ MEMORYHEAP_ASSERT_MESSAGE(end >= m_start && end <= m_end, "Block outside of memory");
+ // why the sizeof here?
+ if(block->m_size + next->m_size + sizeof(HeapBlockDesc) >= size){
+ // enough space to grow
+ next->RemoveHeapFreeBlock();
+ RegisterFree(block);
+ PushMemId(block->m_memId);
+ FillInBlockData(block, next->GetNextConsecutive(), size);
+ PopMemId();
+ return ptr;
+ }
+ }
+
+ // can't grow the existing block, have to get a new one and copy
+ PushMemId(block->m_memId);
+ void *dst = Malloc(size);
+ PopMemId();
+ memcpy(dst, ptr, block->m_size);
+ Free(ptr);
+ return dst;
+}
+
+void
+CMemoryHeap::Free(void *ptr)
+{
+ HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
+ MEMORYHEAP_ASSERT_MESSAGE(block->m_memId != MEMID_FREE, "MemoryHeap corrupt");
+ MEMORYHEAP_ASSERT(m_unkMemId == -1 || m_unkMemId == block->m_memId);
+
+ RegisterFree(block);
+ block->m_memId = MEMID_FREE;
+ CombineFreeBlocks(block);
+ FreeBlock(block);
+ if(block->m_ptrListIndex != -1){
+ int32 idx = block->m_ptrListIndex;
+ gPtrList[idx] = nil;
+ m_ptrListIndexStack.push(idx);
+ }
+ block->m_ptrListIndex = -1;
+}
+
+// allocate 'size' bytes from 'block'
+void
+CMemoryHeap::FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size)
+{
+ block->m_size = size;
+ block->m_ptrListIndex = -1;
+ HeapBlockDesc *remainder = block->GetNextConsecutive();
+ MEMORYHEAP_ASSERT(remainder <= end);
+
+ if(remainder < end-1){
+ RegisterMalloc(block);
+
+ // can fit another block in the remaining space
+ remainder->m_size = GetSizeBetweenBlocks(remainder, end);
+ remainder->m_memId = MEMID_FREE;
+ MEMORYHEAP_ASSERT(remainder->m_size != 0);
+ FreeBlock(remainder);
+ }else{
+ // fully allocate this one
+ if(remainder < end)
+ // no gaps allowed
+ block->m_size = GetSizeBetweenBlocks(block, end);
+ RegisterMalloc(block);
+ }
+}
+
+// Make sure free block has no other free blocks after it
+uint32
+CMemoryHeap::CombineFreeBlocks(HeapBlockDesc *block)
+{
+ HeapBlockDesc *next = block->GetNextConsecutive();
+ if(next->m_memId != MEMID_FREE)
+ return block->m_size;
+ // get rid of free blocks after this one and adjust size
+ for(; next->m_memId == MEMID_FREE; next = next->GetNextConsecutive())
+ next->RemoveHeapFreeBlock();
+ block->m_size = GetSizeBetweenBlocks(block, next);
+ return block->m_size;
+}
+
+// Try to move all registered memory blocks into more optimal location
+void
+CMemoryHeap::TidyHeap(void)
+{
+ for(int i = 0; i < numPtrs; i++){
+ if(gPtrList[i] == nil || *gPtrList[i] == nil)
+ continue;
+ HeapBlockDesc *newblock = WhereShouldMemoryMove(*gPtrList[i]);
+ if(newblock)
+ *gPtrList[i] = MoveHeapBlock(newblock, GetDescFromHeapPointer(*gPtrList[i]));
+ }
+}
+
+// MIAMI: this is empty
+void
+CMemoryHeap::RegisterMemPointer(void *ptr)
+{
+ HeapBlockDesc *block = GetDescFromHeapPointer(*(void**)ptr);
+
+ if(block->m_ptrListIndex != -1)
+ return; // already registered
+
+ int index;
+ if(m_ptrListIndexStack.sp > 0){
+ // re-use a previously free'd index
+ index = m_ptrListIndexStack.pop();
+ }else{
+ // have to find a new index
+ index = gPosnInList;
+
+ void **pp = gPtrList[index];
+ // we're replacing an old pointer here??
+ if(pp && *pp && *pp != (void*)0xDDDDDDDD)
+ GetDescFromHeapPointer(*pp)->m_ptrListIndex = -1;
+
+ gPosnInList++;
+ if(gPosnInList == 4000)
+ gPosnInList = 0;
+ if(numPtrs < 4000)
+ numPtrs++;
+ }
+ gPtrList[index] = (void**)ptr;
+ block->m_ptrListIndex = index;
+}
+
+void*
+CMemoryHeap::MoveMemory(void *ptr)
+{
+ HeapBlockDesc *newblock = WhereShouldMemoryMove(ptr);
+ if(newblock)
+ return MoveHeapBlock(newblock, GetDescFromHeapPointer(ptr));
+ else
+ return ptr;
+}
+
+HeapBlockDesc*
+CMemoryHeap::WhereShouldMemoryMove(void *ptr)
+{
+ HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
+ MEMORYHEAP_ASSERT(block->m_memId != MEMID_FREE);
+
+ HeapBlockDesc *next = block->GetNextConsecutive();
+ if(next->m_memId != MEMID_FREE)
+ return nil;
+
+ // we want to move the block into another block
+ // such that the free space between this and the next block can be minimized
+ HeapBlockDesc *newblock = m_freeList.m_first.FindSmallestFreeBlock(block->m_size);
+ // size of free space wouldn't decrease, so return
+ if(newblock->m_size >= block->m_size + next->m_size)
+ return nil;
+ // size of free space wouldn't decrease enough
+ if(newblock->m_size >= 16 + 1.125f*block->m_size) // what are 16 and 1.125 here? sizeof(HeapBlockDesc)?
+ return nil;
+ return newblock;
+}
+
+void*
+CMemoryHeap::MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src)
+{
+ PushMemId(src->m_memId);
+ dst->RemoveHeapFreeBlock();
+ FillInBlockData(dst, dst->GetNextConsecutive(), src->m_size);
+ PopMemId();
+ memcpy(dst->GetDataPointer(), src->GetDataPointer(), src->m_size);
+ memMoved += src->m_size;
+ dst->m_ptrListIndex = src->m_ptrListIndex;
+ src->m_ptrListIndex = -1;
+ Free(src->GetDataPointer());
+ return dst->GetDataPointer();
+}
+
+uint32
+CMemoryHeap::GetMemoryUsed(int32 id)
+{
+ return m_memUsed[id];
+}
+
+uint32
+CMemoryHeap::GetBlocksUsed(int32 id)
+{
+ return m_blocksUsed[id];
+}
+
+void
+CMemoryHeap::PopMemId(void)
+{
+ assert(m_idStack.sp > 0);
+ m_currentMemID = m_idStack.pop();
+ assert(m_currentMemID != MEMID_FREE);
+}
+
+void
+CMemoryHeap::PushMemId(int32 id)
+{
+ MEMORYHEAP_ASSERT(id != MEMID_FREE);
+ assert(m_idStack.sp < 16);
+ m_idStack.push(m_currentMemID);
+ m_currentMemID = id;
+}
+
+void
+CMemoryHeap::ParseHeap(void)
+{
+ char tmp[16];
+ int fd = CFileMgr::OpenFileForWriting("heap.txt");
+ CTimer::Stop();
+
+ // CMemoryHeap::IntegrityCheck();
+
+ uint32 addrQW = 0;
+ for(HeapBlockDesc *block = m_start; block < m_end; block = block->GetNextConsecutive()){
+ char chr = '*'; // free
+ if(block->m_memId != MEMID_FREE)
+ chr = block->m_memId-1 + 'A';
+ int numQW = block->m_size>>4;
+
+ if((addrQW & 0x3F) == 0){
+ sprintf(tmp, "\n%5dK:", addrQW>>6);
+ CFileMgr::Write(fd, tmp, 8);
+ }
+ CFileMgr::Write(fd, "#", 1); // the descriptor, has to be 16 bytes!!!!
+ addrQW++;
+
+ while(numQW--){
+ if((addrQW & 0x3F) == 0){
+ sprintf(tmp, "\n%5dK:", addrQW>>6);
+ CFileMgr::Write(fd, tmp, 8);
+ }
+ CFileMgr::Write(fd, &chr, 1);
+ addrQW++;
+ }
+ }
+
+ CTimer::Update();
+ CFileMgr::CloseFile(fd);
+}
+
+
+void
+CommonSize::Init(uint32 size)
+{
+ m_freeList.Init();
+ m_size = size;
+ m_failed = 0;
+ m_remaining = 0;
+}
+
+#endif
diff --git a/src/rw/MemoryHeap.h b/src/rw/MemoryHeap.h
new file mode 100644
index 0000000..1a9a51f
--- /dev/null
+++ b/src/rw/MemoryHeap.h
@@ -0,0 +1,204 @@
+#pragma once
+
+// some windows shit
+#ifdef MoveMemory
+#undef MoveMemory
+#endif
+
+#ifdef USE_CUSTOM_ALLOCATOR
+#define PUSH_MEMID(id) gMainHeap.PushMemId(id)
+#define POP_MEMID() gMainHeap.PopMemId()
+#define REGISTER_MEMPTR(ptr) gMainHeap.RegisterMemPointer(ptr)
+#else
+#define PUSH_MEMID(id)
+#define POP_MEMID()
+#define REGISTER_MEMPTR(ptr)
+#endif
+
+enum {
+ MEMID_FREE,
+ MEMID_GAME = 1, // "Game"
+ MEMID_WORLD = 2, // "World"
+ MEMID_ANIMATION = 3, // "Animation"
+ MEMID_POOLS = 4, // "Pools"
+ MEMID_DEF_MODELS = 5, // "Default Models"
+ MEMID_STREAM = 6, // "Streaming"
+ MEMID_STREAM_MODELS = 7, // "Streamed Models"
+ MEMID_STREAM_LODS = 8, // "Streamed LODs"
+ MEMID_STREAM_TEXUTRES = 9, // "Streamed Textures"
+ MEMID_STREAM_COLLISION = 10, // "Streamed Collision"
+ MEMID_STREAM_ANIMATION = 11, // "Streamed Animation"
+ MEMID_TEXTURES = 12, // "Textures"
+ MEMID_COLLISION = 13, // "Collision"
+ MEMID_PRE_ALLOC = 14, // "PreAlloc"
+ MEMID_GAME_PROCESS = 15, // "Game Process"
+ MEMID_SCRIPT = 16, // "Script"
+ MEMID_CARS = 17, // "Cars"
+ MEMID_RENDER = 18, // "Render"
+ MEMID_PED_ATTR = 19, // "Ped Attr"
+ NUM_MEMIDS,
+
+ NUM_FIXED_MEMBLOCKS = 6
+};
+
+template<typename T, uint32 N>
+class CStack
+{
+public:
+ T values[N];
+ uint32 sp;
+
+ CStack() : sp(0) {}
+ void push(const T& val) { values[sp++] = val; }
+ T& pop() { return values[--sp]; }
+};
+
+
+struct HeapBlockDesc
+{
+ uint32 m_size;
+ int16 m_memId;
+ int16 m_ptrListIndex;
+ HeapBlockDesc *m_next;
+ HeapBlockDesc *m_prev;
+
+ HeapBlockDesc *GetNextConsecutive(void)
+ {
+ return (HeapBlockDesc*)((uintptr)this + sizeof(HeapBlockDesc) + m_size);
+ }
+
+ void *GetDataPointer(void)
+ {
+ return (void*)((uintptr)this + sizeof(HeapBlockDesc));
+ }
+
+ void RemoveHeapFreeBlock(void)
+ {
+ m_next->m_prev = m_prev;
+ m_prev->m_next = m_next;
+ }
+
+ // after node
+ void InsertHeapFreeBlock(HeapBlockDesc *node)
+ {
+ m_next = node->m_next;
+ node->m_next->m_prev = this;
+ m_prev = node;
+ node->m_next = this;
+ }
+
+ HeapBlockDesc *FindSmallestFreeBlock(uint32 size)
+ {
+ HeapBlockDesc *b;
+ for(b = m_next; b->m_size < size; b = b->m_next);
+ return b;
+ }
+};
+
+#ifdef USE_CUSTOM_ALLOCATOR
+// TODO: figure something out for 64 bit pointers
+static_assert(sizeof(HeapBlockDesc) == 0x10, "HeapBlockDesc must have 0x10 size otherwise most of assumptions don't make sense");
+#endif
+
+struct HeapBlockList
+{
+ HeapBlockDesc m_first;
+ HeapBlockDesc m_last;
+
+ void Init(void)
+ {
+ m_first.m_next = &m_last;
+ m_last.m_prev = &m_first;
+ }
+
+ void Insert(HeapBlockDesc *node)
+ {
+ node->InsertHeapFreeBlock(&m_first);
+ }
+};
+
+struct CommonSize
+{
+ HeapBlockList m_freeList;
+ uint32 m_size;
+ uint32 m_failed;
+ uint32 m_remaining;
+
+ void Init(uint32 size);
+ void Free(HeapBlockDesc *node)
+ {
+ m_freeList.Insert(node);
+ m_remaining++;
+ }
+ HeapBlockDesc *Malloc(void)
+ {
+ if(m_freeList.m_first.m_next == &m_freeList.m_last){
+ m_failed++;
+ return nil;
+ }
+ HeapBlockDesc *block = m_freeList.m_first.m_next;
+ m_remaining--;
+ block->RemoveHeapFreeBlock();
+ block->m_ptrListIndex = -1;
+ return block;
+ }
+};
+
+class CMemoryHeap
+{
+public:
+ HeapBlockDesc *m_start;
+ HeapBlockDesc *m_end;
+ HeapBlockList m_freeList;
+ CommonSize m_fixedSize[NUM_FIXED_MEMBLOCKS];
+ uint32 m_totalMemUsed;
+ CStack<int32, 16> m_idStack;
+ uint32 m_currentMemID;
+ uint32 *m_memUsed;
+ uint32 m_totalBlocksUsed;
+ uint32 *m_blocksUsed;
+ uint32 m_unkMemId;
+
+ CMemoryHeap(void) : m_start(nil) {}
+ void Init(uint32 total);
+ void RegisterMalloc(HeapBlockDesc *block);
+ void RegisterFree(HeapBlockDesc *block);
+ void *Malloc(uint32 size);
+ void *Realloc(void *ptr, uint32 size);
+ void Free(void *ptr);
+ void FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size);
+ uint32 CombineFreeBlocks(HeapBlockDesc *block);
+ void *MoveMemory(void *ptr);
+ HeapBlockDesc *WhereShouldMemoryMove(void *ptr);
+ void *MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src);
+ void PopMemId(void);
+ void PushMemId(int32 id);
+ void RegisterMemPointer(void *ptr);
+ void TidyHeap(void);
+ uint32 GetMemoryUsed(int32 id);
+ uint32 GetBlocksUsed(int32 id);
+ int32 GetLargestFreeBlock(void) { return m_freeList.m_last.m_prev->m_size; }
+
+ void ParseHeap(void);
+
+ HeapBlockDesc *GetDescFromHeapPointer(void *block)
+ {
+ return (HeapBlockDesc*)((uintptr)block - sizeof(HeapBlockDesc));
+ }
+ uint32 GetSizeBetweenBlocks(HeapBlockDesc *first, HeapBlockDesc *second)
+ {
+ return (uintptr)second - (uintptr)first - sizeof(HeapBlockDesc);
+ }
+ void FreeBlock(HeapBlockDesc *block){
+ for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){
+ if(m_fixedSize[i].m_size == block->m_size){
+ m_fixedSize[i].Free(block);
+ return;
+ }
+ }
+ HeapBlockDesc *b = m_freeList.m_first.FindSmallestFreeBlock(block->m_size);
+ block->InsertHeapFreeBlock(b->m_prev);
+ }
+};
+
+extern CMemoryHeap gMainHeap;
diff --git a/src/rw/MemoryMgr.cpp b/src/rw/MemoryMgr.cpp
new file mode 100644
index 0000000..2379692
--- /dev/null
+++ b/src/rw/MemoryMgr.cpp
@@ -0,0 +1,130 @@
+#include "common.h"
+#include "MemoryHeap.h"
+#include "MemoryMgr.h"
+
+
+uint8 *pMemoryTop;
+
+void
+InitMemoryMgr(void)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+#ifdef GTA_PS2
+#error "finish this"
+#else
+ // randomly allocate 128mb
+ gMainHeap.Init(128*1024*1024);
+#endif
+#endif
+}
+
+
+RwMemoryFunctions memFuncs = {
+ MemoryMgrMalloc,
+ MemoryMgrFree,
+ MemoryMgrRealloc,
+ MemoryMgrCalloc
+};
+
+#ifdef USE_CUSTOM_ALLOCATOR
+// game seems to be using heap directly here, but this is nicer
+void *operator new(size_t sz) { return MemoryMgrMalloc(sz); }
+void *operator new[](size_t sz) { return MemoryMgrMalloc(sz); }
+void operator delete(void *ptr) noexcept { MemoryMgrFree(ptr); }
+void operator delete[](void *ptr) noexcept { MemoryMgrFree(ptr); }
+#endif
+
+void*
+MemoryMgrMalloc(size_t size)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+ void *mem = gMainHeap.Malloc(size);
+#else
+ void *mem = malloc(size);
+#endif
+ if((uint8*)mem + size > pMemoryTop)
+ pMemoryTop = (uint8*)mem + size ;
+ return mem;
+}
+
+void*
+MemoryMgrRealloc(void *ptr, size_t size)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+ void *mem = gMainHeap.Realloc(ptr, size);
+#else
+ void *mem = realloc(ptr, size);
+#endif
+ if((uint8*)mem + size > pMemoryTop)
+ pMemoryTop = (uint8*)mem + size ;
+ return mem;
+}
+
+void*
+MemoryMgrCalloc(size_t num, size_t size)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+ void *mem = gMainHeap.Malloc(num*size);
+#else
+ void *mem = calloc(num, size);
+#endif
+ if((uint8*)mem + size > pMemoryTop)
+ pMemoryTop = (uint8*)mem + size ;
+#ifdef FIX_BUGS
+ memset(mem, 0, num*size);
+#endif
+ return mem;
+}
+
+void
+MemoryMgrFree(void *ptr)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+#ifdef FIX_BUGS
+ // i don't suppose this is handled by RW?
+ if(ptr == nil) return;
+#endif
+ gMainHeap.Free(ptr);
+#else
+ free(ptr);
+#endif
+}
+
+void *
+RwMallocAlign(RwUInt32 size, RwUInt32 align)
+{
+#if defined (FIX_BUGS) || defined(FIX_BUGS_64)
+ uintptr ptralign = align-1;
+ void *mem = (void *)MemoryMgrMalloc(size + sizeof(uintptr) + ptralign);
+
+ ASSERT(mem != nil);
+
+ void *addr = (void *)((((uintptr)mem) + sizeof(uintptr) + ptralign) & ~ptralign);
+
+ ASSERT(addr != nil);
+#else
+ void *mem = (void *)MemoryMgrMalloc(size + align);
+
+ ASSERT(mem != nil);
+
+ void *addr = (void *)((((uintptr)mem) + align) & ~(align - 1));
+
+ ASSERT(addr != nil);
+#endif
+
+ *(((void **)addr) - 1) = mem;
+
+ return addr;
+}
+
+void
+RwFreeAlign(void *mem)
+{
+ ASSERT(mem != nil);
+
+ void *addr = *(((void **)mem) - 1);
+
+ ASSERT(addr != nil);
+
+ MemoryMgrFree(addr);
+}
diff --git a/src/rw/MemoryMgr.h b/src/rw/MemoryMgr.h
new file mode 100644
index 0000000..e296280
--- /dev/null
+++ b/src/rw/MemoryMgr.h
@@ -0,0 +1,12 @@
+#pragma once
+
+extern RwMemoryFunctions memFuncs;
+void InitMemoryMgr(void);
+
+void *MemoryMgrMalloc(size_t size);
+void *MemoryMgrRealloc(void *ptr, size_t size);
+void *MemoryMgrCalloc(size_t num, size_t size);
+void MemoryMgrFree(void *ptr);
+
+void *RwMallocAlign(RwUInt32 size, RwUInt32 align);
+void RwFreeAlign(void *mem);
diff --git a/src/rw/NodeName.cpp b/src/rw/NodeName.cpp
new file mode 100644
index 0000000..a7185e4
--- /dev/null
+++ b/src/rw/NodeName.cpp
@@ -0,0 +1,77 @@
+#include "common.h"
+
+#include "NodeName.h"
+
+static int32 gPluginOffset;
+
+enum
+{
+ ID_NODENAME = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0xFE),
+};
+
+#define NODENAMEEXT(o) (RWPLUGINOFFSET(char, o, gPluginOffset))
+
+void*
+NodeNameConstructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
+{
+ if(gPluginOffset > 0)
+ NODENAMEEXT(object)[0] = '\0';
+ return object;
+}
+
+void*
+NodeNameDestructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
+{
+ return object;
+}
+
+void*
+NodeNameCopy(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject)
+{
+ strncpy(NODENAMEEXT(dstObject), NODENAMEEXT(srcObject), 23);
+ return nil;
+}
+
+RwStream*
+NodeNameStreamRead(RwStream *stream, RwInt32 binaryLength, void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
+{
+ RwStreamRead(stream, NODENAMEEXT(object), binaryLength);
+ NODENAMEEXT(object)[binaryLength] = '\0';
+ return stream;
+}
+
+RwStream*
+NodeNameStreamWrite(RwStream *stream, RwInt32 binaryLength, const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
+{
+ RwStreamWrite(stream, NODENAMEEXT(object), binaryLength);
+ return stream;
+}
+
+RwInt32
+NodeNameStreamGetSize(const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
+{
+ char *name = NODENAMEEXT(object); // can't be nil
+ return name ? (RwInt32)rwstrlen(name) : 0;
+}
+
+bool
+NodeNamePluginAttach(void)
+{
+ gPluginOffset = RwFrameRegisterPlugin(24, ID_NODENAME,
+ NodeNameConstructor,
+ NodeNameDestructor,
+ NodeNameCopy);
+ RwFrameRegisterPluginStream(ID_NODENAME,
+ NodeNameStreamRead,
+ NodeNameStreamWrite,
+ NodeNameStreamGetSize);
+ return gPluginOffset != -1;
+}
+
+char*
+GetFrameNodeName(RwFrame *frame)
+{
+ if(gPluginOffset < 0)
+ return nil;
+ return NODENAMEEXT(frame);
+}
diff --git a/src/rw/NodeName.h b/src/rw/NodeName.h
new file mode 100644
index 0000000..1a3e057
--- /dev/null
+++ b/src/rw/NodeName.h
@@ -0,0 +1,4 @@
+#pragma once
+
+bool NodeNamePluginAttach(void);
+char *GetFrameNodeName(RwFrame *frame);
diff --git a/src/rw/RwHelper.cpp b/src/rw/RwHelper.cpp
new file mode 100644
index 0000000..9b0ba13
--- /dev/null
+++ b/src/rw/RwHelper.cpp
@@ -0,0 +1,800 @@
+#define WITHD3D
+#include "common.h"
+
+#include "Timecycle.h"
+#include "skeleton.h"
+#include "Debug.h"
+#if !defined(FINAL) || defined(DEBUGMENU)
+#include "rtcharse.h"
+#endif
+#ifndef FINAL
+RtCharset *debugCharset;
+bool bDebugRenderGroups;
+#endif
+
+#ifdef PS2_ALPHA_TEST
+bool gPS2alphaTest = true;
+#else
+bool gPS2alphaTest = false;
+#endif
+bool gBackfaceCulling = true;
+
+#if !defined(FINAL) || defined(DEBUGMENU)
+static bool charsetOpen;
+void OpenCharsetSafe()
+{
+ if(!charsetOpen)
+ RtCharsetOpen();
+ charsetOpen = true;
+}
+#endif
+
+void CreateDebugFont()
+{
+#ifndef FINAL
+ RwRGBA color = { 255, 255, 128, 255 };
+ RwRGBA colorbg = { 0, 0, 0, 0 };
+ OpenCharsetSafe();
+ debugCharset = RtCharsetCreate(&color, &colorbg);
+#endif
+}
+
+void DestroyDebugFont()
+{
+#ifndef FINAL
+ RtCharsetDestroy(debugCharset);
+ RtCharsetClose();
+ charsetOpen = false;
+#endif
+}
+
+void ObrsPrintfString(const char *str, short x, short y)
+{
+#ifndef FINAL
+ RtCharsetPrintBuffered(debugCharset, str, x*8, y*16, true);
+#endif
+}
+
+void FlushObrsPrintfs()
+{
+#ifndef FINAL
+ RtCharsetBufferFlush();
+#endif
+}
+
+void
+DefinedState(void)
+{
+ RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSWRAP);
+ RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ //RwRenderStateSet(rwRENDERSTATEALPHAPRIMITIVEBUFFER, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEBORDERCOLOR, (void*)RWRGBALONG(0, 0, 0, 255));
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEFOGCOLOR,
+ (void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255));
+ RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR);
+ RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);
+
+#ifdef LIBRW
+ rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL);
+ rw::SetRenderState(rw::ALPHATESTREF, 3);
+
+ rw::SetRenderState(rw::GSALPHATEST, gPS2alphaTest);
+#else
+ // D3D stuff
+ RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
+ RwD3D8SetRenderState(D3DRS_ALPHAREF, 2);
+#endif
+}
+
+void
+SetCullMode(uint32 mode)
+{
+ if(gBackfaceCulling)
+ RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)mode);
+ else
+ RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);
+}
+
+#ifndef FINAL
+void
+PushRendergroup(const char *name)
+{
+ if(!bDebugRenderGroups)
+ return;
+#if defined(RW_OPENGL)
+ if(GLAD_GL_KHR_debug)
+ glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, name);
+#elif defined(RW_D3D9)
+ static WCHAR tmp[256];
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, tmp, sizeof(tmp));
+ D3DPERF_BeginEvent(0xFFFFFFFF, tmp);
+#endif
+}
+
+void
+PopRendergroup(void)
+{
+ if(!bDebugRenderGroups)
+ return;
+#if defined(RW_OPENGL)
+ if(GLAD_GL_KHR_debug)
+ glPopDebugGroup();
+#elif defined(RW_D3D9)
+ D3DPERF_EndEvent();
+#endif
+}
+#endif
+
+RwFrame*
+GetFirstFrameCallback(RwFrame *child, void *data)
+{
+ *(RwFrame**)data = child;
+ return nil;
+}
+
+RwFrame*
+GetFirstChild(RwFrame *frame)
+{
+ RwFrame *child;
+
+ child = nil;
+ RwFrameForAllChildren(frame, GetFirstFrameCallback, &child);
+ return child;
+}
+
+RwObject*
+GetFirstObjectCallback(RwObject *object, void *data)
+{
+ *(RwObject**)data = object;
+ return nil;
+}
+
+RwObject*
+GetFirstObject(RwFrame *frame)
+{
+ RwObject *obj;
+
+ obj = nil;
+ RwFrameForAllObjects(frame, GetFirstObjectCallback, &obj);
+ return obj;
+}
+
+RpAtomic*
+GetFirstAtomicCallback(RpAtomic *atm, void *data)
+{
+ *(RpAtomic**)data = atm;
+ return nil;
+}
+
+RpAtomic*
+GetFirstAtomic(RpClump *clump)
+{
+ RpAtomic *atm;
+
+ atm = nil;
+ RpClumpForAllAtomics(clump, GetFirstAtomicCallback, &atm);
+ return atm;
+}
+
+RwTexture*
+GetFirstTextureCallback(RwTexture *tex, void *data)
+{
+ *(RwTexture**)data = tex;
+ return nil;
+}
+
+RwTexture*
+GetFirstTexture(RwTexDictionary *txd)
+{
+ RwTexture *tex;
+
+ tex = nil;
+ RwTexDictionaryForAllTextures(txd, GetFirstTextureCallback, &tex);
+ return tex;
+}
+
+bool
+IsClumpSkinned(RpClump *clump)
+{
+ RpAtomic *atomic = GetFirstAtomic(clump);
+ return atomic ? RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)) : nil;
+}
+
+static RpAtomic*
+GetAnimHierarchyCallback(RpAtomic *atomic, void *data)
+{
+ *(RpHAnimHierarchy**)data = RpSkinAtomicGetHAnimHierarchy(atomic);
+ return nil;
+}
+
+RpHAnimHierarchy*
+GetAnimHierarchyFromSkinClump(RpClump *clump)
+{
+ RpHAnimHierarchy *hier = nil;
+ RpClumpForAllAtomics(clump, GetAnimHierarchyCallback, &hier);
+ return hier;
+}
+
+static RwFrame*
+GetAnimHierarchyFromClumpCB(RwFrame *frame, void *data)
+{
+ RpHAnimHierarchy *hier = RpHAnimFrameGetHierarchy(frame);
+ if(hier){
+ *(RpHAnimHierarchy**)data = hier;
+ return nil;
+ }
+ RwFrameForAllChildren(frame, GetAnimHierarchyFromClumpCB, data);
+ return frame;
+}
+
+RpHAnimHierarchy*
+GetAnimHierarchyFromClump(RpClump *clump)
+{
+ RpHAnimHierarchy *hier = nil;
+ RwFrameForAllChildren(RpClumpGetFrame(clump), GetAnimHierarchyFromClumpCB, &hier);
+ return hier;
+}
+
+void
+SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable)
+{
+ int i, parent;
+ RpAtomic *atomic;
+ RpSkin *skin;
+ RpHAnimHierarchy *hier;
+ int numBones;
+ RwMatrix m, invmat;
+ int stack[32];
+ int sp;
+
+ if(boneTable == nil)
+ return;
+
+ atomic = GetFirstAtomic(clump); // mobile, also VC
+ assert(atomic);
+ skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic));
+ assert(skin);
+ hier = GetAnimHierarchyFromSkinClump(clump);
+ assert(hier);
+ boneTable[0].x = 0.0f;
+ boneTable[0].y = 0.0f;
+ boneTable[0].z = 0.0f;
+ numBones = RpSkinGetNumBones(skin);
+ parent = 0;
+ sp = 0;
+#ifdef FIX_BUGS
+ stack[0] = 0; // i think this is ok
+#endif
+ for(i = 1; i < numBones; i++){
+ RwMatrixCopy(&m, &RpSkinGetSkinToBoneMatrices(skin)[i]);
+ RwMatrixInvert(&invmat, &m);
+ const RwMatrix *x = RpSkinGetSkinToBoneMatrices(skin);
+ RwV3dTransformPoints(&boneTable[i], &invmat.pos, 1, &x[parent]);
+ if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX)
+ stack[++sp] = parent;
+ if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX)
+ parent = stack[sp--];
+ else
+ parent = i;
+
+ //assert(parent >= 0 && parent < numBones);
+ }
+}
+
+RpHAnimAnimation*
+HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier)
+{
+ int i;
+#if defined FIX_BUGS || defined LIBRW
+ int numNodes = hier->numNodes*2; // you're supposed to have at least two KFs per node
+#else
+ int numNodes = hier->numNodes;
+#endif
+ RpHAnimAnimation *anim = RpHAnimAnimationCreate(rpHANIMSTDKEYFRAMETYPEID, numNodes, 0, 0.0f);
+ if(anim == nil)
+ return nil;
+ RpHAnimStdKeyFrame *frame;
+ for(i = 0; i < numNodes; i++){
+ frame = (RpHAnimStdKeyFrame*)HANIMFRAME(anim, i); // games uses struct size here, not safe
+ frame->q.real = 1.0f;
+ frame->q.imag.x = frame->q.imag.y = frame->q.imag.z = 0.0f;
+ frame->t.x = frame->t.y = frame->t.z = 0.0f;
+#if defined FIX_BUGS || defined LIBRW
+ // times are subtracted and divided giving NaNs
+ // so they can't both be 0
+ frame->time = i/hier->numNodes;
+#else
+ frame->time = 0.0f;
+#endif
+ frame->prevFrame = nil;
+ }
+ return anim;
+}
+
+void
+RenderSkeleton(RpHAnimHierarchy *hier)
+{
+ int i;
+ int sp;
+ int stack[32];
+ int par;
+ CVector p1, p2;
+ int numNodes = hier->numNodes;
+ RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier);
+ p1 = mats[0].pos;
+
+ par = 0;
+ sp = 0;
+ stack[sp++] = par;
+ for(i = 1; i < numNodes; i++){
+ p1 = mats[par].pos;
+ p2 = mats[i].pos;
+ CDebug::AddLine(p1, p2, 0xFFFFFFFF, 0xFFFFFFFF);
+ if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX)
+ stack[sp++] = par;
+ par = i;
+ if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX)
+ par = stack[--sp];
+ }
+}
+
+
+RwBool Im2DRenderQuad(RwReal x1, RwReal y1, RwReal x2, RwReal y2, RwReal z, RwReal recipCamZ, RwReal uvOffset)
+{
+ RwIm2DVertex vx[4];
+
+ /*
+ * Render an opaque white 2D quad at the given coordinates and
+ * spanning a whole texture.
+ */
+
+ RwIm2DVertexSetScreenX(&vx[0], x1);
+ RwIm2DVertexSetScreenY(&vx[0], y1);
+ RwIm2DVertexSetScreenZ(&vx[0], z);
+ RwIm2DVertexSetIntRGBA(&vx[0], 255, 255, 255, 255);
+ RwIm2DVertexSetRecipCameraZ(&vx[0], recipCamZ);
+ RwIm2DVertexSetU(&vx[0], uvOffset, recipCamZ);
+ RwIm2DVertexSetV(&vx[0], uvOffset, recipCamZ);
+
+ RwIm2DVertexSetScreenX(&vx[1], x1);
+ RwIm2DVertexSetScreenY(&vx[1], y2);
+ RwIm2DVertexSetScreenZ(&vx[1], z);
+ RwIm2DVertexSetIntRGBA(&vx[1], 255, 255, 255, 255);
+ RwIm2DVertexSetRecipCameraZ(&vx[1], recipCamZ);
+ RwIm2DVertexSetU(&vx[1], uvOffset, recipCamZ);
+ RwIm2DVertexSetV(&vx[1], 1.0f + uvOffset, recipCamZ);
+
+ RwIm2DVertexSetScreenX(&vx[2], x2);
+ RwIm2DVertexSetScreenY(&vx[2], y1);
+ RwIm2DVertexSetScreenZ(&vx[2], z);
+ RwIm2DVertexSetIntRGBA(&vx[2], 255, 255, 255, 255);
+ RwIm2DVertexSetRecipCameraZ(&vx[2], recipCamZ);
+ RwIm2DVertexSetU(&vx[2], 1.0f + uvOffset, recipCamZ);
+ RwIm2DVertexSetV(&vx[2], uvOffset, recipCamZ);
+
+ RwIm2DVertexSetScreenX(&vx[3], x2);
+ RwIm2DVertexSetScreenY(&vx[3], y2);
+ RwIm2DVertexSetScreenZ(&vx[3], z);
+ RwIm2DVertexSetIntRGBA(&vx[3], 255, 255, 255, 255);
+ RwIm2DVertexSetRecipCameraZ(&vx[3], recipCamZ);
+ RwIm2DVertexSetU(&vx[3], 1.0f + uvOffset, recipCamZ);
+ RwIm2DVertexSetV(&vx[3], 1.0f + uvOffset, recipCamZ);
+
+ RwIm2DRenderPrimitive(rwPRIMTYPETRISTRIP, vx, 4);
+
+ return TRUE;
+}
+
+bool b_cbsUseLTM = true;
+
+RpAtomic *cbsCalcMeanBSphereRadiusCB(RpAtomic *atomic, void *data)
+{
+ RwV3d atomicPos;
+
+ if ( b_cbsUseLTM )
+ RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetLTM(RpClumpGetFrame(atomic->clump)));
+ else
+ RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetMatrix(RpClumpGetFrame(atomic->clump)));
+
+ RwV3d temp;
+ RwV3dSub(&temp, &atomicPos, &((RwSphere *)data)->center);
+ RwReal radius = RwV3dLength(&temp) + RpAtomicGetBoundingSphere(atomic)->radius;
+
+ if ( ((RwSphere *)data)->radius < radius )
+ ((RwSphere *)data)->radius = radius;
+
+ return atomic;
+}
+
+RpAtomic *cbsCalcMeanBSphereCenterCB(RpAtomic *atomic, void *data)
+{
+ RwV3d atomicPos;
+
+ if ( b_cbsUseLTM )
+ RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetLTM(RpClumpGetFrame(atomic->clump)));
+ else
+ RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetMatrix(RpClumpGetFrame(atomic->clump)));
+
+ RwV3dAdd(&((RwSphere *)data)->center, &((RwSphere *)data)->center, &atomicPos);
+
+ return atomic;
+}
+
+RpClump *RpClumpGetBoundingSphere(RpClump *clump, RwSphere *sphere, bool useLTM)
+{
+ RwMatrix matrix;
+ RwSphere result = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ b_cbsUseLTM = useLTM;
+
+ if ( clump == nil || sphere == nil )
+ return nil;
+
+ sphere->radius = 0.0f;
+ sphere->center.x = 0.0f;
+ sphere->center.y = 0.0f;
+ sphere->center.z = 0.0f;
+
+ RwInt32 numAtomics = RpClumpGetNumAtomics(clump);
+ if ( numAtomics < 1.0f )
+ return nil;
+
+ RpClumpForAllAtomics(clump, cbsCalcMeanBSphereCenterCB, &result);
+
+ RwV3dScale(&result.center, &result.center, 1.0f/numAtomics);
+
+ RpClumpForAllAtomics(clump, cbsCalcMeanBSphereRadiusCB, &result);
+
+ if ( b_cbsUseLTM )
+ RwMatrixInvert(&matrix, RwFrameGetLTM(RpClumpGetFrame(clump)));
+ else
+ RwMatrixInvert(&matrix, RwFrameGetMatrix(RpClumpGetFrame(clump)));
+
+ RwV3dTransformPoints(&result.center, &result.center, 1, &matrix);
+
+ *sphere = result;
+
+ return clump;
+}
+
+void
+CameraSize(RwCamera * camera, RwRect * rect,
+ RwReal viewWindow, RwReal aspectRatio)
+{
+ if (camera)
+ {
+ RwVideoMode videoMode;
+ RwRect r;
+ RwRect origSize = { 0, 0, 0, 0 }; // FIX just to make the compier happy
+ RwV2d vw;
+
+ RwEngineGetVideoModeInfo(&videoMode,
+ RwEngineGetCurrentVideoMode());
+
+ origSize.w = RwRasterGetWidth(RwCameraGetRaster(camera));
+ origSize.h = RwRasterGetHeight(RwCameraGetRaster(camera));
+
+ if (!rect)
+ {
+ if (videoMode.flags & rwVIDEOMODEEXCLUSIVE)
+ {
+ /* For full screen applications, resizing the camera just doesn't
+ * make sense, use the video mode size.
+ */
+
+ r.x = r.y = 0;
+ r.w = videoMode.width;
+ r.h = videoMode.height;
+ rect = &r;
+ }
+ else
+ {
+ /*
+ rect not specified - reuse current values
+ */
+ r.w = RwRasterGetWidth(RwCameraGetRaster(camera));
+ r.h = RwRasterGetHeight(RwCameraGetRaster(camera));
+ r.x = r.y = 0;
+ rect = &r;
+ }
+ }
+
+ if (( origSize.w != rect->w ) || ( origSize.h != rect->h ))
+ {
+ RwRaster *raster;
+ RwRaster *zRaster;
+
+ // BUG: game just changes camera raster's sizes, but this is a hack
+#if defined FIX_BUGS || defined LIBRW
+ /*
+ * Destroy rasters...
+ */
+
+ raster = RwCameraGetRaster(camera);
+ if( raster )
+ {
+ RwRasterDestroy(raster);
+ camera->frameBuffer = nil;
+ }
+
+ zRaster = RwCameraGetZRaster(camera);
+ if( zRaster )
+ {
+ RwRasterDestroy(zRaster);
+ camera->zBuffer = nil;
+ }
+
+ /*
+ * Create new rasters...
+ */
+
+ raster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA);
+ zRaster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER);
+
+ if( raster && zRaster )
+ {
+ RwCameraSetRaster(camera, raster);
+ RwCameraSetZRaster(camera, zRaster);
+ }
+ else
+ {
+ if( raster )
+ {
+ RwRasterDestroy(raster);
+ }
+
+ if( zRaster )
+ {
+ RwRasterDestroy(zRaster);
+ }
+
+ rect->x = origSize.x;
+ rect->y = origSize.y;
+ rect->w = origSize.w;
+ rect->h = origSize.h;
+
+ /*
+ * Use default values...
+ */
+ raster =
+ RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA);
+
+ zRaster =
+ RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER);
+
+ RwCameraSetRaster(camera, raster);
+ RwCameraSetZRaster(camera, zRaster);
+ }
+#else
+ raster = RwCameraGetRaster(camera);
+ zRaster = RwCameraGetZRaster(camera);
+
+ raster->width = zRaster->width = rect->w;
+ raster->height = zRaster->height = rect->h;
+#endif
+ }
+
+ /* Figure out the view window */
+ if (videoMode.flags & rwVIDEOMODEEXCLUSIVE)
+ {
+ /* derive ratio from aspect ratio */
+ vw.x = viewWindow;
+ vw.y = viewWindow / aspectRatio;
+ }
+ else
+ {
+ /* derive from pixel ratios */
+ if (rect->w > rect->h)
+ {
+ vw.x = viewWindow;
+ vw.y = (rect->h * viewWindow) / rect->w;
+ }
+ else
+ {
+ vw.x = (rect->w * viewWindow) / rect->h;
+ vw.y = viewWindow;
+ }
+ }
+
+ RwCameraSetViewWindow(camera, &vw);
+
+ RsGlobal.width = rect->w;
+ RsGlobal.height = rect->h;
+ }
+
+ return;
+}
+
+void
+CameraDestroy(RwCamera *camera)
+{
+ RwRaster *raster, *tmpRaster;
+ RwFrame *frame;
+
+ if (camera)
+ {
+ frame = RwCameraGetFrame(camera);
+ if (frame)
+ {
+ RwFrameDestroy(frame);
+ }
+
+ raster = RwCameraGetRaster(camera);
+ if (raster)
+ {
+ tmpRaster = RwRasterGetParent(raster);
+
+ RwRasterDestroy(raster);
+
+ if ((tmpRaster != nil) && (tmpRaster != raster))
+ {
+ RwRasterDestroy(tmpRaster);
+ }
+ }
+
+ raster = RwCameraGetZRaster(camera);
+ if (raster)
+ {
+ tmpRaster = RwRasterGetParent(raster);
+
+ RwRasterDestroy(raster);
+
+ if ((tmpRaster != nil) && (tmpRaster != raster))
+ {
+ RwRasterDestroy(tmpRaster);
+ }
+ }
+
+ RwCameraDestroy(camera);
+ }
+
+ return;
+}
+
+RwCamera *
+CameraCreate(RwInt32 width, RwInt32 height, RwBool zBuffer)
+{
+ RwCamera *camera;
+
+ camera = RwCameraCreate();
+
+ if (camera)
+ {
+ RwCameraSetFrame(camera, RwFrameCreate());
+ RwCameraSetRaster(camera,
+ RwRasterCreate(0, 0, 0, rwRASTERTYPECAMERA));
+
+ if (zBuffer)
+ {
+ RwCameraSetZRaster(camera,
+ RwRasterCreate(0, 0, 0,
+ rwRASTERTYPEZBUFFER));
+ }
+
+ /* now check that everything is valid */
+ if (RwCameraGetFrame(camera) &&
+ RwCameraGetRaster(camera) &&
+ RwRasterGetParent(RwCameraGetRaster(camera)) &&
+ (!zBuffer || (RwCameraGetZRaster(camera) &&
+ RwRasterGetParent(RwCameraGetZRaster
+ (camera)))))
+ {
+ /* everything OK */
+ return (camera);
+ }
+ }
+
+ /* if we're here then an error must have occurred so clean up */
+
+ CameraDestroy(camera);
+ return (nil);
+}
+
+#ifdef LIBRW
+#include <rpmatfx.h>
+#include "VehicleModelInfo.h"
+
+int32
+findPlatform(rw::Atomic *a)
+{
+ rw::Geometry *g = a->geometry;
+ if(g->instData)
+ return g->instData->platform;
+ return 0;
+}
+
+// Game doesn't read atomic extensions so we never get any other than the default pipe,
+// but we need it for uninstancing
+void
+attachPipe(rw::Atomic *atomic)
+{
+ if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)))
+ atomic->pipeline = rw::skinGlobals.pipelines[rw::platform];
+ else{
+ int fx = rpMATFXEFFECTNULL;
+ RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), CVehicleModelInfo::GetMatFXEffectMaterialCB, &fx);
+ if(fx != rpMATFXEFFECTNULL)
+ RpMatFXAtomicEnableEffects(atomic);
+ }
+}
+
+// Attach pipes for the platform we have native data for so we can uninstance
+void
+switchPipes(rw::Atomic *a, int32 platform)
+{
+ if(a->pipeline && a->pipeline->platform != platform){
+ uint32 plgid = a->pipeline->pluginID;
+ switch(plgid){
+ // assume default pipe won't be attached explicitly
+ case rw::ID_SKIN:
+ a->pipeline = rw::skinGlobals.pipelines[platform];
+ break;
+ case rw::ID_MATFX:
+ a->pipeline = rw::matFXGlobals.pipelines[platform];
+ break;
+ }
+ }
+}
+
+RpAtomic*
+ConvertPlatformAtomic(RpAtomic *atomic, void *data)
+{
+ int32 driver = rw::platform;
+ int32 platform = findPlatform(atomic);
+ if(platform != 0 && platform != driver){
+ attachPipe(atomic); // kludge
+ rw::ObjPipeline *origPipe = atomic->pipeline;
+ rw::platform = platform;
+ switchPipes(atomic, rw::platform);
+ if(atomic->geometry->flags & rw::Geometry::NATIVE)
+ atomic->uninstance();
+ // no ADC in this game
+ //rw::ps2::unconvertADC(atomic->geometry);
+ rw::platform = driver;
+ atomic->pipeline = origPipe;
+ }
+ return atomic;
+}
+#endif
+
+#if defined(FIX_BUGS) && defined(GTA_PC)
+RwUInt32 saved_alphafunc, saved_alpharef;
+
+void
+SetAlphaTest(RwUInt32 alpharef)
+{
+#ifdef LIBRW
+ saved_alphafunc = rw::GetRenderState(rw::ALPHATESTFUNC);
+ saved_alpharef = rw::GetRenderState(rw::ALPHATESTREF);
+
+ rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL);
+ rw::SetRenderState(rw::ALPHATESTREF, 0);
+#else
+ RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &saved_alphafunc);
+ RwD3D8GetRenderState(D3DRS_ALPHAREF, &saved_alpharef);
+
+ RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
+ RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef);
+#endif
+}
+
+void
+RestoreAlphaTest()
+{
+#ifdef LIBRW
+ rw::SetRenderState(rw::ALPHATESTFUNC, saved_alphafunc);
+ rw::SetRenderState(rw::ALPHATESTREF, saved_alpharef);
+#else
+ RwD3D8SetRenderState(D3DRS_ALPHAFUNC, saved_alphafunc);
+ RwD3D8SetRenderState(D3DRS_ALPHAREF, saved_alpharef);
+#endif
+}
+#endif
diff --git a/src/rw/RwHelper.h b/src/rw/RwHelper.h
new file mode 100644
index 0000000..a3a1928
--- /dev/null
+++ b/src/rw/RwHelper.h
@@ -0,0 +1,64 @@
+#pragma once
+
+extern bool bDebugRenderGroups;
+extern bool gPS2alphaTest;
+extern bool gBackfaceCulling;
+
+void OpenCharsetSafe();
+void CreateDebugFont();
+void DestroyDebugFont();
+void ObrsPrintfString(const char *str, short x, short y);
+void FlushObrsPrintfs();
+void DefinedState(void);
+void SetCullMode(uint32 mode);
+RwFrame *GetFirstChild(RwFrame *frame);
+RwObject *GetFirstObject(RwFrame *frame);
+RpAtomic *GetFirstAtomic(RpClump *clump);
+RwTexture *GetFirstTexture(RwTexDictionary *txd);
+
+bool IsClumpSkinned(RpClump *clump);
+RpHAnimHierarchy *GetAnimHierarchyFromSkinClump(RpClump *clump); // get from atomic
+RpHAnimHierarchy *GetAnimHierarchyFromClump(RpClump *clump); // get from frame
+void SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable);
+RpHAnimAnimation *HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier);
+RpAtomic *AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data);
+void RenderSkeleton(RpHAnimHierarchy *hier);
+
+RwBool Im2DRenderQuad(RwReal x1, RwReal y1, RwReal x2, RwReal y2, RwReal z, RwReal recipCamZ, RwReal uvOffset);
+RpClump *RpClumpGetBoundingSphere(RpClump *clump, RwSphere *sphere, bool useLTM);
+
+RwTexDictionary *RwTexDictionaryGtaStreamRead(RwStream *stream);
+RwTexDictionary *RwTexDictionaryGtaStreamRead1(RwStream *stream);
+RwTexDictionary *RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict);
+void ReadVideoCardCapsFile(uint32&, uint32&, uint32&, uint32&);
+bool CheckVideoCardCaps(void);
+void WriteVideoCardCapsFile(void);
+bool CanVideoCardDoDXT(void);
+void ConvertingTexturesScreen(uint32, uint32, const char*);
+void DealWithTxdWriteError(uint32, uint32, const char*);
+bool CreateTxdImageForVideoCard();
+
+bool RpClumpGtaStreamRead1(RwStream *stream);
+RpClump *RpClumpGtaStreamRead2(RwStream *stream);
+void RpClumpGtaCancelStream(void);
+
+void CameraSize(RwCamera *camera,
+ RwRect *rect,
+ RwReal viewWindow,
+ RwReal aspectRatio);
+void CameraDestroy(RwCamera *camera);
+RwCamera *CameraCreate(RwInt32 width,
+ RwInt32 height,
+ RwBool zBuffer);
+
+
+
+RpAtomic *ConvertPlatformAtomic(RpAtomic *atomic, void *data);
+
+#if defined(FIX_BUGS) && defined (GTA_PC)
+void SetAlphaTest(RwUInt32 alpharef);
+void RestoreAlphaTest();
+#else
+#define SetAlphaTest(a) (0)
+#define RestoreAlphaTest() (0)
+#endif \ No newline at end of file
diff --git a/src/rw/RwMatFX.cpp b/src/rw/RwMatFX.cpp
new file mode 100644
index 0000000..c8384b0
--- /dev/null
+++ b/src/rw/RwMatFX.cpp
@@ -0,0 +1,312 @@
+#ifndef LIBRW
+
+#define WITHD3D
+#include "common.h"
+#include "rpmatfx.h"
+
+struct MatFXNothing { int pad[5]; int effect; };
+
+struct MatFXBump
+{
+ RwFrame *bumpFrame;
+ RwTexture *bumpedTex;
+ RwTexture *bumpTex;
+ float negBumpCoefficient;
+ int pad;
+ int effect;
+};
+
+struct MatFXEnv
+{
+ RwFrame *envFrame;
+ RwTexture *envTex;
+ float envCoeff;
+ int envFBalpha;
+ int pad;
+ int effect;
+};
+
+struct MatFXDual
+{
+ RwTexture *dualTex;
+ RwInt32 srcBlend;
+ RwInt32 dstBlend;
+};
+
+
+struct MatFX
+{
+ union {
+ MatFXNothing n;
+ MatFXBump b;
+ MatFXEnv e;
+ MatFXDual d;
+ } fx[2];
+ int effects;
+};
+
+extern "C" {
+ extern int MatFXMaterialDataOffset;
+ extern int MatFXAtomicDataOffset;
+
+ void _rpMatFXD3D8AtomicMatFXEnvRender(RxD3D8InstanceData* inst, int flags, int sel, RwTexture* texture, RwTexture* envMap);
+ void _rpMatFXD3D8AtomicMatFXRenderBlack(RxD3D8InstanceData *inst);
+ void _rpMatFXD3D8AtomicMatFXBumpMapRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *bumpMap, RwTexture *envMap);
+ void _rpMatFXD3D8AtomicMatFXDualPassRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *dualTexture);
+}
+
+
+#ifdef PS2_MATFX
+
+void
+_rpMatFXD3D8AtomicMatFXDefaultRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture)
+{
+ if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture)
+ RwD3D8SetTexture(texture, 0);
+ else
+ RwD3D8SetTexture(nil, 0);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF));
+ RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, inst->vertexAlpha != 0);
+ RwD3D8SetPixelShader(0);
+ RwD3D8SetVertexShader(inst->vertexShader);
+ RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride);
+
+ if(inst->indexBuffer){
+ RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex);
+ RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices);
+ }else
+ RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices);
+}
+
+// map [-1; -1] -> [0; 1], flip V
+static RwMatrix scalenormal = {
+ { 0.5f, 0.0f, 0.0f }, 0,
+ { 0.0f, -0.5f, 0.0f }, 0,
+ { 0.0f, 0.0f, 1.0f }, 0,
+ { 0.5f, 0.5f, 0.0f }, 0,
+
+};
+
+// flipped U for PS2
+static RwMatrix scalenormal_flipU = {
+ { -0.5f, 0.0f, 0.0f }, 0,
+ { 0.0f, -0.5f, 0.0f }, 0,
+ { 0.0f, 0.0f, 1.0f }, 0,
+ { 0.5f, 0.5f, 0.0f }, 0,
+
+};
+
+void
+ApplyEnvMapTextureMatrix(RwTexture *tex, int n, RwFrame *frame)
+{
+ RwD3D8SetTexture(tex, n);
+ RwD3D8SetTextureStageState(n, D3DRS_ALPHAREF, 2);
+ RwD3D8SetTextureStageState(n, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL);
+ if(frame){
+ RwMatrix *envframemat = RwMatrixCreate();
+ RwMatrix *tmpmat = RwMatrixCreate();
+ RwMatrix *envmat = RwMatrixCreate();
+
+ RwMatrixInvert(envframemat, RwFrameGetLTM(frame));
+ // PS2
+ // can this be simplified?
+ *tmpmat = *RwFrameGetLTM(RwCameraGetFrame((RwCamera*)RWSRCGLOBAL(curCamera)));
+ RwV3dNegate(&tmpmat->right, &tmpmat->right);
+ tmpmat->flags = 0;
+ tmpmat->pos.x = 0.0f;
+ tmpmat->pos.y = 0.0f;
+ tmpmat->pos.z = 0.0f;
+ RwMatrixMultiply(envmat, tmpmat, envframemat);
+ *tmpmat = *envmat;
+ // important because envframemat can have a translation that we don't like
+ tmpmat->pos.x = 0.0f;
+ tmpmat->pos.y = 0.0f;
+ tmpmat->pos.z = 0.0f;
+ // for some reason we flip in U as well
+ RwMatrixMultiply(envmat, tmpmat, &scalenormal_flipU);
+
+ RwD3D8SetTransform(D3DTS_TEXTURE0+n, envmat);
+
+ RwMatrixDestroy(envmat);
+ RwMatrixDestroy(tmpmat);
+ RwMatrixDestroy(envframemat);
+ }else
+ RwD3D8SetTransform(D3DTS_TEXTURE0+n, &scalenormal);
+}
+
+void
+_rpMatFXD3D8AtomicMatFXEnvRender_ps2(RxD3D8InstanceData *inst, int flags, int sel, RwTexture *texture, RwTexture *envMap)
+{
+ MatFX *matfx = *RWPLUGINOFFSET(MatFX*, inst->material, MatFXMaterialDataOffset);
+ MatFXEnv *env = &matfx->fx[sel].e;
+
+ uint8 intens = (uint8)(env->envCoeff*255.0f);
+
+ if(intens == 0 || envMap == nil){
+ if(sel == 0)
+ _rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, texture);
+ return;
+ }
+
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF));
+ if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture)
+ RwD3D8SetTexture(texture, 0);
+ else
+ RwD3D8SetTexture(nil, 0);
+ RwD3D8SetPixelShader(0);
+ RwD3D8SetVertexShader(inst->vertexShader);
+ RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride);
+ RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex);
+ if(inst->indexBuffer)
+ RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices);
+ else
+ RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices);
+
+ // Effect pass
+
+ ApplyEnvMapTextureMatrix(envMap, 0, env->envFrame);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwUInt32 src, dst, lighting, zwrite, fog, fogcol;
+ RwRenderStateGet(rwRENDERSTATESRCBLEND, &src);
+ RwRenderStateGet(rwRENDERSTATEDESTBLEND, &dst);
+
+ // This is of course not using framebuffer alpha,
+ // but if the diffuse texture had no alpha, the result should actually be rather the same
+ if(env->envFBalpha)
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ else
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting);
+ RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite);
+ RwD3D8GetRenderState(D3DRS_FOGENABLE, &fog);
+ RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ if(fog){
+ RwD3D8GetRenderState(D3DRS_FOGCOLOR, &fogcol);
+ RwD3D8SetRenderState(D3DRS_FOGCOLOR, 0);
+ }
+
+ D3DCOLOR texfactor = D3DCOLOR_RGBA(intens, intens, intens, intens);
+ RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, texfactor);
+ RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ RwD3D8SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ RwD3D8SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TFACTOR);
+ // alpha unused
+ //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
+
+ if(inst->indexBuffer)
+ RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices);
+ else
+ RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices);
+
+ // Reset states
+
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)src);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)dst);
+ RwD3D8SetRenderState(D3DRS_LIGHTING, lighting);
+ RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, zwrite);
+ if(fog)
+ RwD3D8SetRenderState(D3DRS_FOGCOLOR, fogcol);
+ RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
+ RwD3D8SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 0);
+ RwD3D8SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
+}
+
+void
+_rwD3D8EnableClippingIfNeeded(void *object, RwUInt8 type)
+{
+ int clip;
+ if (type == rpATOMIC)
+ clip = !RwD3D8CameraIsSphereFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), RpAtomicGetWorldBoundingSphere((RpAtomic *)object));
+ else
+ clip = !RwD3D8CameraIsBBoxFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), &((RpWorldSector *)object)->tightBoundingBox);
+ RwD3D8SetRenderState(D3DRS_CLIPPING, clip);
+}
+
+void
+_rwD3D8AtomicMatFXRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags)
+{
+ RwBool lighting;
+ RwBool forceBlack;
+ RxD3D8ResEntryHeader *header;
+ RxD3D8InstanceData *inst;
+ RwInt32 i;
+
+ if (flags & rpGEOMETRYPRELIT) {
+ RwD3D8SetRenderState(D3DRS_COLORVERTEX, 1);
+ RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
+ } else {
+ RwD3D8SetRenderState(D3DRS_COLORVERTEX, 0);
+ RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
+ }
+
+ _rwD3D8EnableClippingIfNeeded(object, type);
+
+ RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting);
+ if (lighting || flags & rpGEOMETRYPRELIT) {
+ forceBlack = FALSE;
+ } else {
+ forceBlack = TRUE;
+ RwD3D8SetTexture(nil, 0);
+ RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(0, 0, 0, 255));
+ RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
+ RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
+ }
+
+ header = (RxD3D8ResEntryHeader *)(repEntry + 1);
+ inst = (RxD3D8InstanceData *)(header + 1);
+ for (i = 0; i < header->numMeshes; i++) {
+ if (forceBlack)
+ _rpMatFXD3D8AtomicMatFXRenderBlack(inst);
+ else {
+ if (lighting)
+ RwD3D8SetSurfaceProperties(&inst->material->color, &inst->material->surfaceProps, flags & rpGEOMETRYMODULATEMATERIALCOLOR);
+ MatFX *matfx = *RWPLUGINOFFSET(MatFX *, inst->material, MatFXMaterialDataOffset);
+ int effect = matfx ? matfx->effects : rpMATFXEFFECTNULL;
+ switch (effect) {
+ case rpMATFXEFFECTNULL:
+ default:
+ _rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, inst->material->texture);
+ break;
+ case rpMATFXEFFECTBUMPMAP:
+ _rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, nil);
+ break;
+ case rpMATFXEFFECTENVMAP:
+ {
+ // TODO: matfx switch in the settings
+ //_rpMatFXD3D8AtomicMatFXEnvRender(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex);
+ _rpMatFXD3D8AtomicMatFXEnvRender_ps2(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex);
+ break;
+ }
+ case rpMATFXEFFECTBUMPENVMAP:
+ _rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, matfx->fx[1].e.envTex);
+ break;
+ case rpMATFXEFFECTDUAL:
+ _rpMatFXD3D8AtomicMatFXDualPassRender(inst, flags, inst->material->texture, matfx->fx[0].d.dualTex);
+ break;
+ }
+ }
+ inst++;
+ }
+
+ if (forceBlack) {
+ RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
+ RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ }
+}
+
+void
+ReplaceMatFxCallback()
+{
+ RxD3D8AllInOneSetRenderCallBack(
+ RxPipelineFindNodeByName(RpMatFXGetD3D8Pipeline(rpMATFXD3D8ATOMICPIPELINE), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil),
+ _rwD3D8AtomicMatFXRenderCallback);
+
+}
+#endif // PS2_MATFX
+
+#endif // !LIBRW
diff --git a/src/rw/RwPS2AlphaTest.cpp b/src/rw/RwPS2AlphaTest.cpp
new file mode 100644
index 0000000..c0d6835
--- /dev/null
+++ b/src/rw/RwPS2AlphaTest.cpp
@@ -0,0 +1,247 @@
+#ifndef LIBRW
+
+#define WITHD3D
+#include "common.h"
+#ifdef PS2_ALPHA_TEST
+#include "rwcore.h"
+
+extern "C" {
+RwBool _rwD3D8RenderStateIsVertexAlphaEnable(void);
+RwBool _rwD3D8RenderStateVertexAlphaEnable(RwBool enable);
+RwRaster *_rwD3D8RWGetRasterStage(RwUInt32 stage);
+}
+
+extern bool gPS2alphaTest;
+
+void
+_rxD3D8DualPassRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags)
+{
+ RxD3D8ResEntryHeader *resEntryHeader;
+ RxD3D8InstanceData *instancedData;
+ RwInt32 numMeshes;
+ RwBool lighting;
+ RwBool vertexAlphaBlend;
+ RwBool forceBlack;
+ RwUInt32 ditherEnable;
+ RwUInt32 shadeMode;
+ void *lastVertexBuffer;
+
+ /* Get lighting state */
+ RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting);
+
+ forceBlack = FALSE;
+
+ if (lighting) {
+ if (flags & rxGEOMETRY_PRELIT) {
+ /* Emmisive color from the vertex colors */
+ RwD3D8SetRenderState(D3DRS_COLORVERTEX, TRUE);
+ RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
+ } else {
+ /* Emmisive color from material, set to black in the submit node */
+ RwD3D8SetRenderState(D3DRS_COLORVERTEX, FALSE);
+ RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
+ }
+ } else {
+ if ((flags & rxGEOMETRY_PRELIT) == 0) {
+ forceBlack = TRUE;
+
+ RwD3D8GetRenderState(D3DRS_DITHERENABLE, &ditherEnable);
+ RwD3D8GetRenderState(D3DRS_SHADEMODE, &shadeMode);
+
+ RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, 0xff000000);
+ RwD3D8SetRenderState(D3DRS_DITHERENABLE, FALSE);
+ RwD3D8SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+ }
+ }
+
+ /* Enable clipping */
+ if (type == rpATOMIC) {
+ RpAtomic *atomic;
+ RwCamera *cam;
+
+ atomic = (RpAtomic *)object;
+
+ cam = RwCameraGetCurrentCamera();
+ // RWASSERT(cam);
+
+ if (RwD3D8CameraIsSphereFullyInsideFrustum(cam, RpAtomicGetWorldBoundingSphere(atomic))) {
+ RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE);
+ } else {
+ RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE);
+ }
+ } else {
+ RpWorldSector *worldSector;
+ RwCamera *cam;
+
+ worldSector = (RpWorldSector *)object;
+
+ cam = RwCameraGetCurrentCamera();
+ // RWASSERT(cam);
+
+ if (RwD3D8CameraIsBBoxFullyInsideFrustum(cam, RpWorldSectorGetTightBBox(worldSector))) {
+ RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE);
+ } else {
+ RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE);
+ }
+ }
+
+ /* Set texture to NULL if hasn't any texture flags */
+ if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2)) == 0) {
+ RwD3D8SetTexture(NULL, 0);
+
+ if (forceBlack) {
+ RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
+ RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
+
+ RwD3D8SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
+ }
+ }
+
+ /* Get vertex alpha Blend state */
+ vertexAlphaBlend = _rwD3D8RenderStateIsVertexAlphaEnable();
+
+ /* Set Last vertex buffer to force the call */
+ lastVertexBuffer = (void *)0xffffffff;
+
+ /* Get the instanced data */
+ resEntryHeader = (RxD3D8ResEntryHeader *)(repEntry + 1);
+ instancedData = (RxD3D8InstanceData *)(resEntryHeader + 1);
+
+ /*
+ * Data shared between meshes
+ */
+
+ /*
+ * Set the Default Pixel shader
+ */
+ RwD3D8SetPixelShader(0);
+
+ /*
+ * Vertex shader
+ */
+ RwD3D8SetVertexShader(instancedData->vertexShader);
+
+ /* Get the number of meshes */
+ numMeshes = resEntryHeader->numMeshes;
+ while (numMeshes--) {
+ // RWASSERT(instancedData->material != NULL);
+
+ if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2))) {
+ RwD3D8SetTexture(instancedData->material->texture, 0);
+
+ if (forceBlack) {
+ /* Only change the colorop, we need to use the texture alpha channel */
+ RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
+ RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
+ }
+ }
+
+ if (instancedData->vertexAlpha || (0xFF != instancedData->material->color.alpha)) {
+ if (!vertexAlphaBlend) {
+ vertexAlphaBlend = TRUE;
+
+ _rwD3D8RenderStateVertexAlphaEnable(TRUE);
+ }
+ } else {
+ if (vertexAlphaBlend) {
+ vertexAlphaBlend = FALSE;
+
+ _rwD3D8RenderStateVertexAlphaEnable(FALSE);
+ }
+ }
+
+ if (lighting) {
+ if (instancedData->vertexAlpha) {
+ RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
+ } else {
+ RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
+ }
+
+ RwD3D8SetSurfaceProperties(&instancedData->material->color, &instancedData->material->surfaceProps, (flags & rxGEOMETRY_MODULATE));
+ }
+
+ /*
+ * Render
+ */
+
+ /* Set the stream source */
+ if (lastVertexBuffer != instancedData->vertexBuffer) {
+ RwD3D8SetStreamSource(0, instancedData->vertexBuffer, instancedData->stride);
+
+ lastVertexBuffer = instancedData->vertexBuffer;
+ }
+ if (!gPS2alphaTest) {
+ /* Set the Index buffer */
+ if (instancedData->indexBuffer != NULL) {
+ RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex);
+
+ /* Draw the indexed primitive */
+ RwD3D8DrawIndexedPrimitive((D3DPRIMITIVETYPE)instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
+ } else {
+ RwD3D8DrawPrimitive((D3DPRIMITIVETYPE)instancedData->primType, instancedData->baseIndex, instancedData->numVertices);
+ }
+ } else {
+ RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex);
+
+ int hasAlpha, alphafunc, alpharef, zwrite;
+ RwD3D8GetRenderState(D3DRS_ALPHABLENDENABLE, &hasAlpha);
+ RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite);
+ if (hasAlpha && zwrite) {
+ RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &alphafunc);
+ RwD3D8GetRenderState(D3DRS_ALPHAREF, &alpharef);
+
+ RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
+ RwD3D8SetRenderState(D3DRS_ALPHAREF, 128);
+
+ if (instancedData->indexBuffer)
+ RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
+ else
+ RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices);
+
+ RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESS);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);
+
+ if (instancedData->indexBuffer)
+ RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
+ else
+ RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices);
+
+ RwD3D8SetRenderState(D3DRS_ALPHAFUNC, alphafunc);
+ RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
+ } else {
+ if (instancedData->indexBuffer)
+ RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
+ else
+ RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices);
+ }
+ }
+
+ /* Move onto the next instancedData */
+ instancedData++;
+ }
+
+ if (forceBlack) {
+ RwD3D8SetRenderState(D3DRS_DITHERENABLE, ditherEnable);
+ RwD3D8SetRenderState(D3DRS_SHADEMODE, shadeMode);
+
+ if (_rwD3D8RWGetRasterStage(0)) {
+ RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ } else {
+ RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
+ RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ }
+ }
+}
+
+void
+ReplaceAtomicPipeCallback()
+{
+ RxD3D8AllInOneSetRenderCallBack(RxPipelineFindNodeByName(RXPIPELINEGLOBAL(platformAtomicPipeline), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil),
+ _rxD3D8DualPassRenderCallback);
+}
+
+#endif // PS2_ALPHA_TEST
+
+#endif // !LIBRW \ No newline at end of file
diff --git a/src/rw/TexRead.cpp b/src/rw/TexRead.cpp
new file mode 100644
index 0000000..1f96180
--- /dev/null
+++ b/src/rw/TexRead.cpp
@@ -0,0 +1,487 @@
+#pragma warning( push )
+#pragma warning( disable : 4005)
+#pragma warning( pop )
+#define FORCE_PC_SCALING
+#ifndef LIBRW
+#define WITHD3D
+#endif
+#include "common.h"
+#ifdef ANISOTROPIC_FILTERING
+#include "rpanisot.h"
+#endif
+#include "crossplatform.h"
+#include "platform.h"
+
+#include "Timer.h"
+#ifdef GTA_PC
+#include "FileMgr.h"
+#include "Pad.h"
+#include "main.h"
+#include "Directory.h"
+#include "Streaming.h"
+#include "TxdStore.h"
+#include "CdStream.h"
+#include "Font.h"
+#include "Sprite2d.h"
+#include "Text.h"
+#include "RwHelper.h"
+#include "Frontend.h"
+#endif //GTA_PC
+
+float texLoadTime;
+int32 texNumLoaded;
+
+#ifdef LIBRW
+#define READNATIVE(stream, tex, size) rwNativeTextureHackRead(stream, tex, size)
+#else
+#define READNATIVE(stream, tex, size) RWSRCGLOBAL(stdFunc[rwSTANDARDNATIVETEXTUREREAD](stream, tex, size))
+#endif
+
+RwTexture*
+RwTextureGtaStreamRead(RwStream *stream)
+{
+ RwUInt32 size, version;
+ RwTexture *tex;
+
+ if(!RwStreamFindChunk(stream, rwID_TEXTURENATIVE, &size, &version))
+ return nil;
+
+ float preloadTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond();
+
+ if(!READNATIVE(stream, &tex, size))
+ return nil;
+
+ if (gGameState == GS_INIT_PLAYING_GAME) {
+ texLoadTime = (texNumLoaded * texLoadTime + (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond() - preloadTime) / (float)(texNumLoaded+1);
+ texNumLoaded++;
+ }
+
+#ifdef ANISOTROPIC_FILTERING
+ if(tex && RpAnisotGetMaxSupportedMaxAnisotropy() > 1) // BUG? this was RpAnisotTextureGetMaxAnisotropy, but that doesn't make much sense
+ RpAnisotTextureSetMaxAnisotropy(tex, RpAnisotGetMaxSupportedMaxAnisotropy());
+#endif
+
+ return tex;
+}
+
+RwTexture*
+destroyTexture(RwTexture *texture, void *data)
+{
+ RwTextureDestroy(texture);
+ return texture;
+}
+
+RwTexDictionary*
+RwTexDictionaryGtaStreamRead(RwStream *stream)
+{
+ RwUInt32 size, version;
+ RwInt32 numTextures;
+ RwTexDictionary *texDict;
+ RwTexture *tex;
+
+ if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version))
+ return nil;
+ if(RwStreamRead(stream, &numTextures, size) != size)
+ return nil;
+
+ texDict = RwTexDictionaryCreate();
+ if(texDict == nil)
+ return nil;
+
+ while(numTextures--){
+ tex = RwTextureGtaStreamRead(stream);
+ if(tex == nil){
+ RwTexDictionaryForAllTextures(texDict, destroyTexture, nil);
+ RwTexDictionaryDestroy(texDict);
+ return nil;
+ }
+ RwTexDictionaryAddTexture(texDict, tex);
+ }
+
+ return texDict;
+}
+
+static int32 numberTextures = -1;
+static int32 streamPosition;
+
+RwTexDictionary*
+RwTexDictionaryGtaStreamRead1(RwStream *stream)
+{
+ RwUInt32 size, version;
+ RwInt32 numTextures;
+ RwTexDictionary *texDict;
+ RwTexture *tex;
+
+ numberTextures = 0;
+ if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version))
+ return nil;
+ assert(size == 4);
+ if(RwStreamRead(stream, &numTextures, size) != size)
+ return nil;
+
+ texDict = RwTexDictionaryCreate();
+ if(texDict == nil)
+ return nil;
+
+ numberTextures = numTextures/2;
+
+ while(numTextures > numberTextures){
+ numTextures--;
+
+ tex = RwTextureGtaStreamRead(stream);
+ if(tex == nil){
+ RwTexDictionaryForAllTextures(texDict, destroyTexture, nil);
+ RwTexDictionaryDestroy(texDict);
+ return nil;
+ }
+ RwTexDictionaryAddTexture(texDict, tex);
+ }
+
+ numberTextures = numTextures;
+ streamPosition = STREAMPOS(stream);
+
+ return texDict;
+}
+
+RwTexDictionary*
+RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict)
+{
+ RwTexture *tex;
+
+ RwStreamSkip(stream, streamPosition - STREAMPOS(stream));
+
+ while(numberTextures--){
+ tex = RwTextureGtaStreamRead(stream);
+ if(tex == nil){
+ RwTexDictionaryForAllTextures(texDict, destroyTexture, nil);
+ RwTexDictionaryDestroy(texDict);
+ return nil;
+ }
+ RwTexDictionaryAddTexture(texDict, tex);
+ }
+
+ return texDict;
+}
+
+#ifdef GTA_PC
+
+#ifdef LIBRW
+
+#define CAPSVERSION 0
+
+struct GPUcaps
+{
+ uint32 version; // so we can force regeneration easily
+ uint32 platform;
+ uint32 subplatform;
+ uint32 dxtSupport;
+};
+
+static void
+GetGPUcaps(GPUcaps *caps)
+{
+ caps->version = CAPSVERSION;
+ caps->platform = rw::platform;
+ caps->subplatform = 0;
+ caps->dxtSupport = 0;
+ // TODO: more later
+#ifdef RW_GL3
+ caps->subplatform = rw::gl3::gl3Caps.gles;
+ caps->dxtSupport = rw::gl3::gl3Caps.dxtSupported;
+#endif
+#ifdef RW_D3D9
+ caps->dxtSupport = 1; // TODO, probably
+#endif
+}
+
+void
+ReadVideoCardCapsFile(GPUcaps *caps)
+{
+ memset(caps, 0, sizeof(GPUcaps));
+
+ int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb");
+ if (file != 0) {
+ CFileMgr::Read(file, (char*)&caps->version, 4);
+ CFileMgr::Read(file, (char*)&caps->platform, 4);
+ CFileMgr::Read(file, (char*)&caps->subplatform, 4);
+ CFileMgr::Read(file, (char*)&caps->dxtSupport, 4);
+ CFileMgr::CloseFile(file);
+ }
+}
+
+bool
+CheckVideoCardCaps(void)
+{
+ GPUcaps caps, fcaps;
+ GetGPUcaps(&caps);
+ ReadVideoCardCapsFile(&fcaps);
+ return caps.version != fcaps.version ||
+ caps.platform != fcaps.platform ||
+ caps.subplatform != fcaps.subplatform ||
+ caps.dxtSupport != fcaps.dxtSupport;
+}
+
+void
+WriteVideoCardCapsFile(void)
+{
+ GPUcaps caps;
+ GetGPUcaps(&caps);
+ int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb");
+ if (file != 0) {
+ CFileMgr::Write(file, (char*)&caps.version, 4);
+ CFileMgr::Write(file, (char*)&caps.platform, 4);
+ CFileMgr::Write(file, (char*)&caps.subplatform, 4);
+ CFileMgr::Write(file, (char*)&caps.dxtSupport, 4);
+ CFileMgr::CloseFile(file);
+ }
+}
+
+
+#else
+extern "C" RwInt32 _rwD3D8FindCorrectRasterFormat(RwRasterType type, RwInt32 flags);
+extern "C" RwBool _rwD3D8CheckValidTextureFormat(RwInt32 format);
+void
+ReadVideoCardCapsFile(uint32 &cap32, uint32 &cap24, uint32 &cap16, uint32 &cap8)
+{
+ cap32 = UINT32_MAX;
+ cap24 = UINT32_MAX;
+ cap16 = UINT32_MAX;
+ cap8 = UINT32_MAX;
+
+ int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb");
+ if (file != 0) {
+ CFileMgr::Read(file, (char*)&cap32, 4);
+ CFileMgr::Read(file, (char*)&cap24, 4);
+ CFileMgr::Read(file, (char*)&cap16, 4);
+ CFileMgr::Read(file, (char*)&cap8, 4);
+ CFileMgr::CloseFile(file);
+ }
+}
+
+bool
+CheckVideoCardCaps(void)
+{
+ uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888);
+ uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888);
+ uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555);
+ uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888);
+ uint32 fcap32, fcap24, fcap16, fcap8;
+ ReadVideoCardCapsFile(fcap32, fcap24, fcap16, fcap8);
+ return cap32 != fcap32 || cap24 != fcap24 || cap16 != fcap16 || cap8 != fcap8;
+}
+
+void
+WriteVideoCardCapsFile(void)
+{
+ uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888);
+ uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888);
+ uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555);
+ uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888);
+ int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb");
+ if (file != 0) {
+ CFileMgr::Write(file, (char*)&cap32, 4);
+ CFileMgr::Write(file, (char*)&cap24, 4);
+ CFileMgr::Write(file, (char*)&cap16, 4);
+ CFileMgr::Write(file, (char*)&cap8, 4);
+ CFileMgr::CloseFile(file);
+ }
+}
+#endif
+
+bool
+CanVideoCardDoDXT(void)
+{
+#ifdef LIBRW
+ // TODO
+#ifdef RW_OPENGL
+ return false;
+#else
+ return true;
+#endif
+#else
+ return _rwD3D8CheckValidTextureFormat(D3DFMT_DXT1) && _rwD3D8CheckValidTextureFormat(D3DFMT_DXT3);
+#endif
+}
+
+void
+ConvertingTexturesScreen(uint32 num, uint32 count, const char *text)
+{
+ HandleExit();
+
+ CSprite2d *splash = LoadSplash(nil);
+ if (!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255))
+ return;
+
+ CSprite2d::SetRecipNearClip();
+ CSprite2d::InitPerFrame();
+ CFont::InitPerFrame();
+ DefinedState();
+
+ RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP);
+ splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, 255));
+
+ CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), SCREEN_SCALE_FROM_RIGHT(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(64, 64, 64, 255));
+#ifdef FIX_BUGS
+ CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), (SCREEN_SCALE_FROM_RIGHT(200.0f) - SCREEN_SCALE_X(200.0f)) * ((float)num / (float)count) + SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(255, 150, 225, 255));
+#else
+ CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), (SCREEN_SCALE_FROM_RIGHT(200.0f) - SCREEN_SCALE_X(200.0f)) * ((float)num / (float)count) + SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(255, 217, 106, 255));
+#endif
+ CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(120.0f), SCREEN_SCALE_Y(150.0f), SCREEN_SCALE_FROM_RIGHT(120.0f), SCREEN_HEIGHT - SCREEN_SCALE_Y(220.0f)), CRGBA(50, 50, 50, 210));
+
+ CFont::SetBackgroundOff();
+ CFont::SetPropOn();
+ CFont::SetScale(SCREEN_SCALE_X(0.45f), SCREEN_SCALE_Y(0.7f));
+ CFont::SetCentreOff();
+ CFont::SetWrapx(SCREEN_SCALE_FROM_RIGHT(170.0f));
+ CFont::SetJustifyOff();
+#ifdef FIX_BUGS
+ CFont::SetColor(CRGBA(255, 150, 225, 255));
+#else
+ CFont::SetColor(CRGBA(255, 217, 106, 255));
+#endif
+ CFont::SetBackGroundOnlyTextOff();
+ CFont::SetFontStyle(FONT_STANDARD);
+ CFont::PrintString(SCREEN_SCALE_X(170.0f), SCREEN_SCALE_Y(160.0f), TheText.Get(text));
+ CFont::DrawFonts();
+ DoRWStuffEndOfFrame();
+}
+
+void
+DealWithTxdWriteError(uint32 num, uint32 count, const char *text)
+{
+ while (!RsGlobal.quit) {
+ ConvertingTexturesScreen(num, count, text);
+ CPad::UpdatePads();
+ if (CPad::GetPad(0)->GetEscapeJustDown())
+ break;
+ }
+ RsGlobal.quit = false;
+ LoadingScreen(nil, nil, nil);
+ RsGlobal.quit = true;
+}
+
+#ifdef LIBRW
+#define STREAMTELL(str) str->tell()
+#else
+#define STREAMTELL(str) filesys->rwftell((str)->Type.file.fpFile)
+#endif
+
+bool
+CreateTxdImageForVideoCard()
+{
+ uint8 *buf = new uint8[CDSTREAM_SECTOR_SIZE];
+ CDirectory *pDir = new CDirectory(TXDSTORESIZE);
+ CDirectory::DirectoryInfo dirInfo;
+
+ CStreaming::FlushRequestList();
+
+#ifndef LIBRW
+ RwFileFunctions *filesys = RwOsGetFileInterface();
+#endif
+
+ RwStream *img = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMWRITE, "models\\txd.img");
+ if (img == nil) {
+ // original code does otherwise and it leaks
+ delete []buf;
+ delete pDir;
+
+ if (_dwOperatingSystemVersion == OS_WINNT || _dwOperatingSystemVersion == OS_WIN2000 || _dwOperatingSystemVersion == OS_WINXP)
+ DealWithTxdWriteError(0, TXDSTORESIZE, "CVT_CRT");
+
+ return false;
+ }
+
+#ifdef RW_GL3
+ // so we can read back DXT with GLES
+ // only works for textures that are not yet loaded
+ // so let's hope that is the case for all
+ rw::gl3::needToReadBackTextures = true;
+#endif
+
+#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION
+ // let's disable vsync and frame limiter to speed up texture conversion
+ // (actually we probably don't need to disable frame limiter in here, but let's do it just in case =P)
+ int8 vsyncState = FrontEndMenuManager.m_PrefsVsync;
+ int8 frameLimiterState = FrontEndMenuManager.m_PrefsFrameLimiter;
+ FrontEndMenuManager.m_PrefsVsync = 0;
+ FrontEndMenuManager.m_PrefsFrameLimiter = 0;
+#endif
+
+ int32 i;
+ for (i = 0; i < TXDSTORESIZE; i++) {
+ ConvertingTexturesScreen(i, TXDSTORESIZE, "CVT_MSG");
+
+ if (CTxdStore::GetSlot(i) != nil && CStreaming::IsObjectInCdImage(i + STREAM_OFFSET_TXD)) {
+#ifdef FIX_BUGS
+ if(strcmp(CTxdStore::GetTxdName(i), "generic") == 0)
+ continue;
+#endif
+
+ CStreaming::RequestTxd(i, STREAMFLAGS_KEEP_IN_MEMORY);
+ CStreaming::RequestModelStream(0);
+ CStreaming::FlushChannels();
+
+ char filename[64];
+ sprintf(filename, "%s.txd", CTxdStore::GetTxdName(i));
+
+ if (CTxdStore::GetSlot(i)->texDict) {
+
+ int32 pos = STREAMTELL(img);
+
+ if (RwTexDictionaryStreamWrite(CTxdStore::GetSlot(i)->texDict, img) == nil) {
+ DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR");
+ RwStreamClose(img, nil);
+ delete []buf;
+ delete pDir;
+ CStreaming::RemoveTxd(i);
+#ifdef RW_GL3
+ rw::gl3::needToReadBackTextures = false;
+#endif
+ return false;
+ }
+
+ int32 size = STREAMTELL(img) - pos;
+ int32 num = size % CDSTREAM_SECTOR_SIZE;
+
+ size /= CDSTREAM_SECTOR_SIZE;
+ if (num != 0) {
+ size++;
+ num = CDSTREAM_SECTOR_SIZE - num;
+ RwStreamWrite(img, buf, num);
+ }
+
+ dirInfo.offset = pos / CDSTREAM_SECTOR_SIZE;
+ dirInfo.size = size;
+ strncpy(dirInfo.name, filename, sizeof(dirInfo.name));
+ pDir->AddItem(dirInfo);
+ CStreaming::RemoveTxd(i);
+ }
+ CStreaming::FlushRequestList();
+ }
+ }
+
+#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION
+ // restore vsync and frame limiter states
+ FrontEndMenuManager.m_PrefsVsync = vsyncState;
+ FrontEndMenuManager.m_PrefsFrameLimiter = frameLimiterState;
+#endif
+
+ RwStreamClose(img, nil);
+ delete []buf;
+
+#ifdef RW_GL3
+ rw::gl3::needToReadBackTextures = false;
+#endif
+
+ if (!pDir->WriteDirFile("models\\txd.dir")) {
+ DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR");
+ delete pDir;
+ return false;
+ }
+
+ delete pDir;
+
+ WriteVideoCardCapsFile();
+ return true;
+}
+#endif // GTA_PC
diff --git a/src/rw/TexturePools.cpp b/src/rw/TexturePools.cpp
new file mode 100644
index 0000000..c2ba6cf
--- /dev/null
+++ b/src/rw/TexturePools.cpp
@@ -0,0 +1,221 @@
+#ifndef LIBRW
+
+#include <d3d8.h>
+#define WITHD3D
+#include "common.h"
+#include "TexturePools.h"
+
+// TODO: this needs to be integrated into RW
+
+extern "C" LPDIRECT3DDEVICE8 _RwD3DDevice;
+
+CTexturePool aTexturePools[12];
+CPaletteList PaletteList;
+int numTexturePools;
+int MaxPaletteIndex;
+bool bUsePaletteIndex = true;
+
+
+void
+CTexturePool::Create(D3DFORMAT _Format, int _size, uint32 mipmapLevels, int32 numTextures)
+{
+ Format = _Format;
+ size = _size;
+ levels = mipmapLevels;
+ pTextures = new IDirect3DTexture8 *[numTextures];
+ texturesMax = numTextures;
+ texturesNum = 0;
+ texturesUsed = 0;
+}
+
+void
+CTexturePool::Release()
+{
+ int i = 0;
+ while (i < texturesNum) {
+ pTextures[i]->Release();
+ i++;
+ }
+
+ delete[] pTextures;
+
+ pTextures = nil;
+ texturesNum = 0;
+ texturesUsed = 0;
+}
+
+IDirect3DTexture8 *
+CTexturePool::FindTexture()
+{
+ if (texturesNum == 0)
+ return nil;
+ texturesUsed--;
+ return pTextures[--texturesNum];
+}
+
+bool
+CTexturePool::AddTexture(IDirect3DTexture8 *texture)
+{
+ ++texturesUsed;
+ if (texturesNum >= texturesMax)
+ return false;
+ pTextures[texturesNum] = texture;
+ ++texturesNum;
+ return true;
+}
+
+void
+CTexturePool::Resize(int numTextures)
+{
+ if (numTextures == texturesMax)
+ return;
+
+ IDirect3DTexture8 **newTextures = new IDirect3DTexture8 *[numTextures];
+
+ for (int i = 0; i < texturesNum && i < numTextures; i++)
+ newTextures[i] = pTextures[i];
+
+ if (numTextures < texturesNum) {
+ for (int i = numTextures; i < texturesNum; i++)
+ pTextures[i]->Release();
+ }
+ delete[] pTextures;
+ pTextures = newTextures;
+ texturesMax = numTextures;
+}
+
+void
+CPaletteList::Alloc(int max)
+{
+ Data = new int[max];
+ Max = max;
+ Num = 0;
+}
+
+void
+CPaletteList::Free()
+{
+ delete[] Data;
+ Data = nil;
+ Num = 0;
+}
+
+int
+CPaletteList::Find()
+{
+ if (Num == 0)
+ return -1;
+ return Data[--Num];
+}
+
+void
+CPaletteList::Add(int item)
+{
+ if (Num < Max)
+ Data[Num++] = item;
+ else {
+ Resize(2 * Max);
+ Add(item);
+ }
+}
+
+void
+CPaletteList::Resize(int max)
+{
+ if (max == Max)
+ return;
+
+ int *newData = new int[4 * max];
+ for (int i = 0; i < Num && i < max; i++)
+ newData[i] = Data[i];
+ delete[] Data;
+ Data = newData;
+ Max = max;
+}
+
+HRESULT
+CreateTexture(int width, int height, int levels, D3DFORMAT Format, IDirect3DTexture8 **texture)
+{
+ if (width == height) {
+ for (int i = 0; i < numTexturePools; i++) {
+ if (width != aTexturePools[i].GetSize() && levels == aTexturePools[i].levels && Format == aTexturePools[i].Format)
+ *texture = aTexturePools[i].FindTexture();
+ }
+ }
+ if (*texture)
+ return D3D_OK;
+ else
+ return _RwD3DDevice->CreateTexture(width, height, levels, 0, Format, D3DPOOL_MANAGED, texture);
+}
+
+void
+ReleaseTexture(IDirect3DTexture8 *texture)
+{
+ int levels = 1;
+ if (texture->GetLevelCount() > 1)
+ levels = 0;
+
+ D3DSURFACE_DESC SURFACE_DESC;
+
+ texture->GetLevelDesc(0, &SURFACE_DESC);
+
+ if (SURFACE_DESC.Width == SURFACE_DESC.Height) {
+ for (int i = 0; i < numTexturePools; i++) {
+ if (SURFACE_DESC.Width == aTexturePools[i].GetSize() && SURFACE_DESC.Format == aTexturePools[i].Format && levels == aTexturePools[i].levels) {
+ if (!aTexturePools[i].AddTexture(texture)) {
+ if (aTexturePools[i].texturesUsed > 3 * aTexturePools[i].texturesMax / 2) {
+ aTexturePools[i].Resize(2 * aTexturePools[i].texturesMax);
+ aTexturePools[i].texturesUsed--;
+ aTexturePools[i].AddTexture(texture);
+ } else {
+ texture->Release();
+ }
+ }
+ return;
+ }
+ }
+ }
+ if (numTexturePools < 12 && bUsePaletteIndex && levels != 0 && SURFACE_DESC.Width == SURFACE_DESC.Height &&
+ (SURFACE_DESC.Width == 64 || SURFACE_DESC.Width == 128 || SURFACE_DESC.Width == 256)) {
+ aTexturePools[numTexturePools].Create(SURFACE_DESC.Format, SURFACE_DESC.Width, 1, 16);
+ aTexturePools[numTexturePools].AddTexture(texture);
+ numTexturePools++;
+ } else
+ texture->Release();
+}
+
+int
+FindAvailablePaletteIndex()
+{
+ int index = PaletteList.Find();
+ if (index == -1)
+ index = MaxPaletteIndex++;
+ return index;
+}
+
+void
+AddAvailablePaletteIndex(int index)
+{
+ if (bUsePaletteIndex)
+ PaletteList.Add(index);
+}
+
+void
+_TexturePoolsInitialise()
+{
+ PaletteList.Alloc(100);
+ MaxPaletteIndex = 0;
+}
+
+void
+_TexturePoolsShutdown()
+{
+ for (int i = 0; i < numTexturePools; i++)
+ aTexturePools[i].Release();
+
+ numTexturePools = 0;
+ bUsePaletteIndex = false;
+ PaletteList.Free();
+}
+
+#endif // !LIBRW \ No newline at end of file
diff --git a/src/rw/TexturePools.h b/src/rw/TexturePools.h
new file mode 100644
index 0000000..7518743
--- /dev/null
+++ b/src/rw/TexturePools.h
@@ -0,0 +1,42 @@
+#pragma once
+
+class CTexturePool
+{
+public:
+ D3DFORMAT Format;
+ int size;
+ uint32 levels;
+ int32 texturesMax;
+ int32 texturesUsed;
+ int32 texturesNum;
+ IDirect3DTexture8 **pTextures;
+
+public:
+ CTexturePool() {}
+ void Create(D3DFORMAT _Format, int size, uint32 mipmapLevels, int32 numTextures);
+ void Release();
+ IDirect3DTexture8 *FindTexture();
+ bool AddTexture(IDirect3DTexture8 *texture);
+ void Resize(int numTextures);
+#ifdef FIX_BUGS
+ int GetSize() { return size; }
+#else
+ float GetSize() { return size; }
+#endif
+};
+
+class CPaletteList
+{
+ int Max;
+ int Num;
+ int *Data;
+public:
+ void Alloc(int max);
+ void Free();
+ int Find();
+ void Add(int item);
+ void Resize(int max);
+};
+
+void _TexturePoolsInitialise();
+void _TexturePoolsShutdown(); \ No newline at end of file
diff --git a/src/rw/TxdStore.cpp b/src/rw/TxdStore.cpp
new file mode 100644
index 0000000..0bd2971
--- /dev/null
+++ b/src/rw/TxdStore.cpp
@@ -0,0 +1,183 @@
+#include "common.h"
+
+#include "templates.h"
+#include "General.h"
+#include "Streaming.h"
+#include "RwHelper.h"
+#include "TxdStore.h"
+
+CPool<TxdDef,TxdDef> *CTxdStore::ms_pTxdPool;
+RwTexDictionary *CTxdStore::ms_pStoredTxd;
+
+void
+CTxdStore::Initialise(void)
+{
+ if(ms_pTxdPool == nil)
+ ms_pTxdPool = new CPool<TxdDef,TxdDef>(TXDSTORESIZE, "TexDictionary");
+}
+
+void
+CTxdStore::Shutdown(void)
+{
+ if(ms_pTxdPool)
+ delete ms_pTxdPool;
+}
+
+void
+CTxdStore::GameShutdown(void)
+{
+ int i;
+
+ for(i = 0; i < TXDSTORESIZE; i++){
+ TxdDef *def = GetSlot(i);
+ if(def && GetNumRefs(i) == 0)
+ RemoveTxdSlot(i);
+ }
+}
+
+int
+CTxdStore::AddTxdSlot(const char *name)
+{
+ TxdDef *def = ms_pTxdPool->New();
+ assert(def);
+ def->texDict = nil;
+ def->refCount = 0;
+ strcpy(def->name, name);
+ return ms_pTxdPool->GetJustIndex(def);
+}
+
+void
+CTxdStore::RemoveTxdSlot(int slot)
+{
+ TxdDef *def = GetSlot(slot);
+ if(def->texDict)
+ RwTexDictionaryDestroy(def->texDict);
+ ms_pTxdPool->Delete(def);
+}
+
+int
+CTxdStore::FindTxdSlot(const char *name)
+{
+ int size = ms_pTxdPool->GetSize();
+ for(int i = 0; i < size; i++){
+ TxdDef *def = GetSlot(i);
+ if(def && !CGeneral::faststricmp(def->name, name))
+ return i;
+ }
+ return -1;
+}
+
+char*
+CTxdStore::GetTxdName(int slot)
+{
+ return GetSlot(slot)->name;
+}
+
+void
+CTxdStore::PushCurrentTxd(void)
+{
+ ms_pStoredTxd = RwTexDictionaryGetCurrent();
+}
+
+void
+CTxdStore::PopCurrentTxd(void)
+{
+ RwTexDictionarySetCurrent(ms_pStoredTxd);
+ ms_pStoredTxd = nil;
+}
+
+void
+CTxdStore::SetCurrentTxd(int slot)
+{
+ RwTexDictionarySetCurrent(GetSlot(slot)->texDict);
+}
+
+void
+CTxdStore::Create(int slot)
+{
+ GetSlot(slot)->texDict = RwTexDictionaryCreate();
+}
+
+int
+CTxdStore::GetNumRefs(int slot)
+{
+ return GetSlot(slot)->refCount;
+}
+
+void
+CTxdStore::AddRef(int slot)
+{
+ GetSlot(slot)->refCount++;
+}
+
+void
+CTxdStore::RemoveRef(int slot)
+{
+ if(--GetSlot(slot)->refCount <= 0)
+ CStreaming::RemoveTxd(slot);
+}
+
+void
+CTxdStore::RemoveRefWithoutDelete(int slot)
+{
+ GetSlot(slot)->refCount--;
+}
+
+bool
+CTxdStore::LoadTxd(int slot, RwStream *stream)
+{
+ TxdDef *def = GetSlot(slot);
+
+ if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){
+ def->texDict = RwTexDictionaryGtaStreamRead(stream);
+ return def->texDict != nil;
+ }
+ printf("Failed to load TXD\n");
+ return false;
+}
+
+bool
+CTxdStore::LoadTxd(int slot, const char *filename)
+{
+ RwStream *stream;
+ bool ret;
+
+ ret = false;
+ _rwD3D8TexDictionaryEnableRasterFormatConversion(true);
+ do
+ stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename);
+ while(stream == nil);
+ ret = LoadTxd(slot, stream);
+ RwStreamClose(stream, nil);
+ return ret;
+}
+
+bool
+CTxdStore::StartLoadTxd(int slot, RwStream *stream)
+{
+ TxdDef *def = GetSlot(slot);
+ if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){
+ def->texDict = RwTexDictionaryGtaStreamRead1(stream);
+ return def->texDict != nil;
+ }else{
+ printf("Failed to load TXD\n");
+ return false;
+ }
+}
+
+bool
+CTxdStore::FinishLoadTxd(int slot, RwStream *stream)
+{
+ TxdDef *def = GetSlot(slot);
+ def->texDict = RwTexDictionaryGtaStreamRead2(stream, def->texDict);
+ return def->texDict != nil;
+}
+
+void
+CTxdStore::RemoveTxd(int slot)
+{
+ TxdDef *def = GetSlot(slot);
+ if(def->texDict)
+ RwTexDictionaryDestroy(def->texDict);
+ def->texDict = nil;
+}
diff --git a/src/rw/TxdStore.h b/src/rw/TxdStore.h
new file mode 100644
index 0000000..937fd1b
--- /dev/null
+++ b/src/rw/TxdStore.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "templates.h"
+
+struct TxdDef {
+ RwTexDictionary *texDict;
+ int refCount;
+ char name[20];
+};
+
+class CTxdStore
+{
+ static CPool<TxdDef,TxdDef> *ms_pTxdPool;
+ static RwTexDictionary *ms_pStoredTxd;
+public:
+ static void Initialise(void);
+ static void Shutdown(void);
+ static void GameShutdown(void);
+ static int AddTxdSlot(const char *name);
+ static void RemoveTxdSlot(int slot);
+ static int FindTxdSlot(const char *name);
+ static char *GetTxdName(int slot);
+ static void PushCurrentTxd(void);
+ static void PopCurrentTxd(void);
+ static void SetCurrentTxd(int slot);
+ static void Create(int slot);
+ static int GetNumRefs(int slot);
+ static void AddRef(int slot);
+ static void RemoveRef(int slot);
+ static void RemoveRefWithoutDelete(int slot);
+ static bool LoadTxd(int slot, RwStream *stream);
+ static bool LoadTxd(int slot, const char *filename);
+ static bool StartLoadTxd(int slot, RwStream *stream);
+ static bool FinishLoadTxd(int slot, RwStream *stream);
+ static void RemoveTxd(int slot);
+
+ static TxdDef *GetSlot(int slot) {
+ assert(slot >= 0);
+ assert(ms_pTxdPool);
+ assert(slot < ms_pTxdPool->GetSize());
+ return ms_pTxdPool->GetSlot(slot);
+ }
+ static bool isTxdLoaded(int slot);
+};
diff --git a/src/rw/VisibilityPlugins.cpp b/src/rw/VisibilityPlugins.cpp
new file mode 100644
index 0000000..01db546
--- /dev/null
+++ b/src/rw/VisibilityPlugins.cpp
@@ -0,0 +1,966 @@
+#include "common.h"
+
+#include "RwHelper.h"
+#include "templates.h"
+#include "main.h"
+#include "Entity.h"
+#include "ModelInfo.h"
+#include "Lights.h"
+#include "RwHelper.h"
+#include "Renderer.h"
+#include "Camera.h"
+#include "VisibilityPlugins.h"
+#include "World.h"
+#include "custompipes.h"
+#include "MemoryHeap.h"
+
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaList;
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaBoatAtomicList;
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaEntityList;
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaUnderwaterEntityList;
+#ifdef NEW_RENDERER
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaBuildingList;
+#endif
+
+int32 CVisibilityPlugins::ms_atomicPluginOffset = -1;
+int32 CVisibilityPlugins::ms_framePluginOffset = -1;
+int32 CVisibilityPlugins::ms_clumpPluginOffset = -1;
+
+RwCamera *CVisibilityPlugins::ms_pCamera;
+RwV3d *CVisibilityPlugins::ms_pCameraPosn;
+float CVisibilityPlugins::ms_cullCompsDist;
+float CVisibilityPlugins::ms_vehicleLod0Dist;
+float CVisibilityPlugins::ms_vehicleLod1Dist;
+float CVisibilityPlugins::ms_vehicleFadeDist;
+float CVisibilityPlugins::ms_bigVehicleLod0Dist;
+float CVisibilityPlugins::ms_bigVehicleLod1Dist;
+float CVisibilityPlugins::ms_pedLod1Dist;
+float CVisibilityPlugins::ms_pedFadeDist;
+
+#define RENDERCALLBACK AtomicDefaultRenderCallBack
+
+void
+CVisibilityPlugins::Initialise(void)
+{
+ m_alphaList.Init(NUMALPHALIST);
+ m_alphaList.head.item.sort = 0.0f;
+ m_alphaList.tail.item.sort = 100000000.0f;
+
+ m_alphaBoatAtomicList.Init(NUMBOATALPHALIST);
+ m_alphaBoatAtomicList.head.item.sort = 0.0f;
+ m_alphaBoatAtomicList.tail.item.sort = 100000000.0f;
+
+#ifdef ASPECT_RATIO_SCALE
+ // default 150 if not enough for bigger FOVs
+ m_alphaEntityList.Init(NUMALPHAENTITYLIST * 3);
+#else
+ m_alphaEntityList.Init(NUMALPHAENTITYLIST);
+#endif // ASPECT_RATIO_SCALE
+ m_alphaEntityList.head.item.sort = 0.0f;
+ m_alphaEntityList.tail.item.sort = 100000000.0f;
+
+ m_alphaUnderwaterEntityList.Init(NUMALPHAUNTERWATERENTITYLIST);
+ m_alphaUnderwaterEntityList.head.item.sort = 0.0f;
+ m_alphaUnderwaterEntityList.tail.item.sort = 100000000.0f;
+
+#ifdef NEW_RENDERER
+ m_alphaBuildingList.Init(NUMALPHAENTITYLIST);
+ m_alphaBuildingList.head.item.sort = 0.0f;
+ m_alphaBuildingList.tail.item.sort = 100000000.0f;
+#endif
+}
+
+void
+CVisibilityPlugins::Shutdown(void)
+{
+ m_alphaList.Shutdown();
+ m_alphaBoatAtomicList.Shutdown();
+ m_alphaEntityList.Shutdown();
+ m_alphaUnderwaterEntityList.Shutdown();
+#ifdef NEW_RENDERER
+ m_alphaBuildingList.Shutdown();
+#endif
+}
+
+void
+CVisibilityPlugins::InitAlphaEntityList(void)
+{
+ m_alphaEntityList.Clear();
+ m_alphaBoatAtomicList.Clear();
+ m_alphaUnderwaterEntityList.Clear();
+#ifdef NEW_RENDERER
+ m_alphaBuildingList.Clear();
+#endif
+}
+
+bool
+CVisibilityPlugins::InsertEntityIntoSortedList(CEntity *e, float dist)
+{
+#ifdef FIX_BUGS
+ if (!e->m_rwObject) return true;
+#endif
+
+ AlphaObjectInfo item;
+ item.entity = e;
+ item.sort = dist;
+#ifdef NEW_RENDERER
+ if(gbNewRenderer && e->IsBuilding())
+ return !!m_alphaBuildingList.InsertSorted(item);
+#endif
+ if(e->bUnderwater && m_alphaUnderwaterEntityList.InsertSorted(item))
+ return true;
+ return !!m_alphaEntityList.InsertSorted(item);
+}
+
+void
+CVisibilityPlugins::InitAlphaAtomicList(void)
+{
+ m_alphaList.Clear();
+}
+
+bool
+CVisibilityPlugins::InsertAtomicIntoSortedList(RpAtomic *a, float dist)
+{
+ AlphaObjectInfo item;
+ item.atomic = a;
+ item.sort = dist;
+ return !!m_alphaList.InsertSorted(item);
+}
+
+bool
+CVisibilityPlugins::InsertAtomicIntoBoatSortedList(RpAtomic *a, float dist)
+{
+ AlphaObjectInfo item;
+ item.atomic = a;
+ item.sort = dist;
+ return !!m_alphaBoatAtomicList.InsertSorted(item);
+}
+
+// can't increase this yet unfortunately...
+// probably have to fix fading for this so material alpha isn't overwritten
+#define VEHICLE_LODDIST_MULTIPLIER (TheCamera.GenerationDistMultiplier)
+
+void
+CVisibilityPlugins::SetRenderWareCamera(RwCamera *camera)
+{
+ ms_pCamera = camera;
+ ms_pCameraPosn = RwMatrixGetPos(RwFrameGetMatrix(RwCameraGetFrame(camera)));
+
+ if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN ||
+ TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED)
+ ms_cullCompsDist = 1000000.0f;
+ else
+ ms_cullCompsDist = sq(TheCamera.LODDistMultiplier * 20.0f);
+
+ ms_vehicleLod0Dist = sq(70.0f * VEHICLE_LODDIST_MULTIPLIER);
+ ms_vehicleLod1Dist = sq(90.0f * VEHICLE_LODDIST_MULTIPLIER);
+ ms_vehicleFadeDist = sq(100.0f * VEHICLE_LODDIST_MULTIPLIER);
+ ms_bigVehicleLod0Dist = sq(60.0f * VEHICLE_LODDIST_MULTIPLIER);
+ ms_bigVehicleLod1Dist = sq(150.0f * VEHICLE_LODDIST_MULTIPLIER);
+ ms_pedLod1Dist = sq(60.0f * TheCamera.LODDistMultiplier);
+ ms_pedFadeDist = sq(70.0f * TheCamera.LODDistMultiplier);
+}
+
+static float DistToCameraSq;
+static float PitchToCamera;
+
+void
+CVisibilityPlugins::SetupVehicleVariables(RpClump *vehicle)
+{
+ if (RwObjectGetType((RwObject*)vehicle) != rpCLUMP)
+ return;
+ DistToCameraSq = GetDistanceSquaredFromCamera(RpClumpGetFrame(vehicle));
+ RwV3d distToCam;
+ RwV3dSub(&distToCam, ms_pCameraPosn, &RwFrameGetMatrix(RpClumpGetFrame(vehicle))->pos);
+ float dist2d = Sqrt(SQR(distToCam.x) + SQR(distToCam.y));
+ PitchToCamera = Atan2(distToCam.z, dist2d);
+}
+
+RpMaterial*
+SetAlphaCB(RpMaterial *material, void *data)
+{
+ ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data;
+ return material;
+}
+
+RpMaterial*
+SetTextureCB(RpMaterial *material, void *data)
+{
+ RpMaterialSetTexture(material, (RwTexture*)data);
+ return material;
+}
+
+void
+CVisibilityPlugins::RenderAtomicList(CLinkList<AlphaObjectInfo> &list)
+{
+ CLink<AlphaObjectInfo> *node;
+ for(node = list.tail.prev; node != &list.head; node = node->prev)
+ RENDERCALLBACK(node->item.atomic);
+}
+
+void
+CVisibilityPlugins::RenderAlphaAtomics(void)
+{
+ RenderAtomicList(m_alphaList);
+}
+
+void
+CVisibilityPlugins::RenderBoatAlphaAtomics(void)
+{
+ SetCullMode(rwCULLMODECULLNONE);
+ RenderAtomicList(m_alphaBoatAtomicList);
+ SetCullMode(rwCULLMODECULLBACK);
+}
+
+void
+CVisibilityPlugins::RenderFadingEntities(CLinkList<AlphaObjectInfo> &list)
+{
+ CLink<AlphaObjectInfo> *node;
+ CSimpleModelInfo *mi;
+ for(node = list.tail.prev; node != &list.head; node = node->prev){
+ CEntity *e = node->item.entity;
+ if(e->m_rwObject == nil)
+ continue;
+#ifdef EXTENDED_PIPELINES
+ if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle()))
+ continue;
+#endif
+ mi = (CSimpleModelInfo *)CModelInfo::GetModelInfo(e->GetModelIndex());
+ if(mi->GetModelType() == MITYPE_SIMPLE && mi->m_noZwrite)
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);
+
+ if(e->bDistanceFade){
+ DeActivateDirectional();
+ SetAmbientColours();
+ e->bImBeingRendered = true;
+ PUSH_RENDERGROUP(mi->GetModelName());
+ RenderFadingAtomic((RpAtomic*)e->m_rwObject, node->item.sort);
+ POP_RENDERGROUP();
+ e->bImBeingRendered = false;
+ }else
+ CRenderer::RenderOneNonRoad(e);
+
+ if(mi->GetModelType() == MITYPE_SIMPLE && mi->m_noZwrite)
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ }
+}
+
+void
+CVisibilityPlugins::RenderFadingEntities(void)
+{
+ RenderFadingEntities(m_alphaEntityList);
+ RenderBoatAlphaAtomics();
+}
+
+void
+CVisibilityPlugins::RenderFadingUnderwaterEntities(void)
+{
+ RenderFadingEntities(m_alphaUnderwaterEntityList);
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderWheelAtomicCB(RpAtomic *atomic)
+{
+ RpAtomic *lodatm;
+ float len;
+ CSimpleModelInfo *mi;
+
+ mi = GetAtomicModelInfo(atomic);
+ len = Sqrt(DistToCameraSq);
+ lodatm = mi->GetAtomicFromDistance(len * TheCamera.LODDistMultiplier / VEHICLE_LODDIST_MULTIPLIER);
+ if(lodatm){
+ if(RpAtomicGetGeometry(lodatm) != RpAtomicGetGeometry(atomic))
+ RpAtomicSetGeometry(atomic, RpAtomicGetGeometry(lodatm), rpATOMICSAMEBOUNDINGSPHERE);
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderObjNormalAtomic(RpAtomic *atomic)
+{
+ RwMatrix *m;
+ RwV3d view;
+ float len;
+
+ m = RwFrameGetLTM(RpAtomicGetFrame(atomic));
+ RwV3dSub(&view, RwMatrixGetPos(m), ms_pCameraPosn);
+ len = RwV3dLength(&view);
+ if(RwV3dDotProduct(&view, RwMatrixGetUp(m)) < -0.3f*len && len > 8.0f)
+ return atomic;
+ RENDERCALLBACK(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderAlphaAtomic(RpAtomic *atomic, int alpha)
+{
+ RpGeometry *geo;
+ uint32 flags;
+
+ geo = RpAtomicGetGeometry(atomic);
+ flags = RpGeometryGetFlags(geo);
+ RpGeometrySetFlags(geo, flags | rpGEOMETRYMODULATEMATERIALCOLOR);
+ RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha);
+ RENDERCALLBACK(atomic);
+ RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255);
+ RpGeometrySetFlags(geo, flags);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderWeaponCB(RpAtomic *atomic)
+{
+ RwMatrix *m;
+ RwV3d view;
+ float maxdist, distsq;
+ CSimpleModelInfo *mi;
+
+ mi = GetAtomicModelInfo(atomic);
+ m = RwFrameGetLTM(RpAtomicGetFrame(atomic));
+ RwV3dSub(&view, RwMatrixGetPos(m), ms_pCameraPosn);
+ maxdist = mi->GetLodDistance(0);
+ distsq = RwV3dDotProduct(&view, &view);
+ if(distsq < maxdist*maxdist)
+ RENDERCALLBACK(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist)
+{
+ RpAtomic *lodatm;
+ float fadefactor;
+ uint32 alpha;
+ CSimpleModelInfo *mi;
+
+ mi = GetAtomicModelInfo(atomic);
+ lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE);
+ if(mi->m_additive)
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+
+ fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE;
+ if(fadefactor > 1.0f)
+ fadefactor = 1.0f;
+ alpha = mi->m_alpha * fadefactor;
+ if(alpha == 255)
+ RENDERCALLBACK(atomic);
+ else{
+ RpGeometry *geo = RpAtomicGetGeometry(lodatm);
+ uint32 flags = RpGeometryGetFlags(geo);
+ RpGeometrySetFlags(geo, flags | rpGEOMETRYMODULATEMATERIALCOLOR);
+ RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha);
+ if(geo != RpAtomicGetGeometry(atomic))
+ RpAtomicSetGeometry(atomic, geo, rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?)
+ RENDERCALLBACK(atomic);
+ RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255);
+ RpGeometrySetFlags(geo, flags);
+ }
+
+ if(mi->m_additive)
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+
+ return atomic;
+}
+
+
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq < ms_vehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot))
+ return atomic;
+ }
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailAlphaCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq < ms_vehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f)
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot))
+ return atomic;
+
+ if(flags & ATOMIC_FLAG_DRAWLAST){
+ // sort before clump
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - 0.0001f))
+ RENDERCALLBACK(atomic);
+ }else{
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot))
+ RENDERCALLBACK(atomic);
+ }
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq < ms_bigVehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f)
+ return atomic;
+ }
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq < ms_bigVehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f)
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot))
+ return atomic;
+
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot))
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailCB_Boat(RpAtomic *atomic)
+{
+ if(DistToCameraSq < ms_vehicleLod0Dist)
+ RENDERCALLBACK(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_Boat(RpAtomic *atomic)
+{
+ if(DistToCameraSq < ms_vehicleLod0Dist){
+ if(GetAtomicId(atomic) & ATOMIC_FLAG_DRAWLAST){
+ if(!InsertAtomicIntoBoatSortedList(atomic, DistToCameraSq))
+ RENDERCALLBACK(atomic);
+ }else
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleLoDetailCB_Boat(RpAtomic *atomic)
+{
+ RpClump *clump;
+ int32 alpha;
+
+ clump = RpAtomicGetClump(atomic);
+ if(DistToCameraSq >= ms_vehicleLod0Dist){
+ alpha = GetClumpAlpha(clump);
+ if(alpha == 255)
+ RENDERCALLBACK(atomic);
+ else
+ RenderAlphaAtomic(atomic, alpha);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq >= ms_bigVehicleLod0Dist &&
+ DistToCameraSq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f)
+ return atomic;
+ }
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq >= ms_bigVehicleLod0Dist &&
+ DistToCameraSq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f)
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f)
+ return atomic;
+
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot))
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleReallyLowDetailCB(RpAtomic *atomic)
+{
+ RpClump *clump;
+ int32 alpha;
+
+ clump = RpAtomicGetClump(atomic);
+ if(DistToCameraSq >= ms_vehicleLod0Dist){
+ alpha = GetClumpAlpha(clump);
+ if(alpha == 255)
+ RENDERCALLBACK(atomic);
+ else
+ RenderAlphaAtomic(atomic, alpha);
+ }
+ return atomic;
+
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic)
+{
+ if(DistToCameraSq >= ms_bigVehicleLod1Dist)
+ RENDERCALLBACK(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderTrainHiDetailCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot))
+ return atomic;
+ }
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderTrainHiDetailAlphaCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f)
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot))
+ return atomic;
+
+ if(flags & ATOMIC_FLAG_DRAWLAST){
+ // sort before clump
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - 0.0001f))
+ RENDERCALLBACK(atomic);
+ }else{
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot))
+ RENDERCALLBACK(atomic);
+ }
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleRotorAlphaCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float dot;
+ RwV3d cam2atm;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ if(DistToCameraSq < ms_bigVehicleLod1Dist){
+ RwV3dSub(&cam2atm, &RwFrameGetLTM(RpAtomicGetFrame(atomic))->pos, ms_pCameraPosn);
+ dot = RwV3dDotProduct(&cam2atm, &RwFrameGetLTM(clumpframe)->at);
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot*20.0f))
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleTailRotorAlphaCB(RpAtomic *atomic)
+{
+ RwMatrix *clumpMat, *atmMat;
+ float dot;
+ RwV3d cam2atm;
+
+ if(DistToCameraSq < ms_bigVehicleLod0Dist){
+ atmMat = RwFrameGetLTM(RpAtomicGetFrame(atomic));
+ clumpMat = RwFrameGetLTM(RpClumpGetFrame(RpAtomicGetClump(atomic)));
+ RwV3dSub(&cam2atm, &atmMat->pos, ms_pCameraPosn);
+ dot = RwV3dDotProduct(&cam2atm, &clumpMat->up) + RwV3dDotProduct(&cam2atm, &clumpMat->right);
+ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - dot))
+ RENDERCALLBACK(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderPlayerCB(RpAtomic *atomic)
+{
+ if(CWorld::Players[0].m_pSkinTexture)
+ RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), SetTextureCB, CWorld::Players[0].m_pSkinTexture);
+ RENDERCALLBACK(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderPedCB(RpAtomic *atomic)
+{
+ int32 alpha;
+ RwV3d cam2atm;
+
+ RwV3dSub(&cam2atm, &RwFrameGetLTM(RpAtomicGetFrame(atomic))->pos, ms_pCameraPosn);
+ if(RwV3dDotProduct(&cam2atm, &cam2atm) < ms_pedLod1Dist){
+ alpha = GetClumpAlpha(RpAtomicGetClump(atomic));
+ if(alpha == 255)
+ RENDERCALLBACK(atomic);
+ else
+ RenderAlphaAtomic(atomic, alpha);
+ }
+ return atomic;
+}
+
+float
+CVisibilityPlugins::GetDistanceSquaredFromCamera(RwV3d *pos)
+{
+ RwV3d dist;
+ RwV3dSub(&dist, pos, ms_pCameraPosn);
+ return RwV3dDotProduct(&dist, &dist);
+}
+
+float
+CVisibilityPlugins::GetDistanceSquaredFromCamera(RwFrame *frame)
+{
+ RwMatrix *m;
+ RwV3d dist;
+ m = RwFrameGetLTM(frame);
+ RwV3dSub(&dist, RwMatrixGetPos(m), ms_pCameraPosn);
+ return RwV3dDotProduct(&dist, &dist);
+}
+
+float
+CVisibilityPlugins::GetDotProductWithCameraVector(RwMatrix *atomicMat, RwMatrix *clumpMat, uint32 flags)
+{
+ RwV3d dist;
+ float dot, dotdoor;
+
+ // Vehicle forward is the y axis (RwMatrix.up)
+ // Vehicle right is the x axis (RwMatrix.right)
+
+ RwV3dSub(&dist, RwMatrixGetPos(atomicMat), ms_pCameraPosn);
+ // forward/backward facing
+ if(flags & (ATOMIC_FLAG_FRONT | ATOMIC_FLAG_REAR))
+ dot = RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat));
+ // left/right facing
+ else if(flags & (ATOMIC_FLAG_LEFT | ATOMIC_FLAG_RIGHT))
+ dot = RwV3dDotProduct(&dist, RwMatrixGetRight(clumpMat));
+ else
+ dot = 0.0f;
+ if(flags & (ATOMIC_FLAG_LEFT | ATOMIC_FLAG_REAR))
+ dot = -dot;
+
+ if(flags & (ATOMIC_FLAG_REARDOOR | ATOMIC_FLAG_FRONTDOOR)){
+ if(flags & ATOMIC_FLAG_REARDOOR)
+ dotdoor = -RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat));
+ else if(flags & ATOMIC_FLAG_FRONTDOOR)
+ dotdoor = RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat));
+ else
+ dotdoor = 0.0f;
+
+ if(dot < 0.0f && dotdoor < 0.0f)
+ dot += dotdoor;
+ if(dot > 0.0f && dotdoor > 0.0f)
+ dot += dotdoor;
+ }
+
+ return dot;
+}
+
+/* These are all unused */
+
+bool
+CVisibilityPlugins::DefaultVisibilityCB(RpClump *clump)
+{
+ return true;
+}
+
+bool
+CVisibilityPlugins::FrustumSphereCB(RpClump *clump)
+{
+ RwSphere sphere;
+ RwFrame *frame = RpClumpGetFrame(clump);
+
+ CClumpModelInfo *modelInfo = (CClumpModelInfo*)GetFrameHierarchyId(frame);
+ sphere.radius = modelInfo->GetColModel()->boundingSphere.radius;
+ sphere.center.x = modelInfo->GetColModel()->boundingSphere.center.x;
+ sphere.center.y = modelInfo->GetColModel()->boundingSphere.center.y;
+ sphere.center.z = modelInfo->GetColModel()->boundingSphere.center.z;
+ RwV3dTransformPoints(&sphere.center, &sphere.center, 1, RwFrameGetLTM(frame));
+ return RwCameraFrustumTestSphere(ms_pCamera, &sphere) != rwSPHEREOUTSIDE;
+}
+
+bool
+CVisibilityPlugins::VehicleVisibilityCB(RpClump *clump)
+{
+ if (GetDistanceSquaredFromCamera(RpClumpGetFrame(clump)) <= ms_vehicleLod1Dist)
+ return FrustumSphereCB(clump);
+ return false;
+}
+
+bool
+CVisibilityPlugins::VehicleVisibilityCB_BigVehicle(RpClump *clump)
+{
+ return FrustumSphereCB(clump);
+}
+
+
+
+
+//
+// RW Plugins
+//
+
+enum
+{
+ ID_VISIBILITYATOMIC = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x00),
+ ID_VISIBILITYCLUMP = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x01),
+ ID_VISIBILITYFRAME = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x02),
+};
+
+bool
+CVisibilityPlugins::PluginAttach(void)
+{
+ ms_atomicPluginOffset = RpAtomicRegisterPlugin(sizeof(AtomicExt),
+ ID_VISIBILITYATOMIC,
+ AtomicConstructor, AtomicDestructor, AtomicCopyConstructor);
+
+ ms_framePluginOffset = RwFrameRegisterPlugin(sizeof(FrameExt),
+ ID_VISIBILITYFRAME,
+ FrameConstructor, FrameDestructor, FrameCopyConstructor);
+
+ ms_clumpPluginOffset = RpClumpRegisterPlugin(sizeof(ClumpExt),
+ ID_VISIBILITYCLUMP,
+ ClumpConstructor, ClumpDestructor, ClumpCopyConstructor);
+ return ms_atomicPluginOffset != -1 && ms_clumpPluginOffset != -1;
+}
+
+#define ATOMICEXT(o) (RWPLUGINOFFSET(AtomicExt, o, ms_atomicPluginOffset))
+#define FRAMEEXT(o) (RWPLUGINOFFSET(FrameExt, o, ms_framePluginOffset))
+#define CLUMPEXT(o) (RWPLUGINOFFSET(ClumpExt, o, ms_clumpPluginOffset))
+
+//
+// Atomic
+//
+
+void*
+CVisibilityPlugins::AtomicConstructor(void *object, int32, int32)
+{
+ ATOMICEXT(object)->modelInfo = nil;
+ return object;
+}
+
+void*
+CVisibilityPlugins::AtomicDestructor(void *object, int32, int32)
+{
+ return object;
+}
+
+void*
+CVisibilityPlugins::AtomicCopyConstructor(void *dst, const void *src, int32, int32)
+{
+ *ATOMICEXT(dst) = *ATOMICEXT(src);
+ return dst;
+}
+
+void
+CVisibilityPlugins::SetAtomicModelInfo(RpAtomic *atomic,
+ CSimpleModelInfo *modelInfo)
+{
+ AtomicExt *ext = ATOMICEXT(atomic);
+ ext->modelInfo = modelInfo;
+}
+
+CSimpleModelInfo*
+CVisibilityPlugins::GetAtomicModelInfo(RpAtomic *atomic)
+{
+ return ATOMICEXT(atomic)->modelInfo;
+}
+
+void
+CVisibilityPlugins::SetAtomicFlag(RpAtomic *atomic, int f)
+{
+ ATOMICEXT(atomic)->flags |= f;
+}
+
+void
+CVisibilityPlugins::ClearAtomicFlag(RpAtomic *atomic, int f)
+{
+ ATOMICEXT(atomic)->flags &= ~f;
+}
+
+int
+CVisibilityPlugins::GetAtomicId(RpAtomic *atomic)
+{
+ return ATOMICEXT(atomic)->flags;
+}
+
+void
+CVisibilityPlugins::SetAtomicRenderCallback(RpAtomic *atomic, RpAtomicCallBackRender cb)
+{
+ if(cb == nil)
+ cb = RENDERCALLBACK; // not necessary
+ RpAtomicSetRenderCallBack(atomic, cb);
+}
+
+//
+// Frame
+//
+
+void*
+CVisibilityPlugins::FrameConstructor(void *object, int32, int32)
+{
+ FRAMEEXT(object)->id = 0;
+ return object;
+}
+
+void*
+CVisibilityPlugins::FrameDestructor(void *object, int32, int32)
+{
+ return object;
+}
+
+void*
+CVisibilityPlugins::FrameCopyConstructor(void *dst, const void *src, int32, int32)
+{
+ *FRAMEEXT(dst) = *FRAMEEXT(src);
+ return dst;
+}
+
+void
+CVisibilityPlugins::SetFrameHierarchyId(RwFrame *frame, intptr id)
+{
+ FRAMEEXT(frame)->id = id;
+}
+
+intptr
+CVisibilityPlugins::GetFrameHierarchyId(RwFrame *frame)
+{
+ return FRAMEEXT(frame)->id;
+}
+
+
+//
+// Clump
+//
+
+void*
+CVisibilityPlugins::ClumpConstructor(void *object, int32, int32)
+{
+ ClumpExt *ext = CLUMPEXT(object);
+ ext->visibilityCB = DefaultVisibilityCB;
+ ext->alpha = 0xFF;
+ return object;
+}
+
+void*
+CVisibilityPlugins::ClumpDestructor(void *object, int32, int32)
+{
+ return object;
+}
+
+void*
+CVisibilityPlugins::ClumpCopyConstructor(void *dst, const void *src, int32, int32)
+{
+ CLUMPEXT(dst)->visibilityCB = CLUMPEXT(src)->visibilityCB;
+ return dst;
+}
+
+void
+CVisibilityPlugins::SetClumpModelInfo(RpClump *clump, CClumpModelInfo *modelInfo)
+{
+ CVehicleModelInfo *vmi;
+ SetFrameHierarchyId(RpClumpGetFrame(clump), (intptr)modelInfo);
+
+ // Unused
+ switch (modelInfo->GetModelType()) {
+ // ignore MLO
+ case MITYPE_VEHICLE:
+ vmi = (CVehicleModelInfo*)modelInfo;
+ if(vmi->m_vehicleType == VEHICLE_TYPE_TRAIN ||
+ vmi->m_vehicleType == VEHICLE_TYPE_HELI ||
+ vmi->m_vehicleType == VEHICLE_TYPE_PLANE)
+ CLUMPEXT(clump)->visibilityCB = VehicleVisibilityCB_BigVehicle;
+ else
+ CLUMPEXT(clump)->visibilityCB = VehicleVisibilityCB;
+ break;
+ default: break;
+ }
+}
+
+void
+CVisibilityPlugins::SetClumpAlpha(RpClump *clump, int alpha)
+{
+ CLUMPEXT(clump)->alpha = alpha;
+}
+
+int
+CVisibilityPlugins::GetClumpAlpha(RpClump *clump)
+{
+ return CLUMPEXT(clump)->alpha;
+}
diff --git a/src/rw/VisibilityPlugins.h b/src/rw/VisibilityPlugins.h
new file mode 100644
index 0000000..9cdbc0c
--- /dev/null
+++ b/src/rw/VisibilityPlugins.h
@@ -0,0 +1,149 @@
+#pragma once
+
+#include "templates.h"
+
+class CEntity;
+class CSimpleModelInfo;
+class CClumpModelInfo;
+
+typedef bool (*ClumpVisibilityCB)(RpClump*);
+
+class CVisibilityPlugins
+{
+public:
+ struct AlphaObjectInfo
+ {
+ union {
+ CEntity *entity;
+ RpAtomic *atomic;
+ };
+ float sort;
+ };
+
+ static CLinkList<AlphaObjectInfo> m_alphaList;
+ static CLinkList<AlphaObjectInfo> m_alphaBoatAtomicList;
+ static CLinkList<AlphaObjectInfo> m_alphaEntityList;
+ static CLinkList<AlphaObjectInfo> m_alphaUnderwaterEntityList;
+#ifdef NEW_RENDERER
+ static CLinkList<AlphaObjectInfo> m_alphaBuildingList;
+#endif
+ static RwCamera *ms_pCamera;
+ static RwV3d *ms_pCameraPosn;
+ static float ms_cullCompsDist;
+ static float ms_vehicleLod0Dist;
+ static float ms_vehicleLod1Dist;
+ static float ms_vehicleFadeDist;
+ static float ms_bigVehicleLod0Dist;
+ static float ms_bigVehicleLod1Dist;
+ static float ms_pedLod1Dist;
+ static float ms_pedFadeDist;
+
+ static void Initialise(void);
+ static void Shutdown(void);
+ static void InitAlphaEntityList(void);
+ static bool InsertEntityIntoSortedList(CEntity *e, float dist);
+ static void InitAlphaAtomicList(void);
+ static bool InsertAtomicIntoSortedList(RpAtomic *a, float dist);
+ static bool InsertAtomicIntoBoatSortedList(RpAtomic *a, float dist);
+
+ static void SetRenderWareCamera(RwCamera *camera);
+ static void SetupVehicleVariables(RpClump *vehicle);
+
+ static RpAtomic *RenderWheelAtomicCB(RpAtomic *atomic);
+ static RpAtomic *RenderObjNormalAtomic(RpAtomic *atomic);
+ static RpAtomic *RenderAlphaAtomic(RpAtomic *atomic, int alpha);
+ static RpAtomic *RenderWeaponCB(RpAtomic *atomic);
+ static RpAtomic *RenderFadingAtomic(RpAtomic *atm, float dist);
+
+ static RpAtomic *RenderVehicleHiDetailCB(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleHiDetailAlphaCB(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleHiDetailCB_Boat(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleHiDetailAlphaCB_Boat(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleLoDetailCB_Boat(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleReallyLowDetailCB(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic);
+ static RpAtomic *RenderTrainHiDetailCB(RpAtomic *atomic);
+ static RpAtomic *RenderTrainHiDetailAlphaCB(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleRotorAlphaCB(RpAtomic *atomic);
+ static RpAtomic *RenderVehicleTailRotorAlphaCB(RpAtomic *atomic);
+
+ static RpAtomic *RenderPlayerCB(RpAtomic *atomic);
+ static RpAtomic *RenderPedCB(RpAtomic *atomic); // for skinned models with only one clump
+
+ static void RenderAtomicList(CLinkList<AlphaObjectInfo> &list);
+ static void RenderAlphaAtomics(void);
+ static void RenderBoatAlphaAtomics(void);
+ static void RenderFadingEntities(CLinkList<AlphaObjectInfo> &list);
+ static void RenderFadingEntities(void);
+ static void RenderFadingUnderwaterEntities(void);
+
+ // All actually unused
+ static bool DefaultVisibilityCB(RpClump *clump);
+ static bool FrustumSphereCB(RpClump *clump);
+ static bool VehicleVisibilityCB(RpClump *clump);
+ static bool VehicleVisibilityCB_BigVehicle(RpClump *clump);
+
+ static float GetDistanceSquaredFromCamera(RwV3d *pos);
+ static float GetDistanceSquaredFromCamera(RwFrame *frame);
+ static float GetDotProductWithCameraVector(RwMatrix *atomicMat, RwMatrix *clumpMat, uint32 flags);
+
+ //
+ // RW Plugins
+ //
+
+ union AtomicExt
+ {
+ CSimpleModelInfo *modelInfo; // used by SimpleModelInfo
+ int flags; // used by ClumpModelInfo
+ };
+ static void SetAtomicModelInfo(RpAtomic*, CSimpleModelInfo*);
+ static CSimpleModelInfo *GetAtomicModelInfo(RpAtomic *atomic);
+ static void SetAtomicFlag(RpAtomic*, int);
+ static void ClearAtomicFlag(RpAtomic*, int);
+ static int GetAtomicId(RpAtomic *atomic);
+ static void SetAtomicRenderCallback(RpAtomic*, RpAtomicCallBackRender);
+
+ static void *AtomicConstructor(void *object, int32 offset, int32 len);
+ static void *AtomicDestructor(void *object, int32 offset, int32 len);
+ static void *AtomicCopyConstructor(void *dst, const void *src,
+ int32 offset, int32 len);
+ static int32 ms_atomicPluginOffset;
+
+ struct FrameExt
+ {
+ // BUG: this is abused to hold a pointer by SetClumpModelInfo
+ intptr id;
+ };
+ static void SetFrameHierarchyId(RwFrame *frame, intptr id);
+ static intptr GetFrameHierarchyId(RwFrame *frame);
+
+ static void *FrameConstructor(void *object, int32 offset, int32 len);
+ static void *FrameDestructor(void *object, int32 offset, int32 len);
+ static void *FrameCopyConstructor(void *dst, const void *src,
+ int32 offset, int32 len);
+ static int32 ms_framePluginOffset;
+
+ // Not actually used
+ struct ClumpExt
+ {
+ ClumpVisibilityCB visibilityCB;
+ int alpha;
+ };
+ static void SetClumpModelInfo(RpClump*, CClumpModelInfo*);
+ static void SetClumpAlpha(RpClump*, int);
+ static int GetClumpAlpha(RpClump*);
+
+ static void *ClumpConstructor(void *object, int32 offset, int32 len);
+ static void *ClumpDestructor(void *object, int32 offset, int32 len);
+ static void *ClumpCopyConstructor(void *dst, const void *src,
+ int32 offset, int32 len);
+ static int32 ms_clumpPluginOffset;
+
+ static bool PluginAttach(void);
+};
+
+RpMaterial *SetAlphaCB(RpMaterial *material, void *data);