Skip to content

Commit a55518e

Browse files
committed
DrawDistance: add quicksort hack to fix sunny beach water
This probably isn't a true fix for the water, think the water/reflection textures either have bad center coord setup, or maybe there's issues with the matrix used for calculating each models screen position (which then gets used for sorting by QuickSort) Maybe similar to the znear projection matrix issue that FixZBufferPrecision worked around (likely, seeing as this issue only happens on PC, similar to the ZBuffer issue) I didn't see anything broken by just skipping quicksort on this stage though, so we'll just do that for now
1 parent 4b004c1 commit a55518e

File tree

5 files changed

+78
-2
lines changed

5 files changed

+78
-2
lines changed

OutRun2006Tweaks.lods.ini

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
# Hill on the road, DD222
2929
0x1 = 0x1F
3030

31+
# BEACH
32+
[Stage 15]
33+
# Black patch above water
34+
SkipQuickSort = True
35+
3136
# LAS VEGAS
3237
[Stage 18]
3338
# Building geo above winding section
@@ -60,6 +65,11 @@
6065
# Bad polygon when entering from bunki
6166
0x1 = 0x6
6267

68+
# BEACH (T)
69+
[Stage 61]
70+
# Black patch above water
71+
SkipQuickSort = True
72+
6373
# BEACH (BR)
6474
[Stage 65]
6575
# Bad polygon when entering from bunki

src/game.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1209,7 +1209,7 @@ typedef struct TDrawBuffer
12091209
{
12101210
int NumBuffers_0;
12111211
int MaxBuffers_4;
1212-
int unk_8[1];
1212+
int field_8;
12131213
int MaxBuffers_C;
12141214
DrawEntry** BufferPtrs_10;
12151215
DrawEntry* Buffer_14;

src/game_addrs.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ namespace Game
100100
inline fn_1arg_int GetMaxCsLen = nullptr;
101101
inline fn_1arg_char GetStageUniqueName = nullptr;
102102

103+
inline void(*QuickSort)(void*, int, int) = nullptr;
104+
inline void(*DrawStoredModel_Internal)(DrawBuffer*) = nullptr;
105+
103106
inline fn_stdcall_1arg_int Sumo_CheckRacerUnlocked = nullptr;
104107

105108
inline const char* SumoNet_OnlineUserName = nullptr;
@@ -266,6 +269,9 @@ namespace Game
266269
GetMaxCsLen = Module::fn_ptr<fn_1arg_int>(0x3D470);
267270
GetStageUniqueName = Module::fn_ptr<fn_1arg_char>(0x4BE80);
268271

272+
QuickSort = Module::fn_ptr(0x499E0, QuickSort);
273+
DrawStoredModel_Internal = Module::fn_ptr(0x5890, DrawStoredModel_Internal);
274+
269275
Sumo_CheckRacerUnlocked = Module::fn_ptr<fn_stdcall_1arg_int>(0xE8410);
270276

271277
SumoNet_OnlineUserName = Module::exe_ptr<const char>(0x430C20);

src/hooks_drawdistance.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
std::array<std::vector<uint16_t>, 256> ObjectNodes;
1111
std::array<std::array<std::bitset<16384>, 256>, 128> ObjectExclusionsPerStage;
12+
std::bitset<128> SkipQuickSortHackStages;
13+
1214
int NumObjects = 0;
1315
int CsLengthNum = 0;
1416

@@ -267,9 +269,18 @@ bool DrawDist_ReadExclusions()
267269
spdlog::error("DrawDist_ReadExclusions - INI contains invalid stage section \"{}\", skipping...", section);
268270
continue;
269271
}
272+
SkipQuickSortHackStages[stageNum] = false;
270273

271274
for (auto& key : ini.Keys(section))
272275
{
276+
if (key == "SkipQuickSort")
277+
{
278+
bool skip = false;
279+
if (ini.Get<bool>(section, key, skip))
280+
SkipQuickSortHackStages[stageNum] = true;
281+
continue;
282+
}
283+
273284
int objectId = -1;
274285
try
275286
{
@@ -329,6 +340,45 @@ bool DrawDist_ReadExclusions()
329340
return true;
330341
}
331342

343+
class SkipQuickSortHack : public Hook
344+
{
345+
inline static SafetyHookInline DrawStoredModel_Execute_hook = {};
346+
static void DrawStoredModel_Execute_dest()
347+
{
348+
if (!SkipQuickSortHackStages[*Game::stg_stage_num])
349+
Game::QuickSort(Game::s_AftDrawBuffer->BufferPtrs_10, 0, Game::s_AftDrawBuffer->NumBuffers_0 - 1);
350+
351+
Game::DrawStoredModel_Internal(Game::s_AftDrawBuffer);
352+
Game::s_AftDrawBuffer->NumBuffers_0 = 0;
353+
Game::s_AftDrawBuffer->field_8 = 0;
354+
}
355+
356+
public:
357+
std::string_view description() override
358+
{
359+
return "SkipQuickSort";
360+
}
361+
362+
bool validate() override
363+
{
364+
return true;
365+
}
366+
367+
bool apply() override
368+
{
369+
constexpr int DrawStoredModel_Execute_Addr = 0x5830;
370+
371+
// This func has some weird securom protection bytes inside, we'll nop the beginning so safetyhook doesn't get confused
372+
Memory::VP::Nop(Module::exe_ptr(DrawStoredModel_Execute_Addr), 5);
373+
DrawStoredModel_Execute_hook = safetyhook::create_inline(Module::exe_ptr(DrawStoredModel_Execute_Addr), DrawStoredModel_Execute_dest);
374+
375+
return true;
376+
}
377+
378+
static SkipQuickSortHack instance;
379+
};
380+
SkipQuickSortHack SkipQuickSortHack::instance;
381+
332382
class DrawDistanceIncrease : public Hook
333383
{
334384
// Stage drawing/culling is based on the player cars position in the stage
@@ -429,7 +479,7 @@ class DrawDistanceIncrease : public Hook
429479

430480
// DEBUG: check exclusions here before adding to *cur
431481
// (if we're at csOffset = 0 exclusions are ignored, since this is what vanilla game would display)
432-
if (csOffset == 0 || !objectExclusions[ObjectNum][*sectionCollList])
482+
if ((csOffset == 0 && Settings::DrawDistanceIncrease > 0) || !objectExclusions[ObjectNum][*sectionCollList])
433483
{
434484
*cur = *sectionCollList;
435485
cur++;

src/plugin.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ namespace Module
3232
template <typename T>
3333
inline T fn_ptr(uintptr_t offset) { if (ExeHandle) return (T)(((uintptr_t)ExeHandle) + offset); else return nullptr; }
3434

35+
// Deduce the type by providing it as an argument, no need for ugly decltype stuff
36+
template <typename T>
37+
inline T fn_ptr(uintptr_t offset, T& var)
38+
{
39+
if (ExeHandle)
40+
return reinterpret_cast<T>(((uintptr_t)ExeHandle) + offset);
41+
else
42+
return nullptr;
43+
}
44+
3545
void init();
3646
}
3747

0 commit comments

Comments
 (0)