diff --git a/features/Light Limit Fix/ParticleLights/black.ini b/features/Light Limit Fix/ParticleLights/black.ini new file mode 100644 index 000000000..ec6858e38 --- /dev/null +++ b/features/Light Limit Fix/ParticleLights/black.ini @@ -0,0 +1,2 @@ +[Light] +Cull = true \ No newline at end of file diff --git a/features/Light Limit Fix/ParticleLights/candleglow01.ini b/features/Light Limit Fix/ParticleLights/candleglow01.ini new file mode 100644 index 000000000..e69de29bb diff --git a/features/Light Limit Fix/ParticleLights/enblightglow.ini b/features/Light Limit Fix/ParticleLights/enblightglow.ini new file mode 100644 index 000000000..4f21558b7 --- /dev/null +++ b/features/Light Limit Fix/ParticleLights/enblightglow.ini @@ -0,0 +1,6 @@ +[Light] +Cull = true +Flicker = true +ColorMultRed = 10.0 +ColorMultGreen = 10.0 +ColorMultBlue = 10.0 \ No newline at end of file diff --git a/features/Light Limit Fix/ParticleLights/fxglowenb.ini b/features/Light Limit Fix/ParticleLights/fxglowenb.ini new file mode 100644 index 000000000..0a54c63fa --- /dev/null +++ b/features/Light Limit Fix/ParticleLights/fxglowenb.ini @@ -0,0 +1,3 @@ +[Light] +Cull = true +RadiusMult = 0.5 \ No newline at end of file diff --git a/features/Light Limit Fix/ParticleLights/glowslightflash.ini b/features/Light Limit Fix/ParticleLights/glowslightflash.ini new file mode 100644 index 000000000..e69de29bb diff --git a/features/Light Limit Fix/ParticleLights/glowsoft01_enbl.ini b/features/Light Limit Fix/ParticleLights/glowsoft01_enbl.ini new file mode 100644 index 000000000..ec6858e38 --- /dev/null +++ b/features/Light Limit Fix/ParticleLights/glowsoft01_enbl.ini @@ -0,0 +1,2 @@ +[Light] +Cull = true \ No newline at end of file diff --git a/src/Features/LightLimitFIx/ParticleLights.cpp b/src/Features/LightLimitFIx/ParticleLights.cpp new file mode 100644 index 000000000..db1bd72d0 --- /dev/null +++ b/src/Features/LightLimitFIx/ParticleLights.cpp @@ -0,0 +1,60 @@ +#include "Features/LightLimitFix/ParticleLights.h" + +#include + +void ParticleLights::GetConfigs() +{ + auto configs = clib_util::distribution::get_configs("Data\\ParticleLights", "", ".ini"); + + if (configs.empty()) { + logger::warn("[LLF] No .ini files were found within the Data\\ParticleLights folder, aborting..."); + return; + } + + logger::info("[LLF] {} matching inis found", configs.size()); + + for (auto& path : configs) { + logger::info("[LLF] loading ini : {}", path); + + CSimpleIniA ini; + ini.SetUnicode(); + ini.SetMultiKey(); + + if (const auto rc = ini.LoadFile(path.c_str()); rc < 0) { + logger::error("\t\t[LLF] couldn't read INI"); + continue; + } + + Config data{}; + data.cull = ini.GetBoolValue("Light", "Cull", false); + data.colorMult.red = (float)ini.GetDoubleValue("Light", "ColorMultRed", 1.0); + data.colorMult.green = (float)ini.GetDoubleValue("Light", "ColorMultGreen", 1.0); + data.colorMult.blue = (float)ini.GetDoubleValue("Light", "ColorMultBlue", 1.0); + data.radiusMult = (float)ini.GetDoubleValue("Light", "RadiusMult", 1.0); + data.flicker = ini.GetBoolValue("Light", "Flicker", false); + data.flickerSpeed = (float)ini.GetDoubleValue("Light", "FlickerSpeed", 1.0); + data.flickerIntensity = (float)ini.GetDoubleValue("Light", "FlickerIntensity", 0.0); + data.flickerMovement = (float)ini.GetDoubleValue("Light", "FlickerMovement", 0.0) / std::numbers::pi_v; + + auto lastSeparatorPos = path.find_last_of("\\/"); + if (lastSeparatorPos != std::string::npos) { + std::string filename = path.substr(lastSeparatorPos + 1); + if (filename.size() < 4) { + logger::error("[LLF] Path too short"); + continue; + } + + filename.erase(filename.length() - 4); // Remove ".ini" +#pragma warning(push) +#pragma warning(disable: 4244) + std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower); +#pragma warning(pop) + + logger::debug("[LLF] Inserting {}", filename); + + particleLightConfigs.insert({ filename, data }); + } else { + logger::error("[LLF] Path incomplete"); + } + } +} diff --git a/src/Features/LightLimitFIx/ParticleLights.h b/src/Features/LightLimitFIx/ParticleLights.h new file mode 100644 index 000000000..35d633956 --- /dev/null +++ b/src/Features/LightLimitFIx/ParticleLights.h @@ -0,0 +1,32 @@ +#pragma once + +class ParticleLights +{ +public: + static ParticleLights* GetSingleton() + { + static ParticleLights singleton; + return &singleton; + } + + enum class Flicker + { + None = 0, + Normal = 1 + }; + + struct Config + { + bool cull; + RE::NiColor colorMult; + float radiusMult; + bool flicker; + float flickerSpeed; + float flickerIntensity; + float flickerMovement; + }; + + std::unordered_map particleLightConfigs; + + void GetConfigs(); +}; \ No newline at end of file diff --git a/src/Features/LightLimitFix.cpp b/src/Features/LightLimitFix.cpp index 0bb6874c0..c0ffe5302 100644 --- a/src/Features/LightLimitFix.cpp +++ b/src/Features/LightLimitFix.cpp @@ -46,7 +46,7 @@ void LightLimitFix::DrawSettings() ImGui::SliderFloat("Billboard Radius", &settings.ParticleLightsRadiusBillboards, 0.0, 1.0); ImGui::Checkbox("Enable Optimization", &settings.EnableParticleLightsOptimization); - ImGui::SliderInt("Optimisation Cluster Radius", (int*)&settings.ParticleLightsOptimisationClusterRadius, 1, 128); + ImGui::SliderInt("Optimisation Cluster Radius", (int*)&settings.ParticleLightsOptimisationClusterRadius, 1, 48); ImGui::TreePop(); } @@ -393,93 +393,73 @@ bool LightLimitFix::CheckParticleLights(RE::BSRenderPass* a_pass, uint32_t a_tec { // See https://www.nexusmods.com/skyrimspecialedition/articles/1391 if (settings.EnableParticleLights) { - // { - // if (auto shaderProperty = netimmerse_cast(a_pass->shaderProperty)) { - // if (auto material = static_cast(shaderProperty->material)) { - // std::string textureName = material->sourceTexturePath.c_str(); - // auto lastSeparatorPos = textureName.find_last_of("\\/"); - // if (lastSeparatorPos != std::string::npos) { - // std::string filename = textureName.substr(lastSeparatorPos + 1); - //#pragma warning(push) - //#pragma warning(disable: 4244) - // std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower); - //#pragma warning(pop) - // std::set cullingList{ - // "fxglowenb.dds", "glowsoft01_enbl.dds", "black.dds", "enblightglow.dds" - // }; - // bool shouldCull = cullingList.contains(filename); - // if (shouldCull) { - // logger::info("tech {:X}", a_technique); - // } - // } - // } - // } - // } - // if (a_technique == 0x4004146F || a_technique == 0x4004046F || a_technique == 0x4000046F) { if (auto shaderProperty = netimmerse_cast(a_pass->shaderProperty)) { if (auto material = static_cast(shaderProperty->material)) { - if (!material->sourceTexturePath.empty() && material->sourceTexturePath.size() > 1) { + if (!material->sourceTexturePath.empty()) { std::string textureName = material->sourceTexturePath.c_str(); + if (textureName.size() < 1) + return false; auto lastSeparatorPos = textureName.find_last_of("\\/"); if (lastSeparatorPos != std::string::npos) { std::string filename = textureName.substr(lastSeparatorPos + 1); + if (filename.size() < 4) + return false; + + filename.erase(filename.length() - 4); // Remove ".dds" #pragma warning(push) #pragma warning(disable: 4244) std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower); #pragma warning(pop) - - std::set lightsList{ - "candleglow01.dds", "glowslightflash.dds", "fxglowenb.dds", "glowsoft01_enbl.dds", "black.dds", "enblightglow.dds" - }; - - if (lightsList.contains(filename)) { - a_pass->geometry->IncRefCount(); - if (const auto particleSystem = netimmerse_cast(a_pass->geometry)) { - if (auto particleData = particleSystem->particleData.get()) { - particleData->IncRefCount(); - } + auto& configs = ParticleLights::GetSingleton()->particleLightConfigs; + auto it = configs.find(filename); + if (it == configs.end()) + return false; + + auto& config = it->second; + a_pass->geometry->IncRefCount(); + if (const auto particleSystem = netimmerse_cast(a_pass->geometry)) { + if (auto particleData = particleSystem->particleData.get()) { + particleData->IncRefCount(); } + } - RE::NiColor color; - color.red = material->baseColor.red * material->baseColorScale * material->baseColor.alpha * shaderProperty->alpha * settings.ParticleLightsBrightness; - color.green = material->baseColor.green * material->baseColorScale * material->baseColor.alpha * shaderProperty->alpha * settings.ParticleLightsBrightness; - color.blue = material->baseColor.blue * material->baseColorScale * material->baseColor.alpha * shaderProperty->alpha * settings.ParticleLightsBrightness; + RE::NiColor color; + color.red = material->baseColor.red * material->baseColorScale * material->baseColor.alpha * shaderProperty->alpha * settings.ParticleLightsBrightness; + color.green = material->baseColor.green * material->baseColorScale * material->baseColor.alpha * shaderProperty->alpha * settings.ParticleLightsBrightness; + color.blue = material->baseColor.blue * material->baseColorScale * material->baseColor.alpha * shaderProperty->alpha * settings.ParticleLightsBrightness; - if (auto emittance = shaderProperty->unk88) { - color.red *= emittance->red; - color.green *= emittance->green; - color.blue *= emittance->blue; - } + if (auto emittance = shaderProperty->unk88) { + color.red *= emittance->red; + color.green *= emittance->green; + color.blue *= emittance->blue; + } - if (auto rendererData = a_pass->geometry->GetGeometryRuntimeData().rendererData) { - if (rendererData->vertexDesc.HasFlag(RE::BSGraphics::Vertex::Flags::VF_COLORS)) { - if (auto triShape = a_pass->geometry->AsTriShape()) { - uint32_t vertexSize = rendererData->vertexDesc.GetSize(); - uint32_t offset = rendererData->vertexDesc.GetAttributeOffset(RE::BSGraphics::Vertex::Attribute::VA_COLOR); - RE::NiColorA vertexColor{}; - for (int v = 0; v < triShape->GetTrishapeRuntimeData().vertexCount; v++) { - if (VertexColor* vertex = reinterpret_cast(&rendererData->rawVertexData[vertexSize * v + offset])) { - RE::NiColorA niColor{ (float)vertex->data[0] / 255.0f, (float)vertex->data[1] / 255.0f, (float)vertex->data[2] / 255.0f, (float)vertex->data[3] / 255.0f }; - if (niColor.alpha > vertexColor.alpha) - vertexColor = niColor; - } + if (auto rendererData = a_pass->geometry->GetGeometryRuntimeData().rendererData) { + if (rendererData->vertexDesc.HasFlag(RE::BSGraphics::Vertex::Flags::VF_COLORS)) { + if (auto triShape = a_pass->geometry->AsTriShape()) { + uint32_t vertexSize = rendererData->vertexDesc.GetSize(); + uint32_t offset = rendererData->vertexDesc.GetAttributeOffset(RE::BSGraphics::Vertex::Attribute::VA_COLOR); + RE::NiColorA vertexColor{}; + for (int v = 0; v < triShape->GetTrishapeRuntimeData().vertexCount; v++) { + if (VertexColor* vertex = reinterpret_cast(&rendererData->rawVertexData[vertexSize * v + offset])) { + RE::NiColorA niColor{ (float)vertex->data[0] / 255.0f, (float)vertex->data[1] / 255.0f, (float)vertex->data[2] / 255.0f, (float)vertex->data[3] / 255.0f }; + if (niColor.alpha > vertexColor.alpha) + vertexColor = niColor; } - color.red *= vertexColor.red * vertexColor.alpha; - color.green *= vertexColor.green * vertexColor.alpha; - color.blue *= vertexColor.blue * vertexColor.alpha; } + color.red *= vertexColor.red * vertexColor.alpha; + color.green *= vertexColor.green * vertexColor.alpha; + color.blue *= vertexColor.blue * vertexColor.alpha; } } + } - queuedParticleLights.insert({ a_pass->geometry, color }); + color *= config.colorMult; - std::set cullingList{ - "fxglowenb.dds", "glowsoft01_enbl.dds", "black.dds", "enblightglow.dds" - }; - bool shouldCull = cullingList.contains(filename); - return settings.EnableParticleLightsCulling && shouldCull; - } + queuedParticleLights.insert({ a_pass->geometry, { color, config } }); + + return settings.EnableParticleLightsCulling && config.cull; } } } @@ -527,7 +507,7 @@ float LightLimitFix::CalculateLightDistance(float3 a_lightPosition, float a_radi return (a_lightPosition.x * a_lightPosition.x) + (a_lightPosition.y * a_lightPosition.y) + (a_lightPosition.z * a_lightPosition.z) - (a_radius * a_radius); } -bool LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData, LightLimitFix::LightData& light, float dimmerMult, int a_eyeIndex, RE::BSGeometry* a_geometry, double timer) +bool LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData, LightLimitFix::LightData& light, ParticleLights::Config& config, float dimmerMult, int a_eyeIndex, RE::BSGeometry* a_geometry, double timer) { static float& lightFadeStart = (*(float*)RELOCATION_ID(527668, 414582).address()); static float& lightFadeEnd = (*(float*)RELOCATION_ID(527669, 414583).address()); @@ -545,7 +525,7 @@ bool LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData auto state = RE::BSGraphics::RendererShadowState::GetSingleton(); if (dimmer != 0) { if ((light.color.x > 0 || light.color.y > 0 || light.color.z > 0) && light.radius > 0) { - if (a_geometry) { + if (a_geometry && config.flicker) { auto seed = (std::uint32_t)std::hash{}(a_geometry); siv::PerlinNoise perlin1{ seed }; @@ -553,12 +533,17 @@ bool LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData siv::PerlinNoise perlin3{ seed + 2 }; siv::PerlinNoise perlin4{ seed + 3 }; - light.positionWS[0].x += (float)perlin1.noise1D(timer) * 5.0f; - light.positionWS[0].y += (float)perlin2.noise1D(timer) * 5.0f; - light.positionWS[0].z += (float)perlin3.noise1D(timer) * 5.0f; + auto scaledTimer = timer * config.flickerSpeed; + + light.positionWS[0].x += (float)perlin1.noise1D(scaledTimer) * config.flickerMovement; + light.positionWS[0].y += (float)perlin2.noise1D(scaledTimer) * config.flickerMovement; + light.positionWS[0].z += (float)perlin3.noise1D(scaledTimer) * config.flickerMovement; light.positionWS[1] = light.positionWS[0]; - dimmer = std::max(0.0f, dimmer - ((float)perlin4.noise1D_01(timer) * 0.5f)); + dimmer = std::max(0.0f, dimmer - ((float)perlin4.noise1D_01(scaledTimer) * config.flickerIntensity)); } + + light.radius *= config.radiusMult; + CachedParticleLight cachedParticleLight{}; cachedParticleLight.color = { light.color.x, light.color.y, light.color.z }; cachedParticleLight.radius = light.radius; @@ -670,9 +655,9 @@ void LightLimitFix::UpdateLights() uint32_t clusteredLights = 0; auto numVertices = particleData->GetActiveVertexCount(); for (std::uint32_t p = 0; p < numVertices; p++) { - light.color.x += particleLight.second.red * particleData->color[p].red * particleData->color[p].alpha; - light.color.y += particleLight.second.green * particleData->color[p].green * particleData->color[p].alpha; - light.color.z += particleLight.second.blue * particleData->color[p].blue * particleData->color[p].alpha; + light.color.x += particleLight.second.first.red * particleData->color[p].red * particleData->color[p].alpha; + light.color.y += particleLight.second.first.green * particleData->color[p].green * particleData->color[p].alpha; + light.color.z += particleLight.second.first.blue * particleData->color[p].blue * particleData->color[p].alpha; float radius = particleData->sizes[p] * settings.ParticleLightsRadius; @@ -691,7 +676,7 @@ void LightLimitFix::UpdateLights() light.radius /= (float)clusteredLights; light.positionWS[eyeIndex] /= (float)clusteredLights; - currentLightCount += AddCachedParticleLights(lightsData, light, 2.0f, eyeIndex); + currentLightCount += AddCachedParticleLights(lightsData, light, particleLight.second.second, 1.0f, eyeIndex); clusteredLights = 0; light.color = { 0, 0, 0 }; @@ -711,16 +696,16 @@ void LightLimitFix::UpdateLights() light.radius /= (float)clusteredLights; light.positionWS[eyeIndex] /= (float)clusteredLights; - currentLightCount += AddCachedParticleLights(lightsData, light, 2.0f, eyeIndex); + currentLightCount += AddCachedParticleLights(lightsData, light, particleLight.second.second, 1.0f, eyeIndex); } } else { // process NiColor LightData light{}; - light.color.x = particleLight.second.red; - light.color.y = particleLight.second.green; - light.color.z = particleLight.second.blue; + light.color.x = particleLight.second.first.red; + light.color.y = particleLight.second.first.green; + light.color.z = particleLight.second.first.blue; float radius = particleLight.first->GetModelData().modelBound.radius * particleLight.first->world.scale; @@ -728,7 +713,7 @@ void LightLimitFix::UpdateLights() SetLightPosition(light, particleLight.first->worldBound.center); //light is complete for both eyes by now - currentLightCount += AddCachedParticleLights(lightsData, light, 1.0f, eyeIndex, particleLight.first, timer); + currentLightCount += AddCachedParticleLights(lightsData, light, particleLight.second.second, 1.0f, eyeIndex, particleLight.first, timer); } } } diff --git a/src/Features/LightLimitFix.h b/src/Features/LightLimitFix.h index 6415bed11..55d7ee54c 100644 --- a/src/Features/LightLimitFix.h +++ b/src/Features/LightLimitFix.h @@ -7,6 +7,7 @@ #include "Feature.h" #include "ShaderCache.h" +#include struct LightLimitFix : Feature { @@ -17,6 +18,7 @@ struct LightLimitFix : Feature return &render; } + static void InstallHooks() { Hooks::Install(); @@ -92,8 +94,8 @@ struct LightLimitFix : Feature RE::BSRenderPass* currentPass = nullptr; - eastl::hash_map queuedParticleLights; - eastl::hash_map particleLights; + eastl::hash_map < RE::BSGeometry*, std::pair> queuedParticleLights; + eastl::hash_map> particleLights; std::uint32_t strictLightsCount = 0; eastl::vector strictLightsData; @@ -108,7 +110,7 @@ struct LightLimitFix : Feature virtual void Draw(const RE::BSShader* shader, const uint32_t descriptor); float CalculateLightDistance(float3 a_lightPosition, float a_radius); - bool AddCachedParticleLights(eastl::vector& lightsData, LightLimitFix::LightData& light, float dimmerMult = 1.0f, int eyeIndex = 0, RE::BSGeometry* a_geometry = nullptr, double timer = 0.0f); + bool AddCachedParticleLights(eastl::vector& lightsData, LightLimitFix::LightData& light, ParticleLights::Config& config, float dimmerMult = 1.0f, int eyeIndex = 0, RE::BSGeometry* a_geometry = nullptr, double timer = 0.0f); void SetLightPosition(LightLimitFix::LightData& a_light, RE::NiPoint3& a_initialPosition); void UpdateLights(); void Bind(); diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index 9280c6984..d7425197b 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -7,6 +7,7 @@ #include "ENB/ENBSeriesAPI.h" #include "Features/ExtendedMaterials.h" #include "Features/LightLimitFix.h" +#include "Features/LightLimitFIx/ParticleLights.h" #define DLLEXPORT __declspec(dllexport) std::list errors; @@ -89,6 +90,8 @@ void MessageHandler(SKSE::MessagingInterface::Message* message) } if (errors.empty()) { + ParticleLights::GetSingleton()->GetConfigs(); + Hooks::Install(); LightLimitFix::InstallHooks();