III/VC: Apply the environment mapping on extra components

Also includes an INI exception list "ExtraCompSpecularityExceptions"
for cars with incorrectly set specular on extras,
like Stallion and Mesa Grande
This commit is contained in:
Silent 2024-03-09 17:04:12 +01:00
parent f72f85ecd6
commit 858abbd342
No known key found for this signature in database
GPG Key ID: AE53149BB0C45AF1
10 changed files with 241 additions and 5 deletions

View File

@ -5,6 +5,8 @@
#include "StoredCar.h"
#include "SVF.h"
#include "Utils/DelimStringReader.h"
#include <rwcore.h>
RwCamera*& Camera = **hook::get_pattern<RwCamera**>( "A1 ? ? ? ? D8 88 ? ? ? ?", 1 );
@ -104,6 +106,30 @@ namespace CompsToUseFix
}
};
// ============= Extra component specularity exceptions =============
namespace ExtraCompSpecularity
{
void ReadExtraCompSpecularityExceptions(const wchar_t* pPath)
{
constexpr size_t SCRATCH_PAD_SIZE = 32767;
WideDelimStringReader reader(SCRATCH_PAD_SIZE);
GetPrivateProfileSectionW(L"ExtraCompSpecularityExceptions", reader.GetBuffer(), reader.GetSize(), pPath);
while (const wchar_t* str = reader.GetString())
{
int32_t toList = wcstol(str, nullptr, 0);
if ( toList > 0 )
SVF::RegisterFeature(toList, SVF::Feature::_INTERNAL_NO_SPECULARITY_ON_EXTRAS);
}
}
bool SpecularityExcluded(int32_t modelID)
{
return SVF::ModelHasFeature(modelID, SVF::Feature::_INTERNAL_NO_SPECULARITY_ON_EXTRAS);
}
}
// ============= Delayed patches =============
namespace DelayedPatches
{
@ -266,6 +292,8 @@ namespace Common {
using namespace Memory;
using namespace hook;
ExtraCompSpecularity::ReadExtraCompSpecularityExceptions(wcModulePath);
// Corrected taxi light placement for Taxi
if ( GetPrivateProfileIntW(L"SilentPatch", L"EnableVehicleCoronaFixes", -1, wcModulePath) == 1 )
{

View File

@ -1,5 +1,12 @@
#pragma once
#include <cstdint>
namespace ExtraCompSpecularity
{
void ReadExtraCompSpecularityExceptions(const wchar_t* pPath);
bool SpecularityExcluded(int32_t modelID);
}
namespace Common
{
namespace Patches

View File

@ -18,6 +18,10 @@ namespace SVF
VICE_CHEETAH_SIREN,
#endif
#if _GTA_III || _GTA_VC
_INTERNAL_NO_SPECULARITY_ON_EXTRAS, // Band-aid fix for leather roofs being reflective now that envmaps are applied on them
#endif
#if _GTA_SA
// Those are fully controlled by SilentPatch
PHOENIX_FLUTTER,

View File

@ -10,6 +10,7 @@
#include "Utils/MemoryMgr.h"
#include "Utils/MemoryMgr.GTA.h"
#include "Utils/Patterns.h"
// Move this to ModUtils when it matures a bit more
#define HOOK_EACH_FUNC_CTR(name, ctr, origFunc, hook) \

View File

@ -1,8 +1,65 @@
#include "StdAfx.h"
#include "ModelInfoIII.h"
#include "Common.h"
#include "RWUtils.hpp"
RpAtomic* (*CVehicleModelInfo::SetEnvironmentMapCB)(RpAtomic* atomic, void* data) = hook::get_pattern<RpAtomic*(RpAtomic*, void*)>("8B 5C 24 14 C6 44 24", -5);
// This is actually CBaseModelInfo, but we currently don't have it defined
CVehicleModelInfo**& ms_modelInfoPtrs = *hook::get_pattern<CVehicleModelInfo**>("8B 2C 85 ? ? ? ? 89 E9", 3);
int32_t& numModelInfos = *hook::get_pattern<int32_t>("81 FD ? ? ? ? 7C B7 31 C0", 2);
static void RemoveSpecularityFromAtomic(RpAtomic* atomic)
{
RpGeometry* geometry = RpAtomicGetGeometry(atomic);
if (geometry != nullptr)
{
RpGeometryForAllMaterials(geometry, [](RpMaterial* material)
{
bool bRemoveSpecularity = false;
// Only remove specularity from the body materials, keep glass intact.
// This is only done on a best-effort basis, as mods can fine-tune it better
// and just remove the model from the exceptions list
RwTexture* texture = RpMaterialGetTexture(material);
if (texture != nullptr)
{
if (strstr(RwTextureGetName(texture), "glass") == nullptr && strstr(RwTextureGetMaskName(texture), "glass") == nullptr)
{
bRemoveSpecularity = true;
}
}
if (bRemoveSpecularity)
{
RpMaterialGetSurfaceProperties(material)->specular = 0.0f;
}
return material;
});
}
}
void CSimpleModelInfo::SetNearDistanceForLOD_SilentPatch()
{
// 100.0f for real LOD's, 0.0f otherwise
m_lodDistances[2] = _strnicmp( m_name, "lod", 3 ) == 0 ? 100.0f : 0.0f;
}
void CVehicleModelInfo::SetEnvironmentMap_ExtraComps()
{
std::invoke(orgSetEnvironmentMap, this);
const int32_t modelID = std::distance(ms_modelInfoPtrs, std::find(ms_modelInfoPtrs, ms_modelInfoPtrs+numModelInfos, this));
const bool bRemoveSpecularity = ExtraCompSpecularity::SpecularityExcluded(modelID);
for (int32_t i = 0; i < m_numComps; i++)
{
if (bRemoveSpecularity)
{
RemoveSpecularityFromAtomic(m_comps[i]);
}
SetEnvironmentMapCB(m_comps[i], m_envMap);
AttachCarPipeToRwObject(reinterpret_cast<RwObject*>(m_comps[i]));
}
}

View File

@ -3,6 +3,9 @@
#include <cstdint>
#include "Maths.h"
#include <rwcore.h>
#include <rpworld.h>
// This really belongs in Maths.h but San Andreas optimized those structured heavily...
struct CColSphere
{
@ -90,4 +93,24 @@ public:
void SetNearDistanceForLOD_SilentPatch();
};
static_assert(sizeof(CSimpleModelInfo) == 0x4C, "Wrong size: CSimpleModelInfo");
static_assert(sizeof(CSimpleModelInfo) == 0x4C, "Wrong size: CSimpleModelInfo");
class CVehicleModelInfo
{
private:
std::byte __pad[472];
RwTexture* m_envMap;
RpAtomic* m_comps[6];
int32_t m_numComps;
public:
static RpAtomic* (*SetEnvironmentMapCB)(RpAtomic* atomic, void* data);
static inline void (CVehicleModelInfo::*orgSetEnvironmentMap)();
void SetEnvironmentMap_ExtraComps();
// For SkyGfx interop
static void AttachCarPipeToRwObject_Default(RwObject*) { }
static inline void (*AttachCarPipeToRwObject)(RwObject* object) = &AttachCarPipeToRwObject_Default;
};
static_assert(sizeof(CVehicleModelInfo) == 0x1F8, "Wrong size: CVehicleModelInfo");

View File

@ -12,6 +12,7 @@
#include <memory>
#include <Shlwapi.h>
#include "Utils/ModuleList.hpp"
#include "Utils/Patterns.h"
#include "Utils/ScopedUnprotect.hpp"
@ -548,6 +549,18 @@ void InjectDelayedPatches_III_Common( bool bHasDebugMenu, const wchar_t* wcModul
using namespace Memory;
using namespace hook;
const ModuleList moduleList;
const HMODULE skygfxModule = moduleList.Get(L"skygfx");
if (skygfxModule != nullptr)
{
auto attachCarPipe = reinterpret_cast<void(*)(RwObject*)>(GetProcAddress(skygfxModule, "AttachCarPipeToRwObject"));
if (attachCarPipe != nullptr)
{
CVehicleModelInfo::AttachCarPipeToRwObject = attachCarPipe;
}
}
// Locale based metric/imperial system INI/debug menu
{
using namespace Localization;
@ -1332,6 +1345,14 @@ void Patch_III_Common()
auto getSkinTexture = get_pattern("E8 ? ? ? ? 89 C3 59 55");
InterceptCall(getSkinTexture, orgRwTextureCreate, RwTextureCreate_SetLinearFilter);
}
// Apply the environment mapping on extra components
{
auto setEnvironmentMap = get_pattern("C7 83 D8 01 00 00 00 00 00 00 E8", 10);
InterceptCall(setEnvironmentMap, CVehicleModelInfo::orgSetEnvironmentMap, &CVehicleModelInfo::SetEnvironmentMap_ExtraComps);
}
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)

View File

@ -5,13 +5,23 @@
class CVehicleModelInfo
{
private:
uint8_t __pad1[60];
unsigned int m_dwType;
uint8_t __pad2[308];
public:
uint8_t __pad1[40];
RpClump* m_clump;
uint8_t __pad3[16];
unsigned int m_dwType;
uint8_t __pad4[11];
int8_t m_numComps;
uint8_t __pad2[268];
RpAtomic* m_comps[6];
uint8_t __pad5[4];
public:
RwFrame* GetExtrasFrame( RpClump* clump );
// For SkyGfx interop
static void AttachCarPipeToRwObject_Default(RwObject*) { }
static inline void (*AttachCarPipeToRwObject)(RwObject* object) = &AttachCarPipeToRwObject_Default;
};
static_assert(sizeof(CVehicleModelInfo) == 0x174, "Wrong size: CvehicleModelInfo");

View File

@ -8,11 +8,13 @@
#include "ModelInfoVC.h"
#include "VehicleVC.h"
#include "SVF.h"
#include "RWUtils.hpp"
#include <array>
#include <memory>
#include <Shlwapi.h>
#include "Utils/ModuleList.hpp"
#include "Utils/Patterns.h"
#include "Utils/ScopedUnprotect.hpp"
@ -61,6 +63,10 @@ static void (*PrintString)(float,float,const wchar_t*);
static RsGlobalType* RsGlobal;
static const void* SubtitlesShadowFix_JumpBack;
// This is actually CBaseModelInfo, but we currently don't have it defined
CVehicleModelInfo**& ms_modelInfoPtrs = *hook::get_pattern<CVehicleModelInfo**>("8B 15 ? ? ? ? 8D 04 24", 2);
int32_t& numModelInfos = *hook::get_pattern<int32_t>("81 FD ? ? ? ? 7C B7", 2);
inline float GetWidthMult()
{
static const float& ResolutionWidthMult = **AddressByVersion<float**>(0x5FA15E, 0x5FA17E, 0x5F9DBE);
@ -474,11 +480,78 @@ namespace RemoveDriverStatusFix
}
// ============= Apply the environment mapping on extra components =============
namespace EnvMapsOnExtras
{
static void RemoveSpecularityFromAtomic(RpAtomic* atomic)
{
RpGeometry* geometry = RpAtomicGetGeometry(atomic);
if (geometry != nullptr)
{
RpGeometryForAllMaterials(geometry, [](RpMaterial* material)
{
bool bRemoveSpecularity = false;
// Only remove specularity from the body materials, keep glass intact.
// This is only done on a best-effort basis, as mods can fine-tune it better
// and just remove the model from the exceptions list
RwTexture* texture = RpMaterialGetTexture(material);
if (texture != nullptr)
{
if (strstr(RwTextureGetName(texture), "glass") == nullptr && strstr(RwTextureGetMaskName(texture), "glass") == nullptr)
{
bRemoveSpecularity = true;
}
}
if (bRemoveSpecularity)
{
RpMaterialGetSurfaceProperties(material)->specular = 0.0f;
}
return material;
});
}
}
static RpClump* (*orgRpClumpForAllAtomics)(RpClump* clump, RpAtomicCallBack callback, void* data);
static RpClump* RpClumpForAllAtomics_ExtraComps(CVehicleModelInfo* modelInfo, RpAtomicCallBack callback, void* data)
{
RpClump* result = orgRpClumpForAllAtomics(modelInfo->m_clump, callback, data);
const int32_t modelID = std::distance(ms_modelInfoPtrs, std::find(ms_modelInfoPtrs, ms_modelInfoPtrs+numModelInfos, modelInfo));
const bool bRemoveSpecularity = ExtraCompSpecularity::SpecularityExcluded(modelID);
for (int32_t i = 0; i < modelInfo->m_numComps; i++)
{
if (bRemoveSpecularity)
{
RemoveSpecularityFromAtomic(modelInfo->m_comps[i]);
}
callback(modelInfo->m_comps[i], data);
CVehicleModelInfo::AttachCarPipeToRwObject(reinterpret_cast<RwObject*>(modelInfo->m_comps[i]));
}
return result;
}
}
void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModulePath )
{
using namespace Memory;
using namespace hook;
const ModuleList moduleList;
const HMODULE skygfxModule = moduleList.Get(L"skygfx");
if (skygfxModule != nullptr)
{
auto attachCarPipe = reinterpret_cast<void(*)(RwObject*)>(GetProcAddress(skygfxModule, "AttachCarPipeToRwObject"));
if (attachCarPipe != nullptr)
{
CVehicleModelInfo::AttachCarPipeToRwObject = attachCarPipe;
}
}
// Locale based metric/imperial system INI/debug menu
{
using namespace Localization;
@ -1204,6 +1277,18 @@ void Patch_VC_Common()
Nop(removeThisPed, 3);
Nop(pedSetOutCar, 3);
}
// Apply the environment mapping on extra components
{
using namespace EnvMapsOnExtras;
auto forAllAtomics = pattern("50 E8 ? ? ? ? 66 8B 4B 44").get_one();
// push eax -> push ebx
Patch<uint8_t>(forAllAtomics.get<void>(), 0x53);
InterceptCall(forAllAtomics.get<void>(1), orgRpClumpForAllAtomics, RpClumpForAllAtomics_ExtraComps);
}
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)