17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-08 21:00:03 +01:00

Merge pull request #245 from MaSzyna-EU07/split/2-materials

Split/2 materials
This commit is contained in:
2026-11-07 11:20:50 +00:00
committed by GitHub
12 changed files with 292 additions and 3 deletions

View File

@@ -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
{

View File

@@ -662,12 +662,176 @@ 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.glossiness = Source.glossiness;
variant.name = Source.name + Suffix;
variant.size = Source.size;
variant.is_good = Source.is_good;
variant.path = Source.path;
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( param_confs const &Conf, std::string const &Name ) {
auto const own { Conf.find( Name ) };
if( own == Conf.end() ) {
return false;
}
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
&& 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<std::pair<std::string, glm::vec4>>{ { "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;
}
auto &slot { Variant.params[ entry->second.location ] };
auto const offset { static_cast<glm::length_t>( entry->second.offset ) };
auto const size { static_cast<glm::length_t>( entry->second.size ) };
for( glm::length_t i = 0; i < size; ++i ) {
slot[ offset + i ] = value[ i ];
}
std::erase_if(
Variant.params_state,
[ &entry ]( gl::shader::param_entry const &State ) {
return State.location == entry->second.location && State.offset == entry->second.offset; } );
}
}
// 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" ) };
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::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 };
std::erase_if(
Variant.params_state,
[ location ]( gl::shader::param_entry const &State ) {
return State.location == location; } );
return true;
}
} // namespace
material_handle
material_manager::create_matte( material_handle const Material ) {
if( Material == null_handle || Material >= m_materials.size() ) {
return Material;
}
if( auto const lookup { m_mattes.find( Material ) }; lookup != m_mattes.end() ) {
return lookup->second;
}
auto matte { variant_of( m_materials[ Material ], ":matte" ) };
make_matte( matte );
auto const mattehandle { static_cast<material_handle>( 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;
}
auto tinted { variant_of( m_materials[ Material ], ":tinted" ) };
if( false == make_tinted( tinted, Colour ) ) {
return Material;
}
auto const tintedhandle { static_cast<material_handle>( 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
@@ -676,6 +840,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

View File

@@ -142,6 +142,13 @@ 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 );
// 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 &
material( material_handle const Material ) const { return m_materials[ Material ]; }
opengl_material &
@@ -161,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<std::string, std::string>
@@ -170,6 +180,8 @@ private:
// members:
material_sequence m_materials;
index_map m_materialmappings;
std::unordered_map<material_handle, material_handle> m_mattes;
std::map<std::tuple<material_handle, float, float, float>, material_handle> m_tints;
};
//---------------------------------------------------------------------------

View File

@@ -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,15 @@ 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 {
if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return no_primitive; }
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 +441,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 +514,15 @@ 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 {
if( ( Geometry.bank == 0 ) || ( Geometry.bank > m_geometrybanks.size() ) ) { return geometry_bank::no_primitive; }
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 {

View File

@@ -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,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...; 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:
@@ -220,6 +224,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 +243,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

View File

@@ -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?

View File

@@ -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,12 @@ 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
{
auto const primitive{m_geometry.primitive(Geometry)};
return primitive == gfx::geometry_bank::no_primitive ? -1 : static_cast<int>(primitive);
}
// material methods
material_handle opengl33_renderer::Fetch_Material(std::string const &Filename, bool const Loadnow)
{
@@ -2225,6 +2236,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<gl::program> opengl33_renderer::Fetch_Shader(const std::string &name)
{
auto it = m_shaders.find(name);

View File

@@ -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 *

View File

@@ -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 {

View File

@@ -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;

View File

@@ -59,16 +59,26 @@ 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*/ ) {
// 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
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.

View File

@@ -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.