Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package/Shaders/Common/Permutation.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ namespace Permutation
static const uint IsBeastRace = (1 << 2);
static const uint EffectShadows = (1 << 3);
static const uint IsTree = (1 << 4);
static const uint GrassSphereNormal = (1 << 5);
}

namespace ExtraFeatureFlags
Expand Down
11 changes: 7 additions & 4 deletions package/Shaders/RunGrass.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Common/MotionBlur.hlsli"
#include "Common/Random.hlsli"
#include "Common/SharedData.hlsli"
#include "Common/Permutation.hlsli"

#ifdef GRASS_LIGHTING
# define GRASS
Expand Down Expand Up @@ -499,11 +500,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
float screenNoise = Random::InterleavedGradientNoise(input.HPosition.xy, SharedData::FrameCount);

// Swaps direction of the backfaces otherwise they seem to get lit from the wrong direction.
if (!frontFace)
normal = -normal;
if (!(Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::GrassSphereNormal)) {
if (!frontFace)
normal = -normal;

normal.z = max(0.0, normal.z);
normal = normalize(float3(normal.xy, max(0, normal.z)));
normal.z = max(0.0, normal.z);
normal = normalize(float3(normal.xy, max(0, normal.z)));
}

float3x3 tbn = 0;

Expand Down
59 changes: 54 additions & 5 deletions src/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,14 @@ namespace EffectExtensions
static void thunk(RE::BSShader* shader, RE::BSRenderPass* pass, uint32_t renderFlags)
{
func(shader, pass, renderFlags);

auto state = globals::state;

state->permutationData.ExtraShaderDescriptor &= ~static_cast<uint32_t>(State::ExtraShaderDescriptors::EffectShadows);

if (auto* shaderProperty = static_cast<RE::BSShaderProperty*>(pass->geometry->GetGeometryRuntimeData().properties[1].get())) {
if (shaderProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kUniformScale)) {
auto state = globals::state;
state->permutationData.ExtraShaderDescriptor |= (uint)State::ExtraShaderDescriptors::EffectShadows;
state->permutationData.ExtraShaderDescriptor |= static_cast<uint32_t>(State::ExtraShaderDescriptors::EffectShadows);
}
}
}
Expand All @@ -188,12 +192,55 @@ namespace LightingExtensions
{
func(shader, pass, renderFlags);

globals::state->permutationData.ExtraShaderDescriptor &= ~static_cast<uint32_t>(State::ExtraShaderDescriptors::IsTree);
auto state = globals::state;

state->permutationData.ExtraShaderDescriptor &= ~static_cast<uint32_t>(State::ExtraShaderDescriptors::IsTree);

if (auto userData = pass->geometry->GetUserData())
if (auto baseObject = userData->GetBaseObject())
if (baseObject->As<RE::TESObjectTREE>())
globals::state->permutationData.ExtraShaderDescriptor |= static_cast<uint32_t>(State::ExtraShaderDescriptors::IsTree);
state->permutationData.ExtraShaderDescriptor |= static_cast<uint32_t>(State::ExtraShaderDescriptors::IsTree);
}
static inline REL::Relocation<decltype(thunk)> func;
};
}

namespace GrassExtensions
{
struct BSGrassShaderProperty_ctor
{
static RE::BSLightingShaderProperty* thunk(RE::BSLightingShaderProperty* property)
{
const uint64_t stackPointer = reinterpret_cast<uint64_t>(_AddressOfReturnAddress());
const uint64_t lightingPropertyAddress = stackPointer + (REL::Module::IsAE() ? 0x68 : 0x70);
auto* lightingProperty = *reinterpret_cast<RE::BSLightingShaderProperty**>(lightingPropertyAddress);

RE::BSLightingShaderProperty* grassProperty = func(property);

if (lightingProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kEffectLighting)) {
grassProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kEffectLighting, true);
}

return grassProperty;
}
static inline REL::Relocation<decltype(thunk)> func;
};
Comment thread
doodlum marked this conversation as resolved.

