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
95 changes: 55 additions & 40 deletions src/Features/ExtendedTranslucency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,56 @@
#include "../Util.h"

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
ExtendedTranslucency::MaterialParams,
ExtendedTranslucency::Settings,
AlphaMode,
AlphaReduction,
AlphaSoftness,
AlphaStrength);
AlphaStrength,
SkinnedOnly);

const RE::BSFixedString ExtendedTranslucency::NiExtraDataName_AnisotropicAlphaMaterial = "AnisotropicAlphaMaterial";

void ExtendedTranslucency::BSLightingShader_SetupGeometry(RE::BSRenderPass* pass)
{
globals::state->permutationData.ExtraFeatureDescriptor &= ~(ExtraFeatureDescriptorMask << ExtraFeatureDescriptorShift);
// TODO: PERFORMANCE: Caching the feature descriptor in map<RE::BSGeometry*, uint> if this get more complex
auto& unknownProperty = pass->geometry->GetGeometryRuntimeData().properties[RE::BSGeometry::States::kProperty];
auto alphaProperty = unknownProperty && unknownProperty->GetRTTI() == globals::rtti::NiAlphaPropertyRTTI.get() ? static_cast<RE::NiAlphaProperty*>(unknownProperty.get()) : nullptr;
auto& feature = globals::features::extendedTranslucency;
// Check alpha property exists and blending is enabled
if (alphaProperty && alphaProperty->GetAlphaBlending() && (pass->geometry->GetGeometryRuntimeData().skinInstance != nullptr || !feature.SkinnedOnly)) {
if (auto* data = pass->geometry->GetExtraData(NiExtraDataName_AnisotropicAlphaMaterial)) {
if (data->GetRTTI() == globals::rtti::NiIntegerExtraDataRTTI.get()) {
uint32_t material = static_cast<uint32_t>(static_cast<RE::NiIntegerExtraData*>(data)->value) & ExtraFeatureDescriptorMask;
if (material == MaterialModel::Disabled) {
// MaterialModel::Disabled (0) is the flag when this extra does not exist
// And it will let the effect use default settings instead of force disable it
// Ensure this is disabled by using the ForceDisabled flag
material = MaterialModel::ForceDisabled;
}
globals::state->permutationData.ExtraFeatureDescriptor |= (material << ExtraFeatureDescriptorShift);

// TODO: Per-material settings from Nif
// Mods supporting this feature should adjust their alpha value in texture already
// And the texture should be adjusted based on full strength param
}
auto SetFeatureDescriptor = [](int material) {
auto& descriptor = globals::state->permutationData.ExtraFeatureDescriptor;
static constexpr int mask = ExtraFeatureDescriptorMask << ExtraFeatureDescriptorShift;
static constexpr int shift = ExtraFeatureDescriptorShift;
descriptor = (descriptor & ~mask) | (material << shift);
};

// Clear the ExtraFeatureDescriptor to disable this effect on default
SetFeatureDescriptor(MaterialModel::DescriptorDisabled);

auto& property0 = pass->geometry->GetGeometryRuntimeData().properties[0];
auto& property1 = pass->geometry->GetGeometryRuntimeData().properties[1];
auto alphaProperty = property0 && property0->GetRTTI() == globals::rtti::NiAlphaPropertyRTTI.get() ? static_cast<RE::NiAlphaProperty*>(property0.get()) : nullptr;
auto lightProperty = property1 && property1->GetRTTI() == globals::rtti::BSLightingShaderPropertyRTTI.get() ? static_cast<RE::BSLightingShaderProperty*>(property1.get()) : nullptr;

// This effect only matters when alpha property exists and blending is enabled
// Geometries with alpha < 1 have an implicit alpha blend property
if (!(lightProperty && lightProperty->alpha < 0.999f) && (!alphaProperty || !alphaProperty->GetAlphaBlending())) {
return;
}

const auto* data = pass->geometry->GetExtraData(NiExtraDataName_AnisotropicAlphaMaterial);
if (!data) {
// If there is no extra data for explicit settings, use the default material model from global user settings
// And respect the SkinnedOnly setting
const auto& feature = globals::features::extendedTranslucency;
if (!feature.settings.SkinnedOnly || pass->geometry->GetGeometryRuntimeData().skinInstance != nullptr) {
SetFeatureDescriptor(MaterialModel::DescriptorUseDefault);
}
} else {
globals::state->permutationData.ExtraFeatureDescriptor |= ((MaterialModel::ForceDisabled) << ExtraFeatureDescriptorShift);
// Read explicit material model from extra data
if (data->GetRTTI() == globals::rtti::NiIntegerExtraDataRTTI.get()) {
uint32_t material = static_cast<uint32_t>(static_cast<const RE::NiIntegerExtraData*>(data)->value) & ExtraFeatureDescriptorMask;
// Promote `Disabled` in settings to `DescriptorDisabled` in shader
material = material == MaterialModel::Disabled ? MaterialModel::DescriptorDisabled : material;
SetFeatureDescriptor(material);
} else {
// logging is too expensive here, treat type error as disable, should only happen for modders
}
}
}

Expand Down Expand Up @@ -70,33 +85,35 @@ void ExtendedTranslucency::PostPostLoad()
void ExtendedTranslucency::DrawSettings()
{
if (ImGui::TreeNodeEx("Translucent Material", ImGuiTreeNodeFlags_DefaultOpen)) {
static const char* AlphaModeNames[4] = {
"Disabled",
"Rim Light",
"Isotropic Fabric",
"Anisotropic Fabric"
static constexpr const char* AlphaModeNames[] = {
"0 - Disabled",
"1 - Rim Edge",
"2 - Isotropic Fabric, Glass, ...",
"3 - Anisotropic Fabric",
};

static constexpr int AlphaModeSize = static_cast<int>(std::size(AlphaModeNames));

bool changed = false;
if (ImGui::Combo("Default Material Model", (int*)&settings.AlphaMode, AlphaModeNames, 4)) {
if (ImGui::Combo("Default Material Model", (int*)&settings.AlphaMode, AlphaModeNames, AlphaModeSize)) {
changed = true;
}
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text(
"Anisotropic transluency will make the surface more opaque when you view it parallel to the surface.\n"
" - Disabled: No anisotropic transluency\n"
" - Rim Light: Naive rim light effect\n"
" - Isotropic Fabric: Imaginary fabric weaved from threads in one direction, respect normal map.\n"
"Anisotropic transluency will adjust the opacity based on your view angle to the translucent surface.\n"
" - Disabled: No anisotropic transluency, flat alpha.\n"
" - Rim Edge: Naive rim light effect with no physics model, the edge of the geometry is always opaque even its full transparent.\n"
" - Isotropic Fabric: Imaginary fabric weaved from threads in one direction, respect normal map, also works well for layer of glass panels.\n"
" - Anisotropic Fabric: Common fabric weaved from tangent and birnormal direction, ignores normal map.\n");
}
if (ImGui::Checkbox("Skinned Mesh Only", &SkinnedOnly)) {
if (ImGui::Checkbox("Skinned Mesh Only", &settings.SkinnedOnly)) {
changed = true;
}
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text("Control if this effect should only apply to skinned mesh, check this option if your are seeing undesired effect on random objects.");
}

if (ImGui::SliderFloat("Transparency Increase", &settings.AlphaReduction, 0.f, 1.f)) {
if (ImGui::SliderFloat("Transparency Increase", &settings.AlphaReduction, 0, 1.f)) {
changed = true;
}
if (auto _tt = Util::HoverTooltipWrapper()) {
Expand Down Expand Up @@ -126,13 +143,11 @@ void ExtendedTranslucency::DrawSettings()
void ExtendedTranslucency::LoadSettings(json& o_json)
{
settings = o_json;
SkinnedOnly = o_json.value("SkinnedOnly", true);
}

void ExtendedTranslucency::SaveSettings(json& o_json)
{
o_json = settings;
o_json["SkinnedOnly"] = SkinnedOnly;
}

void ExtendedTranslucency::RestoreDefaultSettings()
Expand All @@ -145,7 +160,7 @@ std::pair<std::string, std::vector<std::string>> ExtendedTranslucency::GetFeatur
return {
"Extended Translucency provides realistic rendering of thin fabric and other translucent materials.\n"
"This feature supports multiple material models for different types of translucent surfaces.",
{ "Multiple translucency material models (rim light, isotropic/anisotropic fabric)",
{ "Multiple translucency material models (rim edge, isotropic/anisotropic fabric)",
"Realistic fabric translucency with directional light transmission",
"Per-material override support via NIF extra data",
"Configurable transparency and softness controls",
Expand Down
34 changes: 21 additions & 13 deletions src/Features/ExtendedTranslucency.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@

struct ExtendedTranslucency final : Feature
{
static constexpr std::string_view MOD_ID = "150755"sv;

virtual inline std::string GetName() override { return "Extended Translucency"; }
virtual inline std::string GetShortName() override { return "ExtendedTranslucency"; }
virtual inline std::string_view GetShaderDefineName() override { return "EXTENDED_TRANSLUCENCY"; }
virtual inline std::string_view GetCategory() const override { return "Materials"; }
virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); }
virtual inline std::string_view GetShaderDefineName() override { return "EXTENDED_TRANSLUCENCY"sv; }
virtual inline std::string_view GetCategory() const override { return "Materials"sv; }
virtual std::pair<std::string, std::vector<std::string>> GetFeatureSummary() override;
virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return RE::BSShader::Type::Lighting == shaderType; };
virtual void PostPostLoad() override;
virtual void DrawSettings() override;
Expand All @@ -17,39 +21,43 @@ struct ExtendedTranslucency final : Feature
virtual void RestoreDefaultSettings() override;
virtual bool SupportsVR() override { return true; };

virtual std::pair<std::string, std::vector<std::string>> GetFeatureSummary() override;

// Future proof function for UI refactoring
std::string GetFeatureDescription() { return "Realistic rendering of thin fabric and other translucent materials"; } // Feature description for settings page
std::string GetFeatureModLink() { return "https://www.nexusmods.com/skyrimspecialedition/mods/150755"; }

static void BSLightingShader_SetupGeometry(RE::BSRenderPass* pass);

struct Hooks;

// TODO: Support more material model like glasses or arcylic
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note Todos will be assigned to you. So determine if you want this or not.

enum MaterialModel : uint32_t
{
Disabled = 0, // In ExtraFeatureDescriptor, this value means 'Default' instead of 'Disabled'
Disabled = 0, // In user settings, 0 means 'Disabled'
RimLight = 1, // Similar effect like rim light
IsotropicFabric = 2, // 1D fabric model, respect normal map
AnisotropicFabric = 3, // 2D fabric model alone tangent and binormal, ignores normal map
ForceDisabled = 4, // In ExtraFeatureDescriptor, value >= 4 means 'Disabled'

DescriptorUseDefault = 0, // In ExtraFeatureDescriptor, 0 means 'UseDefault' instead of 'Disabled'
DescriptorDisabled = 7, // In ExtraFeatureDescriptor, value >= 5 means 'Disabled'
};

static constexpr uint32_t ExtraFeatureDescriptorShift = 6;
static constexpr uint32_t ExtraFeatureDescriptorMask = 7;

struct alignas(16) MaterialParams
// Settings in both CPU and GPU constant buffer
struct alignas(16) PerFrame
{
uint32_t AlphaMode = MaterialModel::AnisotropicFabric;
float AlphaReduction = 0.15f;
float AlphaSoftness = 0.f;
float AlphaStrength = 0.f;
};

MaterialParams settings;
bool SkinnedOnly = true;
// Settings only in CPU
struct Settings : PerFrame
{
bool SkinnedOnly = true;
};

Settings settings;

const PerFrame& GetCommonBufferData() { return settings; }

static const RE::BSFixedString NiExtraDataName_AnisotropicAlphaMaterial;
};