mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 05:09:20 +02:00
feat(renderer): a matte variant of a material for the parts of rails no wheel rolls on: it lights the same maps as weathered steel, with the weathering scaled per vertex by userdata, so a polished running surface fades into unused steel without a bright stripe; the cut faces of rails are drawn in it in an extra pass
This commit is contained in:
@@ -662,6 +662,54 @@ material_manager::create_from_text( std::string const &Name, std::string const &
|
||||
return materialhandle;
|
||||
}
|
||||
|
||||
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 const &source { m_materials[ Material ] };
|
||||
opengl_material matte;
|
||||
matte.textures = source.textures;
|
||||
matte.texture_variants = source.texture_variants;
|
||||
matte.params = source.params;
|
||||
matte.params_state = source.params_state;
|
||||
matte.shader = source.shader;
|
||||
matte.opacity = source.opacity;
|
||||
matte.selfillum = source.selfillum;
|
||||
matte.glossiness = 0.1f;
|
||||
matte.shadow_rank = source.shadow_rank;
|
||||
matte.name = source.name + ":matte";
|
||||
matte.size = source.size;
|
||||
matte.is_good = source.is_good;
|
||||
matte.path = source.path;
|
||||
if( matte.shader ) {
|
||||
for( auto const &[ key, value ] : std::vector<std::pair<std::string, glm::vec4>>{ { "weathering", glm::vec4{ 1.f } } } ) {
|
||||
auto const entry { matte.shader->param_conf.find( key ) };
|
||||
if( entry == matte.shader->param_conf.end() ) {
|
||||
continue;
|
||||
}
|
||||
for( std::size_t i = 0; i < entry->second.size; ++i ) {
|
||||
matte.params[ entry->second.location ][ entry->second.offset + i ] = value[ static_cast<glm::length_t>( 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<material_handle>( m_materials.size() ) };
|
||||
m_materials.emplace_back( std::move( matte ) );
|
||||
m_mattes.emplace( Material, mattehandle );
|
||||
return mattehandle;
|
||||
}
|
||||
|
||||
void
|
||||
material_manager::on_weather_change() {
|
||||
|
||||
|
||||
@@ -142,6 +142,9 @@ 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 );
|
||||
opengl_material const &
|
||||
material( material_handle const Material ) const { return m_materials[ Material ]; }
|
||||
opengl_material &
|
||||
@@ -170,6 +173,7 @@ private:
|
||||
// members:
|
||||
material_sequence m_materials;
|
||||
index_map m_materialmappings;
|
||||
std::unordered_map<material_handle, material_handle> m_mattes;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -2225,6 +2225,12 @@ 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);
|
||||
}
|
||||
|
||||
std::shared_ptr<gl::program> opengl33_renderer::Fetch_Shader(const std::string &name)
|
||||
{
|
||||
auto it = m_shaders.find(name);
|
||||
@@ -4476,6 +4482,39 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
}
|
||||
flush_batch();
|
||||
|
||||
// cut faces of rails, in matte variants of the rail materials
|
||||
if ((m_renderpass.draw_mode == rendermode::color) || (m_renderpass.draw_mode == rendermode::reflections))
|
||||
{
|
||||
for (auto first{First}; first != Last; ++first)
|
||||
{
|
||||
auto const track{*first};
|
||||
|
||||
if (false == track->m_visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (auto const &cuts : {std::make_pair(track->m_materialcut1, &track->GeometryCut1), std::make_pair(track->m_materialcut2, &track->GeometryCut2)})
|
||||
{
|
||||
if ((cuts.first == null_handle) || cuts.second->empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (track->eEnvironment != e_flat)
|
||||
{
|
||||
flush_batch();
|
||||
setup_environment_light(track->eEnvironment);
|
||||
Bind_Material(cuts.first);
|
||||
draw(std::begin(*cuts.second), std::end(*cuts.second));
|
||||
setup_environment_light();
|
||||
continue;
|
||||
}
|
||||
begin_batch(cuts.first);
|
||||
m_pathbatch.insert(std::end(m_pathbatch), std::begin(*cuts.second), std::end(*cuts.second));
|
||||
}
|
||||
}
|
||||
flush_batch();
|
||||
}
|
||||
|
||||
// fourth pass: per-track sleeper models (sleepermodel optional directive).
|
||||
// drawn after rails/trackbeds so depth pre-pass culling is favourable, and only in passes
|
||||
// where Render_Sleepers actually does work (it gates itself on draw mode / distance).
|
||||
|
||||
@@ -78,6 +78,8 @@ class opengl33_renderer : public gfx_renderer {
|
||||
// material methods
|
||||
material_handle
|
||||
Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override;
|
||||
material_handle
|
||||
Matte_Material( material_handle const Material ) override;
|
||||
void
|
||||
Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override;
|
||||
IMaterial const *
|
||||
|
||||
@@ -3327,6 +3327,32 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First,
|
||||
}
|
||||
}
|
||||
}
|
||||
// cut faces of rails
|
||||
if( ( m_renderpass.draw_mode == rendermode::color )
|
||||
|| ( m_renderpass.draw_mode == rendermode::reflections ) ) {
|
||||
for( auto first { First }; first != Last; ++first ) {
|
||||
|
||||
auto const track { *first };
|
||||
|
||||
if( false == track->m_visible ) {
|
||||
continue;
|
||||
}
|
||||
for( auto const &cuts : { std::make_pair( track->m_materialcut1, &track->GeometryCut1 ), std::make_pair( track->m_materialcut2, &track->GeometryCut2 ) } ) {
|
||||
if( ( cuts.first == null_handle )
|
||||
|| ( cuts.second->empty() ) ) {
|
||||
continue;
|
||||
}
|
||||
if( track->eEnvironment != e_flat ) {
|
||||
setup_environment_light( track->eEnvironment );
|
||||
}
|
||||
Bind_Material( cuts.first );
|
||||
m_geometry.draw( std::begin( *cuts.second ), std::end( *cuts.second ) );
|
||||
if( track->eEnvironment != e_flat ) {
|
||||
setup_environment_light();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// post-render reset
|
||||
switch( m_renderpass.draw_mode ) {
|
||||
case rendermode::shadows:
|
||||
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
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; }
|
||||
// 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.
|
||||
|
||||
@@ -9,6 +9,8 @@ uniform sampler2D headlightmap;
|
||||
|
||||
float glossiness = 1.0;
|
||||
float metalic = 0.0;
|
||||
in vec4 UserData;
|
||||
#param (weathering, 2, 3, 1, zero)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Lighting balance tunables - tweak these to control overall scene
|
||||
@@ -285,6 +287,16 @@ 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[2].w > 0.0 ? clamp(param[2].w * UserData.x, 0.0, 1.0) : 0.0);
|
||||
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));
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user