struct BSGrassShader_SetupGeometry
{
static void thunk(RE::BSShader* shader, RE::BSRenderPass* pass, uint32_t renderFlags)
{
func(shader, pass, renderFlags);

auto state = globals::state;

state->permutationData.ExtraShaderDescriptor &= ~static_cast<uint32_t>(State::ExtraShaderDescriptors::GrassSphereNormal);

if (auto* shaderProperty = static_cast<RE::BSShaderProperty*>(pass->geometry->GetGeometryRuntimeData().properties[1].get())) {
if (shaderProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kEffectLighting)) {
state->permutationData.ExtraShaderDescriptor |= static_cast<uint32_t>(State::ExtraShaderDescriptors::GrassSphereNormal);
}
}
}
static inline REL::Relocation<decltype(thunk)> func;
};
Expand Down Expand Up @@ -896,9 +943,11 @@ namespace Hooks
logger::info("Hooking TESWaterReflections::Update_Actor::GetLOSPosition for Sky Reflection Fix");
stl::write_thunk_call<TESWaterReflections_Update_Actor_GetLOSPosition>(REL::RelocationID(31373, 32160).address() + REL::Relocate(0x1AD, 0x1CA, 0x1ed));

logger::info("Hooking BSEffectShader");
logger::info("Installing SetupGeometry hooks");
stl::write_vfunc<0x6, EffectExtensions::BSEffectShader_SetupGeometry>(RE::VTABLE_BSEffectShader[0]);
stl::write_vfunc<0x6, LightingExtensions::BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]);
stl::write_thunk_call<GrassExtensions::BSGrassShaderProperty_ctor>(REL::RelocationID(15214, 15383).address() + REL::Relocate(0x45B, 0x4F5));
stl::write_vfunc<0x6, GrassExtensions::BSGrassShader_SetupGeometry>(RE::VTABLE_BSGrassShader[0]);

logger::info("Hooking TESObjectLAND");
stl::detour_thunk<TESObjectLAND_SetupMaterial>(REL::RelocationID(18368, 18791));
Expand Down
3 changes: 2 additions & 1 deletion src/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ class State
IsReflections = 1 << 1,
IsBeastRace = 1 << 2,
EffectShadows = 1 << 3,
IsTree = 1 << 4
IsTree = 1 << 4,
GrassSphereNormal = 1 << 5
};

enum class ExtraFeatureDescriptors : uint32_t
Expand Down
201 changes: 0 additions & 201 deletions src/TruePBR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,15 +508,6 @@ namespace Permutations
AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLandLODBlend, landFlagValues, result);
return result;
}

std::unordered_set<uint32_t> GeneratePBRGrassPixelPermutations()
{
using enum SIE::ShaderCache::GrassShaderTechniques;
using enum SIE::ShaderCache::GrassShaderFlags;

return { static_cast<uint32_t>(TruePbr),
static_cast<uint32_t>(TruePbr) | static_cast<uint32_t>(AlphaTest) };
}
}

void TruePBR::GenerateShaderPermutations(RE::BSShader* shader)
Expand All @@ -531,14 +522,6 @@ void TruePBR::GenerateShaderPermutations(RE::BSShader* shader)
state->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor);
std::ignore = shaderCache->GetPixelShader(*shader, pixelShaderDescriptor);
}
} else if (shader->shaderType == RE::BSShader::Type::Grass) {
const auto pixelPermutations = Permutations::GeneratePBRGrassPixelPermutations();
for (auto descriptor : pixelPermutations) {
auto vertexShaderDesriptor = descriptor;
auto pixelShaderDescriptor = descriptor;
state->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor);
std::ignore = shaderCache->GetPixelShader(*shader, pixelShaderDescriptor);
}
}
}

Expand Down Expand Up @@ -1304,180 +1287,6 @@ struct BSTempEffectGeometryDecal_Initialize
static inline REL::Relocation<decltype(thunk)> func;
};

