diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 17:54:45 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 17:54:45 -0500 |
| commit | a0355c9b897281d0bfea48bec9d490724ecabd96 (patch) | |
| tree | 42c56ea4f4829a6115f688956858ada3e59fd9ef /libs/preview_nif/data/shaders/fo4_effectshader.frag | |
| parent | fa43a71f9d05b3673d42296a80decb2228d68f83 (diff) | |
Add NIF previewer plugin with Linux-specific fixes
Vendored from github.com/Parapets/mo2-preview_nif and adapted for Linux:
- BSA texture resolution via MO2 virtual tree with case-insensitive
fallback walk (Linux FS is case-sensitive; NIFs reference textures
with arbitrary case against files on disk)
- Process-wide cached BSA candidate list, keyed by profile path,
priority-sorted, filtering disabled mods
- Path backslash normalization before loose-file lookup
- Polygon offset on SLSF1_Decal-flagged shapes to break z-ties with
coincident base meshes (road decals, moss overlays)
- Opaque framebuffer via glColorMask alpha lock so alpha-blended shapes
can't bleed the dialog background through the preview area
- QMouseEvent::globalPosition().toPoint() for Qt6 compatibility
Preview dialog behavior (organizercore.cpp):
- Switch previewFile and previewFileWithAlternatives from stack-allocated
exec() to heap + ApplicationModal + show() + WA_DeleteOnClose.
- Parent passed as nullptr so preview lifetime is decoupled from the
stack-allocated ModInfoDialog that spawns it. Nested exec() inside
the enclosing dialog's exec() was softlocking on close.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'libs/preview_nif/data/shaders/fo4_effectshader.frag')
| -rw-r--r-- | libs/preview_nif/data/shaders/fo4_effectshader.frag | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/libs/preview_nif/data/shaders/fo4_effectshader.frag b/libs/preview_nif/data/shaders/fo4_effectshader.frag new file mode 100644 index 0000000..31b241f --- /dev/null +++ b/libs/preview_nif/data/shaders/fo4_effectshader.frag @@ -0,0 +1,148 @@ +#version 120 + +uniform sampler2D BaseMap; +uniform sampler2D GreyscaleMap; +uniform samplerCube CubeMap; +uniform sampler2D NormalMap; +uniform sampler2D SpecularMap; + +uniform bool doubleSided; + +uniform bool hasSourceTexture; +uniform bool hasGreyscaleMap; +uniform bool hasCubeMap; +uniform bool hasNormalMap; +uniform bool hasEnvMask; + +uniform bool greyscaleAlpha; +uniform bool greyscaleColor; + +uniform bool useFalloff; +uniform bool hasRGBFalloff; + +uniform bool hasWeaponBlood; + +uniform vec4 glowColor; +uniform float glowMult; + +uniform vec2 uvScale; +uniform vec2 uvOffset; + +uniform vec4 falloffParams; +uniform float falloffDepth; + +uniform float lightingInfluence; +uniform float envReflection; + +uniform mat4 modelViewMatrixInverse; +uniform mat4 worldMatrix; + +varying vec2 TexCoord; +varying vec3 LightDir; +varying vec3 ViewDir; + +varying vec4 A; +varying vec4 C; +varying vec4 D; + +varying vec3 N; +varying vec3 t; +varying vec3 b; +varying vec3 v; + +vec4 colorLookup( float x, float y ) { + + return texture2D( GreyscaleMap, vec2( clamp(x, 0.0, 1.0), clamp(y, 0.0, 1.0)) ); +} + +void main( void ) +{ + vec2 offset = TexCoord * uvScale + uvOffset; + + vec4 baseMap = texture2D( BaseMap, offset ); + vec4 normalMap = texture2D( NormalMap, offset ); + vec4 specMap = texture2D( SpecularMap, offset ); + + vec3 normal = normalize(normalMap.rgb * 2.0 - 1.0); + // Calculate missing blue channel + normal.b = sqrt(1.0 - dot(normal.rg, normal.rg)); + if ( !gl_FrontFacing && doubleSided ) { + normal *= -1.0; + } + + vec3 L = normalize(LightDir); + vec3 V = normalize(ViewDir); + vec3 R = reflect(-L, normal); + vec3 H = normalize( L + V ); + + float NdotL = max( dot(normal, L), 0.000001 ); + float NdotH = max( dot(normal, H), 0.000001 ); + float NdotV = max( dot(normal, V), 0.000001 ); + float LdotH = max( dot(L, H), 0.000001 ); + float NdotNegL = max( dot(normal, -L), 0.000001 ); + + vec3 reflected = reflect( V, normal ); + vec3 reflectedVS = b * reflected.x + t * reflected.y + N * reflected.z; + vec3 reflectedWS = vec3( worldMatrix * (modelViewMatrixInverse * vec4( reflectedVS, 0.0 )) ); + + if ( greyscaleAlpha ) + baseMap.a = 1.0; + + vec4 baseColor = glowColor; + if ( !greyscaleColor ) + baseColor.rgb *= glowMult; + + // Falloff + float falloff = 1.0; + if ( useFalloff || hasRGBFalloff ) { + falloff = smoothstep( falloffParams.x, falloffParams.y, abs(dot(normal, V)) ); + falloff = mix( max(falloffParams.z, 0.0), min(falloffParams.w, 1.0), falloff ); + + if ( useFalloff ) + baseMap.a *= falloff; + + if ( hasRGBFalloff ) + baseMap.rgb *= falloff; + } + + float alphaMult = baseColor.a * baseColor.a; + + vec4 color; + color.rgb = baseMap.rgb * C.rgb * baseColor.rgb; + color.a = alphaMult * C.a * baseMap.a; + + if ( greyscaleColor ) { + vec4 luG = colorLookup( texture2D( BaseMap, offset ).g, baseColor.r * C.r * falloff ); + + color.rgb = luG.rgb; + } + + if ( greyscaleAlpha ) { + vec4 luA = colorLookup( texture2D( BaseMap, offset ).a, color.a ); + + color.a = luA.a; + } + + vec3 diffuse = A.rgb + (D.rgb * NdotL); + color.rgb = mix( color.rgb, color.rgb * D.rgb, lightingInfluence ); + + // Specular + float g = 1.0; + float s = 1.0; + if ( hasEnvMask ) { + g = specMap.r; + s = specMap.g; + } + + // Environment + vec4 cube = textureCube( CubeMap, reflectedWS ); + if ( hasCubeMap ) { + cube.rgb *= envReflection * s; + cube.rgb = mix( cube.rgb, cube.rgb * D.rgb, lightingInfluence ); + + color.rgb += cube.rgb * falloff; + } + + gl_FragColor.rgb = color.rgb; + gl_FragColor.a = color.a; +} |
