From 858abbd342c5ca5cbafb7d1fc87b08ad17669534 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 9 Mar 2024 17:04:12 +0100 Subject: [PATCH] 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 --- SilentPatch/Common.cpp | 28 +++++++ SilentPatch/Common.h | 7 ++ {SilentPatchSA => SilentPatch}/RWUtils.hpp | 0 SilentPatch/SVF.h | 4 + SilentPatch/StdAfx.h | 1 + SilentPatchIII/ModelInfoIII.cpp | 57 +++++++++++++++ SilentPatchIII/ModelInfoIII.h | 25 ++++++- SilentPatchIII/SilentPatchIII.cpp | 21 ++++++ SilentPatchVC/ModelInfoVC.h | 18 ++++- SilentPatchVC/SilentPatchVC.cpp | 85 ++++++++++++++++++++++ 10 files changed, 241 insertions(+), 5 deletions(-) rename {SilentPatchSA => SilentPatch}/RWUtils.hpp (100%) diff --git a/SilentPatch/Common.cpp b/SilentPatch/Common.cpp index 1fa0d61..e38f94d 100644 --- a/SilentPatch/Common.cpp +++ b/SilentPatch/Common.cpp @@ -5,6 +5,8 @@ #include "StoredCar.h" #include "SVF.h" +#include "Utils/DelimStringReader.h" + #include RwCamera*& Camera = **hook::get_pattern( "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 ) { diff --git a/SilentPatch/Common.h b/SilentPatch/Common.h index 3640eaa..8565cf8 100644 --- a/SilentPatch/Common.h +++ b/SilentPatch/Common.h @@ -1,5 +1,12 @@ #pragma once +#include + +namespace ExtraCompSpecularity +{ + void ReadExtraCompSpecularityExceptions(const wchar_t* pPath); + bool SpecularityExcluded(int32_t modelID); +} namespace Common { namespace Patches diff --git a/SilentPatchSA/RWUtils.hpp b/SilentPatch/RWUtils.hpp similarity index 100% rename from SilentPatchSA/RWUtils.hpp rename to SilentPatch/RWUtils.hpp diff --git a/SilentPatch/SVF.h b/SilentPatch/SVF.h index 83691ac..cc9e204 100644 --- a/SilentPatch/SVF.h +++ b/SilentPatch/SVF.h @@ -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, diff --git a/SilentPatch/StdAfx.h b/SilentPatch/StdAfx.h index 53e00fe..f28019e 100644 --- a/SilentPatch/StdAfx.h +++ b/SilentPatch/StdAfx.h @@ -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) \ diff --git a/SilentPatchIII/ModelInfoIII.cpp b/SilentPatchIII/ModelInfoIII.cpp index 2fa52db..dd4d780 100644 --- a/SilentPatchIII/ModelInfoIII.cpp +++ b/SilentPatchIII/ModelInfoIII.cpp @@ -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("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("8B 2C 85 ? ? ? ? 89 E9", 3); +int32_t& numModelInfos = *hook::get_pattern("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(m_comps[i])); + } +} diff --git a/SilentPatchIII/ModelInfoIII.h b/SilentPatchIII/ModelInfoIII.h index c9f640d..ab1a00d 100644 --- a/SilentPatchIII/ModelInfoIII.h +++ b/SilentPatchIII/ModelInfoIII.h @@ -3,6 +3,9 @@ #include #include "Maths.h" +#include +#include + // 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"); \ No newline at end of file +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"); diff --git a/SilentPatchIII/SilentPatchIII.cpp b/SilentPatchIII/SilentPatchIII.cpp index d09faea..ffed668 100644 --- a/SilentPatchIII/SilentPatchIII.cpp +++ b/SilentPatchIII/SilentPatchIII.cpp @@ -12,6 +12,7 @@ #include #include +#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(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) diff --git a/SilentPatchVC/ModelInfoVC.h b/SilentPatchVC/ModelInfoVC.h index fe32b02..fa49108 100644 --- a/SilentPatchVC/ModelInfoVC.h +++ b/SilentPatchVC/ModelInfoVC.h @@ -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"); \ No newline at end of file diff --git a/SilentPatchVC/SilentPatchVC.cpp b/SilentPatchVC/SilentPatchVC.cpp index d3604eb..3e921ed 100644 --- a/SilentPatchVC/SilentPatchVC.cpp +++ b/SilentPatchVC/SilentPatchVC.cpp @@ -8,11 +8,13 @@ #include "ModelInfoVC.h" #include "VehicleVC.h" #include "SVF.h" +#include "RWUtils.hpp" #include #include #include +#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("8B 15 ? ? ? ? 8D 04 24", 2); +int32_t& numModelInfos = *hook::get_pattern("81 FD ? ? ? ? 7C B7", 2); + inline float GetWidthMult() { static const float& ResolutionWidthMult = **AddressByVersion(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(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(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(forAllAtomics.get(), 0x53); + InterceptCall(forAllAtomics.get(1), orgRpClumpForAllAtomics, RpClumpForAllAtomics_ExtraComps); + } } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)