From 74ae36e33aee54b2bd59ad1b035205a9765ed4ca Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 14 Oct 2026 09:55:24 +0200 Subject: [PATCH] refactor(track): the plates, fillers and check rails of a rail drawing are made from one box_profile and one helper for both sides, and the colour of a tread is sampled by sample_dds and sample_image into one colour_mean, a block of a DXT texture decoded by add_dxt_block, instead of one function --- world/RailLayer.cpp | 49 ++++++------ world/RailMaterials.cpp | 166 ++++++++++++++++++++++++---------------- 2 files changed, 123 insertions(+), 92 deletions(-) diff --git a/world/RailLayer.cpp b/world/RailLayer.cpp index d858314c..5e121032 100644 --- a/world/RailLayer.cpp +++ b/world/RailLayer.cpp @@ -149,8 +149,28 @@ void cut_faces( gfx::vertex_array const &Strip, std::size_t const Ring, bool con using namespace track_rails; +namespace { + +gfx::vertex_array box_profile( float const Bottom, float const Top, float const Texture ) { + + return { + {{ 1.f, Bottom, 0.f }, { 1.f, 0.f, 0.f }, { Texture, 0.f }}, + {{ 1.f, Top, 0.f }, { 1.f, 0.f, 0.f }, { Texture, 0.f }}, + {{ 1.f, Top, 0.f }, { 0.f, 1.f, 0.f }, { Texture, 0.f }}, + {{ -1.f, Top, 0.f }, { 0.f, 1.f, 0.f }, { Texture, 0.f }}, + {{ -1.f, Top, 0.f }, { -1.f, 0.f, 0.f }, { Texture, 0.f }}, + {{ -1.f, Bottom, 0.f }, { -1.f, 0.f, 0.f }, { Texture, 0.f }} }; +} + +} // namespace + TTrack::rail_drawing TTrack::make_rail_drawing( gfx::geometrybank_handle const &Bank ) { + auto const sides = [&]( gfx::vertex_array const &Shape, gfx::vertex_array ( &Sides )[ 2 ] ) { + create_track_rail_profile( Sides[ 0 ], Sides[ 1 ], &Shape ); + for( auto &side : Sides ) { + side.resize( Shape.size() ); + } }; rail_drawing drawing; drawing.bank = Bank; drawing.size = rail_dimensions(); @@ -170,10 +190,7 @@ TTrack::rail_drawing TTrack::make_rail_drawing( gfx::geometrybank_handle const & point.position.x = -0.041f + ( point.position.x + 0.041f ) * static_cast( drawing.size.head / 0.0735 ); point.position.y *= static_cast( drawing.size.height / 0.172 ); } - create_track_rail_profile( drawing.checkshapes[ 0 ], drawing.checkshapes[ 1 ], &checkshape ); - for( auto &shape : drawing.checkshapes ) { - shape.resize( checkshape.size() ); - } + sides( checkshape, drawing.checkshapes ); if( false == m_profiles.empty() ) { auto const &outline { track_rail_profile( m_profile1.second ) }; auto texture { 0.f }; @@ -197,18 +214,8 @@ TTrack::rail_drawing TTrack::make_rail_drawing( gfx::geometrybank_handle const & } auto const top { static_cast( ( drawing.size.grooved ? drawing.size.liptop : highest ) - 0.0005 ) }; auto const bottom { static_cast( -drawing.size.height ) }; - gfx::vertex_array plate { - {{ 1.f, bottom, 0.f }, { 1.f, 0.f, 0.f }, { texture, 0.f }}, - {{ 1.f, top, 0.f }, { 1.f, 0.f, 0.f }, { texture, 0.f }}, - {{ 1.f, top, 0.f }, { 0.f, 1.f, 0.f }, { texture, 0.f }}, - {{ -1.f, top, 0.f }, { 0.f, 1.f, 0.f }, { texture, 0.f }}, - {{ -1.f, top, 0.f }, { -1.f, 0.f, 0.f }, { texture, 0.f }}, - {{ -1.f, bottom, 0.f }, { -1.f, 0.f, 0.f }, { texture, 0.f }} }; drawing.platetexture = texture; - create_track_rail_profile( drawing.plates[ 0 ], drawing.plates[ 1 ], &plate ); - for( auto &shape : drawing.plates ) { - shape.resize( plate.size() ); - } + sides( box_profile( bottom, top, texture ), drawing.plates ); auto const neck { static_cast( drawing.size.neck ) }; auto webtexture { texture }; for( std::size_t i = 0; i < outline.size() / 2; ++i ) { @@ -218,17 +225,7 @@ TTrack::rail_drawing TTrack::make_rail_drawing( gfx::geometrybank_handle const & break; } } - gfx::vertex_array filler { - {{ 1.f, bottom, 0.f }, { 1.f, 0.f, 0.f }, { webtexture, 0.f }}, - {{ 1.f, neck, 0.f }, { 1.f, 0.f, 0.f }, { webtexture, 0.f }}, - {{ 1.f, neck, 0.f }, { 0.f, 1.f, 0.f }, { webtexture, 0.f }}, - {{ -1.f, neck, 0.f }, { 0.f, 1.f, 0.f }, { webtexture, 0.f }}, - {{ -1.f, neck, 0.f }, { -1.f, 0.f, 0.f }, { webtexture, 0.f }}, - {{ -1.f, bottom, 0.f }, { -1.f, 0.f, 0.f }, { webtexture, 0.f }} }; - create_track_rail_profile( drawing.fillers[ 0 ], drawing.fillers[ 1 ], &filler ); - for( auto &shape : drawing.fillers ) { - shape.resize( filler.size() ); - } + sides( box_profile( bottom, neck, webtexture ), drawing.fillers ); } return drawing; } diff --git a/world/RailMaterials.cpp b/world/RailMaterials.cpp index 50e402e5..b3a94b09 100644 --- a/world/RailMaterials.cpp +++ b/world/RailMaterials.cpp @@ -26,6 +26,101 @@ material_handle tint_material( material_handle const Material, glm::vec3 const & return GfxRenderer->Tinted_Material( Material, Colour ); } +namespace { + +struct colour_mean { + double sum { 0.0 }; + glm::dvec3 colour { 0.0 }; + std::size_t count { 0 }; + + void add( float const Red, float const Green, float const Blue ) { + sum += 0.2126 * Red + 0.7152 * Green + 0.0722 * Blue; + colour += glm::pow( glm::dvec3{ Red, Green, Blue } / 255.0, glm::dvec3{ 2.2 } ); + ++count; } +}; + +std::pair band_columns( glm::vec2 const &Band, int const Width ) { + + auto const first { std::clamp( static_cast( std::floor( std::min( Band.x, Band.y ) * Width ) ), 0, Width - 1 ) }; + auto const last { std::clamp( static_cast( std::ceil( std::max( Band.x, Band.y ) * Width ) ) - 1, first, Width - 1 ) }; + return { first, last }; +} + +glm::vec3 unpack_565( unsigned const Color ) { + + return glm::vec3{ ( ( Color >> 11 ) & 31 ) * 255.f / 31.f, ( ( Color >> 5 ) & 63 ) * 255.f / 63.f, ( Color & 31 ) * 255.f / 31.f }; +} + +void add_dxt_block( unsigned char const *Block, bool const Alpha, int const Column, int const First, int const Last, colour_mean &Mean ) { + + auto const low { static_cast( Block[ 0 ] | Block[ 1 ] << 8 ) }; + auto const top { static_cast( Block[ 2 ] | Block[ 3 ] << 8 ) }; + auto const opaque { Alpha || low > top }; + glm::vec3 const colors[ 4 ] { + unpack_565( low ), + unpack_565( top ), + opaque ? ( 2.f * unpack_565( low ) + unpack_565( top ) ) / 3.f : 0.5f * ( unpack_565( low ) + unpack_565( top ) ), + opaque ? ( unpack_565( low ) + 2.f * unpack_565( top ) ) / 3.f : glm::vec3{ 0.f } }; + auto const bits { static_cast( Block[ 4 ] ) | static_cast( Block[ 5 ] ) << 8 | static_cast( Block[ 6 ] ) << 16 | static_cast( Block[ 7 ] ) << 24 }; + for( int texel = 0; texel < 16; ++texel ) { + auto const x { Column * 4 + texel % 4 }; + auto const index { ( bits >> ( 2 * texel ) ) & 3 }; + if( x < First || x > Last || ( false == opaque && index == 3 ) ) { + continue; + } + Mean.add( colors[ index ].r, colors[ index ].g, colors[ index ].b ); + } +} + +void sample_dds( std::string const &File, glm::vec2 const &Band, colour_mean &Mean ) { + + std::ifstream input( File, std::ios::binary ); + std::array header {}; + if( !input.read( reinterpret_cast( header.data() ), header.size() ) || false == std::equal( header.begin(), header.begin() + 4, "DDS " ) ) { + return; + } + auto const word = [&]( std::size_t const Offset ) { + return static_cast( header[ Offset ] ) | static_cast( header[ Offset + 1 ] ) << 8 | static_cast( header[ Offset + 2 ] ) << 16 | static_cast( header[ Offset + 3 ] ) << 24; }; + auto const height { static_cast( word( 12 ) ) }; + auto const width { static_cast( word( 16 ) ) }; + auto const fourcc { word( 84 ) }; + auto const blocksize { fourcc == 0x31545844u ? 8 : ( fourcc == 0x33545844u || fourcc == 0x35545844u ) ? 16 : 0 }; + if( blocksize == 0 || width <= 0 || height <= 0 ) { + return; + } + auto const wide { ( width + 3 ) / 4 }; + auto const high { ( height + 3 ) / 4 }; + std::vector data( static_cast( wide ) * high * blocksize ); + if( !input.read( reinterpret_cast( data.data() ), data.size() ) ) { + return; + } + auto const [ first, last ] { band_columns( Band, width ) }; + for( int row = 0; row < high; ++row ) { + for( int column = first / 4; column <= last / 4; ++column ) { + add_dxt_block( &data[ ( static_cast( row ) * wide + column ) * blocksize + ( blocksize - 8 ) ], blocksize == 16, column, first, last, Mean ); + } + } +} + +void sample_image( std::string const &File, glm::vec2 const &Band, colour_mean &Mean ) { + + int width, height, components; + auto *pixels { stbi_load( File.c_str(), &width, &height, &components, 4 ) }; + if( pixels == nullptr ) { + return; + } + auto const [ first, last ] { band_columns( Band, width ) }; + for( int y = 0; y < height; ++y ) { + for( int x = first; x <= last; ++x ) { + auto const *pixel { &pixels[ ( static_cast( y ) * width + x ) * 4 ] }; + Mean.add( pixel[ 0 ], pixel[ 1 ], pixel[ 2 ] ); + } + } + stbi_image_free( pixels ); +} + +} // namespace + std::pair const &tread_sample( material_handle const Material, glm::vec2 const &Band ) { static std::map, std::pair> samples; @@ -45,76 +140,15 @@ std::pair const &tread_sample( material_handle const Material, auto const &image { GfxRenderer->Texture( texture ) }; auto const type { std::string( image.get_type() ) }; auto const file { std::string( image.get_name() ) + type }; - auto const columns = [&]( int const Width ) { - auto const first { std::clamp( static_cast( std::floor( std::min( Band.x, Band.y ) * Width ) ), 0, Width - 1 ) }; - auto const last { std::clamp( static_cast( std::ceil( std::max( Band.x, Band.y ) * Width ) ) - 1, first, Width - 1 ) }; - return std::pair{ first, last }; }; - auto sum { 0.0 }; - glm::dvec3 colour { 0.0 }; - std::size_t count { 0 }; - auto const add = [&]( float const Red, float const Green, float const Blue ) { - sum += 0.2126 * Red + 0.7152 * Green + 0.0722 * Blue; - colour += glm::pow( glm::dvec3{ Red, Green, Blue } / 255.0, glm::dvec3{ 2.2 } ); - ++count; }; + colour_mean mean; if( type == ".dds" ) { - std::ifstream input( file, std::ios::binary ); - std::array header {}; - if( input.read( reinterpret_cast( header.data() ), header.size() ) && std::equal( header.begin(), header.begin() + 4, "DDS " ) ) { - auto const word = [&]( std::size_t const Offset ) { - return static_cast( header[ Offset ] ) | static_cast( header[ Offset + 1 ] ) << 8 | static_cast( header[ Offset + 2 ] ) << 16 | static_cast( header[ Offset + 3 ] ) << 24; }; - auto const height { static_cast( word( 12 ) ) }; - auto const width { static_cast( word( 16 ) ) }; - auto const fourcc { word( 84 ) }; - auto const blocksize { fourcc == 0x31545844u ? 8 : ( fourcc == 0x33545844u || fourcc == 0x35545844u ) ? 16 : 0 }; - if( blocksize > 0 && width > 0 && height > 0 ) { - auto const wide { ( width + 3 ) / 4 }; - auto const high { ( height + 3 ) / 4 }; - std::vector data( static_cast( wide ) * high * blocksize ); - if( input.read( reinterpret_cast( data.data() ), data.size() ) ) { - auto const [ first, last ] { columns( width ) }; - auto const unpack = []( unsigned const Color ) { - return glm::vec3{ ( ( Color >> 11 ) & 31 ) * 255.f / 31.f, ( ( Color >> 5 ) & 63 ) * 255.f / 63.f, ( Color & 31 ) * 255.f / 31.f }; }; - for( int row = 0; row < high; ++row ) { - for( int column = first / 4; column <= last / 4; ++column ) { - auto const *block { &data[ ( static_cast( row ) * wide + column ) * blocksize + ( blocksize - 8 ) ] }; - auto const low { static_cast( block[ 0 ] | block[ 1 ] << 8 ) }; - auto const top { static_cast( block[ 2 ] | block[ 3 ] << 8 ) }; - auto const opaque { blocksize == 16 || low > top }; - glm::vec3 const colors[ 4 ] { - unpack( low ), - unpack( top ), - opaque ? ( 2.f * unpack( low ) + unpack( top ) ) / 3.f : 0.5f * ( unpack( low ) + unpack( top ) ), - opaque ? ( unpack( low ) + 2.f * unpack( top ) ) / 3.f : glm::vec3{ 0.f } }; - auto const bits { static_cast( block[ 4 ] ) | static_cast( block[ 5 ] ) << 8 | static_cast( block[ 6 ] ) << 16 | static_cast( block[ 7 ] ) << 24 }; - for( int texel = 0; texel < 16; ++texel ) { - auto const x { column * 4 + texel % 4 }; - auto const index { ( bits >> ( 2 * texel ) ) & 3 }; - if( x < first || x > last || ( false == opaque && index == 3 ) ) { - continue; - } - add( colors[ index ].r, colors[ index ].g, colors[ index ].b ); - } - } - } - } - } - } + sample_dds( file, Band, mean ); } else { - int width, height, components; - if( auto *pixels { stbi_load( file.c_str(), &width, &height, &components, 4 ) } ) { - auto const [ first, last ] { columns( width ) }; - for( int y = 0; y < height; ++y ) { - for( int x = first; x <= last; ++x ) { - auto const *pixel { &pixels[ ( static_cast( y ) * width + x ) * 4 ] }; - add( pixel[ 0 ], pixel[ 1 ], pixel[ 2 ] ); - } - } - stbi_image_free( pixels ); - } + sample_image( file, Band, mean ); } - if( count > 0 ) { - result = { static_cast( sum / count ), glm::vec3{ colour / static_cast( count ) } }; + if( mean.count > 0 ) { + result = { static_cast( mean.sum / mean.count ), glm::vec3{ mean.colour / static_cast( mean.count ) } }; } return result; }