Last active
July 9, 2022 15:42
-
-
Save emoose/9a374004d80af699df8e91ff875a454b to your computer and use it in GitHub Desktop.
Improved Lighting Shaders Plugin for FNV (https://www.nexusmods.com/newvegas/mods/69833)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Improved Lighting Shaders for FNV, 1.5beta4, by emoose (stoker25) | |
// Released under MIT License | |
#include "nvse/PluginAPI.h" | |
#include "nvse/SafeWrite.h" | |
#include "nvse/GameObjects.h" | |
#include "nvse/ParamInfos.h" | |
#include "NiTypes.h" | |
#include <string> | |
// TODO: | |
// - do pip-light priority stuff inside RenderList_Add_Hook instead? (pip-light will always go through this func) | |
// - support for FO3, OB? | |
// - port SM2 shaders to SM3, allowing 200+ lights at once? (seems possible!) | |
// Number of lights used in the shader | |
// 13 is maximum we can use in SM2 | |
#define TRUE_MAX_LIGHTS 13 | |
// This string will replace the shaderpackage path the game uses | |
// Allowing vanilla game + ILS-enabled game to use seperate shader sets | |
const char* g_shaderPackagePath = "Data\\Shaders\\ILS\\shaderpackage%03i.sdp"; | |
const char* g_iniPath = "Data\\NVSE\\Plugins\\ImprovedLightingShaders.ini"; | |
const char* g_iniApp = "ImprovedLightingShaders"; | |
// Configurable INI settings: | |
int g_iMaxLightCount = 8; | |
bool g_bPrioritisePipLight = true; | |
bool g_bIncreaseLightBuffers = false; | |
// Replacement buffers: | |
NiVector4 g_LightData[32]; // g_LightPos... | |
volatile NiVector4 g_LightSpacer[32]; // in case something overflows | |
NiVector4 g_LightRadius[32]; | |
volatile NiVector4 g_LightSpacer2[32]; // in case something overflows | |
NiVector4 g_AltLightData[32]; | |
volatile NiVector4 g_LightSpacer3[32]; // in case something overflows | |
// g_LightBacking = | |
// NiVector4[32] g_LightBacking unknown backing data | |
// NiVector4[1] g_LightColorAmb ambient light color | |
// NiVector4[32] g_LightColor light colors | |
NiVector4 g_LightBacking[32 + 1 + 32]; | |
extern "C" { | |
bool NVSEPlugin_Query(const NVSEInterface * nvse, PluginInfo * info) | |
{ | |
// fill out the info structure | |
info->infoVersion = PluginInfo::kInfoVersion; | |
info->name = "Improved Lighting Shaders for FNV"; | |
info->version = 8; | |
// version checks | |
if (nvse->nvseVersion < NVSE_VERSION_INTEGER) | |
{ | |
_ERROR("NVSE version too old (got %08X expected at least %08X)", nvse->nvseVersion, NVSE_VERSION_INTEGER); | |
return false; | |
} | |
if (nvse->isEditor) | |
return false; // game only | |
if (nvse->runtimeVersion < RUNTIME_VERSION_1_4_0_525) | |
{ | |
_ERROR("incorrect runtime version (got %08X need at least %08X)", nvse->runtimeVersion, RUNTIME_VERSION_1_4_0_525); | |
return false; | |
} | |
#ifdef NOGORE | |
if (!nvse->isNogore) | |
{ | |
_ERROR("incorrect runtime edition (got %08X need %08X (nogore))", nvse->isNogore, 1); | |
return false; | |
} | |
#else | |
if (nvse->isNogore) | |
{ | |
_ERROR("incorrect runtime edition (got %08X need %08X (standard))", nvse->isNogore, 0); | |
return false; | |
} | |
#endif | |
// version checks pass | |
return true; | |
} | |
typedef void(__cdecl* fn_BA8C50)( | |
BSShaderProperty::RenderPass* a1, | |
BYTE numLights, | |
void* light0, | |
void* light1, void* light2, void* light3, | |
void* light4, void* light5, void* light6, | |
void* light7, void* light8, void* light9, | |
void* light10, void* light11, void* light12 | |
); | |
fn_BA8C50 sub_BA8C50 = (fn_BA8C50)0xBA8C50; | |
typedef void* (__thiscall* fn_GameHeapAllocate)(DWORD a1, DWORD a2); | |
fn_GameHeapAllocate GameHeapAllocate = (fn_GameHeapAllocate)0xAA3E40; | |
typedef void* (__cdecl* fn_BA8EC0)( | |
void* a1, | |
void* triStrips, WORD renderPassNum, | |
BYTE enable, BYTE numLights, | |
void* light0, | |
void* light1, void* light2, void* light3, | |
void* light4, void* light5, void* light6, | |
void* light7, void* light8, void* light9, | |
void* light10, void* light11, void* light12 | |
); | |
fn_BA8EC0 sub_BA8EC0 = (fn_BA8EC0)0xBA8EC0; | |
typedef void(__thiscall* fn_sub_BA8D30)(BSShaderProperty::RenderPassList*, unsigned int); | |
fn_sub_BA8D30 sub_BA8D30 = (fn_sub_BA8D30)0xBA8D30; | |
typedef DWORD(__thiscall* fn_sub_BA3AA0)(BSShaderProperty::RenderPassList*, unsigned int, void**); | |
fn_sub_BA3AA0 sub_BA3AA0 = (fn_sub_BA3AA0)0xBA3AA0; | |
// Custom version of 0xBA9EE0 which allows adding more than 4 lights | |
DWORD __fastcall RenderList_Add_Custom( | |
BSShaderProperty::RenderPassList* _this, DWORD unused_edx, | |
void* triStrips, WORD renderPassNum, | |
BYTE enable, BYTE numLights, | |
void* light0, // base/ambient light? | |
void* light1, void* light2, void* light3, | |
void* light4, void* light5, void* light6, | |
void* light7, void* light8, void* light9, | |
void* light10, void* light11, void* light12 | |
) | |
{ | |
if (_this->iNumPasses < _this->m_usESize) | |
{ | |
auto* v18 = _this->m_pBase[_this->iNumPasses]; | |
sub_BA8C50(v18, numLights, light0, | |
light1, light2, light3, | |
light4, light5, light6, | |
light7, light8, light9, | |
light10, light11, light12 | |
); | |
v18->iPassNum = renderPassNum; | |
v18->bEnabled = enable; | |
v18->spTriStrips = triStrips; | |
v18->unk8 = 0; | |
v18->unk6 = 0; | |
v18->unkB = 0; | |
_this->iNumPasses++; | |
return 0; | |
} | |
void* v11 = GameHeapAllocate(0x011F6238, 0x10u); | |
void* v14 = 0; | |
if (v11) | |
{ | |
v14 = sub_BA8EC0(v11, triStrips, renderPassNum, enable, numLights, light0, | |
light1, light2, light3, | |
light4, light5, light6, | |
light7, light8, light9, | |
light10, light11, light12 | |
); | |
} | |
if (_this->m_usSize >= _this->m_usMaxSize) | |
sub_BA8D30(_this, _this->m_usSize + _this->m_usGrowBy); | |
auto res = sub_BA3AA0(_this, _this->m_usSize, &v14); | |
_this->iNumPasses++; | |
return res; | |
} | |
typedef ShadowSceneLight* (__fastcall* fn_LightList_Iterate)(BSShaderProperty*, int, DListNode<ShadowSceneLight*>**); | |
fn_LightList_Iterate LightList_Begin = (fn_LightList_Iterate)0xB70600; | |
fn_LightList_Iterate LightList_Next = (fn_LightList_Iterate)0xB70700; | |
DWORD __fastcall RenderList_Add_Hook( | |
BSShaderProperty* _this, DWORD unused_edx, | |
void* triStrips, DWORD renderPassNum, | |
BYTE enable, BYTE numLights, | |
void* light0, | |
ShadowSceneLight* oldlight1, ShadowSceneLight* oldlight2, ShadowSceneLight* oldlight3 | |
) | |
{ | |
ShadowSceneLight* lights[TRUE_MAX_LIGHTS - 1] = { | |
nullptr, nullptr, nullptr, | |
nullptr, nullptr, nullptr, | |
nullptr, nullptr, nullptr, | |
nullptr, nullptr, nullptr | |
}; | |
DListNode<ShadowSceneLight*>* cur_node; | |
// Game filters lights based on this for some reason | |
NiPoint3* unknown_purpose = (NiPoint3*)0x11F4998; | |
ShadowSceneLight* next_light = LightList_Begin(_this, 0, &cur_node); | |
for (int i = 0; i < g_iMaxLightCount - 1; i++) | |
{ | |
lights[i] = next_light; | |
for (; lights[i]; lights[i] = LightList_Next(_this, 0, &cur_node)) | |
{ | |
auto light = lights[i]->spBaseLight; | |
if (unknown_purpose->x != light->m_kDiff.r) | |
break; | |
if (unknown_purpose->y != light->m_kDiff.g) | |
break; | |
if (unknown_purpose->z != light->m_kDiff.b) | |
break; | |
} | |
next_light = LightList_Next(_this, 0, &cur_node); | |
} | |
// Add the lights to the render pass | |
// The light count passed to this should equal the number of lights used in the shader | |
// That way any unset lights will be cleared and the shader won't try using any invalid data | |
return RenderList_Add_Custom(_this->pRenderPassList, 0, triStrips, renderPassNum, enable, TRUE_MAX_LIGHTS, light0, | |
lights[0], lights[1], lights[2], | |
lights[3], lights[4], lights[5], | |
lights[6], lights[7], lights[8], | |
lights[9], lights[10], lights[11] | |
); | |
} | |
void __fastcall AddLightingConstants_Hook(BYTE* _this, DWORD unused) | |
{ | |
typedef void(__thiscall* fn_eax_8)(BYTE*, const char*, signed int, DWORD, signed int, signed int, const CHAR*, signed int, signed int, float*, DWORD); | |
//auto vftable = *(DWORD*)_this; | |
//auto eax_8 = (fn_eax_8)(*(DWORD*)(vftable + 8)); | |
auto eax_8 = (fn_eax_8)unused; // edx was set to the fn we need to call, noice | |
// the code we patched over: (adds c22 register to use data from 0x11FD9E8?) | |
if (g_iMaxLightCount > 3) | |
eax_8(_this, "Light Position3", 0x10000007, 0, 22, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[4].x, 0); | |
// new adds (makes c23 thru c28 registers point to the extra lights) | |
if (g_iMaxLightCount > 4) | |
eax_8(_this, "Light Position4", 0x10000007, 0, 23, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[5].x, 0); | |
if (g_iMaxLightCount > 5) | |
eax_8(_this, "Light Position5", 0x10000007, 0, 24, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[6].x, 0); | |
if (g_iMaxLightCount > 6) | |
eax_8(_this, "Light Position6", 0x10000007, 0, 25, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[7].x, 0); | |
if (g_iMaxLightCount > 7) | |
eax_8(_this, "Light Position7", 0x10000007, 0, 26, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[8].x, 0); | |
if (g_iMaxLightCount > 8) | |
eax_8(_this, "Light Position8", 0x10000007, 0, 27, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[9].x, 0); | |
if (g_iMaxLightCount > 9) | |
eax_8(_this, "Light Position9", 0x10000007, 0, 28, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[10].x, 0); | |
if (g_iMaxLightCount > 10) | |
eax_8(_this, "Light Position10", 0x10000007, 0, 29, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[11].x, 0); | |
if (g_iMaxLightCount > 11) | |
eax_8(_this, "Light Position11", 0x10000007, 0, 30, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[12].x, 0); | |
if (g_iMaxLightCount > 12) | |
eax_8(_this, "Light Position12", 0x10000007, 0, 31, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[13].x, 0); | |
} | |
#ifdef PRINT_LIGHT_LIST | |
LightingDataNode* prev_head = nullptr; | |
int prev_list_count = 0; | |
#endif | |
// store replacement here instead of stack, seems more stable(?) | |
// IteratesLights doesn't seem to be multithreaded so should be safe | |
LightingDataNode replacement_node; | |
typedef BYTE* (__fastcall* IteratesLights_fn)(BSShaderPPLightingProperty* _this, DWORD unused, int a2, int a3, DWORD a4, char a5, BYTE* a6, int a7, char a8); | |
IteratesLights_fn IteratesLights = (IteratesLights_fn)0xBDF3E0; | |
BYTE* __fastcall IteratesLights_hook(BSShaderPPLightingProperty* _this, DWORD unused, int a2, int a3, DWORD a4, char a5, BYTE* a6, int a7, char a8) | |
{ | |
LightingDataList* list = &_this->lights; | |
auto* list_head = list->first; | |
auto* list_tail = list->last; | |
// return early if there's nothing to do (3 lights or less = no need to change anything) | |
if (!list_head || list->count <= g_iMaxLightCount) | |
return IteratesLights(_this, unused, a2, a3, a4, a5, a6, a7, a8); | |
#ifdef PRINT_LIGHT_LIST | |
auto light_list_count = list->count; | |
if (prev_head != list_head || prev_list_count != light_list_count) | |
{ | |
prev_head = list_head; | |
prev_list_count = light_list_count; | |
int i = 0; | |
LightListNode* light_prev = 0; | |
while (list_head) | |
{ | |
bool pip_light = false; | |
auto* light_obj = list_head->data->object; | |
char* light_name = 0; | |
if (light_obj) | |
{ | |
light_name = light_obj->name; | |
pip_light = list_head->data->is_pip_light(); | |
} | |
float distance = 0; | |
if (light_prev) | |
distance = list_head->data->distance(light_prev->data); | |
if (light_name) | |
Console_Print("%s%d 0x%p (%s) dist %f", pip_light ? "> " : " ", i, list_head->data, light_name, distance); | |
else | |
Console_Print("%s%d 0x%p dist %f", pip_light ? "> " : " ", i, list_head->data, distance); | |
i++; | |
light_prev = list_head; | |
list_head = list_head->next; | |
} | |
} | |
list_head = list->first; | |
#endif | |
// Search for pip-light inside lights list | |
// (iterate backwards since pip-light is usually near end) | |
// If found, 'move' it to the beginning of the list by unlinking it, and linking in new node (entry_replacement) as the start of the list | |
LightingDataNode* entry_cur = list_tail; | |
LightingDataNode* entry_prev = 0; | |
bool has_pip_light = false; | |
while (entry_cur) | |
{ | |
has_pip_light = false; | |
entry_prev = entry_cur->prev; | |
// check if there's a prev entry before this | |
// (we only want to move the light if it's not already at the front) | |
if (entry_prev) | |
{ | |
has_pip_light = entry_cur->data->is_pip_light(); | |
if (has_pip_light) | |
{ | |
// unlink pip-light from light_list | |
entry_prev->next = entry_cur->next; | |
if (entry_cur->next) | |
entry_cur->next->prev = entry_prev; | |
// entry_replacement setup | |
// (this used to be outside the loop, based on has_pip_light, but moved it here to try reducing number of jumps) | |
{ | |
replacement_node.prev = 0; | |
// copy pip-light data pointer to entry_replacement | |
replacement_node.data = entry_cur->data; | |
// Link entry_replacement to first entry in light_list | |
replacement_node.next = list_head; | |
list_head->prev = &replacement_node; | |
// Update linked-list pointer to our replacement list entry | |
list->first = &replacement_node; | |
// if pip-light was the last node in the list, update list-end ptr to the node before it | |
if (list_tail == entry_cur) | |
list->last = entry_prev; | |
} | |
break; | |
} | |
} | |
entry_cur = entry_prev; | |
} | |
// Call OG function | |
BYTE* retval = IteratesLights(_this, unused, a2, a3, a4, a5, a6, a7, a8); | |
// Revert our changes before returning | |
if (has_pip_light) | |
{ | |
// Relink pip-light with light-list entries | |
entry_prev->next = entry_cur; | |
if (entry_cur->next) | |
entry_cur->next->prev = entry_cur; | |
// Set linked-list pointers back to normal | |
replacement_node.next->prev = 0; | |
list->first = replacement_node.next; | |
list->last = list_tail; | |
} | |
return retval; | |
} | |
// Wrapper around IteratesLights_hook that'll print a msg to console the first time it's called, to let user know that it's active | |
// Re-hooks target after running to point to main hook, so we can skip pointless checks every time this is ran (per-frame?) | |
BYTE* __fastcall IteratesLights_hook_msg(BSShaderPPLightingProperty* _this, DWORD unused, int a2, int a3, DWORD a4, char a5, BYTE* a6, int a7, char a8) | |
{ | |
static bool hookedIndicator = false; | |
if (!hookedIndicator) | |
{ | |
Console_Print("[ImprovedLightingShaders-v1.5beta4] hooked successfully"); | |
//iMaxLightCount = % d, bPrioritisePipLight = % d, bIncreaseLightBuffers = % d", g_iMaxLightCount, g_bPrioritisePipLight ? 1 : 0, g_bIncreaseLightBuffers ? 1 : 0); | |
hookedIndicator = true; | |
} | |
// Re-hook target to use main hook code and skip the message check above | |
if (g_bPrioritisePipLight) | |
WriteRelCall(0xBE0680, (UInt32)IteratesLights_hook); | |
else | |
WriteRelCall(0xBE0680, (UInt32)IteratesLights); // unhook call.. | |
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBE0680, 5); | |
if(g_bPrioritisePipLight) | |
return IteratesLights_hook(_this, unused, a2, a3, a4, a5, a6, a7, a8); | |
return IteratesLights(_this, unused, a2, a3, a4, a5, a6, a7, a8); | |
} | |
void* __cdecl UpdateShaderConstant_LightRadius_Hook(int constIdx, float x, float y, float z, float w) | |
{ | |
int idx = constIdx - 19; // caller added 19 to light idx | |
g_LightRadius[idx].x = x; | |
g_LightRadius[idx].y = y; | |
g_LightRadius[idx].z = z; | |
g_LightRadius[idx].w = w; | |
return &g_LightRadius[idx]; | |
} | |
void* __cdecl UpdateShaderConstant_AltLightData_Hook(int constIdx, float x, float y, float z, float w) | |
{ | |
int idx = constIdx - 11; // caller added 11 to light idx | |
g_AltLightData[idx].x = x; | |
g_AltLightData[idx].y = y; | |
g_AltLightData[idx].z = z; | |
g_AltLightData[idx].w = w; | |
return &g_AltLightData[idx]; | |
} | |
void ClearLightData() | |
{ | |
for (int i = 0; i < TRUE_MAX_LIGHTS; i++) | |
{ | |
g_LightData[i].x = g_LightData[i].y = g_LightData[i].z = g_LightData[i].w = 0.0f; | |
} | |
} | |
void ClearLightData_SetWto1() | |
{ | |
for (int i = 0; i < TRUE_MAX_LIGHTS; i++) | |
{ | |
g_LightData[i].x = g_LightData[i].y = g_LightData[i].z = 0.0f; | |
g_LightData[i].w = 1.0f; | |
} | |
} | |
// Clears lights? Unsure if this is ever used... | |
void __cdecl Hook_B704C0(DWORD lightIdx) | |
{ | |
// These two only get used in this func? | |
DWORD* dword_11FA578 = (DWORD*)0x11FA578; | |
NiVector4* vect_11FA568 = (NiVector4*)0x11FA568; | |
if (!(*dword_11FA578 & 1)) | |
{ | |
*dword_11FA578 |= 1u; | |
vect_11FA568->x = 0.001; | |
vect_11FA568->y = 0.0; | |
vect_11FA568->z = 0.0; | |
vect_11FA568->w = 0.0; | |
} | |
if (lightIdx >= TRUE_MAX_LIGHTS) | |
return; | |
// this sets g_LightBacking[0] + lightIdx, DWORDs? (might be vectors...) | |
memset(&g_LightBacking[lightIdx], 0, 4 * (TRUE_MAX_LIGHTS - lightIdx)); | |
// g_LightColor starts at g_LightBacking[33] | |
NiVector4* light = &g_LightBacking[33 + lightIdx]; | |
do | |
{ | |
NiVector4* colorBlack = (NiVector4*)0x11A9BD0; | |
light->x = colorBlack->x; | |
light->y = colorBlack->y; | |
light->z = colorBlack->z; | |
light->w = colorBlack->w; | |
g_LightRadius[lightIdx].x = vect_11FA568->x; | |
g_LightRadius[lightIdx].y = vect_11FA568->y; | |
g_LightRadius[lightIdx].z = vect_11FA568->z; | |
g_LightRadius[lightIdx].w = vect_11FA568->w; | |
++light; | |
} while (light < &g_LightBacking[33 + TRUE_MAX_LIGHTS]); | |
} | |
BOOL FileExists(LPCTSTR szPath, bool dir) | |
{ | |
DWORD dwAttrib = GetFileAttributes(szPath); | |
if (dir) | |
return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); | |
else | |
return dwAttrib != INVALID_FILE_ATTRIBUTES; | |
} | |
void SetMaxLightCount(int count) | |
{ | |
// minimum 4 lights | |
if (count < 4) | |
count = 4; | |
// max 10 lights if g_bIncreaseLightBuffers = false, otherwise max is 13 | |
if (!g_bIncreaseLightBuffers && count > 10) | |
count = 10; | |
if (g_bIncreaseLightBuffers && count > TRUE_MAX_LIGHTS) | |
count = TRUE_MAX_LIGHTS; | |
if (g_iMaxLightCount == count) | |
return; | |
g_iMaxLightCount = count; | |
// Patch light coloring(?) func to handle more lights | |
SafeWrite8(0xB7082D, g_iMaxLightCount + 1); | |
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xB7082B, 3); | |
// Patch main light handling func to handle more lights | |
SafeWrite8(0xB78B46, g_iMaxLightCount + 1); | |
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xB78B44, 3); | |
} | |
bool Cmd_GetMaxLights_Execute(COMMAND_ARGS) | |
{ | |
*result = g_iMaxLightCount; | |
Console_Print("g_iMaxLightCount -> %d", g_iMaxLightCount); | |
Console_Print("g_bIncreaseLightBuffers -> %d", g_bIncreaseLightBuffers); | |
Console_Print("g_bPrioritisePipLight -> %d", g_bPrioritisePipLight); | |
return true; | |
} | |
bool Cmd_SetMaxLights_Execute(COMMAND_ARGS) | |
{ | |
UInt32 newCount = 0; | |
*result = 0; | |
if (ExtractArgs(EXTRACT_ARGS, &newCount)) | |
SetMaxLightCount(newCount); | |
Console_Print("g_iMaxLightCount -> %d", g_iMaxLightCount); | |
// can't change g_bIncreaseLightBuffers & g_bPrioritisePipLight as they affect hooks | |
return true; | |
} | |
DEFINE_COMMAND_PLUGIN(GetMaxLights, "Gets ImprovedLightingShaders settings", 0, 0, kParams_OneInt) | |
DEFINE_COMMAND_PLUGIN(SetMaxLights, "Sets ImprovedLightingShaders g_iMaxLightCount", 0, 1, kParams_OneInt) | |
bool NVSEPlugin_Load(const NVSEInterface* nvse) | |
{ | |
nvse->SetOpcodeBase(0x3900); | |
nvse->RegisterCommand(&kCommandInfo_GetMaxLights); | |
nvse->RegisterCommand(&kCommandInfo_SetMaxLights); | |
g_bIncreaseLightBuffers = GetPrivateProfileIntA(g_iniApp, "bIncreaseLightBuffers", 0, g_iniPath) == 1; | |
int maxLightCount = GetPrivateProfileIntA(g_iniApp, "iMaxLightCount", 8, g_iniPath); | |
// Set g_iMaxLightCount from INI | |
// (SetMaxLightCount patches 2 spots in the EXE with the light count!) | |
g_iMaxLightCount = 0; | |
SetMaxLightCount(maxLightCount); | |
// Patch offset game uses to build ShaderPackage path, so we can redirect it where we like | |
// This way vanilla shaders can be left untouched, for GECK or other uses. | |
if (FileExists("Data\\Shaders\\ILS\\", true)) | |
{ | |
// Check that all shader packages exist | |
bool ilsShadersExist = true; | |
char temp[128]; | |
for (int i = 2; i < 20; i++) | |
{ | |
if (i == 5 || i == 8) | |
continue; | |
sprintf_s(temp, g_shaderPackagePath, i); | |
ilsShadersExist = FileExists(temp, false); | |
if (!ilsShadersExist) | |
break; | |
} | |
if(ilsShadersExist) | |
SafeWrite32(0xB7FFDA + 1, (UInt32)g_shaderPackagePath); | |
} | |
g_bPrioritisePipLight = GetPrivateProfileIntA(g_iniApp, "bPrioritisePipLight", 1, g_iniPath) == 1; | |
// Hook that allows adding more than 3 lights to the render pass | |
{ | |
WriteRelCall(0xBDF5D0, (UInt32)RenderList_Add_Hook); | |
// TODO: patch that function and remove the 3 loops (since our hook does the exact same loops) | |
// Change asm just before jump to our hook so the class ptr gets passed along to us | |
// mov ecx, esi, nop | |
SafeWrite8(0xBDF5C7, 0x8B); | |
SafeWrite8(0xBDF5C8, 0xCE); | |
SafeWrite8(0xBDF5C9, 0x90); | |
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBDF5C7, 0xE); | |
// Skip code that tries finding lights 2 & 3, since our func will handle that | |
WriteRelJump(0xBDF457, 0xBDF537); | |
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBDF457, 0x5); | |
} | |
// Add extra shader constant registers for any additional lights: | |
{ | |
WriteRelCall(0xB7F039, (UInt32)AddLightingConstants_Hook); | |
// Nop out the code we overwrote since AddLightingConstants_Hook will perform it for us | |
for (UInt32 i = 0xB7F03E; i < 0xB7F05B; i++) | |
SafeWrite8(i, 0x90); | |
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xB7F039, 0x22); | |
} | |
// Hook to move Pip-Light to the front of the light list | |
// TODO: should probably handle this inside RenderList_Add_Hook instead! | |
WriteRelCall(0xBE0680, (UInt32)IteratesLights_hook_msg); | |
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBE0680, 5); | |
#ifdef _DEBUG | |
//Wait for debugger to attach | |
//while (!::IsDebuggerPresent()) | |
// ::Sleep(100); // to avoid 100% CPU load | |
#endif | |
// Update pointers for light data to use our larger buffers instead | |
if (g_bIncreaseLightBuffers) | |
{ | |
// Update g_LightRadius xrefs, seems to fix midrange when using > 8 lights | |
{ | |
WriteRelCall(0xB7094E, (UInt32)UpdateShaderConstant_LightRadius_Hook); | |
SafeWrite32(0xB70D9B + 2, (UInt32)&g_LightRadius[0].x); | |
SafeWrite32(0xB70DA9 + 2, (UInt32)&g_LightRadius[0].y); | |
SafeWrite32(0xB70DB3 + 2, (UInt32)&g_LightRadius[0].z); | |
SafeWrite32(0xB70DB9 + 2, (UInt32)&g_LightRadius[0].w); | |
SafeWrite32(0xB78C4F + 2, (UInt32)&g_LightRadius[0].x); | |
SafeWrite32(0xB78CEE + 2, (UInt32)&g_LightRadius[0].y); | |
SafeWrite32(0xB78D00 + 2, (UInt32)&g_LightRadius[0].z); | |
SafeWrite32(0xB78D3E + 2, (UInt32)&g_LightRadius[0].w); | |
SafeWrite32(0xB78B81 + 2, (UInt32)&g_LightRadius[0].x); | |
SafeWrite32(0xB78CD9 + 2, (UInt32)&g_LightRadius[0].x); | |
SafeWrite32(0xBB1963 + 2, (UInt32)&g_LightRadius[0].x); | |
SafeWrite32(0xBB1975 + 2, (UInt32)&g_LightRadius[0].y); | |
SafeWrite32(0xBB198A + 2, (UInt32)&g_LightRadius[0].z); | |
// Update LightRadius constant xref | |
SafeWrite32(0xBD319C + 1, (UInt32)&g_LightRadius[0].x); | |
// Update weird use of g_LightRadius[1]... | |
SafeWrite32(0xBB1A4A + 2, (UInt32)&g_LightRadius[1].x); | |
} | |
// g_AltLightData xrefs... | |
{ | |
WriteRelCall(0xB70AE4, (UInt32)UpdateShaderConstant_AltLightData_Hook); | |
SafeWrite32(0xB78B7B + 2, (UInt32)&g_AltLightData[0].x); | |
SafeWrite32(0xB78B87 + 2, (UInt32)&g_AltLightData[0].y); | |
SafeWrite32(0xB78B8D + 2, (UInt32)&g_AltLightData[0].z); | |
SafeWrite32(0xB78B99 + 2, (UInt32)&g_AltLightData[0].w); | |
SafeWrite32(0xB78BCE + 2, (UInt32)&g_AltLightData[0].x); | |
SafeWrite32(0xB78BDD + 2, (UInt32)&g_AltLightData[0].y); | |
SafeWrite32(0xB78BE7 + 2, (UInt32)&g_AltLightData[0].z); | |
SafeWrite32(0xBB19FE + 2, (UInt32)&g_AltLightData[1].x); | |
SafeWrite32(0xBB1A0D + 2, (UInt32)&g_AltLightData[1].y); | |
SafeWrite32(0xBB1A1C + 2, (UInt32)&g_AltLightData[1].z); | |
} | |
// Light-data clearing hooks | |
{ | |
// Edit some vftable so we can clear our lights? | |
SafeWrite32(0x1010118, (UInt32)ClearLightData); | |
// Hook data clear code so we can clear the added lights | |
WriteRelCall(0xB7B6C9, (UInt32)ClearLightData_SetWto1); | |
WriteRelJump(0xB7B6C9 + 5, 0xB7B725); | |
} | |
// Point the shader constants to new buffer | |
{ | |
SafeWrite32(0xB7E610 + 1, (UInt32)&g_LightData[0]); | |
SafeWrite32(0xB7EF93 + 1, (UInt32)&g_LightData[0]); | |
SafeWrite32(0xB7EFBD + 1, (UInt32)&g_LightData[1]); | |
SafeWrite32(0xB7EFE4 + 1, (UInt32)&g_LightData[2]); | |
SafeWrite32(0xB7F011 + 1, (UInt32)&g_LightData[3]); | |
// Handled by AddLightingConstants_Hook | |
//SafeWrite32(0xB7F03B + 1, (UInt32)&g_LightData[4]); | |
} | |
// Update light xrefs | |
{ | |
SafeWrite32(0xB78B93 + 2, (UInt32)&g_LightData[0].x); | |
SafeWrite32(0xB78B9F + 2, (UInt32)&g_LightData[0].y); | |
SafeWrite32(0xB78BA5 + 2, (UInt32)&g_LightData[0].z); | |
SafeWrite32(0xB78BAB + 2, (UInt32)&g_LightData[0].w); | |
SafeWrite32(0xB78CB9 + 2, (UInt32)&g_LightData[0].x); | |
SafeWrite32(0xB78CC3 + 2, (UInt32)&g_LightData[0].y); | |
SafeWrite32(0xB78CCD + 2, (UInt32)&g_LightData[0].z); | |
SafeWrite32(0xB78D24 + 2, (UInt32)&g_LightData[0].x); | |
SafeWrite32(0xB78D2E + 2, (UInt32)&g_LightData[0].y); | |
SafeWrite32(0xB78D38 + 2, (UInt32)&g_LightData[0].z); | |
SafeWrite32(0xB78D44 + 2, (UInt32)&g_LightData[0].w); | |
SafeWrite32(0xB7C9A5 + 2, (UInt32)&g_LightData[0].x); | |
SafeWrite32(0xB7C9C0 + 2, (UInt32)&g_LightData[0].y); | |
SafeWrite32(0xB7C9CA + 2, (UInt32)&g_LightData[0].z); | |
SafeWrite32(0xB7C9D4 + 2, (UInt32)&g_LightData[0].w); | |
SafeWrite32(0xB7CC78 + 2, (UInt32)&g_LightData[0].x); | |
SafeWrite32(0xB7CC93 + 2, (UInt32)&g_LightData[0].y); | |
SafeWrite32(0xB7CC9D + 2, (UInt32)&g_LightData[0].z); | |
SafeWrite32(0xB7CCA7 + 2, (UInt32)&g_LightData[0].w); | |
} | |
// Redirect g_LightBacking / 011FA070 | |
{ | |
// handled by hook SafeWrite32(0xB70515 + 3, (UInt32)&g_LightBacking[0]); | |
SafeWrite32(0xB70DCD + 3, (UInt32)&g_LightBacking[0]); | |
} | |
// g_LightColorAmb | |
{ | |
// UpdateShaderConstant - besides the hooked ones above this only has 2 usages, neither seem light related, so we'll leave this as-is | |
//SafeWrite32(0x4E20C9 + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0xB70B69 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0xB70B7B + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0xB70B8B + 1, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0xB70B98 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0xB78AB1 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0xB78ABD + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0xB78AC9 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0xB78AD5 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B78E52 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B78E58 + 1, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B78E5D + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B78E69 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0xB794B1 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0xB7955B + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7BA76 + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7BA7B + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B7BA89 + 1, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B7BA9A + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7BAC7 + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7BAD0 + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B7BADA + 1, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B7BAE8 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7BB8B + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7BB95 + 1, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B7BB9A + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B7BBA0 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7BD25 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7BD2B + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7BD99 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7BDAB + 1, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B7BDB8 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B7BDBE + 1, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7BDEB + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7BDF1 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7C5D5 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7C5DF + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B7C5E9 + 1, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B7C5EE + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7CAAB + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7CAC1 + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B7CAD3 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B7CADF + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00B7CD7E + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00B7CD90 + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00B7CDA6 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00B7CDC5 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0xB7EF3C + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00BACA41 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00BACA4B + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00BACA51 + 1, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00BACA56 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00BB20B5 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00BB20C3 + 1, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00BB20C8 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00BB20CE + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00BB282E + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00BB283B + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00BB2841 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00BB2847 + 1, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00BB2DD6 + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00BD30C6 + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00BD9497 + 1, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00BD949C + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00BD94A2 + 2, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00BD94AC + 1, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00C030D7 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00C030DD + 1, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00C030E8 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00C030F9 + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00C03149 + 2, (UInt32)&g_LightBacking[32].x); | |
SafeWrite32(0x00C03153 + 1, (UInt32)&g_LightBacking[32].y); | |
SafeWrite32(0x00C03158 + 2, (UInt32)&g_LightBacking[32].z); | |
SafeWrite32(0x00C0315E + 2, (UInt32)&g_LightBacking[32].w); | |
SafeWrite32(0x00FB99F7 + 1, (UInt32)&g_LightBacking[32].z); | |
} | |
// g_LightColor0 | |
{ | |
SafeWrite32(0xB633ED + 1, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB633F7 + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0xB633FD + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0xB63403 + 1, (UInt32)&g_LightBacking[33].w); | |
WriteRelJump(0xB704C0, (UInt32)Hook_B704C0); | |
SafeWrite32(0xB7084B + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB70D31 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB70D43 + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0xB70D51 + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0xB70D57 + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0xB78D8E + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB78D94 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB78DA0 + 1, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0xB78DAB + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0xB78DB9 + 1, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0xB78DD4 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB78DE6 + 1, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0xB78DF5 + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0xB78E07 + 1, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0xB78E63 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB78E6F + 1, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0xB78E74 + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0xB78E7A + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0xB78EC2 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB78ECC + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0xB78ED6 + 1, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0xB78EDB + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0xB7C722 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0xB7C72C + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0xB7C732 + 1, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0xB7C737 + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0xB7EF66 + 1, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BB20EC + 1, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BB2101 + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0x00BB2107 + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0x00BB210D + 1, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0x00BB21DA + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BB21E4 + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0x00BB21EA + 1, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0x00BB21EF + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0x00BB2860 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BB2876 + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0x00BB287C + 1, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0x00BB2881 + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0x00BB28A1 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BB28AB + 1, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0x00BB28B0 + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0x00BB28B6 + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0x00BB2E00 + 1, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BBC153 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BBC159 + 1, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BBC164 + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0x00BBC16A + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0x00BBC178 + 1, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0x00BBC199 + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BBC1AB + 2, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0x00BBC1BB + 1, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0x00BBC1CC + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0x00BBC1EC + 2, (UInt32)&g_LightBacking[33].x); | |
SafeWrite32(0x00BBC1F8 + 1, (UInt32)&g_LightBacking[33].y); | |
SafeWrite32(0x00BBC1FD + 2, (UInt32)&g_LightBacking[33].z); | |
SafeWrite32(0x00BBC203 + 2, (UInt32)&g_LightBacking[33].w); | |
SafeWrite32(0x00BD30F0 + 1, (UInt32)&g_LightBacking[33].x); | |
} | |
// g_LightColor1 | |
{ | |
SafeWrite32(0xB7CE09 + 1, (UInt32)&g_LightBacking[34].x); | |
SafeWrite32(0xB7CE12 + 2, (UInt32)&g_LightBacking[34].y); | |
SafeWrite32(0xB7CE18 + 2, (UInt32)&g_LightBacking[34].z); | |
SafeWrite32(0xB7CE1E + 1, (UInt32)&g_LightBacking[34].w); | |
SafeWrite32(0xBB2E2A + 1, (UInt32)&g_LightBacking[34].x); | |
SafeWrite32(0xBD311A + 1, (UInt32)&g_LightBacking[34].x); | |
} | |
} | |
return true; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment