From 674abfeeab7148b570818613236ad434f09f968e Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Wed, 21 Oct 2026 23:15:29 +0200 Subject: [PATCH] fix(track): seasonal and specgloss track materials blended at the joints --- model/material.cpp | 147 ++++++++++++++------ model/material.h | 18 ++- shaders/mat_trackblend.frag | 8 ++ shaders/mat_trackblend_grain.frag | 8 ++ shaders/mat_trackblend_normalmap.frag | 8 ++ shaders/mat_trackblend_normalmap_grain.frag | 8 ++ shaders/mat_trackblend_specgloss.frag | 18 +++ shaders/mat_trackblend_specgloss_grain.frag | 19 +++ shaders/trackblend.glsl | 55 ++++---- 9 files changed, 220 insertions(+), 69 deletions(-) create mode 100644 shaders/mat_trackblend_specgloss.frag create mode 100644 shaders/mat_trackblend_specgloss_grain.frag diff --git a/model/material.cpp b/model/material.cpp index a1439e8b..a791696d 100644 --- a/model/material.cpp +++ b/model/material.cpp @@ -756,6 +756,25 @@ material_manager::create_tinted( material_handle const Material, glm::vec3 const return tintedhandle; } +namespace { + +// the shaders a material is lit by which the blending shaders can light the same way, by the names they're fetched by, in the +// order of the lighting they stand for (1 normalmap, 2 sunless, 3 shadowless) +char const *const blend_normal_shaders[] = { "normalmap", "sunlessnormalmap", "shadowlessnormalmap" }; +char const *const blend_specgloss_shaders[] = { "normalmap_specgloss", "sunlessnormalmap_specgloss", "shadowlessnormalmap_specgloss" }; +int constexpr blend_specgloss { 4 }; // added to the lighting for a material with a specgloss map + +std::string +blend_shader( int const Lighting, bool const Grain ) { + + std::string name { "trackblend" }; + if( Lighting & blend_specgloss ) { name += "_specgloss"; } + else if( Lighting > 0 ) { name += "_normalmap"; } + return ( Grain ? name + "_grain" : name ); +} + +} // namespace + int material_manager::blend_lighting( material_handle const Material ) { @@ -774,28 +793,29 @@ material_manager::blend_lighting( material_handle const Material ) { if( material.is_good && material.shader && material.texture_variants.empty() - && false == material.update_on_weather_change - && false == material.update_on_season_change // a tint takes the parameters the blending shader keeps its lighting in && false == ( material.params[ 2 ].w < 0.f ) && has( "diffuse" ) ) { try { auto lighting { -1 }; + auto const lit_as = [&]( char const *const ( &Shaders )[ 3 ], int const Extra ) { + for( int index = 0; index < 3; ++index ) { + if( material.shader == GfxRenderer->Fetch_Shader( Shaders[ index ] ) ) { + lighting = index + 1 + Extra; + } + } }; if( conf.size() == 1 ) { lighting = 0; } else if( conf.size() == 2 && has( "normalmap" ) ) { - // the shaders lit as the blending one can be, by the names they're fetched by - char const *const shaders[] = { "normalmap", "sunlessnormalmap", "shadowlessnormalmap" }; - for( int index = 0; index < 3; ++index ) { - if( material.shader == GfxRenderer->Fetch_Shader( shaders[ index ] ) ) { - lighting = index + 1; - } - } + lit_as( blend_normal_shaders, 0 ); + } + else if( conf.size() == 3 && has( "normalmap" ) && has( "specgloss" ) ) { + lit_as( blend_specgloss_shaders, blend_specgloss ); } if( lighting >= 0 ) { // the parameters of the material go to the blending one as they are, so they have to be in the same places in both - auto const blend { GfxRenderer->Fetch_Shader( lighting > 0 ? "trackblend_normalmap" : "trackblend" ) }; + auto const blend { GfxRenderer->Fetch_Shader( blend_shader( lighting, false ) ) }; auto const fits { std::all_of( std::begin( material.shader->param_conf ), std::end( material.shader->param_conf ), [&]( auto const &Entry ) { @@ -822,7 +842,6 @@ material_manager::blend_lighting( material_handle const Material ) { + ( material.is_good ? "" : " not good;" ) + ( material.shader ? " a shader with the textures " + textures + ";" : " no shader;" ) + ( material.texture_variants.empty() ? "" : " texture variants;" ) - + ( material.update_on_weather_change || material.update_on_season_change ? " changes with the weather or seasons;" : "" ) + ( material.params[ 2 ].w < 0.f ? " tinted;" : "" ) ); } m_blendables.emplace( Material, result ); @@ -835,16 +854,15 @@ material_manager::blendable( material_handle const Material ) { return blend_lighting( Material ) >= 0; } -material_handle -material_manager::create_blend( material_handle const Material, std::array const &Others, bool const Grain ) { +bool +material_manager::build_blend( blend_key const &Key, opengl_material &Blend ) { - auto const lighting { blend_lighting( Material ) }; + auto const material { std::get<0>( Key ) }; + std::array const others { std::get<1>( Key ), std::get<2>( Key ), std::get<3>( Key ) }; + auto const grain { std::get<4>( Key ) }; + auto const lighting { blend_lighting( material ) }; if( lighting < 0 ) { - return Material; - } - auto const key { std::make_tuple( Material, Others[ 0 ], Others[ 1 ], Others[ 2 ], Grain ) }; - if( auto const lookup { m_blends.find( key ) }; lookup != m_blends.end() ) { - return lookup->second; + return false; } auto const texture_of = [&]( material_handle const Source, char const *Name ) -> texture_handle { if( false == blendable( Source ) ) { @@ -854,52 +872,98 @@ material_manager::create_blend( material_handle const Material, std::arraytexture_conf.find( Name ) }; return ( found != source.shader->texture_conf.end() ? source.textures[ found->second.id ] : null_handle ); }; + // the slots of the textures in the blending shaders (mat_trackblend*.frag): with a specgloss map there's room for the textures + // of two ends alone, p1 and p2, which is all the rails of a plain track have auto const normals { lighting > 0 }; - auto blend { variant_of( m_materials[ Material ], Grain ? ":blendgrain" : ":blend" ) }; - auto result { Material }; + auto const specgloss { ( lighting & blend_specgloss ) != 0 }; + int const diffuseslots[] = { 0, 1, 2, 3 }; + int const normalslots[] = { ( specgloss ? 3 : 4 ), ( specgloss ? 4 : 5 ), ( specgloss ? 5 : 6 ), 7 }; + auto const ends { specgloss ? 2 : 3 }; try { - blend.shader = GfxRenderer->Fetch_Shader( std::string { "trackblend" } + ( normals ? "_normalmap" : "" ) + ( Grain ? "_grain" : "" ) ); - blend.texture_variants.clear(); - blend.textures.fill( null_handle ); - auto const owndiffuse { texture_of( Material, "diffuse" ) }; - auto const ownnormal { texture_of( Material, "normalmap" ) }; - blend.textures[ 0 ] = owndiffuse; + Blend = variant_of( m_materials[ material ], grain ? ":blendgrain" : ":blend" ); + Blend.shader = GfxRenderer->Fetch_Shader( blend_shader( lighting, grain ) ); + Blend.texture_variants.clear(); + Blend.textures.fill( null_handle ); + auto const owndiffuse { texture_of( material, "diffuse" ) }; + auto const ownnormal { texture_of( material, "normalmap" ) }; + Blend.textures[ diffuseslots[ 0 ] ] = owndiffuse; if( normals ) { - blend.textures[ 4 ] = ownnormal; + Blend.textures[ normalslots[ 0 ] ] = ownnormal; } - for( std::size_t other = 0; other < Others.size(); ++other ) { + if( specgloss ) { + Blend.textures[ 6 ] = texture_of( material, "specgloss" ); + } + for( int end = 0; end < ends; ++end ) { // the slots of the ends nothing is blended at take the own textures, the shader samples them only with a weight; a // material without a normal map blended into one with it takes its normal map - auto const diffuse { texture_of( Others[ other ], "diffuse" ) }; - blend.textures[ 1 + other ] = ( diffuse != null_handle ? diffuse : owndiffuse ); + auto const diffuse { texture_of( others[ end ], "diffuse" ) }; + Blend.textures[ diffuseslots[ 1 + end ] ] = ( diffuse != null_handle ? diffuse : owndiffuse ); if( normals ) { - auto const normal { texture_of( Others[ other ], "normalmap" ) }; - blend.textures[ 5 + other ] = ( normal != null_handle ? normal : ownnormal ); + auto const normal { texture_of( others[ end ], "normalmap" ) }; + Blend.textures[ normalslots[ 1 + end ] ] = ( normal != null_handle ? normal : ownnormal ); } } - // lit as the shader of the material: normalmap, sunlessnormalmap or shadowlessnormalmap - blend.params[ 2 ].x = static_cast( lighting ); + // lit as the shader of the material: 0 mat_default, 1 normalmap, 2 sunless, 3 shadowless + Blend.params[ 2 ].x = static_cast( lighting & 3 ); // the parameters taken from the bindings again, by the shader - blend.params_state.clear(); - blend.finalize( true ); - if( blend.is_good ) { - result = static_cast( m_materials.size() ); - m_materials.emplace_back( std::move( blend ) ); - } + Blend.params_state.clear(); + Blend.finalize( true ); + return Blend.is_good; } catch( gl::shader_exception const &e ) { ErrorLog( "invalid shader: " + std::string( e.what() ) ); } + return false; +} + +material_handle +material_manager::create_blend( material_handle const Material, std::array const &Others, bool const Grain ) { + + if( false == blendable( Material ) ) { + return Material; + } + blend_key const key { Material, Others[ 0 ], Others[ 1 ], Others[ 2 ], Grain }; + if( auto const lookup { m_blends.find( key ) }; lookup != m_blends.end() ) { + return lookup->second; + } + auto result { Material }; + opengl_material blend; + if( build_blend( key, blend ) ) { + result = static_cast( m_materials.size() ); + m_materials.emplace_back( std::move( blend ) ); + m_blendsources.emplace_back( result, key ); + } m_blends.emplace( key, result ); return result; } +void +material_manager::rebuild_blends() { + + if( m_blendsources.empty() ) { + return; + } + // the materials blended took the textures of another season or weather, the blending ones take them anew in their places + m_blendables.clear(); + for( auto const &[ handle, key ] : m_blendsources ) { + opengl_material blend; + if( build_blend( key, blend ) ) { + m_materials[ handle ] = std::move( blend ); + } + else { + // can't be blended any more: drawn as the material of the track itself, the weights of its vertices unused + m_materials[ handle ] = variant_of( m_materials[ std::get<0>( key ) ], ":blend" ); + } + } +} + void material_manager::on_weather_change() { for( auto &material : m_materials ) { if( material.update_on_weather_change ) { material.update(); } } + rebuild_blends(); } void @@ -908,6 +972,7 @@ material_manager::on_season_change() { for( auto &material : m_materials ) { if( material.update_on_season_change ) { material.update(); } } + rebuild_blends(); } // checks whether specified material is in the material bank. returns handle to the material, or a null handle diff --git a/model/material.h b/model/material.h index ab84f7a4..4133a366 100644 --- a/model/material.h +++ b/model/material.h @@ -152,8 +152,9 @@ public: // itself where it can't be blended material_handle create_blend( material_handle const Material, std::array const &Others, bool const Grain ); - // whether the material can be blended so: a diffuse texture lit as mat_default, or with a normal map lit as normalmap, - // sunlessnormalmap or shadowlessnormalmap; not changing with the weather and seasons, not tinted + // whether the material can be blended so: a diffuse texture lit as mat_default, or with a normal map (and a specgloss map) + // lit as normalmap, sunlessnormalmap or shadowlessnormalmap (and their _specgloss); not tinted. the blending materials take the + // textures of another season or weather with the materials they blend bool blendable( material_handle const Material ); opengl_material const & @@ -171,14 +172,22 @@ private: // types typedef std::vector material_sequence; typedef std::unordered_map index_map; + // a blending material: the material of the track, those it blends into at the ends, whether in grains + using blend_key = std::tuple; // methods: // checks whether specified texture is in the texture bank. returns texture id, or npos. material_handle find_in_databank( std::string const &Materialname ) const; // how the material is lit, of the materials a track blends: 0 as mat_default, 1 normalmap, 2 sunlessnormalmap, - // 3 shadowlessnormalmap; -1 where it can't be blended + // 3 shadowlessnormalmap, 4 more for their _specgloss; -1 where it can't be blended int blend_lighting( material_handle const Material ); + // the blending material made of the current textures of the materials. returns: false when they can't be blended + bool + build_blend( blend_key const &Key, opengl_material &Blend ); + // the blending materials made again in their places, after the materials changed their textures + void + rebuild_blends(); public: // checks whether specified file exists. returns name of the located file, or empty string. static std::pair @@ -190,7 +199,8 @@ private: index_map m_materialmappings; std::unordered_map m_mattes; std::map, material_handle> m_tints; - std::map, material_handle> m_blends; + std::map m_blends; + std::vector> m_blendsources; std::unordered_map m_blendables; }; diff --git a/shaders/mat_trackblend.frag b/shaders/mat_trackblend.frag index 145b7395..d9b6456f 100644 --- a/shaders/mat_trackblend.frag +++ b/shaders/mat_trackblend.frag @@ -1,2 +1,10 @@ // the rails of a track faded into those of the tracks joined at its ends, see trackblend.glsl +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (diffuse3, 3, sRGB_A) +uniform sampler2D diffuse3; #include diff --git a/shaders/mat_trackblend_grain.frag b/shaders/mat_trackblend_grain.frag index d417fba0..6003b453 100644 --- a/shaders/mat_trackblend_grain.frag +++ b/shaders/mat_trackblend_grain.frag @@ -1,3 +1,11 @@ // the ballast of a track mixed in grains into that of the tracks joined at its ends, see trackblend.glsl #define TRACKBLEND_GRAIN 1 +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (diffuse3, 3, sRGB_A) +uniform sampler2D diffuse3; #include diff --git a/shaders/mat_trackblend_normalmap.frag b/shaders/mat_trackblend_normalmap.frag index 6abb1a2f..996a83fd 100644 --- a/shaders/mat_trackblend_normalmap.frag +++ b/shaders/mat_trackblend_normalmap.frag @@ -1,5 +1,13 @@ // the rails of a track of a normal mapped material faded into those of the tracks joined at its ends, see trackblend.glsl #define TRACKBLEND_NORMALMAP 1 +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (diffuse3, 3, sRGB_A) +uniform sampler2D diffuse3; #texture (normalmap, 4, RGBA) uniform sampler2D normalmap; #texture (normalmap1, 5, RGBA) diff --git a/shaders/mat_trackblend_normalmap_grain.frag b/shaders/mat_trackblend_normalmap_grain.frag index 57fe56cc..537f79d3 100644 --- a/shaders/mat_trackblend_normalmap_grain.frag +++ b/shaders/mat_trackblend_normalmap_grain.frag @@ -1,6 +1,14 @@ // the ballast of a track of a normal mapped material mixed in grains into that of the tracks joined at its ends, see trackblend.glsl #define TRACKBLEND_GRAIN 1 #define TRACKBLEND_NORMALMAP 1 +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (diffuse3, 3, sRGB_A) +uniform sampler2D diffuse3; #texture (normalmap, 4, RGBA) uniform sampler2D normalmap; #texture (normalmap1, 5, RGBA) diff --git a/shaders/mat_trackblend_specgloss.frag b/shaders/mat_trackblend_specgloss.frag new file mode 100644 index 00000000..6b7f1c0d --- /dev/null +++ b/shaders/mat_trackblend_specgloss.frag @@ -0,0 +1,18 @@ +// the rails of a track of a material with normal and specgloss maps faded into those of the tracks joined at its ends, see trackblend.glsl +#define TRACKBLEND_NORMALMAP 1 +#define TRACKBLEND_SPECGLOSS 1 +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (normalmap, 3, RGBA) +uniform sampler2D normalmap; +#texture (normalmap1, 4, RGBA) +uniform sampler2D normalmap1; +#texture (normalmap2, 5, RGBA) +uniform sampler2D normalmap2; +#texture (specgloss, 6, RGBA) +uniform sampler2D specgloss; +#include diff --git a/shaders/mat_trackblend_specgloss_grain.frag b/shaders/mat_trackblend_specgloss_grain.frag new file mode 100644 index 00000000..d144b3ad --- /dev/null +++ b/shaders/mat_trackblend_specgloss_grain.frag @@ -0,0 +1,19 @@ +// the ballast of a track of a material with normal and specgloss maps mixed in grains into that of the tracks joined at its ends, see trackblend.glsl +#define TRACKBLEND_GRAIN 1 +#define TRACKBLEND_NORMALMAP 1 +#define TRACKBLEND_SPECGLOSS 1 +#texture (diffuse, 0, sRGB_A) +uniform sampler2D diffuse; +#texture (diffuse1, 1, sRGB_A) +uniform sampler2D diffuse1; +#texture (diffuse2, 2, sRGB_A) +uniform sampler2D diffuse2; +#texture (normalmap, 3, RGBA) +uniform sampler2D normalmap; +#texture (normalmap1, 4, RGBA) +uniform sampler2D normalmap1; +#texture (normalmap2, 5, RGBA) +uniform sampler2D normalmap2; +#texture (specgloss, 6, RGBA) +uniform sampler2D specgloss; +#include diff --git a/shaders/trackblend.glsl b/shaders/trackblend.glsl index 426df319..6055760d 100644 --- a/shaders/trackblend.glsl +++ b/shaders/trackblend.glsl @@ -1,12 +1,15 @@ // a track drawn with the textures of the tracks joined at its ends blended in near them (world/TrackBlend.h): the weights of the -// tracks joined at the ends p1, p2 and p4 of a switch in UserData.yzw, their textures in diffuse1..3, lit as mat_default. -// TRACKBLEND_GRAIN: the others come in as grains, as the stones of two ballasts would mix, rather than faded in. -// TRACKBLEND_NORMALMAP: with the normal maps of the four in normalmap..normalmap3 (declared by the including shader), lit as -// the shader of the material of the track: param[2].x 1 normalmap, 2 sunlessnormalmap, 3 shadowlessnormalmap +// tracks joined at the ends p1, p2 and p4 of a switch in UserData.yzw, their textures in diffuse1..3, normalmap1..3. the +// including shader declares the textures and sets: +// TRACKBLEND_GRAIN the others come in as grains, as the stones of two ballasts would mix, rather than faded in +// TRACKBLEND_NORMALMAP with the normal maps of the four, lit as the shader of the material of the track: param[2].x 0 as +// mat_default, 1 normalmap, 2 sunlessnormalmap, 3 shadowlessnormalmap +// TRACKBLEND_SPECGLOSS the specgloss map of the track itself as well, the textures of the ends p1 and p2 alone (no room +// for a third set of them) in vec3 f_normal; in vec2 f_coord; in vec4 f_pos; -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP in mat3 f_tbn; #endif @@ -22,15 +25,6 @@ in vec4 f_clip_future_pos; #param (glossiness, 1, 3, 1, glossiness) #param (blendlighting, 2, 0, 1, zero) -#texture (diffuse, 0, sRGB_A) -uniform sampler2D diffuse; -#texture (diffuse1, 1, sRGB_A) -uniform sampler2D diffuse1; -#texture (diffuse2, 2, sRGB_A) -uniform sampler2D diffuse2; -#texture (diffuse3, 3, sRGB_A) -uniform sampler2D diffuse3; - layout(location = 0) out vec4 out_color; #if MOTIONBLUR_ENABLED layout(location = 1) out vec4 out_motion; @@ -40,7 +34,7 @@ layout(location = 1) out vec4 out_motion; #include #include -#if TRACKBLEND_GRAIN +#ifdef TRACKBLEND_GRAIN // grains per repeat of the texture: a ballast texture repeats every few metres, a grain is a handful of its stones const float GRAIN_CELLS = 28.0; @@ -85,7 +79,11 @@ float grain_value(vec2 p) void main() { +#ifdef TRACKBLEND_SPECGLOSS + vec3 weights = clamp(vec3(UserData.yz, 0.0), 0.0, 1.0); +#else vec3 weights = clamp(UserData.yzw, 0.0, 1.0); +#endif float total = weights.x + weights.y + weights.z; if (total > 1.0) { @@ -95,19 +93,19 @@ void main() // the gradients are taken outside the branches the textures are sampled in vec2 dx = dFdx(f_coord); vec2 dy = dFdy(f_coord); -#if TRACKBLEND_GRAIN +#ifdef TRACKBLEND_GRAIN vec2 grainpos = f_coord * GRAIN_CELLS; // grains smaller than a pixel would shimmer, there the textures fade into each other instead float footprint = max(length(dx), length(dy)) * GRAIN_CELLS; float grained = 1.0 - smoothstep(0.35, 0.9, footprint); #endif vec4 tex_color = textureGrad(diffuse, f_coord, dx, dy); -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP vec4 tex_normal = textureGrad(normalmap, f_coord, dx, dy); #endif if (total > 0.0) { -#if TRACKBLEND_GRAIN +#ifdef TRACKBLEND_GRAIN // the grain is of the track joined at the end whose share its value falls in, of the track itself past them float pick = grain_value(grainpos); float first = weights.x; @@ -117,32 +115,34 @@ void main() total = weights.x + weights.y + weights.z; #endif vec4 blended = tex_color * (1.0 - total); -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP vec4 blendednormal = tex_normal * (1.0 - total); #endif if (weights.x > 0.0) { blended += textureGrad(diffuse1, f_coord, dx, dy) * weights.x; -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP blendednormal += textureGrad(normalmap1, f_coord, dx, dy) * weights.x; #endif } if (weights.y > 0.0) { blended += textureGrad(diffuse2, f_coord, dx, dy) * weights.y; -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP blendednormal += textureGrad(normalmap2, f_coord, dx, dy) * weights.y; #endif } +#ifndef TRACKBLEND_SPECGLOSS if (weights.z > 0.0) { blended += textureGrad(diffuse3, f_coord, dx, dy) * weights.z; -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP blendednormal += textureGrad(normalmap3, f_coord, dx, dy) * weights.z; #endif } +#endif tex_color = blended; -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP tex_normal = blendednormal; #endif } @@ -152,14 +152,21 @@ void main() discard; vec3 fragcolor = ambient; -#if TRACKBLEND_NORMALMAP +#ifdef TRACKBLEND_NORMALMAP vec3 normal; normal.xy = (tex_normal.rg * 2.0 - 1.0); normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0)); vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * tex_normal.a; +#ifdef TRACKBLEND_SPECGLOSS + vec4 tex_specgloss = textureGrad(specgloss, f_coord, dx, dy); + float specularity = tex_specgloss.r; + glossiness = tex_specgloss.g * abs(param[1].w); + metalic = tex_specgloss.b; +#else float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; legacy_gloss(); +#endif int lighting = int(param[2].x + 0.5); if (lighting == 2) fragcolor = apply_lights_sunless(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);