VC: Generalize the backface culling fix to peds and map objects

Uses War Drum's DrawBackfaces list + more findings
This commit is contained in:
Silent 2024-05-18 19:09:14 +02:00
parent 4169c9e867
commit 5bd4f144e7
No known key found for this signature in database
GPG key ID: AE53149BB0C45AF1
3 changed files with 93 additions and 23 deletions

View file

@ -18,6 +18,7 @@ namespace SVF
{ "FBI_RANCHER_SIREN", Feature::FBI_RANCHER_SIREN }, { "FBI_RANCHER_SIREN", Feature::FBI_RANCHER_SIREN },
{ "FBI_WASHINGTON_SIREN", Feature::FBI_WASHINGTON_SIREN }, { "FBI_WASHINGTON_SIREN", Feature::FBI_WASHINGTON_SIREN },
{ "VICE_CHEETAH_SIREN", Feature::VICE_CHEETAH_SIREN }, { "VICE_CHEETAH_SIREN", Feature::VICE_CHEETAH_SIREN },
{ "DRAW_BACKFACES", Feature::DRAW_BACKFACES },
#endif #endif
#if _GTA_SA #if _GTA_SA

View file

@ -18,6 +18,8 @@ namespace SVF
FBI_RANCHER_SIREN, FBI_RANCHER_SIREN,
FBI_WASHINGTON_SIREN, FBI_WASHINGTON_SIREN,
VICE_CHEETAH_SIREN, VICE_CHEETAH_SIREN,
DRAW_BACKFACES, // Not vehicle specific, but it'll do
#endif #endif
#if _GTA_III || _GTA_VC #if _GTA_III || _GTA_VC

View file

