summaryrefslogtreecommitdiffstats
path: root/src/render
diff options
context:
space:
mode:
Diffstat (limited to 'src/render')
-rw-r--r--src/render/2dEffect.h39
-rw-r--r--src/render/Clouds.cpp430
-rw-r--r--src/render/Clouds.h20
-rw-r--r--src/render/Coronas.cpp10
-rw-r--r--src/render/Coronas.h13
-rw-r--r--src/render/Draw.cpp6
-rw-r--r--src/render/Draw.h16
-rw-r--r--src/render/Lights.cpp171
-rw-r--r--src/render/Lights.h9
-rw-r--r--src/render/Particle.cpp10
-rw-r--r--src/render/Particle.h82
-rw-r--r--src/render/RenderBuffer.cpp59
-rw-r--r--src/render/RenderBuffer.h10
-rw-r--r--src/render/Renderer.cpp1165
-rw-r--r--src/render/Renderer.h59
-rw-r--r--src/render/Sprite.cpp553
-rw-r--r--src/render/Sprite.h37
-rw-r--r--src/render/VisibilityPlugins.cpp849
-rw-r--r--src/render/VisibilityPlugins.h129
19 files changed, 3667 insertions, 0 deletions
diff --git a/src/render/2dEffect.h b/src/render/2dEffect.h
new file mode 100644
index 00000000..6cba85d1
--- /dev/null
+++ b/src/render/2dEffect.h
@@ -0,0 +1,39 @@
+class C2dEffect
+{
+public:
+ struct Light {
+ float dist;
+ float outerRange;
+ float size;
+ float innerRange;
+ uint8 flash;
+ uint8 wet;
+ uint8 flare;
+ uint8 shadowIntens;
+ uint8 flag;
+ RwTexture *corona;
+ RwTexture *shadow;
+ };
+ struct Particle {
+ int particleType;
+ float dir[3];
+ float scale;
+ };
+ struct Attractor {
+ CVector dir;
+ uint8 flag;
+ uint8 probability;
+ };
+
+ CVector pos;
+ RwRGBA col;
+ uint8 type;
+ union {
+ Light light;
+ Particle particle;
+ Attractor attractor;
+ };
+
+ C2dEffect(void) {}
+};
+static_assert(sizeof(C2dEffect) == 0x34, "C2dEffect: error");
diff --git a/src/render/Clouds.cpp b/src/render/Clouds.cpp
new file mode 100644
index 00000000..64bc93e8
--- /dev/null
+++ b/src/render/Clouds.cpp
@@ -0,0 +1,430 @@
+#include "common.h"
+#include "patcher.h"
+#include "Sprite.h"
+#include "General.h"
+#include "Coronas.h"
+#include "Camera.h"
+#include "TxdStore.h"
+#include "Weather.h"
+#include "Clock.h"
+#include "Timer.h"
+#include "Timecycle.h"
+#include "Renderer.h"
+#include "Clouds.h"
+
+#define SMALLSTRIPHEIGHT 4.0f
+#define HORIZSTRIPHEIGHT 48.0f
+
+RwTexture **gpCloudTex = (RwTexture**)0x9411C0; //[5];
+
+float &CClouds::CloudRotation = *(float*)0x8F5F40;
+uint32 &CClouds::IndividualRotation = *(uint32*)0x943078;
+
+float &CClouds::ms_cameraRoll = *(float*)0x8F29CC;
+float &CClouds::ms_horizonZ = *(float*)0x8F31C0;
+CRGBA &CClouds::ms_colourTop = *(CRGBA*)0x94143C;
+CRGBA &CClouds::ms_colourBottom = *(CRGBA*)0x8F2C38;
+
+void
+CClouds::Init(void)
+{
+ CTxdStore::PushCurrentTxd();
+ CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle"));
+ gpCloudTex[0] = RwTextureRead("cloud1", nil);
+ gpCloudTex[1] = RwTextureRead("cloud2", nil);
+ gpCloudTex[2] = RwTextureRead("cloud3", nil);
+ gpCloudTex[3] = RwTextureRead("cloudhilit", nil);
+ gpCloudTex[4] = RwTextureRead("cloudmasked", nil);
+ CTxdStore::PopCurrentTxd();
+ CloudRotation = 0.0f;
+}
+
+void
+CClouds::Update(void)
+{
+ float s = sin(TheCamera.Orientation - 0.85f);
+ CloudRotation += CWeather::Wind*s*0.0025f;
+ IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep() + 0.3f) * 60.0f;
+}
+
+
+void
+CClouds::Render(void)
+{
+ int i;
+ float szx, szy;
+ RwV3d screenpos;
+ RwV3d worldpos;
+
+ CCoronas::SunBlockedByClouds = false;
+
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ CSprite::InitSpriteBuffer();
+
+ int minute = CClock::GetHours()*60 + CClock::GetMinutes();
+ RwV3d campos = *(RwV3d*)&TheCamera.GetPosition();
+
+ float coverage = CWeather::CloudCoverage <= CWeather::Foggyness ? CWeather::Foggyness : CWeather::CloudCoverage;
+
+ // Moon
+ int moonfadeout = abs(minute - 180); // fully visible at 3AM
+ if(moonfadeout < 180){ // fade in/out 3 hours
+ int brightness = (1.0f - coverage) * (180 - moonfadeout);
+ RwV3d pos = { 0.0f, -100.0f, 15.0f };
+ RwV3dAdd(&worldpos, &campos, &pos);
+ if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[2]->raster);
+ if(CCoronas::bSmallMoon){
+ szx *= 4.0f;
+ szy *= 4.0f;
+ }else{
+ szx *= 10.0f;
+ szy *= 10.0f;
+ }
+ CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
+ szx, szy, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255);
+ }
+ }
+
+ // The R* logo
+ int starintens = 0;
+ if(CClock::GetHours() < 22 && CClock::GetHours() > 5)
+ starintens = 0;
+ else if(CClock::GetHours() > 22 || CClock::GetHours() < 5)
+ starintens = 255;
+ else if(CClock::GetHours() == 22)
+ starintens = 255 * CClock::GetMinutes()/60.0f;
+ else if(CClock::GetHours() == 5)
+ starintens = 255 * (60 - CClock::GetMinutes())/60.0f;
+ if(starintens != 0){
+ // R
+ static float StarCoorsX[9] = { 0.0f, 0.05f, 0.12f, 0.5f, 0.8f, 0.6f, 0.27f, 0.55f, 0.75f };
+ static float StarCoorsY[9] = { 0.0f, 0.45f, 0.9f, 1.0f, 0.85f, 0.52f, 0.48f, 0.35f, 0.2f };
+ static float StarSizes[9] = { 1.0f, 1.4f, 0.9f, 1.0f, 0.6f, 1.5f, 1.3f, 1.0f, 0.8f };
+ int brightness = (1.0f - coverage) * starintens;
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[0]->raster);
+ for(i = 0; i < 11; i++){
+ RwV3d pos = { 100.0f, 0.0f, 10.0f };
+ if(i >= 9) pos.x = -pos.x;
+ RwV3dAdd(&worldpos, &campos, &pos);
+ worldpos.y -= 90.0f*StarCoorsX[i%9];
+ worldpos.z += 80.0f*StarCoorsY[i%9];
+ if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
+ float sz = 0.8f*StarSizes[i%9];
+ CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
+ szx*sz, szy*sz, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255);
+ }
+ }
+ CSprite::FlushSpriteBuffer();
+
+ // *
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[0]->raster);
+ RwV3d pos = { 100.0f, 0.0f, 10.0f };
+ RwV3dAdd(&worldpos, &campos, &pos);
+ worldpos.y -= 90.0f;
+ if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
+ brightness *= (CGeneral::GetRandomNumber()&127) / 640.0f + 0.5f;
+ CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
+ szx*5.0f, szy*5.0f, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255);
+ }
+ }
+
+ // Low clouds
+ static float LowCloudsX[12] = { 1.0f, 0.7f, 0.0f, -0.7f, -1.0f, -0.7f,
+ 0.0f, 0.7f, 0.8f, -0.8f, 0.4f, -0.4f };
+ static float LowCloudsY[12] = { 0.0f, -0.7f, -1.0f, -0.7f, 0.0f, 0.7f,
+ 1.0f, 0.7f, 0.4f, 0.4f, -0.8f, -0.8f };
+ static float LowCloudsZ[12] = { 0.0f, 1.0f, 0.5f, 0.0f, 1.0f, 0.3f,
+ 0.9f, 0.4f, 1.3f, 1.4f, 1.2f, 1.7f };
+ float lowcloudintensity = 1.0f - coverage;
+ int r = CTimeCycle::GetLowCloudsRed() * lowcloudintensity;
+ int g = CTimeCycle::GetLowCloudsGreen() * lowcloudintensity;
+ int b = CTimeCycle::GetLowCloudsBlue() * lowcloudintensity;
+ for(int cloudtype = 0; cloudtype < 3; cloudtype++){
+ for(i = cloudtype; i < 12; i += 3){
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCloudTex[cloudtype]->raster);
+ RwV3d pos = { 800.0f*LowCloudsX[i], 800.0f*LowCloudsY[i], 60.0f*LowCloudsZ[i] };
+ worldpos.x = campos.x + pos.x;
+ worldpos.y = campos.y + pos.y;
+ worldpos.z = 40.0f + pos.z;
+ if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false))
+ CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(screenpos.x, screenpos.y, screenpos.z,
+ szx*320.0f, szy*40.0f, r, g, b, 255, 1.0f/screenpos.z, ms_cameraRoll, 255);
+ }
+ CSprite::FlushSpriteBuffer();
+ }
+
+ // Fluffy clouds
+ float rot_sin = sin(CloudRotation);
+ float rot_cos = cos(CloudRotation);
+ int fluffyalpha = 160 * (1.0f - CWeather::Foggyness);
+ if(fluffyalpha != 0){
+ static float CoorsOffsetX[37] = {
+ 0.0f, 60.0f, 72.0f, 48.0f, 21.0f, 12.0f,
+ 9.0f, -3.0f, -8.4f, -18.0f, -15.0f, -36.0f,
+ -40.0f, -48.0f, -60.0f, -24.0f, 100.0f, 100.0f,
+ 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f,
+ 100.0f, 100.0f, -30.0f, -20.0f, 10.0f, 30.0f,
+ 0.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f
+ };
+ static float CoorsOffsetY[37] = {
+ 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f,
+ 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f,
+ 100.0f, 100.0f, 100.0f, 100.0f, -30.0f, 10.0f,
+ -25.0f, -5.0f, 28.0f, -10.0f, 10.0f, 0.0f,
+ 15.0f, 40.0f, -100.0f, -100.0f, -100.0f, -100.0f,
+ -100.0f, -40.0f, -20.0f, 0.0f, 10.0f, 30.0f, 35.0f
+ };
+ static float CoorsOffsetZ[37] = {
+ 2.0f, 1.0f, 0.0f, 0.3f, 0.7f, 1.4f,
+ 1.7f, 0.24f, 0.7f, 1.3f, 1.6f, 1.0f,
+ 1.2f, 0.3f, 0.7f, 1.4f, 0.0f, 0.1f,
+ 0.5f, 0.4f, 0.55f, 0.75f, 1.0f, 1.4f,
+ 1.7f, 2.0f, 2.0f, 2.3f, 1.9f, 2.4f,
+ 2.0f, 2.0f, 1.5f, 1.2f, 1.7f, 1.5f, 2.1f
+ };
+ static bool bCloudOnScreen[37];
+ float hilight;
+
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCloudTex[4]->raster);
+ for(i = 0; i < 37; i++){
+ RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f };
+ worldpos.x = pos.x*rot_cos + pos.y*rot_sin + campos.x;
+ worldpos.y = pos.x*rot_sin - pos.y*rot_cos + campos.y;
+ worldpos.z = pos.z;
+
+ if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
+ float sundist = sqrt(sq(screenpos.x-CCoronas::SunScreenX) + sq(screenpos.y-CCoronas::SunScreenY));
+ int tr = CTimeCycle::GetFluffyCloudsTopRed();
+ int tg = CTimeCycle::GetFluffyCloudsTopGreen();
+ int tb = CTimeCycle::GetFluffyCloudsTopBlue();
+ int br = CTimeCycle::GetFluffyCloudsBottomRed();
+ int bg = CTimeCycle::GetFluffyCloudsBottomGreen();
+ int bb = CTimeCycle::GetFluffyCloudsBottomBlue();
+ if(sundist < SCREENW/2){
+ hilight = (1.0f - coverage) * (1.0f - sundist/(SCREENW/2));
+ tr = tr*(1.0f-hilight) + 255*hilight;
+ tg = tg*(1.0f-hilight) + 190*hilight;
+ tb = tb*(1.0f-hilight) + 190*hilight;
+ br = br*(1.0f-hilight) + 255*hilight;
+ bg = bg*(1.0f-hilight) + 190*hilight;
+ bb = bb*(1.0f-hilight) + 190*hilight;
+ if(sundist < SCREENW/10)
+ CCoronas::SunBlockedByClouds = true;
+ }else
+ hilight = 0.0f;
+ CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(screenpos.x, screenpos.y, screenpos.z,
+ szx*55.0f, szy*55.0f,
+ tr, tg, tb, br, bg, bb, 0.0f, -1.0f,
+ 1.0f/screenpos.z,
+ IndividualRotation/65336.0f * 2*3.14f + ms_cameraRoll,
+ fluffyalpha);
+ bCloudOnScreen[i] = true;
+ }else
+ bCloudOnScreen[i] = false;
+ }
+ CSprite::FlushSpriteBuffer();
+
+ // Highlights
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCloudTex[3]->raster);
+
+ for(i = 0; i < 37; i++){
+ RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f };
+ worldpos.x = campos.x*rot_cos + campos.y*rot_sin + pos.x;
+ worldpos.y = campos.x*rot_sin + campos.y*rot_cos + pos.y;
+ worldpos.z = pos.z;
+ if(bCloudOnScreen[i] && CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
+ // BUG: this is stupid....would have to do this for each cloud individually
+ if(hilight > 0.0f){
+ CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(screenpos.x, screenpos.y, screenpos.z,
+ szx*30.0f, szy*30.0f,
+ 200*hilight, 0, 0, 255, 1.0f/screenpos.z,
+ 1.7f - CGeneral::GetATanOfXY(screenpos.x-CCoronas::SunScreenX, screenpos.y-CCoronas::SunScreenY) + CClouds::ms_cameraRoll, 255);
+ }
+ }
+ }
+ CSprite::FlushSpriteBuffer();
+ }
+
+ // Rainbow
+ if(CWeather::Rainbow != 0.0f){
+ static uint8 BowRed[6] = { 30, 30, 30, 10, 0, 15 };
+ static uint8 BowGreen[6] = { 0, 15, 30, 30, 0, 0 };
+ static uint8 BowBlue[6] = { 0, 0, 0, 10, 30, 30 };
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[0]->raster);
+ for(i = 0; i < 6; i++){
+ RwV3d pos = { i*1.5f, 100.0f, 5.0f };
+ RwV3dAdd(&worldpos, &campos, &pos);
+ if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false))
+ CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
+ 2.0f*szx, 50.0*szy,
+ BowRed[i]*CWeather::Rainbow, BowGreen[i]*CWeather::Rainbow, BowBlue[i]*CWeather::Rainbow,
+ 255, 1.0f/screenpos.z, 255);
+
+ }
+ CSprite::FlushSpriteBuffer();
+ }
+
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+}
+
+bool
+UseDarkBackground(void)
+{
+ return RwFrameGetLTM(RwCameraGetFrame(TheCamera.m_pRwCamera))->up.z < -0.9f ||
+ gbShowCollisionPolys;
+}
+
+void
+CClouds::RenderBackground(int16 topred, int16 topgreen, int16 topblue,
+ int16 botred, int16 botgreen, int16 botblue, int16 alpha)
+{
+ RwMatrix *mat = RwFrameGetLTM(RwCameraGetFrame(TheCamera.m_pRwCamera));
+ float c = sqrt(mat->right.x * mat->right.x + mat->right.y * mat->right.y);
+ if(c > 1.0f)
+ c = 1.0f;
+ ms_cameraRoll = acos(c);
+ if(mat->right.z < 0.0f)
+ ms_cameraRoll = -ms_cameraRoll;
+
+ if(UseDarkBackground()){
+ ms_colourTop.r = 50;
+ ms_colourTop.g = 50;
+ ms_colourTop.b = 50;
+ ms_colourTop.a = 255;
+ if(gbShowCollisionPolys){
+ if(CTimer::GetFrameCounter() & 1){
+ ms_colourTop.r = 0;
+ ms_colourTop.g = 0;
+ ms_colourTop.b = 0;
+ }else{
+ ms_colourTop.r = 255;
+ ms_colourTop.g = 255;
+ ms_colourTop.b = 255;
+ }
+ }
+ ms_colourBottom = ms_colourTop;
+ CRect r(0, 0, SCREENW, SCREENH);
+ CSprite2d::DrawRect(r, ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
+ }else{
+ ms_horizonZ = CSprite::CalcHorizonCoors();
+
+ // Draw top/bottom gradient
+ float gradheight = SCREENH/2.0f;
+ float topedge = ms_horizonZ - gradheight;
+ float botpos, toppos;
+ if(ms_horizonZ > 0.0f && topedge < SCREENH){
+ ms_colourTop.r = topred;
+ ms_colourTop.g = topgreen;
+ ms_colourTop.b = topblue;
+ ms_colourTop.a = alpha;
+ ms_colourBottom.r = botred;
+ ms_colourBottom.g = botgreen;
+ ms_colourBottom.b = botblue;
+ ms_colourBottom.a = alpha;
+
+ if(ms_horizonZ < SCREENH)
+ botpos = ms_horizonZ;
+ else{
+ float f = (ms_horizonZ - SCREENH)/gradheight;
+ ms_colourBottom.r = topred*f + (1.0f-f)*botred;
+ ms_colourBottom.g = topgreen*f + (1.0f-f)*botgreen;
+ ms_colourBottom.b = topblue*f + (1.0f-f)*botblue;
+ botpos = SCREENH;
+ }
+ if(topedge >= 0.0f)
+ toppos = topedge;
+ else{
+ float f = (0.0f - topedge)/gradheight;
+ ms_colourTop.r = botred*f + (1.0f-f)*topred;
+ ms_colourTop.g = botgreen*f + (1.0f-f)*topgreen;
+ ms_colourTop.b = botblue*f + (1.0f-f)*topblue;
+ toppos = 0.0f;
+ }
+ CSprite2d::DrawRect(CRect(0, toppos, SCREENW, botpos),
+ ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
+ }
+
+ // draw the small stripe (whatever it's supposed to be)
+ if(ms_horizonZ > -SMALLSTRIPHEIGHT && ms_horizonZ < SCREENH){
+ // Same colour as fog
+ ms_colourTop.r = (topred + 2 * botred) / 3;
+ ms_colourTop.g = (topgreen + 2 * botgreen) / 3;
+ ms_colourTop.b = (topblue + 2 * botblue) / 3;
+ CSprite2d::DrawRect(CRect(0, ms_horizonZ, SCREENW, ms_horizonZ+SMALLSTRIPHEIGHT),
+ ms_colourTop, ms_colourTop, ms_colourTop, ms_colourTop);
+ }
+
+ // Only top
+ if(topedge > 0.0f){
+ ms_colourTop.r = topred;
+ ms_colourTop.g = topgreen;
+ ms_colourTop.b = topblue;
+ ms_colourTop.a = alpha;
+ ms_colourBottom.r = topred;
+ ms_colourBottom.g = topgreen;
+ ms_colourBottom.b = topblue;
+ ms_colourBottom.a = alpha;
+
+ botpos = min(SCREENH, topedge);
+ CSprite2d::DrawRect(CRect(0, 0, SCREENW, botpos),
+ ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
+ }
+
+ // Set both to fog colour for RenderHorizon
+ ms_colourTop.r = (topred + 2 * botred) / 3;
+ ms_colourTop.g = (topgreen + 2 * botgreen) / 3;
+ ms_colourTop.b = (topblue + 2 * botblue) / 3;
+ ms_colourBottom.r = (topred + 2 * botred) / 3;
+ ms_colourBottom.g = (topgreen + 2 * botgreen) / 3;
+ ms_colourBottom.b = (topblue + 2 * botblue) / 3;
+ }
+}
+
+void
+CClouds::RenderHorizon(void)
+{
+ if(UseDarkBackground())
+ return;
+
+ ms_colourBottom.a = 230;
+ ms_colourTop.a = 80;
+
+ if(ms_horizonZ > SCREENH)
+ return;
+
+ float z1 = min(ms_horizonZ + SMALLSTRIPHEIGHT, SCREENH);
+ CSprite2d::DrawRectXLU(CRect(0, ms_horizonZ, SCREENW, z1),
+ ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
+
+ // This is just weird
+ float a = SCREENH/400.0f * HORIZSTRIPHEIGHT +
+ SCREENH/300.0f * max(TheCamera.GetPosition().z, 0.0f);
+ float b = TheCamera.GetUp().z < 0.0f ?
+ SCREENH :
+ SCREENH * fabs(TheCamera.GetRight().z);
+ float z2 = z1 + (a + b)*TheCamera.LODDistMultiplier;
+ z2 = min(z2, SCREENH);
+ CSprite2d::DrawRect(CRect(0, z1, SCREENW, z2),
+ ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
+}
+
+STARTPATCHES
+ InjectHook(0x4F6C10, CClouds::Init, PATCH_JUMP);
+ InjectHook(0x4F6CE0, CClouds::Update, PATCH_JUMP);
+ InjectHook(0x4F6D90, CClouds::Render, PATCH_JUMP);
+ InjectHook(0x4F7F00, CClouds::RenderBackground, PATCH_JUMP);
+ InjectHook(0x4F85F0, CClouds::RenderHorizon, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/Clouds.h b/src/render/Clouds.h
new file mode 100644
index 00000000..96f04bb1
--- /dev/null
+++ b/src/render/Clouds.h
@@ -0,0 +1,20 @@
+#pragma once
+
+class CClouds
+{
+public:
+ static float &CloudRotation;
+ static uint32 &IndividualRotation;
+
+ static float &ms_cameraRoll;
+ static float &ms_horizonZ;
+ static CRGBA &ms_colourTop;
+ static CRGBA &ms_colourBottom;
+
+ static void Init(void);
+ static void Update(void);
+ static void Render(void);
+ static void RenderBackground(int16 topred, int16 topgreen, int16 topblue,
+ int16 botred, int16 botgreen, int16 botblue, int16 alpha);
+ static void RenderHorizon(void);
+};
diff --git a/src/render/Coronas.cpp b/src/render/Coronas.cpp
new file mode 100644
index 00000000..39b85246
--- /dev/null
+++ b/src/render/Coronas.cpp
@@ -0,0 +1,10 @@
+#include "common.h"
+#include "Coronas.h"
+
+RwTexture **gpCoronaTexture = (RwTexture**)0x5FAF44; //[9]
+
+float &CCoronas::LightsMult = *(float*)0x5FB088; // 1.0
+float &CCoronas::SunScreenX = *(float*)0x8F4358;
+float &CCoronas::SunScreenY = *(float*)0x8F4354;
+bool &CCoronas::bSmallMoon = *(bool*)0x95CD49;
+bool &CCoronas::SunBlockedByClouds = *(bool*)0x95CD73;
diff --git a/src/render/Coronas.h b/src/render/Coronas.h
new file mode 100644
index 00000000..4ec7dd3b
--- /dev/null
+++ b/src/render/Coronas.h
@@ -0,0 +1,13 @@
+#pragma once
+
+extern RwTexture **gpCoronaTexture; //[9]
+
+class CCoronas
+{
+public:
+ static float &LightsMult;
+ static float &SunScreenY;
+ static float &SunScreenX;
+ static bool &bSmallMoon;
+ static bool &SunBlockedByClouds;
+};
diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp
new file mode 100644
index 00000000..f26f2ada
--- /dev/null
+++ b/src/render/Draw.cpp
@@ -0,0 +1,6 @@
+#include "common.h"
+#include "Draw.h"
+
+float &CDraw::ms_fNearClipZ = *(float*)0x8E2DC4;
+float &CDraw::ms_fFarClipZ = *(float*)0x9434F0;
+float &CDraw::ms_fFOV = *(float*)0x5FBC6C;
diff --git a/src/render/Draw.h b/src/render/Draw.h
new file mode 100644
index 00000000..62fe5193
--- /dev/null
+++ b/src/render/Draw.h
@@ -0,0 +1,16 @@
+#pragma once
+
+class CDraw
+{
+private:
+ static float &ms_fNearClipZ;
+ static float &ms_fFarClipZ;
+ static float &ms_fFOV;
+public:
+ static void SetNearClipZ(float nearclip) { ms_fNearClipZ = nearclip; }
+ static float GetNearClipZ(void) { return ms_fNearClipZ; }
+ static void SetFarClipZ(float farclip) { ms_fFarClipZ = farclip; }
+ static float GetFarClipZ(void) { return ms_fFarClipZ; }
+ static void SetFOV(float fov) { ms_fFOV = fov; }
+ static float GetFOV(void) { return ms_fFOV; }
+};
diff --git a/src/render/Lights.cpp b/src/render/Lights.cpp
new file mode 100644
index 00000000..6962af4d
--- /dev/null
+++ b/src/render/Lights.cpp
@@ -0,0 +1,171 @@
+#include "common.h"
+#include <rwcore.h>
+#include <rpworld.h>
+#include "patcher.h"
+#include "Lights.h"
+#include "Timecycle.h"
+#include "Coronas.h"
+#include "Weather.h"
+#include "CullZones.h"
+#include "MenuManager.h"
+
+RpLight *&pAmbient = *(RpLight**)0x885B6C;
+RpLight *&pDirect = *(RpLight**)0x880F7C;
+
+RwRGBAReal &AmbientLightColourForFrame = *(RwRGBAReal*)0x6F46F8;
+RwRGBAReal &AmbientLightColourForFrame_PedsCarsAndObjects = *(RwRGBAReal*)0x6F1D10;
+RwRGBAReal &DirectionalLightColourForFrame = *(RwRGBAReal*)0x87C6B8;
+
+RwRGBAReal &AmbientLightColour = *(RwRGBAReal*)0x86B0F8;
+RwRGBAReal &DirectionalLightColour = *(RwRGBAReal*)0x72E308;
+
+void
+SetLightsWithTimeOfDayColour(RpWorld *)
+{
+ CVector vec1, vec2, vecsun;
+ RwMatrix mat;
+
+ if(pAmbient){
+ AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed() * CCoronas::LightsMult;
+ AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen() * CCoronas::LightsMult;
+ AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue() * CCoronas::LightsMult;
+ if(CWeather::LightningFlash && !CCullZones::CamNoRain()){
+ AmbientLightColourForFrame.red = 1.0f;
+ AmbientLightColourForFrame.green = 1.0f;
+ AmbientLightColourForFrame.blue = 1.0f;
+ }
+ AmbientLightColourForFrame_PedsCarsAndObjects.red = min(1.0f, AmbientLightColourForFrame.red*1.3f);
+ AmbientLightColourForFrame_PedsCarsAndObjects.green = min(1.0f, AmbientLightColourForFrame.green*1.3f);
+ AmbientLightColourForFrame_PedsCarsAndObjects.blue = min(1.0f, AmbientLightColourForFrame.blue*1.3f);
+ 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(CMenuManager::m_PrefsBrightness > 256){
+ float f1 = 2.0f * (CMenuManager::m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f;
+ float f2 = 3.0f * (CMenuManager::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
+ }
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+STARTPATCHES
+ InjectHook(0x526510, SetLightsWithTimeOfDayColour, PATCH_JUMP);
+ InjectHook(0x526DE0, SetAmbientAndDirectionalColours, PATCH_JUMP);
+ InjectHook(0x526E60, SetBrightMarkerColours, PATCH_JUMP);
+ InjectHook(0x526F10, ReSetAmbientAndDirectionalColours, PATCH_JUMP);
+ InjectHook(0x526F40, DeActivateDirectional, PATCH_JUMP);
+ InjectHook(0x526F50, ActivateDirectional, PATCH_JUMP);
+ InjectHook(0x526F60, SetAmbientColours, PATCH_JUMP);
+ InjectHook(0x526F80, SetAmbientColoursForPedsCarsAndObjects, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/Lights.h b/src/render/Lights.h
new file mode 100644
index 00000000..ca926eb8
--- /dev/null
+++ b/src/render/Lights.h
@@ -0,0 +1,9 @@
+void SetLightsWithTimeOfDayColour(RpWorld *);
+void SetAmbientAndDirectionalColours(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); \ No newline at end of file
diff --git a/src/render/Particle.cpp b/src/render/Particle.cpp
new file mode 100644
index 00000000..9bbc4587
--- /dev/null
+++ b/src/render/Particle.cpp
@@ -0,0 +1,10 @@
+#include "common.h"
+#include "patcher.h"
+#include "Particle.h"
+
+WRAPPER void
+CParticle::AddParticle(tParticleType, const CVector &pos, const CVector &velocity, CEntity *ent,
+ float size, int32 rotationStep, int32 rotation, int startFrame, int lifeSpan)
+{
+ EAXJMP(0x50D140);
+}
diff --git a/src/render/Particle.h b/src/render/Particle.h
new file mode 100644
index 00000000..f711ecf1
--- /dev/null
+++ b/src/render/Particle.h
@@ -0,0 +1,82 @@
+#pragma once
+
+enum tParticleType
+{
+ PARTICLE_SPARK,
+ PARTICLE_SPARK_SMALL,
+ PARTICLE_WHEEL_DIRT,
+ PARTICLE_WHEEL_WATER,
+ PARTICLE_BLOOD,
+ PARTICLE_BLOOD_SMALL,
+ PARTICLE_BLOOD_SPURT,
+ PARTICLE_DEBRIS,
+ PARTICLE_DEBRIS2,
+ PARTICLE_WATER,
+ PARTICLE_FLAME,
+ PARTICLE_FIREBALL,
+ PARTICLE_GUNFLASH,
+ PARTICLE_GUNFLASH_NOANIM,
+ PARTICLE_GUNSMOKE,
+ PARTICLE_GUNSMOKE2,
+ PARTICLE_SMOKE,
+ PARTICLE_SMOKE_SLOWMOTION,
+ PARTICLE_GARAGEPAINT_SPRAY,
+ PARTICLE_SHARD,
+ PARTICLE_SPLASH,
+ PARTICLE_CARFLAME,
+ PARTICLE_STEAM,
+ PARTICLE_STEAM2,
+ PARTICLE_STEAM_NY,
+ PARTICLE_STEAM_NY_SLOWMOTION,
+ PARTICLE_ENGINE_STEAM,
+ PARTICLE_RAINDROP,
+ PARTICLE_RAINDROP_SMALL,
+ PARTICLE_RAIN_SPLASH,
+ PARTICLE_RAIN_SPLASH_BIGGROW,
+ PARTICLE_RAIN_SPLASHUP,
+ PARTICLE_WATERSPRAY,
+ PARTICLE_EXPLOSION_MEDIUM,
+ PARTICLE_EXPLOSION_LARGE,
+ PARTICLE_EXPLOSION_MFAST,
+ PARTICLE_EXPLOSION_LFAST,
+ PARTICLE_CAR_SPLASH,
+ PARTICLE_BOAT_SPLASH,
+ PARTICLE_BOAT_THRUSTJET,
+ PARTICLE_BOAT_WAKE,
+ PARTICLE_WATER_HYDRANT,
+ PARTICLE_WATER_CANNON,
+ PARTICLE_EXTINGUISH_STEAM,
+ PARTICLE_PED_SPLASH,
+ PARTICLE_PEDFOOT_DUST,
+ PARTICLE_HELI_DUST,
+ PARTICLE_HELI_ATTACK,
+ PARTICLE_ENGINE_SMOKE,
+ PARTICLE_ENGINE_SMOKE2,
+ PARTICLE_CARFLAME_SMOKE,
+ PARTICLE_FIREBALL_SMOKE,
+ PARTICLE_PAINT_SMOKE,
+ PARTICLE_TREE_LEAVES,
+ PARTICLE_CARCOLLISION_DUST,
+ PARTICLE_CAR_DEBRIS,
+ PARTICLE_HELI_DEBRIS,
+ PARTICLE_EXHAUST_FUMES,
+ PARTICLE_RUBBER_SMOKE,
+ PARTICLE_BURNINGRUBBER_SMOKE,
+ PARTICLE_BULLETHIT_SMOKE,
+ PARTICLE_GUNSHELL_FIRST,
+ PARTICLE_GUNSHELL,
+ PARTICLE_GUNSHELL_BUMP1,
+ PARTICLE_GUNSHELL_BUMP2,
+ PARTICLE_TEST,
+ PARTICLE_BIRD_FRONT,
+ PARTICLE_RAINDROP_2D,
+};
+
+class CEntity;
+
+class CParticle
+{
+public:
+ static void AddParticle(tParticleType, const CVector &pos, const CVector &velocity, CEntity *ent = nil,
+ float size = 0.0, int32 rotationStep = 0, int32 rotation = 0, int startFrame = 0, int lifeSpan = 0);
+};
diff --git a/src/render/RenderBuffer.cpp b/src/render/RenderBuffer.cpp
new file mode 100644
index 00000000..9a1ed58d
--- /dev/null
+++ b/src/render/RenderBuffer.cpp
@@ -0,0 +1,59 @@
+#include "common.h"
+#include "patcher.h"
+#include "RenderBuffer.h"
+
+int32 &TempBufferVerticesStored = *(int32*)0x8F5F78;
+int32 &TempBufferIndicesStored = *(int32*)0x8F1A4C;
+
+RwIm3DVertex *TempVertexBuffer = (RwIm3DVertex*)0x862330;
+RwImVertexIndex *TempBufferRenderIndexList = (RwImVertexIndex*)0x846288;
+
+int RenderBuffer::VerticesToBeStored;
+int RenderBuffer::IndicesToBeStored;
+
+void
+RenderBuffer::ClearRenderBuffer(void)
+{
+ TempBufferVerticesStored = 0;
+ TempBufferIndicesStored = 0;
+}
+
+void
+RenderBuffer::StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, RwIm3DVertex **vertexStart)
+{
+ if(TempBufferIndicesStored + numIndices >= 1024)
+ RenderStuffInBuffer();
+ if(TempBufferVerticesStored + numVertices >= 256)
+ RenderStuffInBuffer();
+ *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored];
+ *vertexStart = &TempVertexBuffer[TempBufferVerticesStored];
+ IndicesToBeStored = numIndices;
+ VerticesToBeStored = numVertices;
+}
+
+void
+RenderBuffer::StopStoring(void)
+{
+ int i;
+ for(i = TempBufferIndicesStored; i < TempBufferIndicesStored+IndicesToBeStored; i++)
+ TempBufferRenderIndexList[i] += TempBufferVerticesStored;
+ TempBufferIndicesStored += IndicesToBeStored;
+ TempBufferVerticesStored += VerticesToBeStored;
+}
+
+void
+RenderBuffer::RenderStuffInBuffer(void)
+{
+ if(TempBufferVerticesStored && RwIm3DTransform(TempVertexBuffer, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){
+ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored);
+ RwIm3DEnd();
+ }
+ ClearRenderBuffer();
+}
+
+STARTPATCHES
+ InjectHook(0x517620, RenderBuffer::ClearRenderBuffer, PATCH_JUMP);
+ InjectHook(0x517640, RenderBuffer::StartStoring, PATCH_JUMP);
+ InjectHook(0x5176B0, RenderBuffer::StopStoring, PATCH_JUMP);
+ InjectHook(0x5177C0, RenderBuffer::RenderStuffInBuffer, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/RenderBuffer.h b/src/render/RenderBuffer.h
new file mode 100644
index 00000000..66baa2d0
--- /dev/null
+++ b/src/render/RenderBuffer.h
@@ -0,0 +1,10 @@
+class RenderBuffer
+{
+public:
+ static int VerticesToBeStored;
+ static int IndicesToBeStored;
+ static void ClearRenderBuffer(void);
+ static void StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, RwIm3DVertex **vertexStart);
+ static void StopStoring(void);
+ static void RenderStuffInBuffer(void);
+};
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
new file mode 100644
index 00000000..fd949cee
--- /dev/null
+++ b/src/render/Renderer.cpp
@@ -0,0 +1,1165 @@
+#include "common.h"
+#include "patcher.h"
+#include "Lights.h"
+#include "ModelInfo.h"
+#include "Treadable.h"
+#include "Ped.h"
+#include "Vehicle.h"
+#include "Object.h"
+#include "PathFind.h"
+#include "Collision.h"
+#include "VisibilityPlugins.h"
+#include "Clock.h"
+#include "World.h"
+#include "Camera.h"
+#include "ModelIndices.h"
+#include "Streaming.h"
+#include "Renderer.h"
+
+bool gbShowPedRoadGroups;
+bool gbShowCarRoadGroups;
+bool gbShowCollisionPolys;
+
+bool gbDontRenderBuildings;
+bool gbDontRenderBigBuildings;
+bool gbDontRenderPeds;
+bool gbDontRenderObjects;
+
+struct EntityInfo
+{
+ CEntity *ent;
+ float sort;
+};
+
+CLinkList<EntityInfo> &gSortedVehiclesAndPeds = *(CLinkList<EntityInfo>*)0x629AC0;
+
+int32 &CRenderer::ms_nNoOfVisibleEntities = *(int32*)0x940730;
+CEntity **CRenderer::ms_aVisibleEntityPtrs = (CEntity**)0x6E9920;
+int32 &CRenderer::ms_nNoOfInVisibleEntities = *(int32*)0x8F1B78;
+CEntity **CRenderer::ms_aInVisibleEntityPtrs = (CEntity**)0x880B50;
+
+CVector &CRenderer::ms_vecCameraPosition = *(CVector*)0x8E2C3C;
+CVehicle *&CRenderer::m_pFirstPersonVehicle = *(CVehicle**)0x885B80;
+bool &CRenderer::m_loadingPriority = *(bool*)0x95CD86;
+
+void
+CRenderer::Init(void)
+{
+ gSortedVehiclesAndPeds.Init(40);
+ SortBIGBuildings();
+}
+void
+CRenderer::RenderOneRoad(CEntity *e)
+{
+ if(gbDontRenderBuildings)
+ return;
+ if(gbShowCollisionPolys)
+ CCollision::DrawColModel_Coloured(e->GetMatrix(),
+ *CModelInfo::GetModelInfo(e->m_modelIndex)->GetColModel(),
+ e->m_modelIndex);
+ else
+ e->Render();
+}
+
+void
+CRenderer::RenderOneNonRoad(CEntity *e)
+{
+ CPed *ped;
+ CVehicle *veh;
+ int i;
+ bool resetLights;
+
+#ifndef MASTER
+ if(gbShowCollisionPolys){
+ if(!e->IsVehicle()){
+ CCollision::DrawColModel_Coloured(e->GetMatrix(),
+ *CModelInfo::GetModelInfo(e->m_modelIndex)->GetColModel(),
+ e->m_modelIndex);
+ return;
+ }
+ }else if(e->IsBuilding()){
+ if(e->bIsBIGBuilding){
+ if(gbDontRenderBigBuildings)
+ return;
+ }else{
+ if(gbDontRenderBuildings)
+ return;
+ }
+ }else
+#endif
+ if(e->IsPed()){
+#ifndef MASTER
+ if(gbDontRenderPeds)
+ return;
+#endif
+ ped = (CPed*)e;
+ if(ped->m_nPedState == PED_PASSENGER)
+ return;
+ }
+#ifndef MASTER
+ else if(e->IsObject() || e->IsDummy()){
+ if(gbDontRenderObjects)
+ return;
+ }
+#endif
+
+ resetLights = e->SetupLighting();
+
+ if(e->IsVehicle())
+ CVisibilityPlugins::InitAlphaAtomicList();
+
+ // Render Peds in vehicle before vehicle itself
+ if(e->IsVehicle()){
+ veh = (CVehicle*)e;
+ if(veh->pDriver && veh->pDriver->m_nPedState == PED_PASSENGER)
+ veh->pDriver->Render();
+ for(i = 0; i < 8; i++)
+ if(veh->pPassengers[i] && veh->pPassengers[i]->m_nPedState == PED_PASSENGER)
+ veh->pPassengers[i]->Render();
+ }
+ e->Render();
+
+ if(e->IsVehicle()){
+ e->bImBeingRendered = true;
+ CVisibilityPlugins::RenderAlphaAtomics();
+ e->bImBeingRendered = false;
+ }
+
+ e->RemoveLighting(resetLights);
+}
+
+void
+CRenderer::RenderFirstPersonVehicle(void)
+{
+ if(m_pFirstPersonVehicle == nil)
+ return;
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RenderOneNonRoad(m_pFirstPersonVehicle);
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
+}
+
+void
+CRenderer::RenderRoads(void)
+{
+ int i;
+ CTreadable *t;
+
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE);
+ DeActivateDirectional();
+ SetAmbientColours();
+
+ ThePaths.m_pathNodes[-1].group = 6;
+
+ for(i = 0; i < ms_nNoOfVisibleEntities; i++){
+ t = (CTreadable*)ms_aVisibleEntityPtrs[i];
+ if(t->IsBuilding() && t->GetIsATreadable()){
+#ifndef MASTER
+ if(gbShowCarRoadGroups || gbShowPedRoadGroups){
+ int ind = 0;
+ if(gbShowCarRoadGroups)
+ ind += ThePaths.m_pathNodes[t->m_nodeIndicesCars[0]].group;
+ if(gbShowPedRoadGroups)
+ ind += ThePaths.m_pathNodes[t->m_nodeIndicesPeds[0]].group;
+ SetAmbientColoursToIndicateRoadGroup(ind);
+ }
+#endif
+ RenderOneRoad(t);
+#ifndef MASTER
+ if(gbShowCarRoadGroups || gbShowPedRoadGroups)
+ ReSetAmbientAndDirectionalColours();
+#endif
+ }
+ }
+}
+
+void
+CRenderer::RenderEverythingBarRoads(void)
+{
+ int i;
+ CEntity *e;
+ CVector dist;
+ EntityInfo ei;
+
+ gSortedVehiclesAndPeds.Clear();
+
+ for(i = 0; i < ms_nNoOfVisibleEntities; i++){
+ e = ms_aVisibleEntityPtrs[i];
+
+ if(e->IsBuilding() && ((CBuilding*)e)->GetIsATreadable())
+ continue;
+
+ if(e->IsVehicle() ||
+ e->IsPed() && CVisibilityPlugins::GetClumpAlpha((RpClump*)e->m_rwObject) != 255){
+ if(e->IsVehicle() && ((CVehicle*)e)->m_vehType == VEHICLE_TYPE_BOAT){
+ dist = ms_vecCameraPosition - e->GetPosition();
+ if(!CVisibilityPlugins::InsertEntityIntoSortedList(e, dist.Magnitude())){
+ printf("Ran out of space in alpha entity list");
+ RenderOneNonRoad(e);
+ }
+ }else{
+ ei.ent = e;
+ dist = ms_vecCameraPosition - e->GetPosition();
+ ei.sort = dist.MagnitudeSqr();
+ gSortedVehiclesAndPeds.InsertSorted(ei);
+ }
+ }else
+ RenderOneNonRoad(e);
+ }
+}
+
+void
+CRenderer::RenderVehiclesButNotBoats(void)
+{
+ CLink<EntityInfo> *node;
+
+ for(node = gSortedVehiclesAndPeds.tail.prev;
+ node != &gSortedVehiclesAndPeds.head;
+ node = node->prev){
+ CVehicle *v = (CVehicle*)node->item.ent;
+ if(v->IsVehicle() && v->m_vehType == VEHICLE_TYPE_BOAT) // BUG: missing in III
+ continue;
+ RenderOneNonRoad(v);
+ }
+}
+
+void
+CRenderer::RenderBoats(void)
+{
+ CLink<EntityInfo> *node;
+
+ for(node = gSortedVehiclesAndPeds.tail.prev;
+ node != &gSortedVehiclesAndPeds.head;
+ node = node->prev){
+ CVehicle *v = (CVehicle*)node->item.ent;
+ if(!v->IsVehicle()) // BUG: missing in III
+ continue;
+ if(v->m_vehType == VEHICLE_TYPE_BOAT)
+ RenderOneNonRoad(v);
+ }
+}
+
+void
+CRenderer::RenderFadingInEntities(void)
+{
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE);
+ DeActivateDirectional();
+ SetAmbientColours();
+ CVisibilityPlugins::RenderFadingEntities();
+}
+
+enum Visbility
+{
+ VIS_INVISIBLE,
+ VIS_VISIBLE,
+ VIS_OFFSCREEN,
+ VIS_STREAMME
+};
+
+#define LOD_DISTANCE 300.0f
+#define FADE_DISTANCE 20.0f
+#define STREAM_DISTANCE 30.0f
+
+// Time Objects can be time culled if
+// other == -1 || CModelInfo::GetModelInfo(other)->GetRwObject()
+// i.e. we have to draw even at the wrong time if
+// other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil
+
+#define OTHERUNAVAILABLE (other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil)
+#define CANTIMECULL (!OTHERUNAVAILABLE)
+
+int32
+CRenderer::SetupEntityVisibility(CEntity *ent)
+{
+ CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex);
+ CTimeModelInfo *ti;
+ int32 other;
+ float dist;
+
+ bool request = true;
+ if(mi->m_type == MITYPE_TIME){
+ ti = (CTimeModelInfo*)mi;
+ other = ti->GetOtherTimeModel();
+ if(CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff())){
+ // don't fade in, or between time objects
+ if(CANTIMECULL)
+ ti->m_alpha = 255;
+ }else{
+ // Hide if possible
+ if(CANTIMECULL)
+ return VIS_INVISIBLE;
+ // can't cull, so we'll try to draw this one, but don't request
+ // it since what we really want is the other one.
+ request = false;
+ }
+ }else{
+ if(mi->m_type != MITYPE_SIMPLE){
+ if(FindPlayerVehicle() == ent &&
+ TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIRSTPERSON){
+ // Player's vehicle in first person mode
+ if(TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_FORWARD ||
+ ent->GetModelIndex() == MI_RHINO ||
+ ent->GetModelIndex() == MI_COACH ||
+ TheCamera.m_bInATunnelAndABigVehicle){
+ ent->m_flagD40 = true;
+ }else{
+ m_pFirstPersonVehicle = (CVehicle*)ent;
+ ent->m_flagD40 = false;
+ }
+ return VIS_OFFSCREEN;
+ }else{
+ // All sorts of Clumps
+ if(ent->m_rwObject == nil || !ent->bIsVisible)
+ return VIS_INVISIBLE;
+ if(!ent->GetIsOnScreen())
+ return VIS_OFFSCREEN;
+ if(ent->bDrawLast){
+ dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude();
+ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist);
+ ent->bDistanceFade = false;
+ return VIS_INVISIBLE;
+ }else
+ return VIS_VISIBLE;
+ }
+ return VIS_INVISIBLE;
+ }
+ if(ent->m_type == ENTITY_TYPE_OBJECT &&
+ ((CObject*)ent)->ObjectCreatedBy == TEMP_OBJECT){
+ if(ent->m_rwObject == nil || !ent->bIsVisible)
+ return VIS_INVISIBLE;
+ return ent->GetIsOnScreen() ? VIS_VISIBLE : VIS_OFFSCREEN;
+ }
+ }
+
+ // Simple ModelInfo
+
+ dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude();
+
+ // This can only happen with multi-atomic models (e.g. railtracks)
+ // but why do we bump up the distance? can only be fading...
+ if(LOD_DISTANCE + STREAM_DISTANCE < dist && dist < mi->GetLargestLodDistance())
+ dist = mi->GetLargestLodDistance();
+
+ if(ent->m_type == ENTITY_TYPE_OBJECT && ent->bRenderDamaged)
+ mi->m_isDamaged = true;
+
+ RpAtomic *a = mi->GetAtomicFromDistance(dist);
+ if(a){
+ mi->m_isDamaged = 0;
+ if(ent->m_rwObject == nil)
+ ent->CreateRwObject();
+ assert(ent->m_rwObject);
+ RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject;
+ // Make sure our atomic uses the right geometry and not
+ // that of an atomic for another draw distance.
+ if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj))
+ RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), 0);
+ mi->IncreaseAlpha();
+ if(ent->m_rwObject == nil || !ent->bIsVisible)
+ return VIS_INVISIBLE;
+
+ if(!ent->GetIsOnScreen()){
+ mi->m_alpha = 255;
+ return VIS_OFFSCREEN;
+ }
+
+ if(mi->m_alpha != 255){
+ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist);
+ ent->bDistanceFade = true;
+ return VIS_INVISIBLE;
+ }
+
+ if(mi->m_drawLast || ent->bDrawLast){
+ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist);
+ ent->bDistanceFade = false;
+ return VIS_INVISIBLE;
+ }
+ return VIS_VISIBLE;
+ }
+
+ // Object is not loaded, figure out what to do
+
+ if(mi->m_noFade){
+ mi->m_isDamaged = false;
+ // request model
+ if(dist - STREAM_DISTANCE < mi->GetLargestLodDistance() && request)
+ return VIS_STREAMME;
+ return VIS_INVISIBLE;
+ }
+
+ // We might be fading
+
+ a = mi->GetAtomicFromDistance(dist - FADE_DISTANCE);
+ mi->m_isDamaged = false;
+ if(a == nil){
+ // request model
+ if(dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance() && request)
+ return VIS_STREAMME;
+ return VIS_INVISIBLE;
+ }
+
+ if(ent->m_rwObject == nil)
+ ent->CreateRwObject();
+ assert(ent->m_rwObject);
+ RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject;
+ if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj))
+ RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), 0);
+ mi->IncreaseAlpha();
+ if(ent->m_rwObject == nil || !ent->bIsVisible)
+ return VIS_INVISIBLE;
+
+ if(!ent->GetIsOnScreen()){
+ mi->m_alpha = 255;
+ return VIS_OFFSCREEN;
+ }else{
+ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist);
+ ent->bDistanceFade = true;
+ return VIS_OFFSCREEN; // Why this?
+ }
+}
+
+int32
+CRenderer::SetupBigBuildingVisibility(CEntity *ent)
+{
+ CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex);
+ CTimeModelInfo *ti;
+ int32 other;
+
+ if(mi->m_type == MITYPE_TIME){
+ ti = (CTimeModelInfo*)mi;
+ other = ti->GetOtherTimeModel();
+ // Hide objects not in time range if possible
+ if(CANTIMECULL)
+ if(!CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff()))
+ return 0;
+ // Draw like normal
+ }else if(mi->m_type == MITYPE_VEHICLE)
+ return ent->IsVisible();
+
+ float dist = (ms_vecCameraPosition-ent->GetPosition()).Magnitude();
+ CSimpleModelInfo *nonLOD = mi->GetRelatedModel();
+
+ // Find out whether to draw below near distance.
+ // This is only the case if there is a non-LOD which is either not
+ // loaded or not completely faded in yet.
+ if(dist < mi->GetNearDistance() && dist < LOD_DISTANCE + STREAM_DISTANCE){
+ // No non-LOD or non-LOD is completely visible.
+ if(nonLOD == nil ||
+ nonLOD->GetRwObject() && nonLOD->m_alpha == 255)
+ return 0;
+
+ // But if it is a time object, we'd rather draw the wrong
+ // non-LOD than the right LOD.
+ if(nonLOD->m_type == MITYPE_TIME){
+ ti = (CTimeModelInfo*)nonLOD;
+ other = ti->GetOtherTimeModel();
+ if(other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject())
+ return 0;
+ }
+ }
+
+ RpAtomic *a = mi->GetAtomicFromDistance(dist);
+ if(a){
+ if(ent->m_rwObject == nil)
+ ent->CreateRwObject();
+ assert(ent->m_rwObject);
+ RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject;
+
+ // Make sure our atomic uses the right geometry and not
+ // that of an atomic for another draw distance.
+ if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj))
+ RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), 0);
+ if(!ent->IsVisibleComplex())
+ return 0;
+ if(mi->m_drawLast){
+ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist);
+ ent->bDistanceFade = 0;
+ return 0;
+ }
+ return 1;
+ }
+
+ if(mi->m_noFade){
+ ent->DeleteRwObject();
+ return 0;
+ }
+
+
+ // get faded atomic
+ a = mi->GetAtomicFromDistance(dist - FADE_DISTANCE);
+ if(a == nil){
+ ent->DeleteRwObject();
+ return 0;
+ }
+
+ // Fade...
+ if(ent->m_rwObject == nil)
+ ent->CreateRwObject();
+ assert(ent->m_rwObject);
+ RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject;
+ if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj))
+ RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), 0);
+ if(ent->IsVisibleComplex())
+ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist);
+ return 0;
+}
+
+void
+CRenderer::ConstructRenderList(void)
+{
+ ms_nNoOfVisibleEntities = 0;
+ ms_nNoOfInVisibleEntities = 0;
+ ms_vecCameraPosition = TheCamera.GetPosition();
+ // TODO: blocked ranges, but unused
+ ScanWorld();
+}
+
+void
+LimitFrustumVector(CVector &vec1, const CVector &vec2, float l)
+{
+ float f;
+ f = (l - vec2.z) / (vec1.z - vec2.z);
+ vec1.x = f*(vec1.x - vec2.x) + vec2.x;
+ vec1.y = f*(vec1.y - vec2.y) + vec2.y;
+ vec1.z = f*(vec1.z - vec2.z) + vec2.z;
+}
+
+enum Corners
+{
+ CORNER_CAM = 0,
+ CORNER_FAR_TOPLEFT,
+ CORNER_FAR_TOPRIGHT,
+ CORNER_FAR_BOTRIGHT,
+ CORNER_FAR_BOTLEFT,
+ CORNER_LOD_LEFT,
+ CORNER_LOD_RIGHT,
+ CORNER_PRIO_LEFT,
+ CORNER_PRIO_RIGHT,
+};
+
+void
+CRenderer::ScanWorld(void)
+{
+ float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera);
+ RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera);
+ CVector vectors[9];
+ RwMatrix *cammatrix;
+ RwV2d poly[3];
+
+ memset(vectors, 0, sizeof(vectors));
+ vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f;
+ vectors[CORNER_FAR_TOPLEFT].y = vw.y * f;
+ vectors[CORNER_FAR_TOPLEFT].z = f;
+ vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f;
+ vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f;
+ vectors[CORNER_FAR_TOPRIGHT].z = f;
+ vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f;
+ vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f;
+ vectors[CORNER_FAR_BOTRIGHT].z = f;
+ vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f;
+ vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f;
+ vectors[CORNER_FAR_BOTLEFT].z = f;
+
+ cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera));
+
+ m_pFirstPersonVehicle = nil;
+ CVisibilityPlugins::InitAlphaEntityList();
+ CWorld::AdvanceCurrentScanCode();
+
+ if(cammatrix->at.z > 0.0f){
+ // looking up, bottom corners are further away
+ vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f;
+ vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f;
+ }else{
+ // looking down, top corners are further away
+ vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f;
+ vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f;
+ }
+ vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f;
+ vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f;
+ vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z;
+ vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f;
+ vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f;
+ vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z;
+ RwV3dTransformPoints((RwV3d*)vectors, (RwV3d*)vectors, 9, cammatrix);
+
+ m_loadingPriority = false;
+ if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1 ||
+ TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWNPED){
+ CRect rect;
+ int x1, x2, y1, y2;
+ LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]);
+ LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]);
+ LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]);
+ LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]);
+ x1 = CWorld::GetSectorIndexX(rect.left);
+ if(x1 < 0) x1 = 0;
+ x2 = CWorld::GetSectorIndexX(rect.right);
+ if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1;
+ y1 = CWorld::GetSectorIndexY(rect.bottom);
+ if(y1 < 0) y1 = 0;
+ y2 = CWorld::GetSectorIndexY(rect.top);
+ if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1;
+ for(; x1 <= x2; x1++)
+ for(int y = y1; y <= y2; y++)
+ ScanSectorList(CWorld::GetSector(x1, y)->m_lists);
+ }else{
+ CVehicle *train = FindPlayerTrain();
+ if(train && train->GetPosition().z < 0.0f){
+ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x);
+ poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y);
+ poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x);
+ poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y);
+ poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x);
+ poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y);
+ ScanSectorPoly(poly, 3, ScanSectorList_Subway);
+ }else{
+ if(f <= LOD_DISTANCE){
+ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x);
+ poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y);
+ poly[1].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPLEFT].x);
+ poly[1].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPLEFT].y);
+ poly[2].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPRIGHT].x);
+ poly[2].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPRIGHT].y);
+ }else{
+ // priority
+ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x);
+ poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y);
+ poly[1].x = CWorld::GetSectorX(vectors[CORNER_PRIO_LEFT].x);
+ poly[1].y = CWorld::GetSectorY(vectors[CORNER_PRIO_LEFT].y);
+ poly[2].x = CWorld::GetSectorX(vectors[CORNER_PRIO_RIGHT].x);
+ poly[2].y = CWorld::GetSectorY(vectors[CORNER_PRIO_RIGHT].y);
+ ScanSectorPoly(poly, 3, ScanSectorList_Priority);
+
+ // below LOD
+ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x);
+ poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y);
+ poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x);
+ poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y);
+ poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x);
+ poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y);
+ }
+ ScanSectorPoly(poly, 3, ScanSectorList);
+
+ ScanBigBuildingList(CWorld::GetBigBuildingList(CCollision::ms_collisionInMemory));
+ ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_NONE));
+ }
+ }
+}
+
+void
+CRenderer::RequestObjectsInFrustum(void)
+{
+ float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera);
+ RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera);
+ CVector vectors[9];
+ RwMatrix *cammatrix;
+ RwV2d poly[3];
+
+ memset(vectors, 0, sizeof(vectors));
+ vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f;
+ vectors[CORNER_FAR_TOPLEFT].y = vw.y * f;
+ vectors[CORNER_FAR_TOPLEFT].z = f;
+ vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f;
+ vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f;
+ vectors[CORNER_FAR_TOPRIGHT].z = f;
+ vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f;
+ vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f;
+ vectors[CORNER_FAR_BOTRIGHT].z = f;
+ vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f;
+ vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f;
+ vectors[CORNER_FAR_BOTLEFT].z = f;
+
+ cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera));
+
+ CWorld::AdvanceCurrentScanCode();
+
+ if(cammatrix->at.z > 0.0f){
+ // looking up, bottom corners are further away
+ vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f;
+ vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f;
+ }else{
+ // looking down, top corners are further away
+ vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f;
+ vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f;
+ }
+ vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f;
+ vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f;
+ vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z;
+ vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f;
+ vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f;
+ vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z;
+ RwV3dTransformPoints((RwV3d*)vectors, (RwV3d*)vectors, 9, cammatrix);
+
+ if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1 ||
+ TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWNPED){
+ CRect rect;
+ int x1, x2, y1, y2;
+ LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]);
+ LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]);
+ LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]);
+ LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f);
+ rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]);
+ x1 = CWorld::GetSectorIndexX(rect.left);
+ if(x1 < 0) x1 = 0;
+ x2 = CWorld::GetSectorIndexX(rect.right);
+ if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1;
+ y1 = CWorld::GetSectorIndexY(rect.bottom);
+ if(y1 < 0) y1 = 0;
+ y2 = CWorld::GetSectorIndexY(rect.top);
+ if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1;
+ for(; x1 <= x2; x1++)
+ for(int y = y1; y <= y2; y++)
+ ScanSectorList_RequestModels(CWorld::GetSector(x1, y)->m_lists);
+ }else{
+ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x);
+ poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y);
+ poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x);
+ poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y);
+ poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x);
+ poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y);
+ ScanSectorPoly(poly, 3, ScanSectorList_RequestModels);
+ }
+}
+
+float
+CalcNewDelta(RwV2d *a, RwV2d *b)
+{
+ return (b->x - a->x) / (b->y - a->y);
+}
+
+void
+CRenderer::ScanSectorPoly(RwV2d *poly, int32 numVertices, void (*scanfunc)(CPtrList *))
+{
+ float miny, maxy;
+ int y, yend;
+ int x, xstart, xend;
+ int i;
+ int a1, a2, b1, b2;
+ float deltaA, deltaB;
+ float xA, xB;
+
+ miny = poly[0].y;
+ maxy = poly[0].y;
+ a2 = 0;
+ xstart = 9999;
+ xend = -9999;
+
+ for(i = 1; i < numVertices; i++){
+ if(poly[i].y > maxy)
+ maxy = poly[i].y;
+ if(poly[i].y < miny){
+ miny = poly[i].y;
+ a2 = i;
+ }
+ }
+ y = miny;
+ yend = maxy;
+
+ // Go left in poly to find first edge b
+ b2 = a2;
+ for(i = 0; i < numVertices; i++){
+ b1 = b2--;
+ if(b2 < 0) b2 = numVertices-1;
+ if(poly[b1].x < xstart)
+ xstart = poly[b1].x;
+ if((int)poly[b1].y != (int)poly[b2].y)
+ break;
+ }
+ // Go right to find first edge a
+ for(i = 0; i < numVertices; i++){
+ a1 = a2++;
+ if(a2 == numVertices) a2 = 0;
+ if(poly[a1].x > xend)
+ xend = poly[a1].x;
+ if((int)poly[a1].y != (int)poly[a2].y)
+ break;
+ }
+
+ // prestep x1 and x2 to next integer y
+ deltaA = CalcNewDelta(&poly[a1], &poly[a2]);
+ xA = deltaA * (ceilf(poly[a1].y) - poly[a1].y) + poly[a1].x;
+ deltaB = CalcNewDelta(&poly[b1], &poly[b2]);
+ xB = deltaB * (ceilf(poly[b1].y) - poly[b1].y) + poly[b1].x;
+
+ if(y != yend){
+ if(deltaB < 0.0f && (int)xB < xstart)
+ xstart = xB;
+ if(deltaA >= 0.0f && (int)xA > xend)
+ xend = xA;
+ }
+
+ while(y <= yend && y < NUMSECTORS_Y){
+ // scan one x-line
+ if(y >= 0 && xstart < NUMSECTORS_X)
+ for(x = xstart; x <= xend; x++)
+ if(x >= 0 && x != NUMSECTORS_X)
+ scanfunc(CWorld::GetSector(x, y)->m_lists);
+
+ // advance one scan line
+ y++;
+ xA += deltaA;
+ xB += deltaB;
+
+ // update left side
+ if(y == (int)poly[b2].y){
+ // reached end of edge
+ if(y == yend){
+ if(deltaB < 0.0f){
+ do{
+ xstart = poly[b2--].x;
+ if(b2 < 0) b2 = numVertices-1;
+ }while(xstart > (int)poly[b2].x);
+ }else
+ xstart = xB - deltaB;
+ }else{
+ // switch edges
+ if(deltaB < 0.0f)
+ xstart = poly[b2].x;
+ else
+ xstart = xB - deltaB;
+ do{
+ b1 = b2--;
+ if(b2 < 0) b2 = numVertices-1;
+ if((int)poly[b1].x < xstart)
+ xstart = poly[b1].x;
+ }while(y == (int)poly[b2].y);
+ deltaB = CalcNewDelta(&poly[b1], &poly[b2]);
+ xB = deltaB * (ceilf(poly[b1].y) - poly[b1].y) + poly[b1].x;
+ if(deltaB < 0.0f && (int)xB < xstart)
+ xstart = xB;
+ }
+ }else{
+ if(deltaB < 0.0f)
+ xstart = xB;
+ else
+ xstart = xB - deltaB;
+ }
+
+ // update right side
+ if(y == (int)poly[a2].y){
+ // reached end of edge
+ if(y == yend){
+ if(deltaA < 0.0f)
+ xend = xA - deltaA;
+ else{
+ do{
+ xend = poly[a2++].x;
+ if(a2 == numVertices) a2 = 0;
+ }while(xend < (int)poly[a2].x);
+ }
+ }else{
+ // switch edges
+ if(deltaA < 0.0f)
+ xend = xA - deltaA;
+ else
+ xend = poly[a2].x;
+ do{
+ a1 = a2++;
+ if(a2 == numVertices) a2 = 0;
+ if((int)poly[a1].x > xend)
+ xend = poly[a1].x;
+ }while(y == (int)poly[a2].y);
+ deltaA = CalcNewDelta(&poly[a1], &poly[a2]);
+ xA = deltaA * (ceilf(poly[a1].y) - poly[a1].y) + poly[a1].x;
+ if(deltaA >= 0.0f && (int)xA > xend)
+ xend = xA;
+ }
+ }else{
+ if(deltaA < 0.0f)
+ xend = xA - deltaA;
+ else
+ xend = xA;
+ }
+ }
+}
+
+void
+CRenderer::ScanBigBuildingList(CPtrList &list)
+{
+ CPtrNode *node;
+ CEntity *ent;
+
+ for(node = list.first; node; node = node->next){
+ ent = (CEntity*)node->item;
+ if(!ent->m_bZoneCulled && SetupBigBuildingVisibility(ent) == 1)
+ ms_aVisibleEntityPtrs[ms_nNoOfVisibleEntities++] = ent;
+ }
+}
+
+void
+CRenderer::ScanSectorList(CPtrList *lists)
+{
+ CPtrNode *node;
+ CPtrList *list;
+ CEntity *ent;
+ int i;
+ float dx, dy;
+
+ for(i = 0; i < NUMSECTORENTITYLISTS; i++){
+ list = &lists[i];
+ for(node = list->first; node; node = node->next){
+ ent = (CEntity*)node->item;
+ if(ent->m_scanCode == CWorld::GetCurrentScanCode())
+ continue; // already seen
+ ent->m_scanCode = CWorld::GetCurrentScanCode();
+
+ if(IsEntityCullZoneVisible(ent))
+ switch(SetupEntityVisibility(ent)){
+ case VIS_VISIBLE:
+ ms_aVisibleEntityPtrs[ms_nNoOfVisibleEntities++] = ent;
+ break;
+ case VIS_INVISIBLE:
+ if(!IsGlass(ent->GetModelIndex()))
+ break;
+ // fall through
+ case VIS_OFFSCREEN:
+ dx = ms_vecCameraPosition.x - ent->GetPosition().x;
+ dy = ms_vecCameraPosition.y - ent->GetPosition().y;
+ if(dx > -65.0f && dx < 65.0f &&
+ dy > -65.0f && dy < 65.0f &&
+ ms_nNoOfInVisibleEntities < 150)
+ ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent;
+ break;
+ case VIS_STREAMME:
+ if(!CStreaming::ms_disableStreaming)
+ if(!m_loadingPriority || CStreaming::ms_numModelsRequested < 10)
+ CStreaming::RequestModel(ent->GetModelIndex(), 0);
+ break;
+ }
+ else if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){
+ if(!CStreaming::ms_disableStreaming)
+ if(SetupEntityVisibility(ent) == VIS_STREAMME)
+ if(!m_loadingPriority || CStreaming::ms_numModelsRequested < 10)
+ CStreaming::RequestModel(ent->GetModelIndex(), 0);
+ }
+ }
+ }
+}
+
+void
+CRenderer::ScanSectorList_Priority(CPtrList *lists)
+{
+ CPtrNode *node;
+ CPtrList *list;
+ CEntity *ent;
+ int i;
+ float dx, dy;
+
+ for(i = 0; i < NUMSECTORENTITYLISTS; i++){
+ list = &lists[i];
+ for(node = list->first; node; node = node->next){
+ ent = (CEntity*)node->item;
+ if(ent->m_scanCode == CWorld::GetCurrentScanCode())
+ continue; // already seen
+ ent->m_scanCode = CWorld::GetCurrentScanCode();
+
+ if(IsEntityCullZoneVisible(ent))
+ switch(SetupEntityVisibility(ent)){
+ case VIS_VISIBLE:
+ ms_aVisibleEntityPtrs[ms_nNoOfVisibleEntities++] = ent;
+ break;
+ case VIS_INVISIBLE:
+ if(!IsGlass(ent->GetModelIndex()))
+ break;
+ // fall through
+ case VIS_OFFSCREEN:
+ dx = ms_vecCameraPosition.x - ent->GetPosition().x;
+ dy = ms_vecCameraPosition.y - ent->GetPosition().y;
+ if(dx > -65.0f && dx < 65.0f &&
+ dy > -65.0f && dy < 65.0f &&
+ ms_nNoOfInVisibleEntities < 150)
+ ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent;
+ break;
+ case VIS_STREAMME:
+ if(!CStreaming::ms_disableStreaming){
+ CStreaming::RequestModel(ent->GetModelIndex(), 0);
+ if(CStreaming::ms_aInfoForModel[ent->GetModelIndex()].m_loadState != STREAM_LOADED)
+ m_loadingPriority = true;
+ }
+ break;
+ }
+ else if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){
+ if(!CStreaming::ms_disableStreaming)
+ if(SetupEntityVisibility(ent) == VIS_STREAMME)
+ CStreaming::RequestModel(ent->GetModelIndex(), 0);
+ }
+ }
+ }
+}
+
+void
+CRenderer::ScanSectorList_Subway(CPtrList *lists)
+{
+ CPtrNode *node;
+ CPtrList *list;
+ CEntity *ent;
+ int i;
+ float dx, dy;
+
+ for(i = 0; i < NUMSECTORENTITYLISTS; i++){
+ list = &lists[i];
+ for(node = list->first; node; node = node->next){
+ ent = (CEntity*)node->item;
+ if(ent->m_scanCode == CWorld::GetCurrentScanCode())
+ continue; // already seen
+ ent->m_scanCode = CWorld::GetCurrentScanCode();
+ switch(SetupEntityVisibility(ent)){
+ case VIS_VISIBLE:
+ ms_aVisibleEntityPtrs[ms_nNoOfVisibleEntities++] = ent;
+ break;
+ case VIS_OFFSCREEN:
+ dx = ms_vecCameraPosition.x - ent->GetPosition().x;
+ dy = ms_vecCameraPosition.y - ent->GetPosition().y;
+ if(dx > -65.0f && dx < 65.0f &&
+ dy > -65.0f && dy < 65.0f &&
+ ms_nNoOfInVisibleEntities < 150)
+ ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent;
+ break;
+ }
+ }
+ }
+}
+
+void
+CRenderer::ScanSectorList_RequestModels(CPtrList *lists)
+{
+ CPtrNode *node;
+ CPtrList *list;
+ CEntity *ent;
+ int i;
+
+ for(i = 0; i < NUMSECTORENTITYLISTS; i++){
+ list = &lists[i];
+ for(node = list->first; node; node = node->next){
+ ent = (CEntity*)node->item;
+ if(ent->m_scanCode == CWorld::GetCurrentScanCode())
+ continue; // already seen
+ ent->m_scanCode = CWorld::GetCurrentScanCode();
+ if(IsEntityCullZoneVisible(ent) && ShouldModelBeStreamed(ent))
+ CStreaming::RequestModel(ent->GetModelIndex(), 0);
+ }
+ }
+}
+
+// Put big buildings in front
+// This seems pointless because the sector lists shouldn't have big buildings in the first place
+void
+CRenderer::SortBIGBuildings(void)
+{
+ int x, y;
+ for(y = 0; y < NUMSECTORS_Y; y++)
+ for(x = 0; x < NUMSECTORS_X; x++){
+ SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS]);
+ SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]);
+ }
+}
+
+void
+CRenderer::SortBIGBuildingsForSectorList(CPtrList *list)
+{
+ CPtrNode *node;
+ CEntity *ent;
+
+ for(node = list->first; node; node = node->next){
+ ent = (CEntity*)node->item;
+ if(ent->bIsBIGBuilding){
+ list->RemoveNode(node);
+ list->InsertNode(node);
+ }
+ }
+}
+
+bool
+CRenderer::ShouldModelBeStreamed(CEntity *ent)
+{
+ CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex);
+ float dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude();
+ if(mi->m_noFade)
+ return dist - STREAM_DISTANCE < mi->GetLargestLodDistance();
+ else
+ return dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance();
+}
+
+bool
+CRenderer::IsEntityCullZoneVisible(CEntity *ent)
+{
+ CPed *ped;
+ CObject *obj;
+
+ if(ent->m_bZoneCulled)
+ return false;
+
+ switch(ent->m_type){
+ case ENTITY_TYPE_VEHICLE:
+ return IsVehicleCullZoneVisible(ent);
+ case ENTITY_TYPE_PED:
+ ped = (CPed*)ent;
+ if(ped->bInVehicle)
+ return ped->m_pMyVehicle && IsVehicleCullZoneVisible(ped->m_pMyVehicle);
+ return !(ped->m_pCurSurface && ped->m_pCurSurface->m_bZoneCulled2);
+ case ENTITY_TYPE_OBJECT:
+ obj = (CObject*)ent;
+ if(!obj->bIsStatic)
+ return true;
+ return !(obj->m_pCurSurface && obj->m_pCurSurface->m_bZoneCulled2);
+ }
+ return true;
+}
+
+bool
+CRenderer::IsVehicleCullZoneVisible(CEntity *ent)
+{
+ CVehicle *v = (CVehicle*)ent;
+ switch(v->m_status)
+ case STATUS_SIMPLE:
+ case STATUS_PHYSICS:
+ case STATUS_ABANDONED:
+ case STATUS_WRECKED:
+ return !(v->m_pCurSurface && v->m_pCurSurface->m_bZoneCulled2);
+ return true;
+}
+
+STARTPATCHES
+ InjectHook(0x4A7680, CRenderer::Init, PATCH_JUMP);
+
+ InjectHook(0x4A7B90, CRenderer::RenderOneRoad, PATCH_JUMP);
+ InjectHook(0x4A7BA0, CRenderer::RenderOneNonRoad, PATCH_JUMP);
+ InjectHook(0x4A7B20, CRenderer::RenderFirstPersonVehicle, PATCH_JUMP);
+ InjectHook(0x4A78B0, CRenderer::RenderRoads, PATCH_JUMP);
+ InjectHook(0x4A7930, CRenderer::RenderEverythingBarRoads, PATCH_JUMP);
+ InjectHook(0x4A7AA0, CRenderer::RenderVehiclesButNotBoats, PATCH_JUMP);
+ InjectHook(0x4A7AE0, CRenderer::RenderBoats, PATCH_JUMP);
+ InjectHook(0x4A7910, CRenderer::RenderFadingInEntities, PATCH_JUMP);
+
+ InjectHook(0x4A9350, CRenderer::SetupEntityVisibility, PATCH_JUMP);
+ InjectHook(0x4A9920, CRenderer::SetupBigBuildingVisibility, PATCH_JUMP);
+
+ InjectHook(0x4A76B0, CRenderer::ConstructRenderList, PATCH_JUMP);
+ InjectHook(0x4A8970, CRenderer::ScanWorld, PATCH_JUMP);
+ InjectHook(0x4AA240, CRenderer::RequestObjectsInFrustum, PATCH_JUMP);
+ InjectHook(0x4A7F30, CRenderer::ScanSectorPoly, PATCH_JUMP);
+ InjectHook(0x4A9300, CRenderer::ScanBigBuildingList, PATCH_JUMP);
+ InjectHook(0x4A9BB0, CRenderer::ScanSectorList, PATCH_JUMP);
+ InjectHook(0x4A9E30, CRenderer::ScanSectorList_Priority, PATCH_JUMP);
+ InjectHook(0x4AA0A0, CRenderer::ScanSectorList_Subway, PATCH_JUMP);
+ InjectHook(0x4AA1D0, CRenderer::ScanSectorList_RequestModels, PATCH_JUMP);
+
+ InjectHook(0x4AA940, CRenderer::SortBIGBuildings, PATCH_JUMP);
+ InjectHook(0x4AA990, CRenderer::SortBIGBuildingsForSectorList, PATCH_JUMP);
+
+ InjectHook(0x4A9840, CRenderer::ShouldModelBeStreamed, PATCH_JUMP);
+ InjectHook(0x4AAA00, CRenderer::IsEntityCullZoneVisible, PATCH_JUMP);
+ InjectHook(0x4AAAA0, CRenderer::IsVehicleCullZoneVisible, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/Renderer.h b/src/render/Renderer.h
new file mode 100644
index 00000000..970d6ba5
--- /dev/null
+++ b/src/render/Renderer.h
@@ -0,0 +1,59 @@
+#pragma once
+
+class CEntity;
+
+extern bool gbShowPedRoadGroups;
+extern bool gbShowCarRoadGroups;
+extern bool gbShowCollisionPolys;
+
+extern bool gbDontRenderBuildings;
+extern bool gbDontRenderBigBuildings;
+extern bool gbDontRenderPeds;
+extern bool gbDontRenderObjects;
+
+class CVehicle;
+class CPtrList;
+
+class CRenderer
+{
+ static int32 &ms_nNoOfVisibleEntities;
+ static CEntity **ms_aVisibleEntityPtrs; // [2000];
+ static int32 &ms_nNoOfInVisibleEntities;
+ static CEntity **ms_aInVisibleEntityPtrs; // [150];
+
+ static CVector &ms_vecCameraPosition;
+ static CVehicle *&m_pFirstPersonVehicle;
+ static bool &m_loadingPriority;
+public:
+ static void Init(void);
+ // TODO: PreRender, needs CHeli and CShadows
+
+ static void RenderRoads(void);
+ static void RenderFadingInEntities(void);
+ static void RenderEverythingBarRoads(void);
+ static void RenderVehiclesButNotBoats(void);
+ static void RenderBoats(void);
+ static void RenderOneRoad(CEntity *);
+ static void RenderOneNonRoad(CEntity *);
+ static void RenderFirstPersonVehicle(void);
+
+ static int32 SetupEntityVisibility(CEntity *ent);
+ static int32 SetupBigBuildingVisibility(CEntity *ent);
+
+ static void ConstructRenderList(void);
+ static void ScanWorld(void);
+ static void RequestObjectsInFrustum(void);
+ static void ScanSectorPoly(RwV2d *poly, int32 numVertices, void (*scanfunc)(CPtrList *));
+ static void ScanBigBuildingList(CPtrList &list);
+ static void ScanSectorList(CPtrList *lists);
+ static void ScanSectorList_Priority(CPtrList *lists);
+ static void ScanSectorList_Subway(CPtrList *lists);
+ static void ScanSectorList_RequestModels(CPtrList *lists);
+
+ static void SortBIGBuildings(void);
+ static void SortBIGBuildingsForSectorList(CPtrList *list);
+
+ static bool ShouldModelBeStreamed(CEntity *ent);
+ static bool IsEntityCullZoneVisible(CEntity *ent);
+ static bool IsVehicleCullZoneVisible(CEntity *ent);
+};
diff --git a/src/render/Sprite.cpp b/src/render/Sprite.cpp
new file mode 100644
index 00000000..74eefccf
--- /dev/null
+++ b/src/render/Sprite.cpp
@@ -0,0 +1,553 @@
+#include "common.h"
+#include "patcher.h"
+#include "Draw.h"
+#include "Camera.h"
+#include "Sprite.h"
+
+// Get rid of bullshit windows definitions, we're not running on an 8086
+#ifdef far
+#undef far
+#undef near
+#endif
+
+RwIm2DVertex *CSprite2d::maVertices = (RwIm2DVertex*)0x6E9168;
+float &CSprite2d::RecipNearClip = *(float*)0x880DB4;
+
+// Arguments:
+// 2---3
+// | |
+// 0---1
+void
+CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, uint32 far)
+{
+ float screenz, z, recipz;
+
+ if(far){
+ screenz = RwIm2DGetFarScreenZ();
+ z = RwCameraGetFarClipPlane(Scene.camera);
+ }else{
+ screenz = RwIm2DGetNearScreenZ();
+ z = 1.0f/RecipNearClip;
+ }
+ recipz = 1.0f/z;
+
+ // This is what we draw:
+ // 0---1
+ // | / |
+ // 3---2
+ RwIm2DVertexSetScreenX(&maVertices[0], r.left);
+ RwIm2DVertexSetScreenY(&maVertices[0], r.bottom);
+ RwIm2DVertexSetScreenZ(&maVertices[0], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[0], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[0], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a);
+ RwIm2DVertexSetU(&maVertices[0], 0.0f, recipz);
+ RwIm2DVertexSetV(&maVertices[0], 0.0f, recipz);
+
+ RwIm2DVertexSetScreenX(&maVertices[1], r.right);
+ RwIm2DVertexSetScreenY(&maVertices[1], r.bottom);
+ RwIm2DVertexSetScreenZ(&maVertices[1], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[1], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[1], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a);
+ RwIm2DVertexSetU(&maVertices[1], 1.0f, recipz);
+ RwIm2DVertexSetV(&maVertices[1], 0.0f, recipz);
+
+ RwIm2DVertexSetScreenX(&maVertices[2], r.right);
+ RwIm2DVertexSetScreenY(&maVertices[2], r.top);
+ RwIm2DVertexSetScreenZ(&maVertices[2], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[2], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[2], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a);
+ RwIm2DVertexSetU(&maVertices[2], 1.0f, recipz);
+ RwIm2DVertexSetV(&maVertices[2], 1.0f, recipz);
+
+ RwIm2DVertexSetScreenX(&maVertices[3], r.left);
+ RwIm2DVertexSetScreenY(&maVertices[3], r.top);
+ RwIm2DVertexSetScreenZ(&maVertices[3], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[3], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[3], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a);
+ RwIm2DVertexSetU(&maVertices[3], 0.0f, recipz);
+ RwIm2DVertexSetV(&maVertices[3], 1.0f, recipz);
+}
+
+void
+CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3,
+ float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2)
+{
+ float screenz, z, recipz;
+
+ screenz = RwIm2DGetNearScreenZ();
+ z = 1.0f/RecipNearClip;
+ recipz = 1.0f/z;
+
+ // This is what we draw:
+ // 0---1
+ // | / |
+ // 3---2
+ RwIm2DVertexSetScreenX(&maVertices[0], r.left);
+ RwIm2DVertexSetScreenY(&maVertices[0], r.bottom);
+ RwIm2DVertexSetScreenZ(&maVertices[0], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[0], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[0], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a);
+ RwIm2DVertexSetU(&maVertices[0], u0, recipz);
+ RwIm2DVertexSetV(&maVertices[0], v0, recipz);
+
+ RwIm2DVertexSetScreenX(&maVertices[1], r.right);
+ RwIm2DVertexSetScreenY(&maVertices[1], r.bottom);
+ RwIm2DVertexSetScreenZ(&maVertices[1], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[1], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[1], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a);
+ RwIm2DVertexSetU(&maVertices[1], u1, recipz);
+ RwIm2DVertexSetV(&maVertices[1], v1, recipz);
+
+ RwIm2DVertexSetScreenX(&maVertices[2], r.right);
+ RwIm2DVertexSetScreenY(&maVertices[2], r.top);
+ RwIm2DVertexSetScreenZ(&maVertices[2], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[2], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[2], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a);
+ RwIm2DVertexSetU(&maVertices[2], u2, recipz);
+ RwIm2DVertexSetV(&maVertices[2], v2, recipz);
+
+ RwIm2DVertexSetScreenX(&maVertices[3], r.left);
+ RwIm2DVertexSetScreenY(&maVertices[3], r.top);
+ RwIm2DVertexSetScreenZ(&maVertices[3], screenz);
+ RwIm2DVertexSetCameraZ(&maVertices[3], z);
+ RwIm2DVertexSetRecipCameraZ(&maVertices[3], recipz);
+ RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a);
+ RwIm2DVertexSetU(&maVertices[3], u3, recipz);
+ RwIm2DVertexSetV(&maVertices[3], v3, recipz);
+}
+
+void
+CSprite2d::SetRenderState(void)
+{
+ if(m_pTexture)
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(m_pTexture));
+ else
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+}
+
+void
+CSprite2d::DrawRect(const CRect &r, const CRGBA &col)
+{
+ SetVertices(r, col, col, col, col, false);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(col.a != 255));
+ RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD);
+}
+
+void
+CSprite2d::DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
+{
+ SetVertices(r, c0, c1, c2, c3, false);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+}
+
+void
+CSprite2d::DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3)
+{
+ SetVertices(r, c0, c1, c2, c3, false);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+}
+
+
+
+float &CSprite::m_f2DNearScreenZ = *(float*)0x8F1ABC;
+float &CSprite::m_f2DFarScreenZ = *(float*)0x8F2C94;
+int32 &CSprite::m_bFlushSpriteBufferSwitchZTest = *(int32*)0x8F5FB0;
+
+float
+CSprite::CalcHorizonCoors(void)
+{
+ CVector p = TheCamera.GetPosition() + CVector(TheCamera.CamFrontXNorm, TheCamera.CamFrontYNorm, 0.0f)*3000.0f;
+ p.z = 0.0f;
+ p = TheCamera.m_viewMatrix * p;
+ return p.y * RsGlobal.maximumHeight / p.z;
+}
+
+bool
+CSprite::CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip)
+{
+ CVector viewvec = TheCamera.m_viewMatrix * *(CVector*)&in;
+ *out = *(RwV3d*)&viewvec;
+ if(out->z <= CDraw::GetNearClipZ() + 1.0f) return false;
+ if(out->z >= CDraw::GetFarClipZ() && farclip) return false;
+ float recip = 1.0f/out->z;
+ out->x *= RsGlobal.maximumWidth * recip;
+ out->y *= RsGlobal.maximumHeight * recip;
+ // What is this? size?
+ *outw = 70.0f/CDraw::GetFOV();
+ *outh = 70.0f/CDraw::GetFOV();
+ *outw *= RsGlobal.maximumWidth * recip;
+ *outh *= RsGlobal.maximumHeight * recip;
+ return true;
+}
+
+#define SPRITEBUFFERSIZE 64
+static int32 &nSpriteBufferIndex = *(int32*)0x649A80;
+static RwIm2DVertex *SpriteBufferVerts = (RwIm2DVertex*)0x649A84; //[SPRITEBUFFERSIZE*6];
+static RwIm2DVertex *verts = (RwIm2DVertex*)0x64C484; //[4];
+
+void
+CSprite::InitSpriteBuffer(void)
+{
+ m_f2DNearScreenZ = RwIm2DGetNearScreenZ();
+ m_f2DFarScreenZ = RwIm2DGetFarScreenZ();
+}
+
+void
+CSprite::FlushSpriteBuffer(void)
+{
+ if(nSpriteBufferIndex > 0){
+ if(m_bFlushSpriteBufferSwitchZTest){
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ }else
+ RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6);
+ nSpriteBufferIndex = 0;
+ }
+}
+
+void
+CSprite::RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a)
+{
+ static short indices[] = { 0, 1, 2, 3 };
+ // 0---3
+ // | |
+ // 1---2
+ float xs[4];
+ float ys[4];
+ float us[4];
+ float vs[4];
+ int i;
+
+ xs[0] = x-w; us[0] = 0.0f;
+ xs[1] = x-w; us[1] = 0.0f;
+ xs[2] = x+w; us[2] = 1.0f;
+ xs[3] = x+w; us[3] = 1.0f;
+
+ ys[0] = y-h; vs[0] = 0.0f;
+ ys[1] = y+h; vs[1] = 1.0f;
+ ys[2] = y+h; vs[2] = 1.0f;
+ ys[3] = y-h; vs[3] = 0.0f;
+
+ // clip
+ for(i = 0; i < 4; i++){
+ if(xs[i] < 0.0f){
+ us[i] = -xs[i] / (2.0f*w);
+ xs[i] = 0.0f;
+ }
+ if(xs[i] > RsGlobal.maximumWidth){
+ us[i] = 1.0f - (xs[i]-RsGlobal.maximumWidth) / (2.0f*w);
+ xs[i] = RsGlobal.maximumWidth;
+ }
+ if(ys[i] < 0.0f){
+ vs[i] = -ys[i] / (2.0f*h);
+ ys[i] = 0.0f;
+ }
+ if(ys[i] > RsGlobal.maximumHeight){
+ vs[i] = 1.0f - (ys[i]-RsGlobal.maximumHeight) / (2.0f*h);
+ ys[i] = RsGlobal.maximumHeight;
+ }
+ }
+
+ // (DrawZ - DrawNear)/(DrawFar - DrawNear) = (SpriteZ-SpriteNear)/(SpriteFar-SpriteNear)
+ // So to calculate SpriteZ:
+ float screenz = m_f2DNearScreenZ +
+ (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
+ ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);
+
+ for(i = 0; i < 4; i++){
+ RwIm2DVertexSetScreenX(&verts[i], xs[i]);
+ RwIm2DVertexSetScreenY(&verts[i], ys[i]);
+ RwIm2DVertexSetScreenZ(&verts[i], screenz);
+ RwIm2DVertexSetCameraZ(&verts[i], z);
+ RwIm2DVertexSetRecipCameraZ(&verts[i], recipz);
+ RwIm2DVertexSetIntRGBA(&verts[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
+ RwIm2DVertexSetU(&verts[i], us[i], recipz);
+ RwIm2DVertexSetV(&verts[i], vs[i], recipz);
+ }
+ RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, verts, 4);
+}
+
+void
+CSprite::RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a)
+{
+ m_bFlushSpriteBufferSwitchZTest = 0;
+
+ // 0---3
+ // | |
+ // 1---2
+ float xs[4];
+ float ys[4];
+ float us[4];
+ float vs[4];
+ int i;
+
+ xs[0] = x-w; us[0] = 0.0f;
+ xs[1] = x-w; us[1] = 0.0f;
+ xs[2] = x+w; us[2] = 1.0f;
+ xs[3] = x+w; us[3] = 1.0f;
+
+ ys[0] = y-h; vs[0] = 0.0f;
+ ys[1] = y+h; vs[1] = 1.0f;
+ ys[2] = y+h; vs[2] = 1.0f;
+ ys[3] = y-h; vs[3] = 0.0f;
+
+ // clip
+ for(i = 0; i < 4; i++){
+ if(xs[i] < 0.0f){
+ us[i] = -xs[i] / (2.0f*w);
+ xs[i] = 0.0f;
+ }
+ if(xs[i] > RsGlobal.maximumWidth){
+ us[i] = 1.0f - (xs[i]-RsGlobal.maximumWidth) / (2.0f*w);
+ xs[i] = RsGlobal.maximumWidth;
+ }
+ if(ys[i] < 0.0f){
+ vs[i] = -ys[i] / (2.0f*h);
+ ys[i] = 0.0f;
+ }
+ if(ys[i] > RsGlobal.maximumHeight){
+ vs[i] = 1.0f - (ys[i]-RsGlobal.maximumHeight) / (2.0f*h);
+ ys[i] = RsGlobal.maximumHeight;
+ }
+ }
+
+ float screenz = m_f2DNearScreenZ +
+ (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
+ ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);
+
+ RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
+ static int indices[6] = { 0, 1, 2, 3, 0, 2 };
+ for(i = 0; i < 6; i++){
+ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
+ RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
+ RwIm2DVertexSetScreenZ(&vert[i], screenz);
+ RwIm2DVertexSetCameraZ(&vert[i], z);
+ RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
+ RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
+ RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
+ RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
+ }
+ nSpriteBufferIndex++;
+ if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
+ FlushSpriteBuffer();
+}
+
+void
+CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a)
+{
+ m_bFlushSpriteBufferSwitchZTest = 0;
+ // TODO: replace with lookup
+ float c = cos(DEGTORAD(rotation));
+ float s = sin(DEGTORAD(rotation));
+
+ float xs[4];
+ float ys[4];
+ float us[4];
+ float vs[4];
+ int i;
+
+ xs[0] = x - c*w - s*h; us[0] = 0.0f;
+ xs[1] = x - c*w + s*h; us[1] = 0.0f;
+ xs[2] = x + c*w + s*h; us[2] = 1.0f;
+ xs[3] = x + c*w - s*h; us[3] = 1.0f;
+
+ ys[0] = y - c*h + s*w; vs[0] = 0.0f;
+ ys[1] = y + c*h + s*w; vs[1] = 1.0f;
+ ys[2] = y + c*h - s*w; vs[2] = 1.0f;
+ ys[3] = y - c*h - s*w; vs[3] = 0.0f;
+
+ // No clipping, just culling
+ if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
+ if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
+ if(xs[0] > RsGlobal.maximumWidth && xs[1] > RsGlobal.maximumWidth &&
+ xs[2] > RsGlobal.maximumWidth && xs[3] > RsGlobal.maximumWidth) return;
+ if(ys[0] > RsGlobal.maximumHeight && ys[1] > RsGlobal.maximumHeight &&
+ ys[2] > RsGlobal.maximumHeight && ys[3] > RsGlobal.maximumHeight) return;
+
+ float screenz = m_f2DNearScreenZ +
+ (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
+ ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);
+
+ RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
+ static int indices[6] = { 0, 1, 2, 3, 0, 2 };
+ for(i = 0; i < 6; i++){
+ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
+ RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
+ RwIm2DVertexSetScreenZ(&vert[i], screenz);
+ RwIm2DVertexSetCameraZ(&vert[i], z);
+ RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
+ RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
+ RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
+ RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
+ }
+ nSpriteBufferIndex++;
+ if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
+ FlushSpriteBuffer();
+}
+
+void
+CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a)
+{
+ m_bFlushSpriteBufferSwitchZTest = 0;
+ float c = cos(DEGTORAD(rotation));
+ float s = sin(DEGTORAD(rotation));
+
+ float xs[4];
+ float ys[4];
+ float us[4];
+ float vs[4];
+ int i;
+
+ xs[0] = x + w*(-c-s); us[0] = 0.0f;
+ xs[1] = x + w*(-c+s); us[1] = 0.0f;
+ xs[2] = x + w*(+c+s); us[2] = 1.0f;
+ xs[3] = x + w*(+c-s); us[3] = 1.0f;
+
+ ys[0] = y + h*(-c+s); vs[0] = 0.0f;
+ ys[1] = y + h*(+c+s); vs[1] = 1.0f;
+ ys[2] = y + h*(+c-s); vs[2] = 1.0f;
+ ys[3] = y + h*(-c-s); vs[3] = 0.0f;
+
+ // No clipping, just culling
+ if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
+ if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
+ if(xs[0] > RsGlobal.maximumWidth && xs[1] > RsGlobal.maximumWidth &&
+ xs[2] > RsGlobal.maximumWidth && xs[3] > RsGlobal.maximumWidth) return;
+ if(ys[0] > RsGlobal.maximumHeight && ys[1] > RsGlobal.maximumHeight &&
+ ys[2] > RsGlobal.maximumHeight && ys[3] > RsGlobal.maximumHeight) return;
+
+ float screenz = m_f2DNearScreenZ +
+ (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
+ ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);
+
+ RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
+ static int indices[6] = { 0, 1, 2, 3, 0, 2 };
+ for(i = 0; i < 6; i++){
+ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
+ RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
+ RwIm2DVertexSetScreenZ(&vert[i], screenz);
+ RwIm2DVertexSetCameraZ(&vert[i], z);
+ RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
+ RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a);
+ RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
+ RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
+ }
+ nSpriteBufferIndex++;
+ if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
+ FlushSpriteBuffer();
+}
+
+void
+CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a)
+{
+ m_bFlushSpriteBufferSwitchZTest = 0;
+ float c = cos(DEGTORAD(rotation));
+ float s = sin(DEGTORAD(rotation));
+
+ float xs[4];
+ float ys[4];
+ float us[4];
+ float vs[4];
+ float cf[4];
+ int i;
+
+ xs[0] = x + w*(-c-s); us[0] = 0.0f;
+ xs[1] = x + w*(-c+s); us[1] = 0.0f;
+ xs[2] = x + w*(+c+s); us[2] = 1.0f;
+ xs[3] = x + w*(+c-s); us[3] = 1.0f;
+
+ ys[0] = y + h*(-c+s); vs[0] = 0.0f;
+ ys[1] = y + h*(+c+s); vs[1] = 1.0f;
+ ys[2] = y + h*(+c-s); vs[2] = 1.0f;
+ ys[3] = y + h*(-c-s); vs[3] = 0.0f;
+
+ // No clipping, just culling
+ if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
+ if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
+ if(xs[0] > RsGlobal.maximumWidth && xs[1] > RsGlobal.maximumWidth &&
+ xs[2] > RsGlobal.maximumWidth && xs[3] > RsGlobal.maximumWidth) return;
+ if(ys[0] > RsGlobal.maximumHeight && ys[1] > RsGlobal.maximumHeight &&
+ ys[2] > RsGlobal.maximumHeight && ys[3] > RsGlobal.maximumHeight) return;
+
+ // Colour factors, cx/y is the direction in which colours change from rgb1 to rgb2
+ cf[0] = (cx*(-c-s) + cy*(-c+s))*0.5f + 0.5f;
+ cf[0] = clamp(cf[0], 0.0f, 1.0f);
+ cf[1] = (cx*(-c-s) + cy*(-c+s))*0.5f + 0.5f;
+ cf[1] = clamp(cf[1], 0.0f, 1.0f);
+ cf[2] = (cx*(-c-s) + cy*(-c+s))*0.5f + 0.5f;
+ cf[2] = clamp(cf[2], 0.0f, 1.0f);
+ cf[3] = (cx*(-c-s) + cy*(-c+s))*0.5f + 0.5f;
+ cf[3] = clamp(cf[3], 0.0f, 1.0f);
+
+ float screenz = m_f2DNearScreenZ +
+ (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() /
+ ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z);
+
+ RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6];
+ static int indices[6] = { 0, 1, 2, 3, 0, 2 };
+ for(i = 0; i < 6; i++){
+ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]);
+ RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]);
+ RwIm2DVertexSetScreenZ(&vert[i], screenz);
+ RwIm2DVertexSetCameraZ(&vert[i], z);
+ RwIm2DVertexSetRecipCameraZ(&vert[i], recipz);
+ RwIm2DVertexSetIntRGBA(&vert[i],
+ r1*cf[indices[i]] + r2*(1.0f - cf[indices[i]]),
+ g1*cf[indices[i]] + g2*(1.0f - cf[indices[i]]),
+ b1*cf[indices[i]] + b2*(1.0f - cf[indices[i]]),
+ a);
+ RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz);
+ RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz);
+ }
+ nSpriteBufferIndex++;
+ if(nSpriteBufferIndex >= SPRITEBUFFERSIZE)
+ FlushSpriteBuffer();
+}
+
+STARTPATCHES
+ InjectHook(0x51EE90, (void (*)(const CRect&, const CRGBA&, const CRGBA&, const CRGBA&, const CRGBA&, uint32))CSprite2d::SetVertices, PATCH_JUMP);
+ InjectHook(0x51F220, (void (*)(const CRect&, const CRGBA&, const CRGBA&, const CRGBA&, const CRGBA&,
+ float, float, float, float, float, float, float, float))CSprite2d::SetVertices, PATCH_JUMP);
+ InjectHook(0x51F970, (void (*)(const CRect&, const CRGBA&))CSprite2d::DrawRect, PATCH_JUMP);
+ InjectHook(0x51FA00, (void (*)(const CRect&, const CRGBA&, const CRGBA&, const CRGBA&, const CRGBA&))CSprite2d::DrawRect, PATCH_JUMP);
+ InjectHook(0x51FA80, CSprite2d::DrawRectXLU, PATCH_JUMP);
+
+ InjectHook(0x51C4A0, CSprite::CalcHorizonCoors, PATCH_JUMP);
+ InjectHook(0x51C3A0, CSprite::CalcScreenCoors, PATCH_JUMP);
+ InjectHook(0x51C590, CSprite::InitSpriteBuffer, PATCH_JUMP);
+ InjectHook(0x51C520, CSprite::FlushSpriteBuffer, PATCH_JUMP);
+ InjectHook(0x51C960, CSprite::RenderOneXLUSprite, PATCH_JUMP);
+ InjectHook(0x51C5D0, CSprite::RenderBufferedOneXLUSprite, PATCH_JUMP);
+ InjectHook(0x51D5B0, CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension, PATCH_JUMP);
+ InjectHook(0x51CCD0, CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect, PATCH_JUMP);
+ InjectHook(0x51D9E0, CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/Sprite.h b/src/render/Sprite.h
new file mode 100644
index 00000000..53d36de3
--- /dev/null
+++ b/src/render/Sprite.h
@@ -0,0 +1,37 @@
+#pragma once
+
+class CSprite2d
+{
+ RwTexture *m_pTexture;
+
+ static RwIm2DVertex *maVertices; //[4];
+public:
+ static float &RecipNearClip;
+
+ void SetRenderState(void);
+
+ static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, uint32 far);
+ static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3,
+ float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2);
+ static void DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);
+ static void DrawRect(const CRect &r, const CRGBA &col);
+ static void DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3);
+};
+
+class CSprite
+{
+ static float &m_f2DNearScreenZ;
+ static float &m_f2DFarScreenZ;
+ static int32 &m_bFlushSpriteBufferSwitchZTest;
+public:
+ static float CalcHorizonCoors(void);
+ static bool CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip);
+ static void InitSpriteBuffer(void);
+ static void FlushSpriteBuffer(void);
+ static void RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a);
+ static void RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a);
+ static void RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
+ static void RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
+ // cx/y is the direction in which the colour changes
+ static void RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a);
+};
diff --git a/src/render/VisibilityPlugins.cpp b/src/render/VisibilityPlugins.cpp
new file mode 100644
index 00000000..d9e87553
--- /dev/null
+++ b/src/render/VisibilityPlugins.cpp
@@ -0,0 +1,849 @@
+#include "common.h"
+#include "patcher.h"
+#include "templates.h"
+#include "Entity.h"
+#include "ModelInfo.h"
+#include "Lights.h"
+#include "Renderer.h"
+#include "VisibilityPlugins.h"
+
+#define FADE_DISTANCE 20.0f
+
+/*
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaList;
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaEntityList;
+
+int32 CVisibilityPlugins::ms_atomicPluginOffset = -1;
+int32 CVisibilityPlugins::ms_framePluginOffset = -1;
+int32 CVisibilityPlugins::ms_clumpPluginOffset = -1;
+*/
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> &CVisibilityPlugins::m_alphaList = *(CLinkList<CVisibilityPlugins::AlphaObjectInfo>*)0x8F42E4;
+CLinkList<CVisibilityPlugins::AlphaObjectInfo> &CVisibilityPlugins::m_alphaEntityList = *(CLinkList<CVisibilityPlugins::AlphaObjectInfo>*)0x943084;
+
+int32 &CVisibilityPlugins::ms_atomicPluginOffset = *(int32*)0x600124;
+int32 &CVisibilityPlugins::ms_framePluginOffset = *(int32*)0x600128;
+int32 &CVisibilityPlugins::ms_clumpPluginOffset = *(int32*)0x60012C;
+
+RwV3d *&CVisibilityPlugins::ms_pCameraPosn = *(RwV3d**)0x8F6270;
+float &CVisibilityPlugins::ms_cullCompsDist = *(float*)0x8F2BC4;
+float &CVisibilityPlugins::ms_vehicleLod0Dist = *(float*)0x885B28;
+float &CVisibilityPlugins::ms_vehicleLod1Dist = *(float*)0x885B30;
+float &CVisibilityPlugins::ms_vehicleFadeDist = *(float*)0x8E28B4;
+float &CVisibilityPlugins::ms_bigVehicleLod0Dist = *(float*)0x8E2A84;
+float &CVisibilityPlugins::ms_bigVehicleLod1Dist = *(float*)0x8E2A8C;
+float &CVisibilityPlugins::ms_pedLod0Dist = *(float*)0x8F2BD4;
+float &CVisibilityPlugins::ms_pedLod1Dist = *(float*)0x8F2BD8;
+float &CVisibilityPlugins::ms_pedFadeDist = *(float*)0x8E2C34;
+
+void
+CVisibilityPlugins::Initialise(void)
+{
+ m_alphaList.Init(20);
+ m_alphaList.head.item.sort = 0.0f;
+ m_alphaList.tail.item.sort = 100000000.0f;
+ m_alphaEntityList.Init(350); // TODO: set back to 150 when things are fixed
+ m_alphaEntityList.head.item.sort = 0.0f;
+ m_alphaEntityList.tail.item.sort = 100000000.0f;
+}
+
+void
+CVisibilityPlugins::InitAlphaEntityList(void)
+{
+ m_alphaEntityList.Clear();
+}
+
+bool
+CVisibilityPlugins::InsertEntityIntoSortedList(CEntity *e, float dist)
+{
+ AlphaObjectInfo item;
+ item.entity = e;
+ item.sort = dist;
+ bool ret = !!m_alphaEntityList.InsertSorted(item);
+// if(!ret)
+// printf("list full %d\n", m_alphaEntityList.Count());
+ return ret;
+}
+
+void
+CVisibilityPlugins::InitAlphaAtomicList(void)
+{
+ m_alphaList.Clear();
+}
+
+bool
+CVisibilityPlugins::InsertAtomicIntoSortedList(RpAtomic *a, float dist)
+{
+ AlphaObjectInfo item;
+ item.atomic = a;
+ item.sort = dist;
+ bool ret = !!m_alphaList.InsertSorted(item);
+// if(!ret)
+// printf("list full %d\n", m_alphaList.Count());
+ return ret;
+}
+
+RpMaterial*
+SetAlphaCB(RpMaterial *material, void *data)
+{
+ material->color.alpha = (uint8)(uint32)data;
+ return material;
+}
+
+RpMaterial*
+SetTextureCB(RpMaterial *material, void *data)
+{
+ RpMaterialSetTexture(material, (RwTexture*)data);
+ return material;
+}
+
+void
+CVisibilityPlugins::RenderAlphaAtomics(void)
+{
+ CLink<AlphaObjectInfo> *node;
+ for(node = m_alphaList.tail.prev;
+ node != &m_alphaList.head;
+ node = node->prev)
+ AtomicDefaultRenderCallBack(node->item.atomic);
+}
+
+void
+CVisibilityPlugins::RenderFadingEntities(void)
+{
+ CLink<AlphaObjectInfo> *node;
+ CSimpleModelInfo *mi;
+ for(node = m_alphaEntityList.tail.prev;
+ node != &m_alphaEntityList.head;
+ node = node->prev){
+ CEntity *e = node->item.entity;
+ if(e->m_rwObject == nil)
+ continue;
+ mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(e->m_modelIndex);
+ if(mi->m_noZwrite)
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);
+
+ if(e->bDistanceFade){
+ DeActivateDirectional();
+ SetAmbientColours();
+ e->bImBeingRendered = true;
+ RenderFadingAtomic((RpAtomic*)e->m_rwObject, node->item.sort);
+ e->bImBeingRendered = false;
+ }else
+ CRenderer::RenderOneNonRoad(e);
+
+ if(mi->m_noZwrite)
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ }
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderWheelAtomicCB(RpAtomic *atomic)
+{
+ RpAtomic *lodatm;
+ RwMatrix *m;
+ RwV3d view;
+ float len;
+ CSimpleModelInfo *mi;
+
+ mi = GetAtomicModelInfo(atomic);
+ m = RwFrameGetLTM(RpAtomicGetFrame(atomic));
+ RwV3dSub(&view, RwMatrixGetPos(m), ms_pCameraPosn);
+ len = RwV3dLength(&view);
+ lodatm = mi->GetAtomicFromDistance(len);
+ if(lodatm){
+ if(RpAtomicGetGeometry(lodatm) != RpAtomicGetGeometry(atomic))
+ RpAtomicSetGeometry(atomic, RpAtomicGetGeometry(lodatm), rpATOMICSAMEBOUNDINGSPHERE);
+ AtomicDefaultRenderCallBack(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;
+ AtomicDefaultRenderCallBack(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);
+ AtomicDefaultRenderCallBack(atomic);
+ RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255);
+ RpGeometrySetFlags(geo, flags);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist)
+{
+ RpAtomic *lodatm;
+ float fadefactor;
+ uint8 alpha;
+ CSimpleModelInfo *mi;
+
+ mi = GetAtomicModelInfo(atomic);
+ lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE);
+ if(mi->m_additive){
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ AtomicDefaultRenderCallBack(atomic);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ }else{
+ fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE;
+ if(fadefactor > 1.0f)
+ fadefactor = 1.0f;
+ alpha = mi->m_alpha * fadefactor;
+ if(alpha == 255)
+ AtomicDefaultRenderCallBack(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, 0);
+ AtomicDefaultRenderCallBack(atomic);
+ RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255);
+ RpGeometrySetFlags(geo, flags);
+ }
+ }
+ return atomic;
+}
+
+
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq < ms_vehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*distsq < dot*dot))
+ return atomic;
+ }
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailAlphaCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq < ms_vehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0)
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*distsq < dot*dot))
+ return atomic;
+
+ if(flags & ATOMIC_FLAG_DRAWLAST){
+ // sort before clump
+ if(!InsertAtomicIntoSortedList(atomic, distsq - 0.0001f))
+ AtomicDefaultRenderCallBack(atomic);
+ }else{
+ if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq < ms_bigVehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f)
+ return atomic;
+ }
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq < ms_bigVehicleLod0Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f)
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0)
+ return atomic;
+
+ if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleHiDetailCB_Boat(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq < ms_bigVehicleLod1Dist)
+ AtomicDefaultRenderCallBack(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq >= ms_bigVehicleLod0Dist &&
+ distsq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f)
+ return atomic;
+ }
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq >= ms_bigVehicleLod0Dist &&
+ distsq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f)
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0)
+ return atomic;
+
+ if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleReallyLowDetailCB(RpAtomic *atomic)
+{
+ RpClump *clump;
+ float dist;
+ int32 alpha;
+
+ clump = RpAtomicGetClump(atomic);
+ dist = GetDistanceSquaredFromCamera(RpClumpGetFrame(clump));
+ if(dist >= ms_vehicleLod0Dist){
+ alpha = GetClumpAlpha(clump);
+ if(alpha == 255)
+ AtomicDefaultRenderCallBack(atomic);
+ else
+ RenderAlphaAtomic(atomic, alpha);
+ }
+ return atomic;
+
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq >= ms_bigVehicleLod1Dist)
+ AtomicDefaultRenderCallBack(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderTrainHiDetailCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0){
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*distsq < dot*dot))
+ return atomic;
+ }
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderTrainHiDetailAlphaCB(RpAtomic *atomic)
+{
+ RwFrame *clumpframe;
+ float distsq, dot;
+ uint32 flags;
+
+ clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
+ distsq = GetDistanceSquaredFromCamera(clumpframe);
+ if(distsq < ms_bigVehicleLod1Dist){
+ flags = GetAtomicId(atomic);
+ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)),
+ RwFrameGetLTM(clumpframe), flags);
+ if(distsq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0)
+ if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*distsq < dot*dot))
+ return atomic;
+
+ if(flags & ATOMIC_FLAG_DRAWLAST){
+ // sort before clump
+ if(!InsertAtomicIntoSortedList(atomic, distsq - 0.0001f))
+ AtomicDefaultRenderCallBack(atomic);
+ }else{
+ if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
+ AtomicDefaultRenderCallBack(atomic);
+ }
+ }
+ return atomic;
+}
+
+// TODO: this is part of a struct
+static RwTexture *&playerskin = *(RwTexture**)0x941428;
+
+RpAtomic*
+CVisibilityPlugins::RenderPlayerCB(RpAtomic *atomic)
+{
+ if(playerskin)
+ RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), SetTextureCB, playerskin);
+ AtomicDefaultRenderCallBack(atomic);
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderPedLowDetailCB(RpAtomic *atomic)
+{
+ RpClump *clump;
+ float dist;
+ int32 alpha;
+
+ clump = RpAtomicGetClump(atomic);
+ dist = GetDistanceSquaredFromCamera(RpClumpGetFrame(clump));
+ if(dist >= ms_pedLod0Dist){
+ alpha = GetClumpAlpha(clump);
+ if(alpha == 255)
+ AtomicDefaultRenderCallBack(atomic);
+ else
+ RenderAlphaAtomic(atomic, alpha);
+ }
+ return atomic;
+}
+
+RpAtomic*
+CVisibilityPlugins::RenderPedHiDetailCB(RpAtomic *atomic)
+{
+ RpClump *clump;
+ float dist;
+ int32 alpha;
+
+ clump = RpAtomicGetClump(atomic);
+ dist = GetDistanceSquaredFromCamera(RpClumpGetFrame(clump));
+ if(dist < ms_pedLod0Dist){
+ alpha = GetClumpAlpha(clump);
+ if(alpha == 255)
+ AtomicDefaultRenderCallBack(atomic);
+ else
+ RenderAlphaAtomic(atomic, alpha);
+ }
+ return atomic;
+}
+
+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)
+{
+ // TODO, but unused
+ return true;
+}
+
+bool
+CVisibilityPlugins::VehicleVisibilityCB(RpClump *clump)
+{
+ // TODO, but unused
+ return true;
+}
+
+bool
+CVisibilityPlugins::VehicleVisibilityCB_BigVehicle(RpClump *clump)
+{
+ // TODO, but unused
+ return true;
+}
+
+
+
+
+//
+// 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;
+ switch(modelInfo->m_type)
+ case MITYPE_SIMPLE:
+ case MITYPE_TIME:
+ if(modelInfo->m_normalCull)
+ SetAtomicRenderCallback(atomic, RenderObjNormalAtomic);
+}
+
+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;
+}
+
+// This is rather useless, but whatever
+void
+CVisibilityPlugins::SetAtomicRenderCallback(RpAtomic *atomic, RpAtomicCallBackRender cb)
+{
+ if(cb == nil)
+ cb = AtomicDefaultRenderCallBack; // 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, int32 id)
+{
+ FRAMEEXT(frame)->id = id;
+}
+
+int32
+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), (int32)modelInfo);
+
+ // Unused
+ switch(modelInfo->m_type){
+ // 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;
+ }
+}
+
+void
+CVisibilityPlugins::SetClumpAlpha(RpClump *clump, int alpha)
+{
+ CLUMPEXT(clump)->alpha = alpha;
+}
+
+int
+CVisibilityPlugins::GetClumpAlpha(RpClump *clump)
+{
+ return CLUMPEXT(clump)->alpha;
+}
+
+
+STARTPATCHES
+ InjectHook(0x527E50, CVisibilityPlugins::Initialise, PATCH_JUMP);
+ InjectHook(0x528F90, CVisibilityPlugins::InitAlphaEntityList, PATCH_JUMP);
+ InjectHook(0x528FF0, CVisibilityPlugins::InsertEntityIntoSortedList, PATCH_JUMP);
+ InjectHook(0x528F80, CVisibilityPlugins::InitAlphaAtomicList, PATCH_JUMP);
+ InjectHook(0x528FA0, CVisibilityPlugins::InsertAtomicIntoSortedList, PATCH_JUMP);
+ InjectHook(0x527F60, SetAlphaCB, PATCH_JUMP);
+ InjectHook(0x529040, CVisibilityPlugins::RenderAlphaAtomics, PATCH_JUMP);
+ InjectHook(0x529070, CVisibilityPlugins::RenderFadingEntities, PATCH_JUMP);
+
+ InjectHook(0x527F70, CVisibilityPlugins::RenderWheelAtomicCB, PATCH_JUMP);
+ InjectHook(0x528000, CVisibilityPlugins::RenderObjNormalAtomic, PATCH_JUMP);
+ InjectHook(0x5280B0, CVisibilityPlugins::RenderAlphaAtomic, PATCH_JUMP);
+ InjectHook(0x528100, CVisibilityPlugins::RenderFadingAtomic, PATCH_JUMP);
+
+ InjectHook(0x5283E0, CVisibilityPlugins::RenderVehicleHiDetailCB, PATCH_JUMP);
+ InjectHook(0x5284B0, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB, PATCH_JUMP);
+ InjectHook(0x5288A0, CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle, PATCH_JUMP);
+ InjectHook(0x528A10, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle, PATCH_JUMP);
+ InjectHook(0x528AD0, CVisibilityPlugins::RenderVehicleHiDetailCB_Boat, PATCH_JUMP);
+ InjectHook(0x5287F0, CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle, PATCH_JUMP);
+ InjectHook(0x528940, CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle, PATCH_JUMP);
+ InjectHook(0x528240, CVisibilityPlugins::RenderVehicleReallyLowDetailCB, PATCH_JUMP);
+ InjectHook(0x5287B0, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle, PATCH_JUMP);
+ InjectHook(0x5285D0, CVisibilityPlugins::RenderTrainHiDetailCB, PATCH_JUMP);
+ InjectHook(0x5286A0, CVisibilityPlugins::RenderTrainHiDetailAlphaCB, PATCH_JUMP);
+
+ InjectHook(0x528BC0, CVisibilityPlugins::RenderPedHiDetailCB, PATCH_JUMP);
+ InjectHook(0x528B60, CVisibilityPlugins::RenderPedLowDetailCB, PATCH_JUMP);
+
+
+ InjectHook(0x527DC0, CVisibilityPlugins::PluginAttach, PATCH_JUMP);
+
+ InjectHook(0x527EC0, CVisibilityPlugins::SetAtomicModelInfo, PATCH_JUMP);
+ InjectHook(0x527F00, CVisibilityPlugins::GetAtomicModelInfo, PATCH_JUMP);
+ InjectHook(0x527F10, CVisibilityPlugins::SetAtomicFlag, PATCH_JUMP);
+ InjectHook(0x527F30, CVisibilityPlugins::ClearAtomicFlag, PATCH_JUMP);
+ InjectHook(0x527F50, CVisibilityPlugins::GetAtomicId, PATCH_JUMP);
+ InjectHook(0x528C20, CVisibilityPlugins::SetAtomicRenderCallback, PATCH_JUMP);
+
+ InjectHook(0x528D60, CVisibilityPlugins::SetFrameHierarchyId, PATCH_JUMP);
+ InjectHook(0x528D80, CVisibilityPlugins::GetFrameHierarchyId, PATCH_JUMP);
+
+ InjectHook(0x528ED0, CVisibilityPlugins::SetClumpModelInfo, PATCH_JUMP);
+ InjectHook(0x528F50, CVisibilityPlugins::SetClumpAlpha, PATCH_JUMP);
+ InjectHook(0x528F70, CVisibilityPlugins::GetClumpAlpha, PATCH_JUMP);
+
+
+ InjectHook(0x529120, CVisibilityPlugins::GetDistanceSquaredFromCamera, PATCH_JUMP);
+ InjectHook(0x5282A0, CVisibilityPlugins::GetDotProductWithCameraVector, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/VisibilityPlugins.h b/src/render/VisibilityPlugins.h
new file mode 100644
index 00000000..f041b24e
--- /dev/null
+++ b/src/render/VisibilityPlugins.h
@@ -0,0 +1,129 @@
+#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_alphaEntityList;
+ 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_pedLod0Dist;
+ static float &ms_pedLod1Dist;
+ static float &ms_pedFadeDist;
+
+ static void Initialise(void);
+ static void InitAlphaEntityList(void);
+ static bool InsertEntityIntoSortedList(CEntity *e, float dist);
+ static void InitAlphaAtomicList(void);
+ static bool InsertAtomicIntoSortedList(RpAtomic *a, float dist);
+
+ static RpAtomic *RenderWheelAtomicCB(RpAtomic *atomic);
+ static RpAtomic *RenderObjNormalAtomic(RpAtomic *atomic);
+ static RpAtomic *RenderAlphaAtomic(RpAtomic *atomic, int alpha);
+ 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 *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 *RenderPlayerCB(RpAtomic *atomic);
+ static RpAtomic *RenderPedLowDetailCB(RpAtomic *atomic);
+ static RpAtomic *RenderPedHiDetailCB(RpAtomic *atomic);
+
+ static void RenderAlphaAtomics(void);
+ static void RenderFadingEntities(void);
+
+ // All actually unused
+ static bool DefaultVisibilityCB(RpClump *clump);
+ static bool FrustumSphereCB(RpClump *clump);
+// static bool MloVisibilityCB(RpClump *clump);
+ static bool VehicleVisibilityCB(RpClump *clump);
+ static bool VehicleVisibilityCB_BigVehicle(RpClump *clump);
+
+ 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
+ int32 id;
+ };
+ static void SetFrameHierarchyId(RwFrame *frame, int32 id);
+ static int32 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);
+};