struct BSGrassShaderProperty_ctor
{
static RE::BSLightingShaderProperty* thunk(RE::BSLightingShaderProperty* property)
{
const uint64_t stackPointer = reinterpret_cast<uint64_t>(_AddressOfReturnAddress());
const uint64_t lightingPropertyAddress = stackPointer + (REL::Module::IsAE() ? 0x68 : 0x70);
auto* lightingProperty = *reinterpret_cast<RE::BSLightingShaderProperty**>(lightingPropertyAddress);

RE::BSLightingShaderProperty* grassProperty = func(property);

if (lightingProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting)) {
if (auto* pbrSrcMaterial = static_cast<BSLightingShaderMaterialPBR*>(lightingProperty->material)) {
BSLightingShaderMaterialPBR srcMaterial;
grassProperty->LinkMaterial(&srcMaterial, true);

// Since grass actually uses kVertexLighting flag we need to switch to some flag which it does not
// use, specifically kMenuScreen.
grassProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kMenuScreen, true);

auto pbrMaterial = static_cast<BSLightingShaderMaterialPBR*>(grassProperty->material);
pbrMaterial->pbrFlags = pbrSrcMaterial->pbrFlags;
pbrMaterial->normalTexture = pbrSrcMaterial->normalTexture;
pbrMaterial->rmaosTexture = pbrSrcMaterial->rmaosTexture;
pbrMaterial->featuresTexture0 = pbrSrcMaterial->featuresTexture0;
pbrMaterial->featuresTexture1 = pbrSrcMaterial->featuresTexture1;
pbrMaterial->specularColorScale = pbrSrcMaterial->specularColorScale;
pbrMaterial->specularPower = pbrSrcMaterial->specularPower;
pbrMaterial->specularColor = pbrSrcMaterial->specularColor;
pbrMaterial->subSurfaceLightRolloff = pbrSrcMaterial->subSurfaceLightRolloff;
pbrMaterial->coatRoughness = pbrSrcMaterial->coatRoughness;
}
}

return grassProperty;
}
static inline REL::Relocation<decltype(thunk)> func;
};

struct BSGrassShaderProperty_GetRenderPasses
{
static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, std::uint32_t renderFlags, RE::BSShaderAccumulator* accumulator)
{
auto renderPasses = func(property, geometry, renderFlags, accumulator);
if (renderPasses == nullptr) {
return renderPasses;
}

const bool isPbr = property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kMenuScreen);
if (isPbr) {
auto currentPass = renderPasses->head;
while (currentPass != nullptr) {
if (currentPass->shader->shaderType == RE::BSShader::Type::Grass && currentPass->passEnum != 0x5C00005C) {
currentPass->passEnum = 0x5C000042;
}
currentPass = currentPass->next;
}
}

return renderPasses;
}
static inline REL::Relocation<decltype(thunk)> func;
};

struct BSGrassShader_SetupTechnique
{
static bool thunk(RE::BSShader* shader, uint32_t globalTechnique)
{
if (globalTechnique == 0x5C000042) {
auto shadowState = globals::game::shadowState;
auto graphicsState = globals::game::graphicsState;
auto renderer = globals::game::renderer;

const uint32_t localTechnique = static_cast<uint32_t>(SIE::ShaderCache::GrassShaderTechniques::TruePbr);
uint32_t shaderDescriptor = localTechnique;
if (graphicsState->useEarlyZ) {
shaderDescriptor |= static_cast<uint32_t>(SIE::ShaderCache::GrassShaderFlags::AlphaTest);
}

const bool began = Hooks::BSShader_BeginTechnique::thunk(shader, shaderDescriptor, shaderDescriptor, false);
if (!began) {
return false;
}

static auto fogMethod = REL::Relocation<void (*)()>(REL::RelocationID(100000, 106707));
fogMethod();

if (!globals::game::bShadowsOnGrass->GetBool()) {
shadowState->SetPSTexture(1, graphicsState->GetRuntimeData().defaultTextureWhite->rendererTexture);
shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT);
shadowState->SetPSTextureFilterMode(1, RE::BSGraphics::TextureFilterMode::kNearest);
} else {
shadowState->SetPSTexture(1, renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGET::kSHADOW_MASK]);
shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT);
shadowState->SetPSTextureFilterMode(1, globals::game::shadowMaskQuarter->GetSInt() != 4 ? RE::BSGraphics::TextureFilterMode::kBilinear : RE::BSGraphics::TextureFilterMode::kNearest);
}

return true;
}

return func(shader, globalTechnique);
};
static inline REL::Relocation<decltype(thunk)> func;
};

struct BSGrassShader_SetupMaterial
{
static void thunk(RE::BSShader* shader, RE::BSLightingShaderMaterialBase const* material)
{
const auto state = globals::state;
const auto technique = static_cast<SIE::ShaderCache::GrassShaderTechniques>(state->currentPixelDescriptor & 0b1111);

const auto& grassPSConstants = ShaderConstants::GrassPS::Get();

if (technique == SIE::ShaderCache::GrassShaderTechniques::TruePbr) {
auto shadowState = globals::game::shadowState;

RE::BSGraphics::Renderer::PreparePSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial);

auto* pbrMaterial = static_cast<const BSLightingShaderMaterialPBR*>(material);
shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture);
shadowState->SetPSTextureAddressMode(0, static_cast<RE::BSGraphics::TextureAddressMode>(pbrMaterial->textureClampMode));
shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic);

