From e72870ad76ed7f358bc5e17107e7b37793c703b0 Mon Sep 17 00:00:00 2001 From: jerrrrycho Date: Sun, 13 Sep 2026 18:37:12 +0200 Subject: [PATCH] fix: sonar sast fixes --- model/Model3d.cpp | 6 +- model/material.cpp | 222 ++++++++++++++++++----------------- model/material.h | 15 ++- rendering/openglrenderer.cpp | 6 +- utilities/utilities.cpp | 2 +- 5 files changed, 136 insertions(+), 115 deletions(-) diff --git a/model/Model3d.cpp b/model/Model3d.cpp index a89c2598..38f39080 100644 --- a/model/Model3d.cpp +++ b/model/Model3d.cpp @@ -2494,8 +2494,8 @@ void accumulate_mesh_bounds(TSubModel const *Submodel, glm::vec3 &Minimum, glm:: { while (Submodel != nullptr) { - auto const &vertices{(Submodel->m_geometry.handle != null_handle ? GfxRenderer->Vertices(Submodel->m_geometry.handle) : Submodel->Vertices)}; - if (false == vertices.empty()) + if (auto const &vertices{(Submodel->m_geometry.handle != null_handle ? GfxRenderer->Vertices(Submodel->m_geometry.handle) : Submodel->Vertices)}; + false == vertices.empty()) { // the transformation chain can still hold a rotation, so the vertices go through it // rather than get taken at their face value @@ -2551,7 +2551,7 @@ void TModel3d::assign_variant_groups() // separate branches drawn in disjoint distance ranges, and grouping them by position is what keeps the // object on the same variant no matter which of them is currently on screen std::vector grouppositions; // position of each group, indexed by its id - auto const groupid = [&grouppositions](TSubModel const *Branch) -> int { + auto const groupid = [&grouppositions](TSubModel const *Branch) { auto const position{branch_position(Branch)}; for (std::size_t index = 0; index < grouppositions.size(); ++index) { diff --git a/model/material.cpp b/model/material.cpp index a7e40dfb..19fa0b37 100644 --- a/model/material.cpp +++ b/model/material.cpp @@ -64,6 +64,74 @@ std::map texture_bindings { { "normalmap", 1 } }; +int opengl_material::texture_slot( std::string const &Key ) +{ + auto key { Key }; + + if( ( key.size() > 0 ) && ( key[ 0 ] != '_' ) ) + { + // numbered binding, "textureN:" fills the slot N-1 + key.erase( key.find_first_not_of( "-1234567890" ) ); + size_t const num = std::stoi( key ) - 1; + if( num < gl::MAX_TEXTURES ) { + return static_cast( num ); + } + log_error( "invalid texture binding: " + std::to_string( num ) ); + is_good = false; + return -1; + } + + if( key.size() > 2 ) + { + // named binding, the slot it fills is up to the shader + key.erase( 0, 1 ); + key.pop_back(); + if( shader && ( shader->texture_conf.find( key ) != shader->texture_conf.end() ) ) { + return static_cast( shader->texture_conf[ key ].id ); + } + auto const lookup { texture_bindings.find( key ) }; + if( ( shader == nullptr ) && ( lookup != texture_bindings.end() ) ) { + return lookup->second; + } + // ignore unrecognized texture bindings in legacy render mode, it's most likely data for more advanced shaders + if( Global.GfxRenderer == "default" ) { + log_error( "unknown texture binding: " + key ); + is_good = false; + } + return -1; + } + + log_error( "unrecognized texture binding: " + key ); + is_good = false; + return -1; +} + +void opengl_material::build_texture_variants( std::vector const &Bindings, bool const Loadnow ) +{ + if( true == Bindings.empty() ) { return; } + + // build a texture set for each entry of the largest of the declared pools. + // pools of different size can be combined, shorter ones simply repeat from the start + std::size_t variantcount { 0 }; + for( auto const &[ slot, pool ] : Bindings ) { + variantcount = std::max( variantcount, pool->size() ); + } + texture_variants.resize( variantcount, textures ); + + for( auto const &[ slot, pool ] : Bindings ) { + for( std::size_t variantindex = 0; variantindex < variantcount; ++variantindex ) { + auto const &texturename { ( *pool )[ variantindex % pool->size() ] }; + auto const texturehandle { GfxRenderer->Fetch_Texture( texturename, Loadnow ) }; + if( texturehandle == null_handle ) { + // a single bad entry shouldn't take down the whole material, fall back to the basic texture + log_error( "missing texture variant: " + texturename ); + continue; + } + texture_variants[ variantindex ][ slot ] = texturehandle; + } + } +} + void opengl_material::finalize(bool Loadnow) { is_good = true; @@ -71,81 +139,20 @@ void opengl_material::finalize(bool Loadnow) if (parse_info) { // texture bindings which come with a pool of alternatives, gathered while resolving regular bindings - std::vector const *>> variantbindings; + std::vector variantbindings; - for (auto const &it : parse_info->tex_mapping) + for( auto const &[ bindingkey, binding ] : parse_info->tex_mapping ) { - std::string key = it.first; - std::string value = it.second.name; - // slot the binding resolves to, or -1 if it can't be resolved - int slot { -1 }; + auto const slot { texture_slot( bindingkey ) }; + if( slot < 0 ) { continue; } - if (key.size() > 0 && key[0] != '_') - { - key.erase( key.find_first_not_of( "-1234567890" ) ); - size_t num = std::stoi(key) - 1; - if (num < gl::MAX_TEXTURES) { - slot = static_cast( num ); - } - else { - log_error("invalid texture binding: " + std::to_string(num)); - is_good = false; - } - } - else if (key.size() > 2) - { - key.erase(0, 1); - key.pop_back(); - std::map::iterator lookup; - if( shader && shader->texture_conf.find( key ) != shader->texture_conf.end() ) { - slot = shader->texture_conf[ key ].id; - } - else if( shader == nullptr - && ( lookup = texture_bindings.find( key ) ) != texture_bindings.end() ) { - slot = lookup->second; - } - else { - // ignore unrecognized texture bindings in legacy render mode, it's most likely data for more advanced shaders - if( Global.GfxRenderer == "default" ) { - log_error( "unknown texture binding: " + key ); - is_good = false; - } - } - } - else { - log_error("unrecognized texture binding: " + key); - is_good = false; - } - - if( slot >= 0 ) { - textures[ slot ] = GfxRenderer->Fetch_Texture( value, Loadnow ); - if( it.second.variants.size() > 1 ) { - variantbindings.emplace_back( static_cast( slot ), &( it.second.variants ) ); - } + textures[ slot ] = GfxRenderer->Fetch_Texture( binding.name, Loadnow ); + if( binding.variants.size() > 1 ) { + variantbindings.emplace_back( static_cast( slot ), &( binding.variants ) ); } } - if( false == variantbindings.empty() ) { - // build a texture set for each entry of the largest of the declared pools. - // pools of different size can be combined, shorter ones simply repeat from the start - size_t variantcount { 0 }; - for( auto const &binding : variantbindings ) { - variantcount = std::max( variantcount, binding.second->size() ); - } - texture_variants.resize( variantcount, textures ); - for( auto const &binding : variantbindings ) { - auto const &pool { *( binding.second ) }; - for( size_t variantindex = 0; variantindex < variantcount; ++variantindex ) { - auto const texturehandle { GfxRenderer->Fetch_Texture( pool[ variantindex % pool.size() ], Loadnow ) }; - if( texturehandle == null_handle ) { - // a single bad entry shouldn't take down the whole material, fall back to the basic texture - log_error( "missing texture variant: " + pool[ variantindex % pool.size() ] ); - continue; - } - texture_variants[ variantindex ][ binding.first ] = texturehandle; - } - } - } + build_texture_variants( variantbindings, Loadnow ); if (!shader) { @@ -323,6 +330,44 @@ bool is_weather( std::string const &String ) { } // imports member data pair from the config file +void opengl_material::deserialize_texture_mapping( cParser &Input, std::string const &Key, int const Priority ) { + + // a texture binding can be provided as a pool of alternatives, "textureN_variants: [ a b c ]". + // unlike a regular random set, which is resolved once when the material is loaded and thus shared + // by everything using it, such pool is preserved whole and one entry is picked per model instance + auto key { Key }; + auto const variantmarker { key.find( "_variants" ) }; + auto const isvariantset { variantmarker != std::string::npos }; + if( true == isvariantset ) { + // reduce the key to its plain form, "N_variants:" becomes "N:" + key.erase( variantmarker, std::string( "_variants" ).size() ); + } + + std::vector variants; + std::string value; + if( false == isvariantset ) { + value = deserialize_random_set( Input ); + replace_slashes( value ); + } + else { + variants = deserialize_set( Input ); + for( auto &variant : variants ) { + replace_slashes( variant ); + } + if( true == variants.empty() ) { + log_error( "empty texture variant pool: " + key ); + return; + } + // first entry doubles as the regular, variant-agnostic texture of the material + value = variants.front(); + } + + auto const [ it, inserted ] { parse_info->tex_mapping.try_emplace( key, parse_info_s::tex_def{ value, Priority, variants } ) }; + if( ( false == inserted ) && ( Priority > it->second.priority ) ) { + it->second = parse_info_s::tex_def{ value, Priority, variants }; + } +} + bool opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow ) { @@ -358,44 +403,7 @@ opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool c else if (key.compare(0, 7, "texture") == 0) { key.erase(0, 7); - - // a texture binding can be provided as a pool of alternatives, "textureN_variants: [ a b c ]". - // unlike a regular random set, which is resolved once when the material is loaded and thus shared - // by everything using it, such pool is preserved whole and one entry is picked per model instance - auto const variantmarker { key.find( "_variants" ) }; - auto const isvariantset { variantmarker != std::string::npos }; - if( isvariantset ) { - // reduce the key to its plain form, "N_variants:" becomes "N:" - key.erase( variantmarker, std::string( "_variants" ).size() ); - } - - std::vector variants; - std::string value; - if( false == isvariantset ) { - value = deserialize_random_set( Input ); - replace_slashes( value ); - } - else { - variants = deserialize_set( Input ); - for( auto &variant : variants ) { - replace_slashes( variant ); - } - if( true == variants.empty() ) { - log_error( "empty texture variant pool: " + key ); - return true; - } - // first entry doubles as the regular, variant-agnostic texture of the material - value = variants.front(); - } - - auto it = parse_info->tex_mapping.find(key); - if (it == parse_info->tex_mapping.end()) - parse_info->tex_mapping.emplace(std::make_pair(key, parse_info_s::tex_def({ value, Priority, variants }))); - else if (Priority > it->second.priority) - { - parse_info->tex_mapping.erase(it); - parse_info->tex_mapping.emplace(std::make_pair(key, parse_info_s::tex_def({ value, Priority, variants }))); - } + deserialize_texture_mapping( Input, key, Priority ); } else if (key.compare(0, 5, "param") == 0) { key.erase(0, 5); diff --git a/model/material.h b/model/material.h index 03fa32a5..7e04514c 100644 --- a/model/material.h +++ b/model/material.h @@ -18,7 +18,7 @@ http://mozilla.org/MPL/2.0/. // a collection of parameters for the rendering setup. // for modern opengl this translates to set of attributes for shaders struct opengl_material : public IMaterial { - typedef std::array texture_set; + using texture_set = std::array; texture_set textures = { null_handle }; // alternative texture sets, defined with "textureN_variants:" key. @@ -85,10 +85,23 @@ struct opengl_material : public IMaterial { bool update_on_season_change{ false }; private: +// types + // texture binding which comes with a pool of alternatives: slot the binding fills, and the pool itself + using variant_binding = std::pair const *>; + // methods // imports member data pair from the config file bool deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow ); + // imports texture binding, potentially carrying a pool of alternatives, from the config file + void + deserialize_texture_mapping( cParser &Input, std::string const &Key, int const Priority ); + // returns texture slot provided binding key resolves to, or -1 if it doesn't resolve to any + int + texture_slot( std::string const &Key ); + // fills the alternative texture sets from provided pools, one set per entry of the longest pool + void + build_texture_variants( std::vector const &Bindings, bool const Loadnow ); void log_error(const std::string &str); // members diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp index 310a71ec..1da66ba5 100644 --- a/rendering/openglrenderer.cpp +++ b/rendering/openglrenderer.cpp @@ -2877,7 +2877,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // material configuration: // textures... if( Submodel->m_material < 0 ) { // zmienialne skóry - Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ], Submodel ); + Bind_Material( TSubModel::ReplacableSkinId[ -Submodel->m_material ], Submodel ); } else { // również 0 @@ -2895,7 +2895,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { ::glColor3fv( glm::value_ptr( pick_color( m_pickcontrolsitems.size() ) ) ); // textures... if( Submodel->m_material < 0 ) { // zmienialne skóry - Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ], Submodel ); + Bind_Material( TSubModel::ReplacableSkinId[ -Submodel->m_material ], Submodel ); } else { // również 0 @@ -3921,7 +3921,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // material configuration: // textures... if( Submodel->m_material < 0 ) { // zmienialne skóry - Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ], Submodel ); + Bind_Material( TSubModel::ReplacableSkinId[ -Submodel->m_material ], Submodel ); } else { // również 0 diff --git a/utilities/utilities.cpp b/utilities/utilities.cpp index 94b6c35d..6a2f90bb 100644 --- a/utilities/utilities.cpp +++ b/utilities/utilities.cpp @@ -512,7 +512,7 @@ std::vector deserialize_set(cParser &Input, char const *Break) std::vector tokens; auto token{Input.getToken(true, Break)}; - std::replace(token.begin(), token.end(), '\\', '/'); + std::ranges::replace(token, '\\', '/'); if (token != "[") { // simple case, single token