@ -10,6 +10,7 @@
#include "SVF.h" #include "SVF.h"
#include "RWUtils.hpp" #include "RWUtils.hpp"
#include "TheFLAUtils.h" #include "TheFLAUtils.h"
#include "ParseUtils.hpp"
#include <array> #include <array>
#include <memory> #include <memory>
@ -20,6 +21,7 @@
#include "Utils/Patterns.h" #include "Utils/Patterns.h"
#include "Utils/ScopedUnprotect.hpp" #include "Utils/ScopedUnprotect.hpp"
#include "Utils/HookEach.hpp" #include "Utils/HookEach.hpp"
#include "Utils/DelimStringReader.h"
#include "debugmenu_public.h" #include "debugmenu_public.h"
@ -714,10 +716,26 @@ namespace VariableResets
} }
// ============= Disabled backface culling on detached car parts ============= // ============= Disabled backface culling on detached car parts, peds and specific models =============
namespace CarPartsBackfaceCulling namespace SelectableBackfaceCulling
{ {
// Only the parts of CObject we need void ReadDrawBackfacesExclusions(const wchar_t* pPath)
{
constexpr size_t SCRATCH_PAD_SIZE = 32767;
WideDelimStringReader reader(SCRATCH_PAD_SIZE);
GetPrivateProfileSectionW(L"DrawBackfaces", reader.GetBuffer(), reader.GetSize(), pPath);
while (const wchar_t* str = reader.GetString())
{
auto modelID = ParseUtils::TryParseInt(str);
if (modelID)
SVF::RegisterFeature(*modelID, SVF::Feature::DRAW_BACKFACES);
else
SVF::RegisterFeature(ParseUtils::ParseString(str), SVF::Feature::DRAW_BACKFACES);
}
}
// Only the parts of CEntity and CObject we need
enum // m_objectCreatedBy enum // m_objectCreatedBy
{ {
GAME_OBJECT = 1, GAME_OBJECT = 1,
@ -725,6 +743,14 @@ namespace CarPartsBackfaceCulling
TEMP_OBJECT = 3, TEMP_OBJECT = 3,
}; };
struct Entity
{
std::byte __pad[80];
uint8_t m_nType : 3;
std::byte __pad2[11];
FLAUtils::int16 m_modelIndex;
};
struct Object struct Object
{ {
std::byte __pad[364]; std::byte __pad[364];
@ -741,43 +767,81 @@ namespace CarPartsBackfaceCulling
FLAUtils::int16 m_wCarPartModelIndex; FLAUtils::int16 m_wCarPartModelIndex;
}; };
static void* ObjectRender_Prologue_JumpBack; static void* EntityRender_Prologue_JumpBack;
__declspec(naked) static void __fastcall ObjectRender_Original(Object*) __declspec(naked) static void __fastcall EntityRender_Original(Entity*)
{ {
_asm _asm
{ {
push ebx push ebx
push esi
mov ebx, ecx mov ebx, ecx
push edi cmp dword ptr [ebx+4Ch], 0
jmp [ObjectRender_Prologue_JumpBack] jmp [EntityRender_Prologue_JumpBack]
} }
} }
// If CObject::Render is re-routed by another mod, we overwrite this later static bool ShouldDisableBackfaceCulling(const Entity* entity)
static void (__fastcall *orgObjectRender)(Object* obj) = &ObjectRender_Original; {
const uint8_t entityType = entity->m_nType;
static void __fastcall ObjectRender_BackfaceCulling(Object* obj) // Vehicles disable BFC elsewhere already
if (entityType == 2)
{
return false;
}
// Always disable BFC on peds
if (entityType == 3)
{
return true;
}
// For objects, do extra checks
if (entityType == 4)
{
const Object* object = reinterpret_cast<const Object*>(entity);
return object->m_wCarPartModelIndex.Get() != -1 && object->m_objectCreatedBy == TEMP_OBJECT && object->bUseVehicleColours;
}
// For everything else, check the exclusion list
return SVF::ModelHasFeature(entity->m_modelIndex.Get(), SVF::Feature::DRAW_BACKFACES);
}
// If CEntity::Render is re-routed by another mod, we overwrite this later
static void (__fastcall *orgEntityRender)(Entity* obj) = &EntityRender_Original;
static void __fastcall EntityRender_BackfaceCulling(Entity* obj)
{ {
RwScopedRenderState<rwRENDERSTATECULLMODE> cullState; RwScopedRenderState<rwRENDERSTATECULLMODE> cullState;
if (obj->m_wCarPartModelIndex.Get() != -1 && obj->m_objectCreatedBy == TEMP_OBJECT && obj->bUseVehicleColours) if (ShouldDisableBackfaceCulling(obj))
{ {
RwRenderStateSet(rwRENDERSTATECULLMODE, reinterpret_cast<void*>(rwCULLMODECULLNONE)); RwRenderStateSet(rwRENDERSTATECULLMODE, reinterpret_cast<void*>(rwCULLMODECULLNONE));
} }
orgObjectRender(obj); orgEntityRender(obj);
} }
} }
namespace SVFReadyHook namespace SVFReadyHook
{ {
static void* (*GetModelInfo)(const char*, int*);
static void (*orgInitialiseObjectData)(const char*); static void (*orgInitialiseObjectData)(const char*);
static void InitialiseObjectData_ReadySVF(const char* path) static void InitialiseObjectData_ReadySVF(const char* path)
{ {
orgInitialiseObjectData(path); orgInitialiseObjectData(path);
SVF::MarkModelNamesReady(); SVF::MarkModelNamesReady();
// This is a bit dirty, but whatever
// Tooled Up in North Point Mall needs a "draw last" flag, or else our BFC changes break it very badly
// AmmuNation and other stores already have that flag, this one does not
void* model = GetModelInfo("mall_hardware", nullptr);
if (model != nullptr)
{
uint16_t* flags = reinterpret_cast<uint16_t*>(static_cast<char*>(model) + 0x42);
*flags |= 0xC0;
}
} }
} }
@ -945,6 +1009,7 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule
auto initialiseObjectData = get_pattern("E8 ? ? ? ? 59 E8 ? ? ? ? E8 ? ? ? ? 31 DB"); auto initialiseObjectData = get_pattern("E8 ? ? ? ? 59 E8 ? ? ? ? E8 ? ? ? ? 31 DB");
auto getModelInfo = (void*(*)(const char*, int*))get_pattern("57 31 FF 55 8B 6C 24 14", -6); auto getModelInfo = (void*(*)(const char*, int*))get_pattern("57 31 FF 55 8B 6C 24 14", -6);
GetModelInfo = getModelInfo;
InterceptCall(initialiseObjectData, orgInitialiseObjectData, InitialiseObjectData_ReadySVF); InterceptCall(initialiseObjectData, orgInitialiseObjectData, InitialiseObjectData_ReadySVF);
SVF::RegisterGetModelInfoCB(getModelInfo); SVF::RegisterGetModelInfoCB(getModelInfo);
} }
@ -965,6 +1030,8 @@ void InjectDelayedPatches_VC_Common()
const bool hasDebugMenu = DebugMenuLoad(); const bool hasDebugMenu = DebugMenuLoad();
SelectableBackfaceCulling::ReadDrawBackfacesExclusions(wcModulePath);
InjectDelayedPatches_VC_Common( hasDebugMenu, wcModulePath ); InjectDelayedPatches_VC_Common( hasDebugMenu, wcModulePath );
Common::Patches::III_VC_DelayedCommon( hasDebugMenu, wcModulePath ); Common::Patches::III_VC_DelayedCommon( hasDebugMenu, wcModulePath );
@ -1697,22 +1764,22 @@ void Patch_VC_Common()
TXN_CATCH(); TXN_CATCH();
// Disabled backface culling on detached car parts // Disabled backface culling on detached car parts, peds and specific models
try try
{ {
using namespace CarPartsBackfaceCulling; using namespace SelectableBackfaceCulling;
auto object_render = pattern("55 83 EC 68 8A 43 54").get_one(); auto entity_render = pattern("56 75 06 5E 5B C3").get_one();
ObjectRender_Prologue_JumpBack = object_render.get<void>(); EntityRender_Prologue_JumpBack = entity_render.get<void>();
// Check if CObject::Render is already re-routed by something else // Check if CEntity::Render is already re-routed by something else
if (*object_render.get<uint8_t>(-5) == 0xE9) if (*entity_render.get<uint8_t>(-7) == 0xE9)
{ {
ReadCall(object_render.get<void>(-5), orgObjectRender); ReadCall(entity_render.get<void>(-7), orgEntityRender);
} }
InjectHook(object_render.get<void>(-5), ObjectRender_BackfaceCulling, HookType::Jump); InjectHook(entity_render.get<void>(-7), EntityRender_BackfaceCulling, HookType::Jump);
} }
TXN_CATCH(); TXN_CATCH();
} }