diff options
-rw-r--r-- | src/Camera.cpp | 11 | ||||
-rw-r--r-- | src/Camera.h | 1 | ||||
-rw-r--r-- | src/Text.cpp | 20 | ||||
-rw-r--r-- | src/Text.h | 12 | ||||
-rw-r--r-- | src/Timecycle.h | 9 | ||||
-rw-r--r-- | src/Zones.cpp | 51 | ||||
-rw-r--r-- | src/Zones.h | 8 | ||||
-rw-r--r-- | src/common.h | 3 | ||||
-rw-r--r-- | src/config.h | 7 | ||||
-rw-r--r-- | src/main.cpp | 165 | ||||
-rw-r--r-- | src/math/Vector.h | 1 | ||||
-rw-r--r-- | src/re3.cpp | 32 | ||||
-rw-r--r-- | src/render/Coronas.cpp | 578 | ||||
-rw-r--r-- | src/render/Coronas.h | 84 | ||||
-rw-r--r-- | src/render/Credits.cpp | 2 | ||||
-rw-r--r-- | src/render/Credits.h | 2 | ||||
-rw-r--r-- | src/render/Lights.cpp | 164 | ||||
-rw-r--r-- | src/render/Lights.h | 11 | ||||
-rw-r--r-- | src/render/PointLights.cpp | 283 | ||||
-rw-r--r-- | src/render/PointLights.h | 34 | ||||
-rw-r--r-- | src/render/Sprite.cpp | 60 | ||||
-rw-r--r-- | src/render/Sprite.h | 1 | ||||
-rw-r--r-- | src/render/Sprite2d.cpp | 8 | ||||
-rw-r--r-- | src/render/Sprite2d.h | 4 |
24 files changed, 1505 insertions, 46 deletions
diff --git a/src/Camera.cpp b/src/Camera.cpp index fe96f574..98966549 100644 --- a/src/Camera.cpp +++ b/src/Camera.cpp @@ -69,6 +69,17 @@ CCamera::IsBoxVisible(RwV3d *box, const CMatrix *mat) return true; } +int +CCamera::GetLookDirection(void) +{ + if(Cams[ActiveCam].Mode == CCam::MODE_CAMONASTRING || + Cams[ActiveCam].Mode == CCam::MODE_FIRSTPERSON || + Cams[ActiveCam].Mode == CCam::MODE_BEHINDBOAT || + Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED) + return Cams[ActiveCam].DirectionWasLooking; + return LOOKING_FORWARD;; +} + WRAPPER void CCamera::Fade(float timeout, int16 direction) { EAXJMP(0x46B3A0); } WRAPPER void CCamera::ProcessFade(void) { EAXJMP(0x46F080); } WRAPPER void CCamera::ProcessMusicFade(void) { EAXJMP(0x46F1E0); } diff --git a/src/Camera.h b/src/Camera.h index 30ca98e6..a5602cf0 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -444,6 +444,7 @@ int m_iModeObbeCamIsInForCar; bool IsPointVisible(const CVector ¢er, const CMatrix *mat); bool IsSphereVisible(const CVector ¢er, float radius, const CMatrix *mat); bool IsBoxVisible(RwV3d *box, const CMatrix *mat); + int GetLookDirection(void); void Fade(float timeout, int16 direction); int GetScreenFadeStatus(void); diff --git a/src/Text.cpp b/src/Text.cpp index 0281043d..eeb46253 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -5,7 +5,7 @@ #include "Messages.h" #include "Text.h" -static wchar_t WideErrorString[25]; +static wchar WideErrorString[25]; CText &TheText = *(CText*)0x941520; @@ -90,7 +90,7 @@ CText::Unload(void) keyArray.Unload(); } -wchar_t* +wchar* CText::Get(const char *key) { return keyArray.Search(key); @@ -119,11 +119,11 @@ CKeyArray::Unload(void) } void -CKeyArray::Update(wchar_t *chars) +CKeyArray::Update(wchar *chars) { int i; for(i = 0; i < numEntries; i++) - entries[i].value = (wchar_t*)((uint8*)chars + (uintptr)entries[i].value); + entries[i].value = (wchar*)((uint8*)chars + (uintptr)entries[i].value); } CKeyEntry* @@ -146,7 +146,7 @@ CKeyArray::BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 hi return nil; } -wchar_t* +wchar* CKeyArray::Search(const char *key) { CKeyEntry *found; @@ -169,8 +169,8 @@ CData::Load(uint32 length, uint8 *data, int *offset) uint32 i; uint8 *rawbytes; - numChars = length / sizeof(wchar_t); - chars = new wchar_t[numChars]; + numChars = length / sizeof(wchar); + chars = new wchar[numChars]; rawbytes = (uint8*)chars; for(i = 0; i < length; i++) @@ -185,6 +185,12 @@ CData::Unload(void) numChars = 0; } +void +AsciiToUnicode(const char *cs, uint16 *ws) +{ + while((*ws++ = *cs++) != '\0'); +} + STARTPATCHES InjectHook(0x52C3C0, &CText::Load, PATCH_JUMP); InjectHook(0x52C580, &CText::Unload, PATCH_JUMP); @@ -1,8 +1,10 @@ #pragma once +void AsciiToUnicode(const char *cs, wchar *ws); + struct CKeyEntry { - wchar_t *value; + wchar *value; char key[8]; }; // If this fails, CKeyArray::Load will have to be fixed @@ -16,15 +18,15 @@ public: void Load(uint32 length, uint8 *data, int *offset); void Unload(void); - void Update(wchar_t *chars); + void Update(wchar *chars); CKeyEntry *BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 high); - wchar_t *Search(const char *key); + wchar *Search(const char *key); }; class CData { public: - wchar_t *chars; + wchar *chars; int numChars; void Load(uint32 length, uint8 *data, int *offset); @@ -41,7 +43,7 @@ public: ~CText(void); void Load(void); void Unload(void); - wchar_t *Get(const char *key); + wchar *Get(const char *key); }; extern CText &TheText; diff --git a/src/Timecycle.h b/src/Timecycle.h index 1698e0c5..fa59dfd1 100644 --- a/src/Timecycle.h +++ b/src/Timecycle.h @@ -104,6 +104,13 @@ public: static int GetSkyBottomRed(void) { return m_nCurrentSkyBottomRed; } static int GetSkyBottomGreen(void) { return m_nCurrentSkyBottomGreen; } static int GetSkyBottomBlue(void) { return m_nCurrentSkyBottomBlue; } + static int GetSunCoreRed(void) { return m_nCurrentSunCoreRed; } + static int GetSunCoreGreen(void) { return m_nCurrentSunCoreGreen; } + static int GetSunCoreBlue(void) { return m_nCurrentSunCoreBlue; } + static int GetSunCoronaRed(void) { return m_nCurrentSunCoronaRed; } + static int GetSunCoronaGreen(void) { return m_nCurrentSunCoronaGreen; } + static int GetSunCoronaBlue(void) { return m_nCurrentSunCoronaBlue; } + static float GetSunSize(void) { return m_fCurrentSunSize; } static float GetFarClip(void) { return m_fCurrentFarClip; } static float GetFogStart(void) { return m_fCurrentFogStart; } @@ -119,4 +126,6 @@ public: static int GetFogRed(void) { return m_nCurrentFogColourRed; } static int GetFogGreen(void) { return m_nCurrentFogColourGreen; } static int GetFogBlue(void) { return m_nCurrentFogColourBlue; } + + static const CVector &GetSunPosition(void) { return m_VectorToSun[m_CurrentStoredValue]; } }; diff --git a/src/Zones.cpp b/src/Zones.cpp index 6c8f66ce..741fff7d 100644 --- a/src/Zones.cpp +++ b/src/Zones.cpp @@ -19,6 +19,26 @@ CZoneInfo *CTheZones::ZoneInfoArray = (CZoneInfo*)0x714400; #define SWAPF(a, b) { float t; t = a; a = b; b = t; } +static void +CheckZoneInfo(CZoneInfo *info) +{ + assert(info->carThreshold[0] >= 0); + assert(info->carThreshold[0] <= info->carThreshold[1]); + assert(info->carThreshold[1] <= info->carThreshold[2]); + assert(info->carThreshold[2] <= info->carThreshold[3]); + assert(info->carThreshold[3] <= info->carThreshold[4]); + assert(info->carThreshold[4] <= info->carThreshold[5]); + assert(info->carThreshold[5] <= info->copThreshold); + assert(info->copThreshold <= info->gangThreshold[0]); + assert(info->gangThreshold[0] <= info->gangThreshold[1]); + assert(info->gangThreshold[1] <= info->gangThreshold[2]); + assert(info->gangThreshold[2] <= info->gangThreshold[3]); + assert(info->gangThreshold[3] <= info->gangThreshold[4]); + assert(info->gangThreshold[4] <= info->gangThreshold[5]); + assert(info->gangThreshold[5] <= info->gangThreshold[6]); + assert(info->gangThreshold[6] <= info->gangThreshold[7]); + assert(info->gangThreshold[7] <= info->gangThreshold[8]); +} void CTheZones::Init(void) @@ -49,6 +69,7 @@ CTheZones::Init(void) zonei->gangThreshold[6] = zonei->gangThreshold[5]; zonei->gangThreshold[7] = zonei->gangThreshold[6]; zonei->gangThreshold[8] = zonei->gangThreshold[7]; + CheckZoneInfo(zonei); } TotalNumberOfZoneInfos = 1; // why 1? @@ -360,10 +381,12 @@ CTheZones::GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info) else{ if(CClock::GetIsTimeInRange(19, 22)){ n = (CClock::GetHours() - 19) / 3.0f; - d = n - 1.0f; + assert(n >= 0.0f && n <= 1.0f); + d = 1.0f - n; }else{ d = (CClock::GetHours() - 5) / 3.0f; - n = d - 1.0f; + assert(d >= 0.0f && d <= 1.0f); + n = 1.0f - d; } info->carDensity = day->carDensity * d + night->carDensity * n; info->carThreshold[0] = day->carThreshold[0] * d + night->carThreshold[0] * n; @@ -399,6 +422,8 @@ CTheZones::GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info) info->pedGroup = day->pedGroup; else info->pedGroup = night->pedGroup; + + CheckZoneInfo(info); } void @@ -415,6 +440,8 @@ CTheZones::SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity, zone = GetZone(zoneid); info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; + CheckZoneInfo(info); + if(carDensity != -1) info->carDensity = carDensity; int16 oldCar1Num = info->carThreshold[1] - info->carThreshold[0]; int16 oldCar2Num = info->carThreshold[2] - info->carThreshold[1]; @@ -463,6 +490,8 @@ CTheZones::SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity, else info->gangThreshold[7] = info->gangThreshold[6] + oldGang7Num; if(gang8Num != -1) info->gangThreshold[8] = info->gangThreshold[7] + gang8Num; else info->gangThreshold[8] = info->gangThreshold[7] + oldGang8Num; + + CheckZoneInfo(info); } void @@ -477,15 +506,15 @@ CTheZones::SetZonePedInfo(uint16 zoneid, uint8 day, int16 pedDensity, info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; if(pedDensity != -1) info->pedDensity = pedDensity; if(copDensity != -1) info->copDensity = copDensity; - if(gang0Density != -1) info->gangThreshold[0] = gang0Density; - if(gang1Density != -1) info->gangThreshold[1] = gang1Density; - if(gang2Density != -1) info->gangThreshold[2] = gang2Density; - if(gang3Density != -1) info->gangThreshold[3] = gang3Density; - if(gang4Density != -1) info->gangThreshold[4] = gang4Density; - if(gang5Density != -1) info->gangThreshold[5] = gang5Density; - if(gang6Density != -1) info->gangThreshold[6] = gang6Density; - if(gang7Density != -1) info->gangThreshold[7] = gang7Density; - if(gang8Density != -1) info->gangThreshold[8] = gang8Density; + if(gang0Density != -1) info->gangDensity[0] = gang0Density; + if(gang1Density != -1) info->gangDensity[1] = gang1Density; + if(gang2Density != -1) info->gangDensity[2] = gang2Density; + if(gang3Density != -1) info->gangDensity[3] = gang3Density; + if(gang4Density != -1) info->gangDensity[4] = gang4Density; + if(gang5Density != -1) info->gangDensity[5] = gang5Density; + if(gang6Density != -1) info->gangDensity[6] = gang6Density; + if(gang7Density != -1) info->gangDensity[7] = gang7Density; + if(gang8Density != -1) info->gangDensity[8] = gang8Density; } void diff --git a/src/Zones.h b/src/Zones.h index c7745e29..291e6f60 100644 --- a/src/Zones.h +++ b/src/Zones.h @@ -33,10 +33,10 @@ class CZoneInfo { public: // Car data - uint16 carDensity; - uint16 carThreshold[6]; - uint16 copThreshold; - uint16 gangThreshold[9]; + int16 carDensity; + int16 carThreshold[6]; + int16 copThreshold; + int16 gangThreshold[9]; // Ped data uint16 pedDensity; diff --git a/src/common.h b/src/common.h index 6031364f..9acf7638 100644 --- a/src/common.h +++ b/src/common.h @@ -40,12 +40,13 @@ typedef int32_t int32, Int32; typedef uintptr_t uintptr; typedef uint64_t uint64, UInt64; typedef int64_t int64, Int64; +// hardcode ucs-2 +typedef uint16_t wchar, WChar; typedef float Float; typedef double Double; typedef bool Bool; typedef char Char; -typedef wchar_t WChar; #define nil NULL diff --git a/src/config.h b/src/config.h index c12f2da2..3a4896a2 100644 --- a/src/config.h +++ b/src/config.h @@ -51,8 +51,13 @@ enum Config { NUMWEATHERS = 4, NUMHOURS = 24, + NUMEXTRADIRECTIONALS = 4, NUMANTENNAS = 8, + 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 +#define RANDOMSPLASH +#define CHATTYSPLASH diff --git a/src/main.cpp b/src/main.cpp index 88d45994..cd2da378 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,9 @@ #include "Credits.h" #include "CullZones.h" #include "TimeCycle.h" +#include "TxdStore.h" +#include "FileMgr.h" +#include "Text.h" #include "Frontend.h" #define DEFAULT_VIEWWINDOW (tan(CDraw::GetFOV() * (360.0f / PI))) @@ -69,6 +72,10 @@ void RenderMenus(void); void DoFade(void); void Render2dStuffAfterFade(void); +CSprite2d *LoadSplash(const char *name); +void DestroySplashScreen(void); + + extern void (*DebugMenuProcess)(void); extern void (*DebugMenuRender)(void); @@ -180,6 +187,30 @@ FrontendIdle(void) } bool +DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha) +{ + CRGBA TopColor(TopRed, TopGreen, TopBlue, Alpha); + CRGBA BottomColor(BottomRed, BottomGreen, BottomBlue, Alpha); + + float viewWindow = tan(DEGTORAD(CDraw::GetFOV() * 0.5f)); + // ASPECT + float aspectRatio = CMenuManager::m_PrefsUseWideScreen ? 16.0f/9.0f : 4.0f/3.0f; + CameraSize(Scene.camera, nil, viewWindow, aspectRatio); + CVisibilityPlugins::SetRenderWareCamera(Scene.camera); + RwCameraClear(Scene.camera, &gColourTop, rwCAMERACLEARZ); + + if(!RsCameraBeginUpdate(Scene.camera)) + return false; + + CSprite2d::InitPerFrame(); + + if(Alpha != 0) + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREENW, SCREENH), BottomColor, BottomColor, TopColor, TopColor); + + return true; +} + +bool DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha) { float viewWindow = tan(DEGTORAD(CDraw::GetFOV() * 0.5f)); @@ -360,7 +391,7 @@ DoFade(void) } if(CDraw::FadeValue != 0 || CMenuManager::m_PrefsBrightness < 256){ - // LoadSplash + CSprite2d *splash = LoadSplash(nil); CRGBA fadeColor; CRect rect; @@ -410,7 +441,7 @@ DoFade(void) fadeColor.g = 255; fadeColor.b = 255; fadeColor.a = CDraw::FadeValue; - CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREENW, SCREENH), fadeColor, fadeColor, fadeColor, fadeColor); + splash->Draw(CRect(0.0f, 0.0f, SCREENW, SCREENH), fadeColor, fadeColor, fadeColor, fadeColor); } } } @@ -422,6 +453,130 @@ Render2dStuffAfterFade(void) CFont::DrawFonts(); } +CSprite2d splash; +int splashTxdId = -1; + +CSprite2d* +LoadSplash(const char *name) +{ + RwTexDictionary *txd; + char filename[140]; + RwTexture *tex = nil; + + if(name == nil) + return &splash; + if(splashTxdId == -1) + splashTxdId = CTxdStore::AddTxdSlot("splash"); + + txd = CTxdStore::GetSlot(splashTxdId)->texDict; + if(txd) + tex = RwTexDictionaryFindNamedTexture(txd, name); + // if texture is found, splash was already set up below + + if(tex == nil){ + CFileMgr::SetDir("TXD\\"); + sprintf(filename, "%s.txd", name); + if(splash.m_pTexture) + splash.Delete(); + if(txd) + CTxdStore::RemoveTxd(splashTxdId); + CTxdStore::LoadTxd(splashTxdId, filename); + CTxdStore::AddRef(splashTxdId); + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(splashTxdId); + splash.SetTexture(name); + CTxdStore::PopCurrentTxd(); + CFileMgr::SetDir(""); + } + + return &splash; +} + +void +DestroySplashScreen(void) +{ + splash.Delete(); + if(splashTxdId != -1) + CTxdStore::RemoveTxdSlot(splashTxdId); + splashTxdId = -1; +} + +float NumberOfChunksLoaded; +#define TOTALNUMCHUNKS 73.0f + +// TODO: compare with PS2 +void +LoadingScreen(char *str1, char *str2, char *splashscreen) +{ + CSprite2d *splash; + +#ifndef RANDOMSPLASH + if(CGame::frenchGame || CGame::germanGame || !CGame::nastyGame) + splashscreen = "mainsc2"; + else + splashscreen = "mainsc1"; +#endif + + splash = LoadSplash(splashscreen); + + if(RsGlobal.quit) + return; + + if(DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)){ + CSprite2d::SetRecipNearClip(); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + DefinedState(); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + splash->Draw(CRect(0.0f, 0.0f, SCREENW, SCREENH), CRGBA(255, 255, 255, 255)); + + if(str1){ + NumberOfChunksLoaded += 1; + + float hpos = SCREEN_STRETCH_X(40); + float length = SCREENW - SCREEN_STRETCH_X(100); + float vpos = SCREENH - SCREEN_STRETCH_Y(13); + float height = SCREEN_STRETCH_Y(7); + CSprite2d::DrawRect(CRect(hpos, vpos, hpos + length, vpos + height), CRGBA(40, 53, 68, 255)); + + length *= NumberOfChunksLoaded/TOTALNUMCHUNKS; + CSprite2d::DrawRect(CRect(hpos, vpos, hpos + length, vpos + height), CRGBA(81, 106, 137, 255)); + + // this is done by the game but is unused + CFont::SetScale(SCREEN_STRETCH_X(2), SCREEN_STRETCH_Y(2)); + CFont::SetPropOn(); + CFont::SetRightJustifyOn(); + CFont::SetFontStyle(FONT_HEADING); + +#ifdef CHATTYSPLASH + // my attempt + static wchar tmpstr[80]; + float scale = SCREEN_STRETCH_Y(0.8f); + vpos -= 50*scale; + CFont::SetScale(scale, scale); + CFont::SetPropOn(); + CFont::SetRightJustifyOff(); + CFont::SetFontStyle(FONT_BANK); + CFont::SetColor(CRGBA(255, 255, 255, 255)); + AsciiToUnicode(str1, tmpstr); + CFont::PrintString(hpos, vpos, tmpstr); + vpos += 25*scale; + AsciiToUnicode(str2, tmpstr); + CFont::PrintString(hpos, vpos, tmpstr); +#endif + } + + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); + } +} + +void +ResetLoadingScreenBar(void) +{ + NumberOfChunksLoaded = 0.0f; +} + #include "rwcore.h" #include "rpworld.h" #include "rpmatfx.h" @@ -589,6 +744,7 @@ STARTPATCHES InjectHook(0x48E480, Idle, PATCH_JUMP); InjectHook(0x48E700, FrontendIdle, PATCH_JUMP); + InjectHook(0x48CF10, DoRWStuffStartOfFrame, PATCH_JUMP); InjectHook(0x48D040, DoRWStuffStartOfFrame_Horizon, PATCH_JUMP); InjectHook(0x48E030, RenderScene, PATCH_JUMP); InjectHook(0x48E080, RenderDebugShit, PATCH_JUMP); @@ -597,6 +753,11 @@ STARTPATCHES InjectHook(0x48E450, RenderMenus, PATCH_JUMP); InjectHook(0x48D120, DoFade, PATCH_JUMP); InjectHook(0x48E470, Render2dStuffAfterFade, PATCH_JUMP); + + InjectHook(0x48D550, LoadSplash, PATCH_JUMP); + InjectHook(0x48D670, DestroySplashScreen, PATCH_JUMP); + InjectHook(0x48D770, LoadingScreen, PATCH_JUMP); + InjectHook(0x48D760, ResetLoadingScreenBar, PATCH_JUMP); InjectHook(0x48D470, PluginAttach, PATCH_JUMP); InjectHook(0x48D520, Initialise3D, PATCH_JUMP); diff --git a/src/math/Vector.h b/src/math/Vector.h index d89ca375..60fcdee5 100644 --- a/src/math/Vector.h +++ b/src/math/Vector.h @@ -25,6 +25,7 @@ public: float Magnitude(void) const { return sqrt(x*x + y*y + z*z); } float MagnitudeSqr(void) const { return x*x + y*y + z*z; } float Magnitude2D(void) const { return sqrt(x*x + y*y); } + float MagnitudeSqr2D(void) const { return x*x + y*y; } void Normalise(void) { float sq = MagnitudeSqr(); if(sq > 0.0f){ diff --git a/src/re3.cpp b/src/re3.cpp index b3484c60..b7404ba2 100644 --- a/src/re3.cpp +++ b/src/re3.cpp @@ -61,6 +61,8 @@ int (*open_script_orig)(const char *path, const char *mode); int open_script(const char *path, const char *mode) { + if(GetAsyncKeyState('G') & 0x8000) + return open_script_orig("main.scm", mode); if(GetAsyncKeyState('D') & 0x8000) return open_script_orig("main_d.scm", mode); // if(GetAsyncKeyState('R') & 0x8000) @@ -104,8 +106,6 @@ delayedPatches10(int a, int b) DebugMenuAddVarBool8("Debug", "Don't render Objects", (int8*)&gbDontRenderObjects, nil); DebugMenuAddVar("Debug", "Dbg Surface", &gDbgSurf, nil, 1, 0, 34, nil); - DebugMenuAddVar("Debug", "blur type", &TheCamera.m_BlurType, nil, 1, 0, 10, nil); - DebugMenuAddCmd("Debug", "Start Credits", CCredits::Start); DebugMenuAddCmd("Debug", "Stop Credits", CCredits::Stop); } @@ -113,6 +113,30 @@ 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() { @@ -121,6 +145,10 @@ patch() Patch<float>(0x46BC61+6, 1.0f); // car distance InjectHook(0x59E460, printf, PATCH_JUMP); + // stolen from silentpatch (sorry) + Patch<WORD>(0x5382BF, 0x0EEB); + InjectHook(0x5382EC, HeadlightsFix, PATCH_JUMP); + InterceptCall(&open_script_orig, open_script, 0x438869); InterceptCall(&RsEventHandler_orig, delayedPatches10, 0x58275E); 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); |