shadowState->SetPSTexture(2, pbrMaterial->normalTexture->rendererTexture);
shadowState->SetPSTextureAddressMode(2, static_cast<RE::BSGraphics::TextureAddressMode>(pbrMaterial->textureClampMode));
shadowState->SetPSTextureFilterMode(2, RE::BSGraphics::TextureFilterMode::kAnisotropic);

shadowState->SetPSTexture(3, pbrMaterial->rmaosTexture->rendererTexture);
shadowState->SetPSTextureAddressMode(3, static_cast<RE::BSGraphics::TextureAddressMode>(pbrMaterial->textureClampMode));
shadowState->SetPSTextureFilterMode(3, RE::BSGraphics::TextureFilterMode::kAnisotropic);

stl::enumeration<PBRShaderFlags> shaderFlags;
if (pbrMaterial->pbrFlags.any(PBRFlags::Subsurface)) {
shaderFlags.set(PBRShaderFlags::Subsurface);
}

const bool hasSubsurface = pbrMaterial->featuresTexture0 != nullptr && pbrMaterial->featuresTexture0 != globals::game::graphicsState->GetRuntimeData().defaultTextureWhite;
if (hasSubsurface) {
shadowState->SetPSTexture(4, pbrMaterial->featuresTexture0->rendererTexture);
shadowState->SetPSTextureAddressMode(4, static_cast<RE::BSGraphics::TextureAddressMode>(pbrMaterial->textureClampMode));
shadowState->SetPSTextureFilterMode(4, RE::BSGraphics::TextureFilterMode::kAnisotropic);

shaderFlags.set(PBRShaderFlags::HasFeaturesTexture0);
}

{
shadowState->SetPSConstant(shaderFlags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, grassPSConstants.PBRFlags);
}

{
std::array<float, 3> PBRParams1;
PBRParams1[0] = pbrMaterial->GetRoughnessScale();
PBRParams1[1] = pbrMaterial->GetSpecularLevel();
shadowState->SetPSConstant(PBRParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, grassPSConstants.PBRParams1);
}

{
std::array<float, 4> PBRParams2;
PBRParams2[0] = pbrMaterial->GetSubsurfaceColor().red;
PBRParams2[1] = pbrMaterial->GetSubsurfaceColor().green;
PBRParams2[2] = pbrMaterial->GetSubsurfaceColor().blue;
PBRParams2[3] = pbrMaterial->GetSubsurfaceOpacity();
shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, grassPSConstants.PBRParams2);
}

RE::BSGraphics::Renderer::FlushPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial);
RE::BSGraphics::Renderer::ApplyPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial);
} else {
func(shader, material);
}
};
static inline REL::Relocation<decltype(thunk)> func;
};

struct TESBoundObject_Clone3D
{
static RE::NiAVObject* thunk(RE::TESBoundObject* object, RE::TESObjectREFR* ref, bool arg3)
Expand Down Expand Up @@ -1565,16 +1374,6 @@ void TruePBR::PostPostLoad()
logger::info("Hooking BSTempEffectGeometryDecal");
stl::write_vfunc<0x25, BSTempEffectGeometryDecal_Initialize>(RE::VTABLE_BSTempEffectGeometryDecal[0]);

logger::info("Hooking BSGrassShaderProperty::ctor");
stl::write_thunk_call<BSGrassShaderProperty_ctor>(REL::RelocationID(15214, 15383).address() + REL::Relocate(0x45B, 0x4F5));

logger::info("Hooking BSGrassShaderProperty");
stl::write_vfunc<0x2A, BSGrassShaderProperty_GetRenderPasses>(RE::VTABLE_BSGrassShaderProperty[0]);

logger::info("Hooking BSGrassShader");
stl::write_vfunc<0x2, BSGrassShader_SetupTechnique>(RE::VTABLE_BSGrassShader[0]);
stl::write_vfunc<0x4, BSGrassShader_SetupMaterial>(RE::VTABLE_BSGrassShader[0]);

logger::info("Hooking TESObjectSTAT");
stl::write_vfunc<0x4A, TESBoundObject_Clone3D>(RE::VTABLE_TESObjectSTAT[0]);
}
Expand Down