From c40c7acb96a926378df7d7b43920966ab31ff95e Mon Sep 17 00:00:00 2001 From: aap Date: Sat, 1 Jun 2019 19:18:19 +0200 Subject: [PATCH] added CPointLights --- src/Zones.cpp | 20 +++ src/config.h | 5 +- src/re3.cpp | 27 ++++ src/render/PointLights.cpp | 283 ++++++++++++++++++++++++++++++++++++- src/render/PointLights.h | 34 +++++ 5 files changed, 366 insertions(+), 3 deletions(-) diff --git a/src/Zones.cpp b/src/Zones.cpp index 6caffde2..9b40db0f 100644 --- a/src/Zones.cpp +++ b/src/Zones.cpp @@ -360,9 +360,11 @@ CTheZones::GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info) else{ if(CClock::GetIsTimeInRange(19, 22)){ n = (CClock::GetHours() - 19) / 3.0f; + assert(n >= 0.0f && n <= 1.0f); d = n - 1.0f; }else{ d = (CClock::GetHours() - 5) / 3.0f; + assert(d >= 0.0f && d <= 1.0f); n = d - 1.0f; } info->carDensity = day->carDensity * d + night->carDensity * n; @@ -399,6 +401,24 @@ CTheZones::GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info) info->pedGroup = day->pedGroup; else info->pedGroup = night->pedGroup; + + assert(info->carDensity >= 0); + assert(info->carThreshold[0] >= 0); + assert(info->carThreshold[1] >= 0); + assert(info->carThreshold[2] >= 0); + assert(info->carThreshold[3] >= 0); + assert(info->carThreshold[4] >= 0); + assert(info->carThreshold[5] >= 0); + assert(info->copThreshold >= 0); + assert(info->gangThreshold[0] >= 0); + assert(info->gangThreshold[1] >= 0); + assert(info->gangThreshold[2] >= 0); + assert(info->gangThreshold[3] >= 0); + assert(info->gangThreshold[4] >= 0); + assert(info->gangThreshold[5] >= 0); + assert(info->gangThreshold[6] >= 0); + assert(info->gangThreshold[7] >= 0); + assert(info->gangThreshold[8] >= 0); } // BUG: there might be a bug somewhere in there that causes diff --git a/src/config.h b/src/config.h index f366b76e..ebfc6cdb 100644 --- a/src/config.h +++ b/src/config.h @@ -53,8 +53,9 @@ enum Config { NUMEXTRADIRECTIONALS = 4, NUMANTENNAS = 8, - NUMCORONAS = 56 + NUMCORONAS = 56, + NUMPOINTLIGHTS = 32 }; #define GTA3_1_1_PATCH FALSE -#define USE_PS2_RAND FALSE \ No newline at end of file +#define USE_PS2_RAND FALSE diff --git a/src/re3.cpp b/src/re3.cpp index 572f4c05..e99384e1 100644 --- a/src/re3.cpp +++ b/src/re3.cpp @@ -111,6 +111,29 @@ delayedPatches10(int a, int b) return RsEventHandler_orig(a, b); } +void __declspec(naked) HeadlightsFix() +{ + static const float fMinusOne = -1.0f; + _asm + { + fld [esp+708h-690h] + fcomp fMinusOne + fnstsw ax + and ah, 5 + cmp ah, 1 + jnz HeadlightsFix_DontLimit + fld fMinusOne + fstp [esp+708h-690h] + +HeadlightsFix_DontLimit: + fld [esp+708h-690h] + fabs + fld st + push 0x5382F2 + retn + } +} + void patch() @@ -120,6 +143,10 @@ patch() Patch(0x46BC61+6, 1.0f); // car distance InjectHook(0x59E460, printf, PATCH_JUMP); + // stolen from silentpatch (sorry) + Patch(0x5382BF, 0x0EEB); + InjectHook(0x5382EC, HeadlightsFix, PATCH_JUMP); + InterceptCall(&open_script_orig, open_script, 0x438869); InterceptCall(&RsEventHandler_orig, delayedPatches10, 0x58275E); 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); };