summaryrefslogtreecommitdiffstats
path: root/src/render
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/render/Coronas.cpp578
-rw-r--r--src/render/Coronas.h84
-rw-r--r--src/render/Credits.cpp2
-rw-r--r--src/render/Credits.h2
-rw-r--r--src/render/Lights.cpp164
-rw-r--r--src/render/Lights.h11
-rw-r--r--src/render/PointLights.cpp283
-rw-r--r--src/render/PointLights.h34
-rw-r--r--src/render/Sprite.cpp60
-rw-r--r--src/render/Sprite.h1
-rw-r--r--src/render/Sprite2d.cpp8
-rw-r--r--src/render/Sprite2d.h4
12 files changed, 1218 insertions, 13 deletions
diff --git a/src/render/Coronas.cpp b/src/render/Coronas.cpp
index ee3f3da1..5947a77f 100644
--- a/src/render/Coronas.cpp
+++ b/src/render/Coronas.cpp
@@ -1,7 +1,52 @@
#include "common.h"
#include "patcher.h"
+#include "General.h"
+#include "TxdStore.h"
+#include "Camera.h"
+#include "Sprite.h"
+#include "Timer.h"
+#include "World.h"
+#include "Weather.h"
+#include "Collision.h"
+#include "TimeCycle.h"
#include "Coronas.h"
+struct FlareDef
+{
+ float position;
+ float size;
+ int16 red;
+ int16 green;
+ int16 blue;
+ int16 alpha;
+ int16 texture;
+};
+
+FlareDef SunFlareDef[] = {
+ { -0.5f, 15.0f, 50, 50, 0, 200, 1 },
+ { -1.0f, 10.0f, 50, 20, 0, 200, 2 },
+ { -1.5f, 15.0f, 50, 0, 0, 200, 3 },
+ { -2.5f, 25.0f, 50, 0, 0, 200, 1 },
+ { 0.5f, 12.5f, 40, 40, 25, 200, 1 },
+ { 0.05f, 20.0f, 30, 22, 9, 200, 2 },
+ { 1.3f, 7.5f, 50, 30, 9, 200, 3 },
+ { 0.0f, 0.0f, 255, 255, 255, 255, 0 }
+};
+
+FlareDef HeadLightsFlareDef[] = {
+ { -0.5f, 15.5, 70, 70, 70, 200, 1 },
+ { -1.0f, 10.0, 70, 70, 70, 200, 2 },
+ { -1.5f, 5.5f, 50, 50, 50, 200, 3 },
+ { 0.5f, 12.0f, 50, 50, 50, 200, 1 },
+ { 0.05f, 20.0f, 40, 40, 40, 200, 2 },
+ { 1.3f, 8.0f, 60, 60, 60, 200, 3 },
+ { -2.0f, 12.0f, 50, 50, 50, 200, 1 },
+ { -2.3f, 15.0f, 40, 40, 40, 200, 2 },
+ { -3.0f, 16.0f, 40, 40, 40, 200, 3 },
+ { 0.0f, 0.0f, 255, 255, 255, 255, 0 }
+};
+
+
RwTexture **gpCoronaTexture = (RwTexture**)0x5FAF44; //[9]
float &CCoronas::LightsMult = *(float*)0x5FB088; // 1.0
@@ -9,6 +54,535 @@ float &CCoronas::SunScreenX = *(float*)0x8F4358;
float &CCoronas::SunScreenY = *(float*)0x8F4354;
bool &CCoronas::bSmallMoon = *(bool*)0x95CD49;
bool &CCoronas::SunBlockedByClouds = *(bool*)0x95CD73;
+int &CCoronas::bChangeBrightnessImmediately = *(int*)0x8E2C30;
+
+CRegisteredCorona *CCoronas::aCoronas = (CRegisteredCorona*)0x72E518;
+
+//WRAPPER void CCoronas::Render(void) { EAXJMP(0x4F8FB0); }
+//WRAPPER void CCoronas::RenderReflections(void) { EAXJMP(0x4F9B40); }
+
+const char aCoronaSpriteNames[][32] = {
+ "coronastar",
+ "corona",
+ "coronamoon",
+ "coronareflect",
+ "coronaheadlightline",
+ "coronahex",
+ "coronacircle",
+ "coronaringa",
+ "streek"
+};
+
+void
+CCoronas::Init(void)
+{
+ int i;
+
+ CTxdStore::PushCurrentTxd();
+ CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle"));
+
+ for(i = 0; i < 9; i++)
+ if(gpCoronaTexture[i] == nil)
+ gpCoronaTexture[i] = RwTextureRead(aCoronaSpriteNames[i], nil);
+
+ CTxdStore::PopCurrentTxd();
+
+ for(i = 0; i < NUMCORONAS; i++)
+ aCoronas[i].id = 0;
+}
+
+void
+CCoronas::Shutdown(void)
+{
+ int i;
+ for(i = 0; i < 9; i++)
+ if(gpCoronaTexture[i]){
+ RwTextureDestroy(gpCoronaTexture[i]);
+ gpCoronaTexture[i] = nil;
+ }
+}
+
+void
+CCoronas::Update(void)
+{
+ int i;
+ static int LastCamLook = 0;
+
+ LightsMult = min(LightsMult + 0.03f * CTimer::GetTimeStep(), 1.0f);
+
+ int CamLook = 0;
+ if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) CamLook |= 1;
+ if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) CamLook |= 2;
+ if(TheCamera.Cams[TheCamera.ActiveCam].LookingBehind) CamLook |= 4;
+ // BUG?
+ if(TheCamera.GetLookDirection() == LOOKING_BEHIND) CamLook |= 8;
+
+ if(LastCamLook != CamLook)
+ bChangeBrightnessImmediately = 3;
+ else
+ bChangeBrightnessImmediately = max(bChangeBrightnessImmediately-1, 0);
+ LastCamLook = CamLook;
+
+ for(i = 0; i < NUMCORONAS; i++)
+ if(aCoronas[i].id != 0)
+ aCoronas[i].Update();
+}
+
+void
+CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
+ const CVector &coors, float size, float drawDist, RwTexture *tex,
+ int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle)
+{
+ int i;
+
+ if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D())
+ return;
+
+ for(i = 0; i < NUMCORONAS; i++)
+ if(aCoronas[i].id == id)
+ break;
+
+ if(i == NUMCORONAS){
+ // add a new one
+
+ // find empty slot
+ for(i = 0; i < NUMCORONAS; i++)
+ if(aCoronas[i].id == 0)
+ break;
+ if(i == NUMCORONAS)
+ return; // no space
+
+ aCoronas[i].fadeAlpha = 0;
+ aCoronas[i].offScreen = true;
+ aCoronas[i].firstUpdate = true;
+ aCoronas[i].renderReflection = false;
+ aCoronas[i].lastLOScheck = 0;
+ aCoronas[i].sightClear = false;
+ aCoronas[i].hasValue[0] = false;
+ aCoronas[i].hasValue[1] = false;
+ aCoronas[i].hasValue[2] = false;
+ aCoronas[i].hasValue[3] = false;
+ aCoronas[i].hasValue[4] = false;
+ aCoronas[i].hasValue[5] = false;
+
+ }else{
+ // use existing one
+
+ if(aCoronas[i].fadeAlpha == 0 && alpha == 0){
+ // unregister
+ aCoronas[i].id = 0;
+ return;
+ }
+ }
+
+ aCoronas[i].id = id;
+ aCoronas[i].red = red;
+ aCoronas[i].green = green;
+ aCoronas[i].blue = blue;
+ aCoronas[i].alpha = alpha;
+ aCoronas[i].coors = coors;
+ aCoronas[i].size = size;
+ aCoronas[i].someAngle = someAngle;
+ aCoronas[i].registeredThisFrame = true;
+ aCoronas[i].drawDist = drawDist;
+ aCoronas[i].texture = tex;
+ aCoronas[i].flareType = flareType;
+ aCoronas[i].reflection = reflection;
+ aCoronas[i].LOScheck = LOScheck;
+ aCoronas[i].drawStreak = drawStreak;
+}
+
+void
+CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
+ const CVector &coors, float size, float drawDist, uint8 type,
+ int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle)
+{
+ RegisterCorona(id, red, green, blue, alpha, coors, size, drawDist,
+ gpCoronaTexture[type], flareType, reflection, LOScheck, drawStreak, someAngle);
+}
+
+void
+CCoronas::UpdateCoronaCoors(int id, const CVector &coors, float drawDist, float someAngle)
+{
+ int i;
+
+ if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D())
+ return;
+
+ for(i = 0; i < NUMCORONAS; i++)
+ if(aCoronas[i].id == id)
+ break;
+
+ if(i == NUMCORONAS)
+ return;
+
+ if(aCoronas[i].fadeAlpha == 0)
+ aCoronas[i].id = 0; // faded out, remove
+ else{
+ aCoronas[i].coors = coors;
+ aCoronas[i].someAngle = someAngle;
+ }
+}
+
+static RwIm2DVertex vertexbufferX[2];
+
+// TODO? not sure streaks look quite right...
+void
+CCoronas::Render(void)
+{
+ int i, j;
+ int screenw, screenh;
+
+ screenw = RwRasterGetWidth(RwCameraGetRaster(Scene.camera));
+ screenh = RwRasterGetHeight(RwCameraGetRaster(Scene.camera));
+
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+
+ for(i = 0; i < NUMCORONAS; i++){
+ for(j = 5; j > 0; j--){
+ aCoronas[i].prevX[j] = aCoronas[i].prevX[j-1];
+ aCoronas[i].prevY[j] = aCoronas[i].prevY[j-1];
+ aCoronas[i].prevRed[j] = aCoronas[i].prevRed[j-1];
+ aCoronas[i].prevGreen[j] = aCoronas[i].prevGreen[j-1];
+ aCoronas[i].prevBlue[j] = aCoronas[i].prevBlue[j-1];
+ aCoronas[i].hasValue[j] = aCoronas[i].hasValue[j-1];
+ }
+ aCoronas[i].hasValue[0] = false;
+
+ if(aCoronas[i].id == 0 ||
+ aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0)
+ continue;
+
+ CVector spriteCoors;
+ float spritew, spriteh;
+ if(CSprite::CalcScreenCoors(aCoronas[i].coors, spriteCoors, &spritew, &spriteh, true)){
+ aCoronas[i].offScreen = false;
+
+ if(spriteCoors.x < 0.0f || spriteCoors.y < 0.0f ||
+ spriteCoors.x > screenw || spriteCoors.y > screenh){
+ aCoronas[i].offScreen = true;
+ aCoronas[i].sightClear = false;
+ }else{
+ if(CTimer::GetTimeInMilliseconds() > aCoronas[i].lastLOScheck + 2000){
+ aCoronas[i].lastLOScheck = CTimer::GetTimeInMilliseconds();
+ aCoronas[i].sightClear = CWorld::GetIsLineOfSightClear(
+ aCoronas[i].coors, TheCamera.Cams[TheCamera.ActiveCam].Source,
+ true, true, false, false, false, true, false);
+ }
+
+ // add new streak point
+ if(aCoronas[i].sightClear){
+ aCoronas[i].prevX[0] = spriteCoors.x;
+ aCoronas[i].prevY[0] = spriteCoors.y;
+ aCoronas[i].prevRed[0] = aCoronas[i].red;
+ aCoronas[i].prevGreen[0] = aCoronas[i].green;
+ aCoronas[i].prevBlue[0] = aCoronas[i].blue;
+ aCoronas[i].hasValue[0] = true;
+ }
+
+ // if distance too big, break streak
+ if(aCoronas[i].hasValue[1]){
+ if(fabs(aCoronas[i].prevX[0] - aCoronas[i].prevX[1]) > 50.0f ||
+ fabs(aCoronas[i].prevY[0] - aCoronas[i].prevY[1]) > 50.0f)
+ aCoronas[i].hasValue[0] = false;
+ }
+ }
+
+
+ if(aCoronas[i].fadeAlpha == 0)
+ continue;
+
+ if(spriteCoors.z < aCoronas[i].drawDist){
+ float recipz = 1.0f/spriteCoors.z;
+ float fadeDistance = aCoronas[i].drawDist / 2.0f;
+ float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance;
+ int totalFade = aCoronas[i].fadeAlpha * distanceFade;
+
+ if(aCoronas[i].LOScheck)
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ else
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+
+ // render corona itself
+ if(aCoronas[i].texture){
+ float fogscale = CWeather::Foggyness*min(spriteCoors.z, 40.0f)/40.0f + 1.0f;
+ if(CCoronas::aCoronas[i].id == SUN_CORE)
+ spriteCoors.z = 0.95f * RwCameraGetFarClipPlane(Scene.camera);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(aCoronas[i].texture));
+ spriteCoors.z -= 1.5f;
+
+ if(aCoronas[i].texture == gpCoronaTexture[8]){
+ // what's this?
+ float f = 1.0f - aCoronas[i].someAngle*2.0f/PI;
+ float wscale = 6.0f*sq(sq(sq(f))) + 0.5f;
+ float hscale = 0.35f - (wscale - 0.5f) * 0.06f;
+ hscale = max(hscale, 0.15f);
+
+ CSprite::RenderOneXLUSprite(spriteCoors.x, spriteCoors.y, spriteCoors.z,
+ spritew * aCoronas[i].size * wscale,
+ spriteh * aCoronas[i].size * fogscale * hscale,
+ CCoronas::aCoronas[i].red / fogscale,
+ CCoronas::aCoronas[i].green / fogscale,
+ CCoronas::aCoronas[i].blue / fogscale,
+ totalFade,
+ recipz,
+ 255);
+ }else{
+ CSprite::RenderOneXLUSprite_Rotate_Aspect(
+ spriteCoors.x, spriteCoors.y, spriteCoors.z,
+ spritew * aCoronas[i].size * fogscale,
+ spriteh * aCoronas[i].size * fogscale,
+ CCoronas::aCoronas[i].red / fogscale,
+ CCoronas::aCoronas[i].green / fogscale,
+ CCoronas::aCoronas[i].blue / fogscale,
+ totalFade,
+ recipz,
+ 20.0f * recipz,
+ 255);
+ }
+ }
+
+ // render flares
+ if(aCoronas[i].flareType != FLARE_NONE){
+ FlareDef *flare;
+
+ switch(aCoronas[i].flareType){
+ case FLARE_SUN: flare = SunFlareDef; break;
+ case FLARE_HEADLIGHTS: flare = HeadLightsFlareDef; break;
+ default: assert(0);
+ }
+
+ for(; flare->texture; flare++){
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[flare->texture + 4]));
+ CSprite::RenderOneXLUSprite(
+ (spriteCoors.x - (screenw/2)) * flare->position + (screenw/2),
+ (spriteCoors.y - (screenh/2)) * flare->position + (screenh/2),
+ spriteCoors.z,
+ 4.0f*flare->size * spritew/spriteh,
+ 4.0f*flare->size,
+ (flare->red * aCoronas[i].red)>>8,
+ (flare->green * aCoronas[i].green)>>8,
+ (flare->blue * aCoronas[i].blue)>>8,
+ (totalFade * flare->alpha)>>8,
+ recipz, 255);
+ }
+ }
+ }else{
+ aCoronas[i].offScreen = true;
+ aCoronas[i].sightClear = false;
+ }
+ }
+ }
+
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+
+ // streaks
+ for(i = 0; i < NUMCORONAS; i++){
+ if(aCoronas[i].id == 0 || !aCoronas[i].drawStreak)
+ break;
+
+ for(j = 0; j < 5; j++){
+ if(!aCoronas[i].hasValue[j] || !aCoronas[i].hasValue[j+1])
+ continue;
+
+ RwIm2DVertexSetScreenX(&vertexbufferX[0], aCoronas[i].prevX[j]);
+ RwIm2DVertexSetScreenY(&vertexbufferX[0], aCoronas[i].prevY[j]);
+ RwIm2DVertexSetIntRGBA(&vertexbufferX[0], aCoronas[i].prevRed[j], aCoronas[i].prevGreen[j], aCoronas[i].prevBlue[j], 255);
+ RwIm2DVertexSetScreenX(&vertexbufferX[1], aCoronas[i].prevX[j+1]);
+ RwIm2DVertexSetScreenY(&vertexbufferX[1], aCoronas[i].prevY[j+1]);
+ RwIm2DVertexSetIntRGBA(&vertexbufferX[1], aCoronas[i].prevRed[j+1], aCoronas[i].prevGreen[j+1], aCoronas[i].prevBlue[j+1], 255);
+
+ // BUG: game doesn't do this
+ RwIm2DVertexSetScreenZ(&vertexbufferX[0], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&vertexbufferX[0], RwCameraGetNearClipPlane(Scene.camera));
+ RwIm2DVertexSetRecipCameraZ(&vertexbufferX[0], 1.0f/RwCameraGetNearClipPlane(Scene.camera));
+ RwIm2DVertexSetScreenZ(&vertexbufferX[1], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&vertexbufferX[1], RwCameraGetNearClipPlane(Scene.camera));
+ RwIm2DVertexSetRecipCameraZ(&vertexbufferX[1], 1.0f/RwCameraGetNearClipPlane(Scene.camera));
+
+ RwIm2DRenderLine(vertexbufferX, 2, 0, 1);
+ }
+ }
+
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+}
+
+void
+CCoronas::RenderReflections(void)
+{
+ int i;
+ CColPoint point;
+ CEntity *entity;
+
+ if(CWeather::WetRoads > 0.0f){
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[3]));
+
+ for(i = 0; i < NUMCORONAS; i++){
+ if(aCoronas[i].id == 0 ||
+ aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0)
+ continue;
+
+ // check if we want a reflection on this corona
+ if(aCoronas[i].renderReflection){
+ if(((CTimer::GetFrameCounter() + i) & 0xF) == 0 &&
+ CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil))
+ aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z;
+ }else{
+ if(CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil)){
+ aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z;
+ aCoronas[i].renderReflection = true;
+ }
+ }
+
+ if(!aCoronas[i].renderReflection)
+ continue;
+
+ // Don't draw if reflection is too high
+ if(aCoronas[i].heightAboveRoad < 20.0){
+ // don't draw if camera is below road
+ if(CCoronas::aCoronas[i].coors.z - aCoronas[i].heightAboveRoad > TheCamera.GetPosition().z)
+ continue;
+
+ CVector coors = aCoronas[i].coors;
+ coors.z -= 2.0f*aCoronas[i].heightAboveRoad;
+
+ CVector spriteCoors;
+ float spritew, spriteh;
+ if(CSprite::CalcScreenCoors(coors, spriteCoors, &spritew, &spriteh, true)){
+ float drawDist = 0.75f * aCoronas[i].drawDist;
+ drawDist = min(drawDist, 50.0f);
+ if(spriteCoors.z < drawDist){
+ float fadeDistance = drawDist / 2.0f;
+ float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance;
+ distanceFade = clamp(distanceFade, 0.0f, 1.0f);
+ float recipz = 1.0f/RwCameraGetNearClipPlane(Scene.camera);
+ int intensity = (20.0f - aCoronas[i].heightAboveRoad) * 230.0 * distanceFade*CWeather::WetRoads * 0.05f;
+
+ CSprite::RenderBufferedOneXLUSprite(
+ spriteCoors.x, spriteCoors.y, RwIm2DGetNearScreenZ(),
+ spritew * aCoronas[i].size * 0.75f,
+ spriteh * aCoronas[i].size * 2.0f,
+ (intensity * CCoronas::aCoronas[i].red)>>8,
+ (intensity * CCoronas::aCoronas[i].green)>>8,
+ (intensity * CCoronas::aCoronas[i].blue)>>8,
+ 255,
+ recipz,
+ 255);
+ }
+ }
+ }
+ }
+ CSprite::FlushSpriteBuffer();
+
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ }else{
+ for(i = 0; i < NUMCORONAS; i++)
+ aCoronas[i].renderReflection = false;
+ }
+}
+
+void
+CCoronas::DoSunAndMoon(void)
+{
+ // yeah, moon is done somewhere else....
+
+ CVector sunCoors = CTimeCycle::GetSunPosition();
+ sunCoors *= 150.0f;
+ sunCoors += TheCamera.GetPosition();
+
+ if(CTimeCycle::GetSunPosition().z > -0.2f){
+ float size = ((CGeneral::GetRandomNumber()&0xFF) * 0.005f + 10.0f) * CTimeCycle::GetSunSize();
+ RegisterCorona(SUN_CORE,
+ CTimeCycle::GetSunCoreRed(), CTimeCycle::GetSunCoreGreen(), CTimeCycle::GetSunCoreBlue(),
+ 255, sunCoors, size,
+ 999999.88f, TYPE_STAR, FLARE_NONE, REFLECTION_OFF, LOSCHECK_OFF, STREAK_OFF, 0.0f);
+
+ if(CTimeCycle::GetSunPosition().z > 0.0f)
+ RegisterCorona(SUN_CORONA,
+ CTimeCycle::GetSunCoronaRed(), CTimeCycle::GetSunCoronaGreen(), CTimeCycle::GetSunCoronaBlue(),
+ 255, sunCoors, 25.0f * CTimeCycle::GetSunSize(),
+ 999999.88f, TYPE_STAR, FLARE_SUN, REFLECTION_OFF, LOSCHECK_ON, STREAK_OFF, 0.0f);
+ }
+
+ CVector spriteCoors;
+ float spritew, spriteh;
+ if(CSprite::CalcScreenCoors(sunCoors, spriteCoors, &spritew, &spriteh, true)){
+ SunScreenX = spriteCoors.x;
+ SunScreenY = spriteCoors.y;
+ }else{
+ SunScreenX = 1000000.0f;
+ SunScreenY = 1000000.0f;
+ }
+}
+
+void
+CRegisteredCorona::Update(void)
+{
+ if(!registeredThisFrame)
+ alpha = 0;
+
+ if(LOScheck &&
+ (CCoronas::SunBlockedByClouds && id == CCoronas::SUN_CORONA ||
+ !CWorld::GetIsLineOfSightClear(coors, TheCamera.GetPosition(), true, false, false, false, false, false))){
+ // Corona is blocked, fade out
+ fadeAlpha = max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f);
+ }else if(offScreen){
+ // Same when off screen
+ fadeAlpha = max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f);
+ }else{
+ // Visible
+ if(alpha > fadeAlpha){
+ // fade in
+ fadeAlpha = min(fadeAlpha + 15.0f*CTimer::GetTimeStep(), alpha);
+ if(CCoronas::bChangeBrightnessImmediately)
+ fadeAlpha = alpha;
+ }else if(alpha < fadeAlpha){
+ // too visible, decrease alpha but not below alpha
+ fadeAlpha = max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), alpha);
+ }
+
+ // darken scene when the sun is visible
+ if(id == CCoronas::SUN_CORONA)
+ CCoronas::LightsMult = max(CCoronas::LightsMult - CTimer::GetTimeStep()*0.06f, 0.6f);
+ }
+
+ // remove if invisible
+ if(fadeAlpha == 0 && !firstUpdate)
+ id = 0;
+ firstUpdate = false;
+ registeredThisFrame = false;
+}
+
+STARTPATCHES
+ InjectHook(0x4F9F90, CCoronas::Init, PATCH_JUMP);
+ InjectHook(0x4FA050, CCoronas::Shutdown, PATCH_JUMP);
+ InjectHook(0x4F8EC0, CCoronas::Update, PATCH_JUMP);
+ InjectHook(0x4FA0E0, (void (*)(uint32, uint8, uint8, uint8, uint8, const CVector&, float, float, RwTexture*, int8, uint8, uint8, uint8, float))CCoronas::RegisterCorona, PATCH_JUMP);
+ InjectHook(0x4FA080, (void (*)(uint32, uint8, uint8, uint8, uint8, const CVector&, float, float, uint8, int8, uint8, uint8, uint8, float))CCoronas::RegisterCorona, PATCH_JUMP);
+ InjectHook(0x4FA2D0, CCoronas::UpdateCoronaCoors, PATCH_JUMP);
+ InjectHook(0x4F8FB0, CCoronas::Render, PATCH_JUMP);
+ InjectHook(0x4F9B40, CCoronas::RenderReflections, PATCH_JUMP);
+ InjectHook(0x4FA380, CCoronas::DoSunAndMoon, PATCH_JUMP);
-WRAPPER void CCoronas::Render(void) { EAXJMP(0x4F8FB0); }
-WRAPPER void CCoronas::RenderReflections(void) { EAXJMP(0x4F9B40); }
+ InjectHook(0x4F8C40, &CRegisteredCorona::Update, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/Coronas.h b/src/render/Coronas.h
index 796983bd..ed69b1e6 100644
--- a/src/render/Coronas.h
+++ b/src/render/Coronas.h
@@ -2,15 +2,99 @@
extern RwTexture **gpCoronaTexture; //[9]
+struct CRegisteredCorona
+{
+ uint32 id;
+ uint32 lastLOScheck;
+ RwTexture *texture;
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ uint8 alpha; // alpha when fully visible
+ uint8 fadeAlpha; // actual value used for rendering, faded
+ CVector coors;
+ float size;
+ float someAngle;
+ bool registeredThisFrame;
+ float drawDist;
+ int8 flareType;
+ int8 reflection;
+
+ uint8 LOScheck : 1;
+ uint8 offScreen : 1;
+ uint8 firstUpdate : 1;
+ uint8 drawStreak : 1;
+ uint8 sightClear : 1;
+
+ bool renderReflection;
+ float heightAboveRoad;
+
+ float prevX[6];
+ float prevY[6];
+ uint8 prevRed[6];
+ uint8 prevGreen[6];
+ uint8 prevBlue[6];
+ bool hasValue[6];
+
+ void Update(void);
+};
+static_assert(sizeof(CRegisteredCorona) == 0x80, "CRegisteredCorona: error");
+
class CCoronas
{
+ static CRegisteredCorona *aCoronas; //[NUMCORONAS];
public:
+ enum {
+ SUN_CORE = 1,
+ SUN_CORONA
+ };
+ enum {
+ TYPE_STAR,
+ TYPE_NORMAL,
+ TYPE_MOON,
+ TYPE_REFLECT,
+ TYPE_HEADLIGHT,
+ TYPE_HEX,
+ TYPE_CIRCLE,
+ TYPE_RING,
+ TYPE_STREAK,
+ };
+ enum {
+ FLARE_NONE,
+ FLARE_SUN,
+ FLARE_HEADLIGHTS
+ };
+ enum {
+ REFLECTION_OFF,
+ REFLECTION_ON,
+ };
+ enum {
+ LOSCHECK_OFF,
+ LOSCHECK_ON,
+ };
+ enum {
+ STREAK_OFF,
+ STREAK_ON,
+ };
+
static float &LightsMult;
static float &SunScreenY;
static float &SunScreenX;
static bool &bSmallMoon;
static bool &SunBlockedByClouds;
+ static int &bChangeBrightnessImmediately;
+ static void Init(void);
+ static void Shutdown(void);
+ static void Update(void);
+ static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
+ const CVector &coors, float size, float drawDist, RwTexture *tex,
+ int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle);
+ static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
+ const CVector &coors, float size, float drawDist, uint8 type,
+ int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle);
+ static void UpdateCoronaCoors(int id, const CVector &coors, float drawDist, float someAngle);
static void Render(void);
static void RenderReflections(void);
+ static void DoSunAndMoon(void);
};
diff --git a/src/render/Credits.cpp b/src/render/Credits.cpp
index a8b10634..8365d02b 100644
--- a/src/render/Credits.cpp
+++ b/src/render/Credits.cpp
@@ -37,7 +37,7 @@ CCredits::PrintCreditSpace(float space, uint32 &line)
}
void
-CCredits::PrintCreditText(float scaleX, float scaleY, wchar_t *text, uint32 &lineoffset, float scrolloffset)
+CCredits::PrintCreditText(float scaleX, float scaleY, wchar *text, uint32 &lineoffset, float scrolloffset)
{
float start = SCREENH + 50.0f;
float y = lineoffset + start - scrolloffset;
diff --git a/src/render/Credits.h b/src/render/Credits.h
index ed7f8a74..cbe63172 100644
--- a/src/render/Credits.h
+++ b/src/render/Credits.h
@@ -11,5 +11,5 @@ public:
static bool AreCreditsDone(void) { return bCreditsGoing; }
static void Render(void);
static void PrintCreditSpace(float space, uint32 &line);
- static void PrintCreditText(float scaleX, float scaleY, wchar_t *text, uint32 &lineoffset, float scrolloffset);
+ static void PrintCreditText(float scaleX, float scaleY, wchar *text, uint32 &lineoffset, float scrolloffset);
};
diff --git a/src/render/Lights.cpp b/src/render/Lights.cpp
index 7954a07d..1e27ec48 100644
--- a/src/render/Lights.cpp
+++ b/src/render/Lights.cpp
@@ -11,6 +11,9 @@
RpLight *&pAmbient = *(RpLight**)0x885B6C;
RpLight *&pDirect = *(RpLight**)0x880F7C;
+RpLight **pExtraDirectionals = (RpLight**)0x60009C;
+int *LightStrengths = (int*)0x87BEF0;
+int &NumExtraDirLightsInWorld = *(int*)0x64C608;
RwRGBAReal &AmbientLightColourForFrame = *(RwRGBAReal*)0x6F46F8;
RwRGBAReal &AmbientLightColourForFrame_PedsCarsAndObjects = *(RwRGBAReal*)0x6F1D10;
@@ -85,6 +88,151 @@ SetLightsWithTimeOfDayColour(RpWorld *)
}
}
+RpWorld*
+LightsCreate(RpWorld *world)
+{
+ int i;
+ RwRGBAReal color;
+ RwFrame *frame;
+
+ if(world == nil)
+ return nil;
+
+ pAmbient = RpLightCreate(rpLIGHTAMBIENT);
+ RpLightSetFlags(pAmbient, rpLIGHTLIGHTATOMICS);
+ color.red = 0.25f;
+ color.green = 0.25f;
+ color.blue = 0.2f;
+ RpLightSetColor(pAmbient, &color);
+
+ pDirect = RpLightCreate(rpLIGHTDIRECTIONAL);
+ RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
+ color.red = 1.0f;
+ color.green = 0.84f;
+ color.blue = 0.45f;
+ RpLightSetColor(pDirect, &color);
+ RpLightSetRadius(pDirect, 2.0f);
+ frame = RwFrameCreate();
+ RpLightSetFrame(pDirect, frame);
+ RwV3d axis = { 1.0f, 1.0f, 0.0f };
+ RwFrameRotate(frame, &axis, 160.0f, rwCOMBINEPRECONCAT);
+
+ RpWorldAddLight(world, pAmbient);
+ RpWorldAddLight(world, pDirect);
+
+ for(i = 0; i < NUMEXTRADIRECTIONALS; i++){
+ pExtraDirectionals[i] = RpLightCreate(rpLIGHTDIRECTIONAL);
+ RpLightSetFlags(pExtraDirectionals[i], 0);
+ color.red = 1.0f;
+ color.green = 0.5f;
+ color.blue = 0.0f;
+ RpLightSetColor(pExtraDirectionals[i], &color);
+ RpLightSetRadius(pExtraDirectionals[i], 2.0f);
+ frame = RwFrameCreate();
+ RpLightSetFrame(pExtraDirectionals[i], frame);
+ RpWorldAddLight(world, pExtraDirectionals[i]);
+ }
+
+ return world;
+}
+
+void
+LightsDestroy(RpWorld *world)
+{
+ int i;
+
+ if(world == nil)
+ return;
+
+ if(pAmbient){
+ RpWorldRemoveLight(world, pAmbient);
+ RpLightDestroy(pAmbient);
+ pAmbient = nil;
+ }
+
+ if(pDirect){
+ RpWorldRemoveLight(world, pDirect);
+ RwFrameDestroy(RpLightGetFrame(pDirect));
+ RpLightDestroy(pDirect);
+ pDirect = nil;
+ }
+
+ for(i = 0; i < NUMEXTRADIRECTIONALS; i++)
+ if(pExtraDirectionals[i]){
+ RpWorldRemoveLight(world, pExtraDirectionals[i]);
+ RwFrameDestroy(RpLightGetFrame(pExtraDirectionals[i]));
+ RpLightDestroy(pExtraDirectionals[i]);
+ pExtraDirectionals[i] = nil;
+ }
+}
+
+void
+WorldReplaceNormalLightsWithScorched(RpWorld *world, float l)
+{
+ RwRGBAReal color;
+ color.red = l;
+ color.green = l;
+ color.blue = l;
+ RpLightSetColor(pAmbient, &color);
+ RpLightSetFlags(pDirect, 0);
+}
+
+void
+WorldReplaceScorchedLightsWithNormal(RpWorld *world)
+{
+ RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
+ RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
+}
+
+void
+AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue)
+{
+ float strength;
+ int weakest;
+ int i, n;
+ RwRGBAReal color;
+ RwV3d *dir;
+
+ strength = max(max(red, green), blue);
+ n = -1;
+ if(NumExtraDirLightsInWorld < NUMEXTRADIRECTIONALS)
+ n = NumExtraDirLightsInWorld;
+ else{
+ weakest = strength;
+ for(i = 0; i < NUMEXTRADIRECTIONALS; i++)
+ if(LightStrengths[i] < weakest){
+ weakest = LightStrengths[i];
+ n = i;
+ }
+ }
+
+ if(n < 0)
+ return;
+
+ color.red = red;
+ color.green = green;
+ color.blue = blue;
+ RpLightSetColor(pExtraDirectionals[n], &color);
+ dir = RwMatrixGetAt(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n])));
+ dir->x = -dirx;
+ dir->y = -diry;
+ dir->z = -dirz;
+ RwMatrixUpdate(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n])));
+ RwFrameUpdateObjects(RpLightGetFrame(pExtraDirectionals[n]));
+ RpLightSetFlags(pExtraDirectionals[n], rpLIGHTLIGHTATOMICS);
+ LightStrengths[n] = strength;
+ NumExtraDirLightsInWorld = min(NumExtraDirLightsInWorld+1, NUMEXTRADIRECTIONALS);
+}
+
+void
+RemoveExtraDirectionalLights(RpWorld *world)
+{
+ int i;
+ for(i = 0; i < NumExtraDirLightsInWorld; i++)
+ RpLightSetFlags(pExtraDirectionals[i], 0);
+ NumExtraDirLightsInWorld = 0;
+}
+
void
SetAmbientAndDirectionalColours(float f)
{
@@ -159,13 +307,27 @@ SetAmbientColoursToIndicateRoadGroup(int i)
RpLightSetColor(pAmbient, &AmbientLightColour);
}
+void
+SetAmbientColours(RwRGBAReal *color)
+{
+ RpLightSetColor(pAmbient, color);
+}
+
+
STARTPATCHES
InjectHook(0x526510, SetLightsWithTimeOfDayColour, PATCH_JUMP);
+ InjectHook(0x5269A0, LightsCreate, PATCH_JUMP);
+ InjectHook(0x526B40, LightsDestroy, PATCH_JUMP);
+ InjectHook(0x526C10, WorldReplaceNormalLightsWithScorched, PATCH_JUMP);
+ InjectHook(0x526C50, WorldReplaceScorchedLightsWithNormal, PATCH_JUMP);
+ InjectHook(0x526C70, AddAnExtraDirectionalLight, PATCH_JUMP);
+ InjectHook(0x526DB0, RemoveExtraDirectionalLights, 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(0x526F60, (void (*)(void))SetAmbientColours, PATCH_JUMP);
InjectHook(0x526F80, SetAmbientColoursForPedsCarsAndObjects, PATCH_JUMP);
+ InjectHook(0x526FA0, (void (*)(RwRGBAReal*))SetAmbientColours, PATCH_JUMP);
ENDPATCHES
diff --git a/src/render/Lights.h b/src/render/Lights.h
index ca926eb8..6fdd51de 100644
--- a/src/render/Lights.h
+++ b/src/render/Lights.h
@@ -1,4 +1,12 @@
+#pragma once
+
void SetLightsWithTimeOfDayColour(RpWorld *);
+RpWorld *LightsCreate(RpWorld *world);
+void LightsDestroy(RpWorld *world);
+void WorldReplaceNormalLightsWithScorched(RpWorld *world, float l);
+void WorldReplaceScorchedLightsWithNormal(RpWorld *world);
+void AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue);
+void RemoveExtraDirectionalLights(RpWorld *world);
void SetAmbientAndDirectionalColours(float f);
void SetBrightMarkerColours(float f);
void ReSetAmbientAndDirectionalColours(void);
@@ -6,4 +14,5 @@ void DeActivateDirectional(void);
void ActivateDirectional(void);
void SetAmbientColours(void);
void SetAmbientColoursForPedsCarsAndObjects(void);
-void SetAmbientColoursToIndicateRoadGroup(int i); \ No newline at end of file
+void SetAmbientColoursToIndicateRoadGroup(int i);
+void SetAmbientColours(RwRGBAReal *color);
diff --git a/src/render/PointLights.cpp b/src/render/PointLights.cpp
index 34cfa658..0eb41821 100644
--- a/src/render/PointLights.cpp
+++ b/src/render/PointLights.cpp
@@ -1,13 +1,294 @@
#include "common.h"
#include "patcher.h"
+#include "Lights.h"
+#include "Camera.h"
+#include "Weather.h"
+#include "World.h"
+#include "Collision.h"
+#include "Sprite.h"
+#include "Timer.h"
#include "PointLights.h"
int16 &CPointLights::NumLights = *(int16*)0x95CC3E;
+CRegisteredPointLight *CPointLights::aLights = (CRegisteredPointLight*)0x7096D0;
-WRAPPER void CPointLights::RenderFogEffect(void) { EAXJMP(0x510C30); }
+//WRAPPER void CPointLights::RenderFogEffect(void) { EAXJMP(0x510C30); }
void
CPointLights::InitPerFrame(void)
{
NumLights = 0;
}
+
+#define MAX_DIST 22.0f
+
+void
+CPointLights::AddLight(uint8 type, CVector coors, CVector dir, float radius, float red, float green, float blue, uint8 fogType, bool castExtraShadows)
+{
+ CVector dist;
+ float distance;
+
+ // The check is done in some weird way in the game
+ // we're doing it a bit better here
+ if(NumLights >= NUMPOINTLIGHTS)
+ return;
+
+ dist = coors - TheCamera.GetPosition();
+ if(fabs(dist.x) < MAX_DIST && fabs(dist.y) < MAX_DIST){
+ distance = dist.Magnitude();
+ if(distance < MAX_DIST){
+ aLights[NumLights].type = type;
+ aLights[NumLights].fogType = fogType;
+ aLights[NumLights].coors = coors;
+ aLights[NumLights].dir = dir;
+ aLights[NumLights].radius = radius;
+ aLights[NumLights].castExtraShadows = castExtraShadows;
+ if(distance < MAX_DIST*0.75f){
+ aLights[NumLights].red = red;
+ aLights[NumLights].green = green;
+ aLights[NumLights].blue = blue;
+ }else{
+ float fade = 1.0f - (distance/MAX_DIST - 0.75f)*4.0f;
+ aLights[NumLights].red = red * fade;
+ aLights[NumLights].green = green * fade;
+ aLights[NumLights].blue = blue * fade;
+ }
+ NumLights++;
+ }
+ }
+}
+
+float
+CPointLights::GenerateLightsAffectingObject(CVector *objCoors)
+{
+ int i;
+ float ret;
+ CVector dist;
+ float radius, distance;
+
+ ret = 1.0f;
+ for(i = 0; i < NumLights; i++){
+ if(aLights[i].type == LIGHT_FOGONLY_3 || aLights[i].type == LIGHT_FOGONLY_4)
+ continue;
+
+ // same weird distance calculation. simplified here
+ dist = aLights[i].coors - *objCoors;
+ radius = aLights[i].radius;
+ if(fabs(dist.x) < radius &&
+ fabs(dist.y) < radius &&
+ fabs(dist.z) < radius){
+
+ distance = dist.Magnitude();
+ if(distance < radius){
+
+ float distNorm = distance/radius;
+ if(aLights[i].type == LIGHT_DARKEN){
+ // darken the object the closer it is
+ ret *= distNorm;
+ }else{
+ float intensity;
+ if(distNorm < 0.5f)
+ // near enough
+ intensity = 1.0f;
+ else
+ // attenuate
+ intensity = 1.0f - (distNorm - 0.5f)*2.0f;
+
+ if(distance != 0.0f){
+ CVector dir = dist / distance;
+
+ if(aLights[i].type == LIGHT_DIRECTIONAL){
+ float dot = -DotProduct(dir, aLights[i].dir);
+ intensity *= max((dot-0.5f)*2.0f, 0.0f);
+ }
+
+ if(intensity > 0.0f)
+ AddAnExtraDirectionalLight(Scene.world,
+ dir.x, dir.y, dir.z,
+ aLights[i].red*intensity, aLights[i].green*intensity, aLights[i].blue*intensity);
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+extern RwRaster *&gpPointlightRaster;
+
+void
+CPointLights::RemoveLightsAffectingObject(void)
+{
+ RemoveExtraDirectionalLights(Scene.world);
+}
+
+// for directional fog
+#define FOG_AREA_LENGTH 12.0f
+#define FOG_AREA_WIDTH 5.0f
+// for pointlight fog
+#define FOG_AREA_RADIUS 9.0f
+
+float FogSizes[8] = { 1.3f, 2.0f, 1.7f, 2.0f, 1.4f, 2.1f, 1.5f, 2.3f };
+
+void
+CPointLights::RenderFogEffect(void)
+{
+ int i;
+ float fogginess;
+ CColPoint point;
+ CEntity *entity;
+ float xmin, ymin;
+ float xmax, ymax;
+ int16 xi, yi;
+ CVector spriteCoors;
+ float spritew, spriteh;
+
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpPointlightRaster);
+
+ for(i = 0; i < NumLights; i++){
+ if(aLights[i].fogType != FOG_NORMAL && aLights[i].fogType != FOG_ALWAYS)
+ continue;
+
+ fogginess = aLights[i].fogType == FOG_ALWAYS ? 1.0f : CWeather::Foggyness;
+ if(fogginess == 0.0f)
+ continue;
+
+ if(aLights[i].type == LIGHT_DIRECTIONAL){
+
+ // TODO: test this. haven't found directional fog so far
+
+ float coors2X = aLights[i].coors.x + FOG_AREA_LENGTH*aLights[i].dir.x;
+ float coors2Y = aLights[i].coors.y + FOG_AREA_LENGTH*aLights[i].dir.y;
+
+ if(coors2X < aLights[i].coors.x){
+ xmin = coors2X;
+ xmax = aLights[i].coors.x;
+ }else{
+ xmax = coors2X;
+ xmin = aLights[i].coors.x;
+ }
+ if(coors2Y < aLights[i].coors.y){
+ ymin = coors2Y;
+ ymax = aLights[i].coors.y;
+ }else{
+ ymax = coors2Y;
+ ymin = aLights[i].coors.y;
+ }
+
+ xmin -= 5.0f;
+ ymin -= 5.0f;
+ xmax += 5.0f;
+ ymax += 5.0f;
+
+ for(xi = (int16)xmin - (int16)xmin % 4; xi <= (int16)xmax + 4; xi += 4){
+ for(yi = (int16)ymin - (int16)ymin % 4; yi <= (int16)ymax + 4; yi += 4){
+ // Some kind of pseudo random number?
+ int r = (xi ^ yi)>>2 & 0xF;
+ if((r & 1) == 0)
+ continue;
+
+ // Check if fog effect is close enough to directional line in x and y
+ float dx = xi - aLights[i].coors.x;
+ float dy = yi - aLights[i].coors.y;
+ float dot = dx*aLights[i].dir.x + dy*aLights[i].dir.y;
+ float distsq = sq(dx) + sq(dy);
+ float linedistsq = distsq - sq(dot);
+ if(dot > 0.0f && dot < FOG_AREA_LENGTH && linedistsq < sq(FOG_AREA_WIDTH)){
+ CVector fogcoors(xi, yi, aLights[i].coors.z + 1.0f);
+ if(CWorld::ProcessVerticalLine(fogcoors, fogcoors.z - 20.0f,
+ point, entity, true, false, false, false, true, false, nil)){
+ // Now same check again in xyz
+ fogcoors.z = point.point.z + 1.3f;
+ // actually we don't have to recalculate x and y, but the game does it that way
+ dx = xi - aLights[i].coors.x;
+ dy = yi - aLights[i].coors.y;
+ float dz = fogcoors.z - aLights[i].coors.z;
+ dot = dx*aLights[i].dir.x + dy*aLights[i].dir.y + dz*aLights[i].dir.z;
+ distsq = sq(dx) + sq(dy) + sq(dz);
+ linedistsq = distsq - sq(dot);
+ if(dot > 0.0f && dot < FOG_AREA_LENGTH && linedistsq < sq(FOG_AREA_WIDTH)){
+ float intensity = 158.0f * fogginess;
+ // more intensity the smaller the angle
+ intensity *= dot/sqrt(distsq);
+ // more intensity the closer to light source
+ intensity *= 1.0f - sq(dot/FOG_AREA_LENGTH);
+ // more intensity the closer to line
+ intensity *= 1.0f - sq(sqrt(linedistsq) / FOG_AREA_WIDTH);
+
+ if(CSprite::CalcScreenCoors(fogcoors, spriteCoors, &spritew, &spriteh, true)){
+ float rotation = (CTimer::GetTimeInMilliseconds()&0x1FFF) * 2*3.14f / 0x1FFF;
+ float size = FogSizes[r>>1];
+ CSprite::RenderOneXLUSprite_Rotate_Aspect(spriteCoors.x, spriteCoors.y, spriteCoors.z,
+ spritew * size, spriteh * size,
+ aLights[i].red * intensity, aLights[i].green * intensity, aLights[i].blue * intensity,
+ intensity, 1/spriteCoors.z, rotation, 255);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }else if(aLights[i].type == LIGHT_POINT || aLights[i].type == LIGHT_FOGONLY_3 || aLights[i].type == LIGHT_FOGONLY_4){
+ if(CWorld::ProcessVerticalLine(aLights[i].coors, aLights[i].coors.z - 20.0f,
+ point, entity, true, false, false, false, true, false, nil)){
+
+ xmin = aLights[i].coors.x - FOG_AREA_RADIUS;
+ ymin = aLights[i].coors.y - FOG_AREA_RADIUS;
+ xmax = aLights[i].coors.x + FOG_AREA_RADIUS;
+ ymax = aLights[i].coors.y + FOG_AREA_RADIUS;
+
+ for(xi = (int16)xmin - (int16)xmin % 2; xi <= (int16)xmax + 2; xi += 2){
+ for(yi = (int16)ymin - (int16)ymin % 2; yi <= (int16)ymax + 2; yi += 2){
+ // Some kind of pseudo random number?
+ int r = (xi ^ yi)>>1 & 0xF;
+ if((r & 1) == 0)
+ continue;
+
+ float dx = xi - aLights[i].coors.x;
+ float dy = yi - aLights[i].coors.y;
+ float lightdist = sqrt(sq(dx) + sq(dy));
+ if(lightdist < FOG_AREA_RADIUS){
+ dx = xi - TheCamera.GetPosition().x;
+ dy = yi - TheCamera.GetPosition().y;
+ float camdist = sqrt(sq(dx) + sq(dy));
+ if(camdist < MAX_DIST){
+ float intensity;
+ // distance fade
+ if(camdist < MAX_DIST/2)
+ intensity = 1.0f;
+ else
+ intensity = 1.0f - (camdist - MAX_DIST/2) / (MAX_DIST/2);
+ intensity *= 132.0f * fogginess;
+ // more intensity the closer to light source
+ intensity *= 1.0f - sq(lightdist / FOG_AREA_RADIUS);
+
+ CVector fogcoors(xi, yi, point.point.z + 1.6f);
+ if(CSprite::CalcScreenCoors(fogcoors, spriteCoors, &spritew, &spriteh, true)){
+ float rotation = (CTimer::GetTimeInMilliseconds()&0x3FFF) * 2*3.14f / 0x3FFF;
+ float size = FogSizes[r>>1];
+ CSprite::RenderOneXLUSprite_Rotate_Aspect(spriteCoors.x, spriteCoors.y, spriteCoors.z,
+ spritew * size, spriteh * size,
+ aLights[i].red * intensity, aLights[i].green * intensity, aLights[i].blue * intensity,
+ intensity, 1/spriteCoors.z, rotation, 255);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+STARTPATCHES
+ InjectHook(0x510790, CPointLights::AddLight, PATCH_JUMP);
+ InjectHook(0x510960, CPointLights::GenerateLightsAffectingObject, PATCH_JUMP);
+ InjectHook(0x510C20, CPointLights::RemoveLightsAffectingObject, PATCH_JUMP);
+ InjectHook(0x510C30, CPointLights::RenderFogEffect, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/render/PointLights.h b/src/render/PointLights.h
index e72e8ede..288571d0 100644
--- a/src/render/PointLights.h
+++ b/src/render/PointLights.h
@@ -1,9 +1,43 @@
#pragma once
+class CRegisteredPointLight
+{
+public:
+ CVector coors;
+ CVector dir;
+ float radius;
+ float red;
+ float green;
+ float blue;
+ int8 type;
+ int8 fogType;
+ bool castExtraShadows;
+};
+static_assert(sizeof(CRegisteredPointLight) == 0x2C, "CRegisteredPointLight: error");
+
class CPointLights
{
+ // Probably have to make this public for shadows later
static int16 &NumLights;
+ static CRegisteredPointLight *aLights; //[NUMPOINTLIGHTS]
public:
+ enum {
+ LIGHT_POINT,
+ LIGHT_DIRECTIONAL,
+ LIGHT_DARKEN, // no effects at all
+ // these have only fog, otherwise no difference?
+ LIGHT_FOGONLY_3,
+ LIGHT_FOGONLY_4,
+ };
+ enum {
+ FOG_NONE,
+ FOG_NORMAL, // taken from Foggyness
+ FOG_ALWAYS
+ };
+
static void InitPerFrame(void);
+ static void AddLight(uint8 type, CVector coors, CVector dir, float radius, float red, float green, float blue, uint8 fogType, bool castExtraShadows);
+ static float GenerateLightsAffectingObject(CVector *objCoors);
+ static void RemoveLightsAffectingObject(void);
static void RenderFogEffect(void);
};
diff --git a/src/render/Sprite.cpp b/src/render/Sprite.cpp
index 2b669c87..92c3e8a6 100644
--- a/src/render/Sprite.cpp
+++ b/src/render/Sprite.cpp
@@ -132,6 +132,65 @@ CSprite::RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r
}
void
+CSprite::RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a)
+{
+ float c = cos(DEGTORAD(rotation));
+ float s = sin(DEGTORAD(rotation));
+
+ float xs[4];
+ float ys[4];
+ float us[4];
+ float vs[4];
+ int i;
+
+ // Fade out when too near
+ // why not in buffered version?
+ if(z < 3.0f){
+ if(z < 1.5f)
+ return;
+ int f = (z - 1.5f)/1.5f * 255;
+ r = f*r >> 8;
+ g = f*g >> 8;
+ b = f*b >> 8;
+ intens = f*intens >> 8;
+ }
+
+ xs[0] = x + w*(-c-s); us[0] = 0.0f;
+ xs[1] = x + w*(-c+s); us[1] = 0.0f;
+ xs[2] = x + w*(+c+s); us[2] = 1.0f;
+ xs[3] = x + w*(+c-s); us[3] = 1.0f;
+
+ ys[0] = y + h*(-c+s); vs[0] = 0.0f;
+ ys[1] = y + h*(+c+s); vs[1] = 1.0f;
+ ys[2] = y + h*(+c-s); vs[2] = 1.0f;
+ ys[3] = y + h*(-c-s); vs[3] = 0.0f;
+
+ // No clipping, just culling
+ if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return;
+ if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return;
+ if(xs[0] > 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);
+
+ 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;
@@ -537,6 +596,7 @@ STARTPATCHES
InjectHook(0x51C5B0, CSprite::InitSpriteBuffer2D, PATCH_JUMP);
InjectHook(0x51C520, CSprite::FlushSpriteBuffer, PATCH_JUMP);
InjectHook(0x51C960, CSprite::RenderOneXLUSprite, PATCH_JUMP);
+ InjectHook(0x51D110, CSprite::RenderOneXLUSprite_Rotate_Aspect, PATCH_JUMP);
InjectHook(0x51C5D0, CSprite::RenderBufferedOneXLUSprite, PATCH_JUMP);
InjectHook(0x51D5B0, CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension, PATCH_JUMP);
InjectHook(0x51CCD0, CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect, PATCH_JUMP);
diff --git a/src/render/Sprite.h b/src/render/Sprite.h
index 7bc5c35e..33953ff3 100644
--- a/src/render/Sprite.h
+++ b/src/render/Sprite.h
@@ -13,6 +13,7 @@ public:
static void InitSpriteBuffer2D(void);
static void FlushSpriteBuffer(void);
static void RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a);
+ static void RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
static void RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a);
static void RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
static void RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a);
diff --git a/src/render/Sprite2d.cpp b/src/render/Sprite2d.cpp
index 3c699650..fd5900b8 100644
--- a/src/render/Sprite2d.cpp
+++ b/src/render/Sprite2d.cpp
@@ -78,7 +78,7 @@ CSprite2d::Delete(void)
}
void
-CSprite2d::SetTexture(char *name)
+CSprite2d::SetTexture(const char *name)
{
Delete();
if(name)
@@ -86,7 +86,7 @@ CSprite2d::SetTexture(char *name)
}
void
-CSprite2d::SetTexture(char *name, char *mask)
+CSprite2d::SetTexture(const char *name, const char *mask)
{
Delete();
if(name)
@@ -468,8 +468,8 @@ STARTPATCHES
InjectHook(0x51EA00, &CSprite2d::Delete, PATCH_JUMP);
InjectHook(0x51F950, &CSprite2d::SetRenderState, PATCH_JUMP);
- InjectHook(0x51EA40, (void (CSprite2d::*)(char*))&CSprite2d::SetTexture, PATCH_JUMP);
- InjectHook(0x51EA70, (void (CSprite2d::*)(char*,char*))&CSprite2d::SetTexture, PATCH_JUMP);
+ InjectHook(0x51EA40, (void (CSprite2d::*)(const char*))&CSprite2d::SetTexture, PATCH_JUMP);
+ InjectHook(0x51EA70, (void (CSprite2d::*)(const char*,const char*))&CSprite2d::SetTexture, PATCH_JUMP);
InjectHook(0x51EAA0, &CSprite2d::SetAddressing, PATCH_JUMP);
InjectHook(0x51EE90, (void (*)(const CRect&, C4, uint32))CSprite2d::SetVertices, PATCH_JUMP);
diff --git a/src/render/Sprite2d.h b/src/render/Sprite2d.h
index 1813d4ee..d5f0a5ae 100644
--- a/src/render/Sprite2d.h
+++ b/src/render/Sprite2d.h
@@ -23,8 +23,8 @@ public:
~CSprite2d(void) { Delete(); };
void Delete(void);
void SetRenderState(void);
- void SetTexture(char *name);
- void SetTexture(char *name, char *mask);
+ void SetTexture(const char *name);
+ void SetTexture(const char *name, const char *mask);
void SetAddressing(RwTextureAddressMode addr);
void Draw(float x, float y, float w, float h, const CRGBA &col);
void Draw(const CRect &rect, const CRGBA &col);