From a24118baf7630eb0ab545c947d7874135a96356d Mon Sep 17 00:00:00 2001 From: maj00r Date: Tue, 3 Nov 2026 21:50:31 +0100 Subject: [PATCH 1/4] feat(render): a geometry chunk emptied in place, the kind of its primitive told; a chunk gaining or losing its userdata gets a new buffer Erase() empties a chunk and keeps its handle to be filled again, for geometry made anew as it changes. Primitive() tells whether a chunk holds triangles or strips, -1 from a renderer which doesn't know. The VAO bank reused the buffer of a chunk of the same length even when the userdata came or went, the layout of the buffer differing then. --- rendering/geometrybank.cpp | 36 ++++++++++++++++++++++++++++++ rendering/geometrybank.h | 6 +++++ rendering/opengl33geometrybank.cpp | 3 ++- rendering/opengl33renderer.cpp | 24 +++++++++++++++++++- rendering/opengl33renderer.h | 7 ++++++ rendering/openglrenderer.cpp | 5 +++++ rendering/openglrenderer.h | 1 + rendering/renderer.h | 8 +++++++ 8 files changed, 88 insertions(+), 2 deletions(-) diff --git a/rendering/geometrybank.cpp b/rendering/geometrybank.cpp index f2db8b8bf..2a1ff5ee6 100644 --- a/rendering/geometrybank.cpp +++ b/rendering/geometrybank.cpp @@ -318,6 +318,20 @@ geometry_bank::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gf return replace( Vertices, Userdata, Geometry, gfx::geometry_bank::chunk( Geometry ).vertices.size() ); } +void +geometry_bank::erase( gfx::geometry_handle const &Geometry ) { + + if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return; } + + auto &chunk = gfx::geometry_bank::chunk( Geometry ); + if( chunk.vertices.empty() && chunk.indices.empty() ) { return; } + + gfx::vertex_array().swap( chunk.vertices ); + gfx::index_array().swap( chunk.indices ); + gfx::userdata_array().swap( chunk.userdata ); + replace_( Geometry ); +} + // draws geometry stored in specified chunk std::size_t geometry_bank::draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) { @@ -360,6 +374,13 @@ geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const { return geometry_bank::chunk( Geometry ).vertices; } +// kind of geometry of specified chunk +unsigned int +geometry_bank::primitive( gfx::geometry_handle const &Geometry ) const { + + return geometry_bank::chunk( Geometry ).type; +} + // provides direct access to vertex user data of specfied chunk userdata_array const & geometry_bank::userdata( gfx::geometry_handle const &Geometry ) const { @@ -418,6 +439,14 @@ geometrybank_manager::append( gfx::vertex_array &Vertices, userdata_array &Userd return bank( Geometry ).first->append( Vertices, Userdata, Geometry ); } + +void +geometrybank_manager::erase( gfx::geometry_handle const &Geometry ) { + + if( ( Geometry.bank == 0 ) || ( Geometry.bank > m_geometrybanks.size() ) ) { return; } + + bank( Geometry ).first->erase( Geometry ); +} // draws geometry stored in specified chunk void geometrybank_manager::draw( gfx::geometry_handle const &Geometry, unsigned int const Streams ) { @@ -483,6 +512,13 @@ geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const { return bank( Geometry ).first->vertices( Geometry ); } +// kind of geometry of specified chunk +unsigned int +geometrybank_manager::primitive( gfx::geometry_handle const &Geometry ) const { + + return bank( Geometry ).first->primitive( Geometry ); +} + // provides direct access to vertex user data of specfied chunk gfx::userdata_array const & geometrybank_manager::userdata( gfx::geometry_handle const &Geometry ) const { diff --git a/rendering/geometrybank.h b/rendering/geometrybank.h index 20baf4ed2..537c79ba2 100644 --- a/rendering/geometrybank.h +++ b/rendering/geometrybank.h @@ -117,6 +117,7 @@ public: auto replace( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk auto append( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry ) -> bool; + void erase( gfx::geometry_handle const &Geometry ); // draws geometry stored in specified chunk auto draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t; // draws geometry stored in specified chunk N times via glDrawElementsInstanced* @@ -138,6 +139,8 @@ public: auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; // provides direct access to vertex user data of specfied chunk auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &; + // kind of geometry of specified chunk, GL_TRIANGLES, GL_TRIANGLE_STRIP... + auto primitive( gfx::geometry_handle const &Geometry ) const -> unsigned int; protected: // types: @@ -220,6 +223,7 @@ public: auto replace( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk auto append( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool; + void erase( gfx::geometry_handle const &Geometry ); // draws geometry stored in specified chunk void draw( gfx::geometry_handle const &Geometry, unsigned int const Streams = basic_streams ); // draws geometry stored in specified chunk InstanceCount times via GPU instancing. @@ -238,6 +242,8 @@ public: auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; // provides direct access to vertex data of specfied chunk auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &; + // kind of geometry of specified chunk + auto primitive( gfx::geometry_handle const &Geometry ) const -> unsigned int; // sets target texture unit for the texture data stream auto units() -> gfx::stream_units & { return m_units; } // provides access to primitives count diff --git a/rendering/opengl33geometrybank.cpp b/rendering/opengl33geometrybank.cpp index 4713ec69b..978a1b75d 100644 --- a/rendering/opengl33geometrybank.cpp +++ b/rendering/opengl33geometrybank.cpp @@ -33,7 +33,8 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) { auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; chunkrecord.is_good = false; // if the overall length of the chunk didn't change we can get away with reusing the old buffer... - if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) { + if( ( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) + || ( geometry_bank::chunk( Geometry ).userdata.empty() == chunkrecord.has_userdata ) ) { // ...but otherwise we'll need to allocate a new one // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index d20454c35..97b2fa8d2 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -2201,8 +2201,13 @@ bool opengl33_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array return m_geometry.append(Vertices, Userdata, Geometry); } +void opengl33_renderer::Erase(gfx::geometry_handle const &Geometry) +{ + m_geometry.erase(Geometry); +} + // provides direct access to index data of specfied chunk -gfx::index_array const & opengl33_renderer::Indices(gfx::geometry_handle const &Geometry) const +gfx::index_array const & opengl33_renderer::Indices(gfx::geometry_handle const &Geometry) const { return m_geometry.indices(Geometry); } @@ -2218,6 +2223,11 @@ gfx::userdata_array const &opengl33_renderer::UserData(const gfx::geometry_handl return m_geometry.userdata(Geometry); } +int opengl33_renderer::Primitive(gfx::geometry_handle const &Geometry) const +{ + return static_cast(m_geometry.primitive(Geometry)); +} + // material methods material_handle opengl33_renderer::Fetch_Material(std::string const &Filename, bool const Loadnow) { @@ -2225,6 +2235,18 @@ material_handle opengl33_renderer::Fetch_Material(std::string const &Filename, b return m_materials.create(Filename, Loadnow); } +material_handle opengl33_renderer::Matte_Material(material_handle const Material) +{ + + return m_materials.create_matte(Material); +} + +material_handle opengl33_renderer::Tinted_Material(material_handle const Material, glm::vec3 const &Colour) +{ + + return m_materials.create_tinted(Material, Colour); +} + std::shared_ptr opengl33_renderer::Fetch_Shader(const std::string &name) { auto it = m_shaders.find(name); diff --git a/rendering/opengl33renderer.h b/rendering/opengl33renderer.h index 64574420f..b04153586 100644 --- a/rendering/opengl33renderer.h +++ b/rendering/opengl33renderer.h @@ -66,6 +66,7 @@ class opengl33_renderer : public gfx_renderer { bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; // adds supplied vertex data at the end of specified chunk bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; + void Erase( gfx::geometry_handle const &Geometry ) override; // provides direct access to index data of specfied chunk gfx::index_array const & Indices( gfx::geometry_handle const &Geometry ) const override; @@ -75,9 +76,15 @@ class opengl33_renderer : public gfx_renderer { // provides direct access to vertex data of specfied chunk gfx::userdata_array const & UserData( gfx::geometry_handle const &Geometry ) const override; + int + Primitive( gfx::geometry_handle const &Geometry ) const override; // material methods material_handle Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override; + material_handle + Matte_Material( material_handle const Material ) override; + material_handle + Tinted_Material( material_handle const Material, glm::vec3 const &Colour ) override; void Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override; IMaterial const * diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp index 59152c64d..51ac1e715 100644 --- a/rendering/openglrenderer.cpp +++ b/rendering/openglrenderer.cpp @@ -1758,6 +1758,11 @@ bool opengl_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &U return m_geometry.append( Vertices, Userdata, Geometry ); } +void opengl_renderer::Erase( gfx::geometry_handle const &Geometry ) +{ + m_geometry.erase( Geometry ); +} + // provides direct access to vertex data of specfied chunk gfx::index_array const & opengl_renderer::Indices( gfx::geometry_handle const &Geometry ) const { diff --git a/rendering/openglrenderer.h b/rendering/openglrenderer.h index 78fffa40b..da3faad06 100644 --- a/rendering/openglrenderer.h +++ b/rendering/openglrenderer.h @@ -65,6 +65,7 @@ public: bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; // adds supplied vertex data at the end of specified chunk bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; + void Erase( gfx::geometry_handle const &Geometry ) override; // provides direct access to index data of specfied chunk gfx::index_array const & Indices( gfx::geometry_handle const &Geometry ) const override; diff --git a/rendering/renderer.h b/rendering/renderer.h index 0b9eb60f4..4dabc204a 100644 --- a/rendering/renderer.h +++ b/rendering/renderer.h @@ -59,16 +59,24 @@ public: virtual auto Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) -> bool = 0; // adds supplied vertex data at the end of specified chunk virtual auto Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) -> bool = 0; + // empties specified chunk, its handle stays valid to be filled again + virtual void Erase( gfx::geometry_handle const &Geometry ) {} // provides direct access to index data of specfied chunk virtual auto Indices( gfx::geometry_handle const &Geometry ) const->gfx::index_array const & = 0; // provides direct access to vertex data of specfied chunk virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0; // provides direct access to vertex user data of specfied chunk virtual auto UserData( gfx::geometry_handle const &Geometry ) const ->gfx::userdata_array const & = 0; + // kind of geometry of specified chunk, -1 when the renderer doesn't tell + virtual auto Primitive( gfx::geometry_handle const &Geometry ) const -> int { return -1; } // material methods virtual auto Fetch_Material( std::string const &Filename, bool const Loadnow = true ) -> material_handle = 0; virtual void Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) = 0; virtual auto Material( material_handle const Material ) const -> IMaterial const * = 0; + // the same material with its highlights damped, for faces which aren't polished like the surfaces they are cut from. backends which can't make one give back the material itself + virtual auto Matte_Material( material_handle const Material ) -> material_handle { return Material; } + // the same material with its texture tinted by the colour, as far as the userdata of the vertices says. backends which can't make one give back the material itself + virtual auto Tinted_Material( material_handle const Material, glm::vec3 const &Colour ) -> material_handle { return Material; } // heightmap terrain: material of a chunk blending the materials of up to 8 layers by their weights. // Weights: (Samples + 1)^2 values per layer, planar, rows of growing z, the weights of a sample adding up to 255; null with a single layer. // Placement: corner of the chunk (x, z) in the texture coordinates of its mesh, and the size of the chunk. From 89c4555e1e9b410c149331cb485c5baf48f1285d Mon Sep 17 00:00:00 2001 From: maj00r Date: Tue, 3 Nov 2026 21:50:32 +0100 Subject: [PATCH 2/4] feat(render): matte and tinted copies of a material, the steel weathered or tinted as far as the userdata of its vertices says Matte_Material() gives a copy of a material with its glossiness damped and its weathering on: the faces cut out of a polished surface, as the sides of a worn rail head, darken and lose the shine by the userdata of their vertices. Tinted_Material() gives a copy whose texture is tinted to a colour as far instead. Both are made once per material. The weathering and the tint colour have a param slot of their own, param[3]: the shaders of the materials use all four components of param[2] among them (detail, parallax, water, the terrain placement), and light_common.glsl is included in all of them. MAX_PARAMS grows from 3 to 4, the model UBO by 16 bytes. Neither copy is made when another param of the shader lies on the components it would set. --- gl/ubo.h | 2 +- model/material.cpp | 117 ++++++++++++++++++++++++++++++++++++++ model/material.h | 7 +++ shaders/light_common.glsl | 18 ++++++ 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/gl/ubo.h b/gl/ubo.h index d5335b21d..9d2c96912 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -62,7 +62,7 @@ namespace gl static_assert(sizeof(scene_ubs) == 400, "bad size of ubs"); - const size_t MAX_PARAMS = 3; + const size_t MAX_PARAMS = 4; struct model_ubs { diff --git a/model/material.cpp b/model/material.cpp index dd7598ab1..ddc521e59 100644 --- a/model/material.cpp +++ b/model/material.cpp @@ -662,6 +662,123 @@ material_manager::create_from_text( std::string const &Name, std::string const & return materialhandle; } +namespace { + +opengl_material +variant_of( opengl_material const &Source, std::string const &Suffix ) { + + opengl_material variant; + variant.textures = Source.textures; + variant.texture_variants = Source.texture_variants; + variant.params = Source.params; + variant.params_state = Source.params_state; + variant.shader = Source.shader; + variant.opacity = Source.opacity; + variant.selfillum = Source.selfillum; + variant.shadow_rank = Source.shadow_rank; + variant.name = Source.name + Suffix; + variant.size = Source.size; + variant.is_good = Source.is_good; + variant.path = Source.path; + return variant; +} + +// whether another param of the shader lies on a component of the named one, which a value set for that one would change too +bool +shares_slot( std::unordered_map const &Conf, std::string const &Name ) { + + auto const own { Conf.find( Name ) }; + if( own == Conf.end() ) { + return false; + } + return std::any_of( + std::begin( Conf ), std::end( Conf ), + [&]( auto const &Entry ) { + return Entry.first != Name + && Entry.second.location == own->second.location + && Entry.second.offset < own->second.offset + own->second.size + && own->second.offset < Entry.second.offset + Entry.second.size; } ); +} + +} // namespace + +material_handle +material_manager::create_matte( material_handle const Material ) { + + if( Material == null_handle || Material >= m_materials.size() ) { + return Material; + } + auto const lookup { m_mattes.find( Material ) }; + if( lookup != m_mattes.end() ) { + return lookup->second; + } + auto matte { variant_of( m_materials[ Material ], ":matte" ) }; + matte.glossiness = 0.1f; + if( matte.shader ) { + for( auto const &[ key, value ] : std::vector>{ { "weathering", glm::vec4{ 1.f } } } ) { + auto const entry { matte.shader->param_conf.find( key ) }; + if( entry == matte.shader->param_conf.end() + || shares_slot( matte.shader->param_conf, key ) ) { + continue; + } + for( std::size_t i = 0; i < entry->second.size; ++i ) { + matte.params[ entry->second.location ][ entry->second.offset + i ] = value[ static_cast( i ) ]; + } + matte.params_state.erase( + std::remove_if( + std::begin( matte.params_state ), std::end( matte.params_state ), + [&]( gl::shader::param_entry const &State ) { + return State.location == entry->second.location && State.offset == entry->second.offset; } ), + std::end( matte.params_state ) ); + } + } + auto const mattehandle { static_cast( m_materials.size() ) }; + m_materials.emplace_back( std::move( matte ) ); + m_mattes.emplace( Material, mattehandle ); + return mattehandle; +} + +material_handle +material_manager::create_tinted( material_handle const Material, glm::vec3 const &Colour ) { + + if( Material == null_handle || Material >= m_materials.size() || !m_materials[ Material ].shader ) { + return Material; + } + auto const key { std::make_tuple( Material, Colour.r, Colour.g, Colour.b ) }; + if( auto const lookup { m_tints.find( key ) }; lookup != m_tints.end() ) { + return lookup->second; + } + // the colour in the rgb of the slot of the weathering, which tells the shader to tint by its sign; nothing else in that slot + auto const &conf { m_materials[ Material ].shader->param_conf }; + auto const weathering { conf.find( "weathering" ) }; + auto const tint { conf.find( "tint" ) }; + if( weathering == conf.end() + || tint == conf.end() + || weathering->second.offset != 3 + || tint->second.location != weathering->second.location + || tint->second.offset != 0 + || std::any_of( + std::begin( conf ), std::end( conf ), + [&]( auto const &Entry ) { + return Entry.first != weathering->first && Entry.first != tint->first && Entry.second.location == weathering->second.location; } ) ) { + return Material; + } + auto tinted { variant_of( m_materials[ Material ], ":tinted" ) }; + tinted.glossiness = m_materials[ Material ].glossiness; + auto const location { weathering->second.location }; + tinted.params[ location ] = glm::vec4{ Colour, -1.f }; + tinted.params_state.erase( + std::remove_if( + std::begin( tinted.params_state ), std::end( tinted.params_state ), + [&]( gl::shader::param_entry const &State ) { + return State.location == location; } ), + std::end( tinted.params_state ) ); + auto const tintedhandle { static_cast( m_materials.size() ) }; + m_materials.emplace_back( std::move( tinted ) ); + m_tints.emplace( key, tintedhandle ); + return tintedhandle; +} + void material_manager::on_weather_change() { diff --git a/model/material.h b/model/material.h index f329a3256..05a68880f 100644 --- a/model/material.h +++ b/model/material.h @@ -142,6 +142,11 @@ public: // material described by provided text (the content of a .mat file), not kept in a file. returns: handle to the material or null_handle material_handle create_from_text( std::string const &Name, std::string const &Definition ); + // copy of provided material with its specular highlights and reflections damped. returns: handle to the copy, made once per material + material_handle + create_matte( material_handle const Material ); + material_handle + create_tinted( material_handle const Material, glm::vec3 const &Colour ); opengl_material const & material( material_handle const Material ) const { return m_materials[ Material ]; } opengl_material & @@ -170,6 +175,8 @@ private: // members: material_sequence m_materials; index_map m_materialmappings; + std::unordered_map m_mattes; + std::map, material_handle> m_tints; }; //--------------------------------------------------------------------------- diff --git a/shaders/light_common.glsl b/shaders/light_common.glsl index 0801b09dc..6261a090d 100644 --- a/shaders/light_common.glsl +++ b/shaders/light_common.glsl @@ -9,6 +9,11 @@ uniform sampler2D headlightmap; float glossiness = 1.0; float metalic = 0.0; +in vec4 UserData; +// a slot of its own, the shaders of the materials using all of param[2]: how weathered the steel is as far as the userdata of +// its vertices says; below 0: the texture tinted to the colour of tint as far instead +#param (weathering, 3, 3, 1, zero) +#param (tint, 3, 0, 3, one) // --------------------------------------------------------------------- // Lighting balance tunables - tweak these to control overall scene @@ -285,6 +290,19 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl { // Diffuse colour is applied once, to the albedo; the light terms below are not scaled by it again texturecolor *= material_diffuse(); + float weathered = (param[3].w > 0.0 ? clamp(param[3].w * UserData.x, 0.0, 1.0) : 0.0); + float tinted = (param[3].w < 0.0 ? clamp(-param[3].w * UserData.x, 0.0, 1.0) : 0.0); + // a tint only scales the colour of the texture, the steel keeps its own polish + float polished = weathered * smoothstep(0.03, 0.12, dot(texturecolor, vec3(0.2126, 0.7152, 0.0722))); + float weatheredluma = min(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 0.05); + vec3 unused = texturecolor * (weatheredluma / max(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 1e-4)); + unused = mix(unused, vec3(1.0, 0.82, 0.74) * (weatheredluma / 0.8525), 0.5); + texturecolor = mix(texturecolor, unused, smoothstep(0.0, 0.4, weathered)); + texturecolor = (tinted > 0.0 ? texturecolor * mix(vec3(1.0), param[3].rgb, tinted) : texturecolor); + specularity = mix(specularity, min(specularity, 0.03), smoothstep(0.0, 0.4, polished)); + metalic = mix(metalic, 0.0, polished); + glossiness = mix(glossiness, min(glossiness, 0.06 * abs(param[1].w)), polished); + reflectivity = mix(reflectivity, min(reflectivity, 0.08 * param[1].z), smoothstep(0.0, 0.4, polished)); // Scale ambient before it gets tinted by the texture. // Sun, headlights and emission are added afterwards so they are NOT // attenuated by AMBIENT_SCALE - this only dims the indirect term. From 48af69cbf19e475d4472d6347e64f29b770a1ddf Mon Sep 17 00:00:00 2001 From: maj00r Date: Tue, 3 Nov 2026 22:00:48 +0100 Subject: [PATCH 3/4] fix(render): the matte and tinted copies of a material made again with its season and weather, the primitive of a handle to no chunk told as none The copies don't update themselves, their names aren't files to read again: after a change of the weather or the season the material got its new textures and its copies kept the old ones. They are made again from it then, under their handles. primitive() of an empty handle or one past the banks read out of the bank list; it gives no_primitive there, -1 from the renderer. --- model/material.cpp | 131 ++++++++++++++++++++++----------- model/material.h | 5 ++ rendering/geometrybank.cpp | 4 + rendering/geometrybank.h | 3 +- rendering/opengl33renderer.cpp | 3 +- 5 files changed, 102 insertions(+), 44 deletions(-) diff --git a/model/material.cpp b/model/material.cpp index ddc521e59..2b92986d0 100644 --- a/model/material.cpp +++ b/model/material.cpp @@ -676,6 +676,7 @@ variant_of( opengl_material const &Source, std::string const &Suffix ) { variant.opacity = Source.opacity; variant.selfillum = Source.selfillum; variant.shadow_rank = Source.shadow_rank; + variant.glossiness = Source.glossiness; variant.name = Source.name + Suffix; variant.size = Source.size; variant.is_good = Source.is_good; @@ -700,6 +701,65 @@ shares_slot( std::unordered_map const &Con && own->second.offset < Entry.second.offset + Entry.second.size; } ); } +// the highlights of a copy of a material damped and its weathering on +void +make_matte( opengl_material &Variant ) { + + Variant.glossiness = 0.1f; + if( !Variant.shader ) { + return; + } + for( auto const &[ key, value ] : std::vector>{ { "weathering", glm::vec4{ 1.f } } } ) { + auto const entry { Variant.shader->param_conf.find( key ) }; + if( entry == Variant.shader->param_conf.end() + || shares_slot( Variant.shader->param_conf, key ) ) { + continue; + } + for( std::size_t i = 0; i < entry->second.size; ++i ) { + Variant.params[ entry->second.location ][ entry->second.offset + i ] = value[ static_cast( i ) ]; + } + Variant.params_state.erase( + std::remove_if( + std::begin( Variant.params_state ), std::end( Variant.params_state ), + [&]( gl::shader::param_entry const &State ) { + return State.location == entry->second.location && State.offset == entry->second.offset; } ), + std::end( Variant.params_state ) ); + } +} + +// the colour in the rgb of the slot of the weathering of a copy of a material, which tells the shader to tint by its sign. returns: +// whether the shader can tint it, the weathering and the tint alone in that slot +bool +make_tinted( opengl_material &Variant, glm::vec3 const &Colour ) { + + if( !Variant.shader ) { + return false; + } + auto const &conf { Variant.shader->param_conf }; + auto const weathering { conf.find( "weathering" ) }; + auto const tint { conf.find( "tint" ) }; + if( weathering == conf.end() + || tint == conf.end() + || weathering->second.offset != 3 + || tint->second.location != weathering->second.location + || tint->second.offset != 0 + || std::any_of( + std::begin( conf ), std::end( conf ), + [&]( auto const &Entry ) { + return Entry.first != weathering->first && Entry.first != tint->first && Entry.second.location == weathering->second.location; } ) ) { + return false; + } + auto const location { weathering->second.location }; + Variant.params[ location ] = glm::vec4{ Colour, -1.f }; + Variant.params_state.erase( + std::remove_if( + std::begin( Variant.params_state ), std::end( Variant.params_state ), + [&]( gl::shader::param_entry const &State ) { + return State.location == location; } ), + std::end( Variant.params_state ) ); + return true; +} + } // namespace material_handle @@ -713,25 +773,7 @@ material_manager::create_matte( material_handle const Material ) { return lookup->second; } auto matte { variant_of( m_materials[ Material ], ":matte" ) }; - matte.glossiness = 0.1f; - if( matte.shader ) { - for( auto const &[ key, value ] : std::vector>{ { "weathering", glm::vec4{ 1.f } } } ) { - auto const entry { matte.shader->param_conf.find( key ) }; - if( entry == matte.shader->param_conf.end() - || shares_slot( matte.shader->param_conf, key ) ) { - continue; - } - for( std::size_t i = 0; i < entry->second.size; ++i ) { - matte.params[ entry->second.location ][ entry->second.offset + i ] = value[ static_cast( i ) ]; - } - matte.params_state.erase( - std::remove_if( - std::begin( matte.params_state ), std::end( matte.params_state ), - [&]( gl::shader::param_entry const &State ) { - return State.location == entry->second.location && State.offset == entry->second.offset; } ), - std::end( matte.params_state ) ); - } - } + make_matte( matte ); auto const mattehandle { static_cast( m_materials.size() ) }; m_materials.emplace_back( std::move( matte ) ); m_mattes.emplace( Material, mattehandle ); @@ -748,43 +790,47 @@ material_manager::create_tinted( material_handle const Material, glm::vec3 const if( auto const lookup { m_tints.find( key ) }; lookup != m_tints.end() ) { return lookup->second; } - // the colour in the rgb of the slot of the weathering, which tells the shader to tint by its sign; nothing else in that slot - auto const &conf { m_materials[ Material ].shader->param_conf }; - auto const weathering { conf.find( "weathering" ) }; - auto const tint { conf.find( "tint" ) }; - if( weathering == conf.end() - || tint == conf.end() - || weathering->second.offset != 3 - || tint->second.location != weathering->second.location - || tint->second.offset != 0 - || std::any_of( - std::begin( conf ), std::end( conf ), - [&]( auto const &Entry ) { - return Entry.first != weathering->first && Entry.first != tint->first && Entry.second.location == weathering->second.location; } ) ) { + auto tinted { variant_of( m_materials[ Material ], ":tinted" ) }; + if( false == make_tinted( tinted, Colour ) ) { return Material; } - auto tinted { variant_of( m_materials[ Material ], ":tinted" ) }; - tinted.glossiness = m_materials[ Material ].glossiness; - auto const location { weathering->second.location }; - tinted.params[ location ] = glm::vec4{ Colour, -1.f }; - tinted.params_state.erase( - std::remove_if( - std::begin( tinted.params_state ), std::end( tinted.params_state ), - [&]( gl::shader::param_entry const &State ) { - return State.location == location; } ), - std::end( tinted.params_state ) ); auto const tintedhandle { static_cast( m_materials.size() ) }; m_materials.emplace_back( std::move( tinted ) ); m_tints.emplace( key, tintedhandle ); return tintedhandle; } +void +material_manager::update_variants( bool const Weather ) { + + // the copies don't update themselves, their names aren't files to read again + auto const changed = [&]( material_handle const Source ) { + auto const &source { m_materials[ Source ] }; + return Weather ? source.update_on_weather_change : source.update_on_season_change; }; + for( auto const &[ source, matte ] : m_mattes ) { + if( changed( source ) ) { + auto variant { variant_of( m_materials[ source ], ":matte" ) }; + make_matte( variant ); + m_materials[ matte ] = std::move( variant ); + } + } + for( auto const &[ key, tinted ] : m_tints ) { + auto const source { std::get<0>( key ) }; + if( changed( source ) ) { + auto variant { variant_of( m_materials[ source ], ":tinted" ) }; + make_tinted( variant, glm::vec3 { std::get<1>( key ), std::get<2>( key ), std::get<3>( key ) } ); + m_materials[ tinted ] = std::move( variant ); + } + } +} + void material_manager::on_weather_change() { for( auto &material : m_materials ) { if( material.update_on_weather_change ) { material.update(); } } + update_variants( true ); } void @@ -793,6 +839,7 @@ material_manager::on_season_change() { for( auto &material : m_materials ) { if( material.update_on_season_change ) { material.update(); } } + update_variants( false ); } // 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 05a68880f..82d14557c 100644 --- a/model/material.h +++ b/model/material.h @@ -145,6 +145,8 @@ public: // copy of provided material with its specular highlights and reflections damped. returns: handle to the copy, made once per material material_handle create_matte( material_handle const Material ); + // copy of provided material with its texture tinted to the colour as far as the userdata of the vertices says. returns: handle to + // the copy, made once per material and colour; the material itself when its shader can't tint it material_handle create_tinted( material_handle const Material, glm::vec3 const &Colour ); opengl_material const & @@ -166,6 +168,9 @@ private: // checks whether specified texture is in the texture bank. returns texture id, or npos. material_handle find_in_databank( std::string const &Materialname ) const; + // the matte and tinted copies of the materials changed with the weather or the season made again from them, under their handles + void + update_variants( bool const Weather ); public: // checks whether specified file exists. returns name of the located file, or empty string. static std::pair diff --git a/rendering/geometrybank.cpp b/rendering/geometrybank.cpp index 2a1ff5ee6..aa7c1f8b5 100644 --- a/rendering/geometrybank.cpp +++ b/rendering/geometrybank.cpp @@ -378,6 +378,8 @@ geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const { unsigned int geometry_bank::primitive( gfx::geometry_handle const &Geometry ) const { + if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return no_primitive; } + return geometry_bank::chunk( Geometry ).type; } @@ -516,6 +518,8 @@ geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const { unsigned int geometrybank_manager::primitive( gfx::geometry_handle const &Geometry ) const { + if( ( Geometry.bank == 0 ) || ( Geometry.bank > m_geometrybanks.size() ) ) { return geometry_bank::no_primitive; } + return bank( Geometry ).first->primitive( Geometry ); } diff --git a/rendering/geometrybank.h b/rendering/geometrybank.h index 537c79ba2..c01e46fe3 100644 --- a/rendering/geometrybank.h +++ b/rendering/geometrybank.h @@ -139,8 +139,9 @@ public: auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; // provides direct access to vertex user data of specfied chunk auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &; - // kind of geometry of specified chunk, GL_TRIANGLES, GL_TRIANGLE_STRIP... + // kind of geometry of specified chunk, GL_TRIANGLES, GL_TRIANGLE_STRIP...; no_primitive for a handle to no chunk auto primitive( gfx::geometry_handle const &Geometry ) const -> unsigned int; + static constexpr unsigned int no_primitive { ~0u }; protected: // types: diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index 97b2fa8d2..9b42bb280 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -2225,7 +2225,8 @@ gfx::userdata_array const &opengl33_renderer::UserData(const gfx::geometry_handl int opengl33_renderer::Primitive(gfx::geometry_handle const &Geometry) const { - return static_cast(m_geometry.primitive(Geometry)); + auto const primitive{m_geometry.primitive(Geometry)}; + return primitive == gfx::geometry_bank::no_primitive ? -1 : static_cast(primitive); } // material methods From d232cd0a6937dda6bb482fac2a9b23e10bdd7fb7 Mon Sep 17 00:00:00 2001 From: maj00r Date: Fri, 6 Nov 2026 19:58:24 +0100 Subject: [PATCH 4/4] refactor: the Sonar remarks on the material copies taken care of - the params of a shader named by the type the shader keeps them in - ranges::any_of and erase_if, with explicit captures - the weathering set component by component with an index of the type of the vector - the lookups declared in the ifs that use them - the empty Erase of the base renderer told why it is empty --- model/material.cpp | 51 ++++++++++++++++++++++---------------------- rendering/renderer.h | 4 +++- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/model/material.cpp b/model/material.cpp index 2b92986d0..4e96ddd8c 100644 --- a/model/material.cpp +++ b/model/material.cpp @@ -684,17 +684,20 @@ variant_of( opengl_material const &Source, std::string const &Suffix ) { return variant; } +// the params of a shader, as the shader keeps them +using param_confs = decltype( gl::shader::param_conf ); + // whether another param of the shader lies on a component of the named one, which a value set for that one would change too bool -shares_slot( std::unordered_map const &Conf, std::string const &Name ) { +shares_slot( param_confs const &Conf, std::string const &Name ) { auto const own { Conf.find( Name ) }; if( own == Conf.end() ) { return false; } - return std::any_of( - std::begin( Conf ), std::end( Conf ), - [&]( auto const &Entry ) { + return std::ranges::any_of( + Conf, + [ &Name, &own ]( auto const &Entry ) { return Entry.first != Name && Entry.second.location == own->second.location && Entry.second.offset < own->second.offset + own->second.size @@ -715,15 +718,16 @@ make_matte( opengl_material &Variant ) { || shares_slot( Variant.shader->param_conf, key ) ) { continue; } - for( std::size_t i = 0; i < entry->second.size; ++i ) { - Variant.params[ entry->second.location ][ entry->second.offset + i ] = value[ static_cast( i ) ]; + auto &slot { Variant.params[ entry->second.location ] }; + auto const offset { static_cast( entry->second.offset ) }; + auto const size { static_cast( entry->second.size ) }; + for( glm::length_t i = 0; i < size; ++i ) { + slot[ offset + i ] = value[ i ]; } - Variant.params_state.erase( - std::remove_if( - std::begin( Variant.params_state ), std::end( Variant.params_state ), - [&]( gl::shader::param_entry const &State ) { - return State.location == entry->second.location && State.offset == entry->second.offset; } ), - std::end( Variant.params_state ) ); + std::erase_if( + Variant.params_state, + [ &entry ]( gl::shader::param_entry const &State ) { + return State.location == entry->second.location && State.offset == entry->second.offset; } ); } } @@ -737,26 +741,24 @@ make_tinted( opengl_material &Variant, glm::vec3 const &Colour ) { } auto const &conf { Variant.shader->param_conf }; auto const weathering { conf.find( "weathering" ) }; - auto const tint { conf.find( "tint" ) }; - if( weathering == conf.end() + if( auto const tint { conf.find( "tint" ) }; + weathering == conf.end() || tint == conf.end() || weathering->second.offset != 3 || tint->second.location != weathering->second.location || tint->second.offset != 0 - || std::any_of( - std::begin( conf ), std::end( conf ), - [&]( auto const &Entry ) { + || std::ranges::any_of( + conf, + [ &weathering, &tint ]( auto const &Entry ) { return Entry.first != weathering->first && Entry.first != tint->first && Entry.second.location == weathering->second.location; } ) ) { return false; } auto const location { weathering->second.location }; Variant.params[ location ] = glm::vec4{ Colour, -1.f }; - Variant.params_state.erase( - std::remove_if( - std::begin( Variant.params_state ), std::end( Variant.params_state ), - [&]( gl::shader::param_entry const &State ) { - return State.location == location; } ), - std::end( Variant.params_state ) ); + std::erase_if( + Variant.params_state, + [ location ]( gl::shader::param_entry const &State ) { + return State.location == location; } ); return true; } @@ -768,8 +770,7 @@ material_manager::create_matte( material_handle const Material ) { if( Material == null_handle || Material >= m_materials.size() ) { return Material; } - auto const lookup { m_mattes.find( Material ) }; - if( lookup != m_mattes.end() ) { + if( auto const lookup { m_mattes.find( Material ) }; lookup != m_mattes.end() ) { return lookup->second; } auto matte { variant_of( m_materials[ Material ], ":matte" ) }; diff --git a/rendering/renderer.h b/rendering/renderer.h index 4dabc204a..1d60a1d95 100644 --- a/rendering/renderer.h +++ b/rendering/renderer.h @@ -60,7 +60,9 @@ public: // adds supplied vertex data at the end of specified chunk virtual auto Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) -> bool = 0; // empties specified chunk, its handle stays valid to be filled again - virtual void Erase( gfx::geometry_handle const &Geometry ) {} + virtual void Erase( gfx::geometry_handle const & /*Geometry*/ ) { + // a renderer which keeps no chunks of its own has none to empty + } // provides direct access to index data of specfied chunk virtual auto Indices( gfx::geometry_handle const &Geometry ) const->gfx::index_array const & = 0; // provides direct access to vertex data of specfied chunk