mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-23 08:51:11 +02:00
feat(track): the texture of a shared running surface blends into the texture of the rail it lies on: 2 cm on each side of the boundary both are drawn in tinted variants of their materials that fade per vertex into the mean colour of the two treads, sampled from the texture files, so the edge of the wheels of the other track leaves a soft band instead of a line
This commit is contained in:
@@ -662,6 +662,29 @@ 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;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
material_handle
|
||||
material_manager::create_matte( material_handle const Material ) {
|
||||
|
||||
@@ -672,21 +695,8 @@ material_manager::create_matte( material_handle const 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;
|
||||
auto matte { variant_of( m_materials[ Material ], ":matte" ) };
|
||||
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 ) };
|
||||
@@ -710,6 +720,42 @@ material_manager::create_matte( material_handle const Material ) {
|
||||
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 const &conf { m_materials[ Material ].shader->param_conf };
|
||||
auto const weathering { conf.find( "weathering" ) };
|
||||
if( weathering == conf.end()
|
||||
|| weathering->second.offset != 3
|
||||
|| std::any_of(
|
||||
std::begin( conf ), std::end( conf ),
|
||||
[&]( auto const &Entry ) {
|
||||
return Entry.first != weathering->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<material_handle>( m_materials.size() ) };
|
||||
m_materials.emplace_back( std::move( tinted ) );
|
||||
m_tints.emplace( key, tintedhandle );
|
||||
return tintedhandle;
|
||||
}
|
||||
|
||||
void
|
||||
material_manager::on_weather_change() {
|
||||
|
||||
|
||||
@@ -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 );
|
||||
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 &
|
||||
@@ -174,6 +176,7 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -2231,6 +2231,12 @@ 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);
|
||||
|
||||
@@ -80,6 +80,8 @@ class opengl33_renderer : public gfx_renderer {
|
||||
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 *
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
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; }
|
||||
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.
|
||||
|
||||
@@ -288,11 +288,13 @@ 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 tinted = (param[2].w < 0.0 ? clamp(-param[2].w * UserData.x, 0.0, 1.0) : 0.0);
|
||||
float polished = max(weathered * smoothstep(0.03, 0.12, dot(texturecolor, vec3(0.2126, 0.7152, 0.0722))), tinted);
|
||||
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 ? mix(texturecolor, param[2].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);
|
||||
|
||||
@@ -721,7 +721,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
|
||||
}
|
||||
}
|
||||
Ring.emplace_back( pt, glm::normalize( norm ), glm::vec2{ shape.texture.x, 0.f } );
|
||||
Wear.push_back( Rampwidth > 0.0 ? static_cast<float>( std::clamp( ( outside - worn + Rampwidth ) / Rampwidth, 0.0, 1.0 ) ) : 1.f );
|
||||
Wear.push_back( Rampwidth != 0.0 ? static_cast<float>( std::clamp( ( Rampwidth > 0.0 ? outside - worn : worn - outside ) / std::abs( Rampwidth ) + 1.0, 0.0, 1.0 ) ) : 1.f );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -197,15 +197,20 @@ material_handle cut_material( material_handle const Material ) {
|
||||
return unrolled ? GfxRenderer->Fetch_Material( "editor_unrolled" ) : GfxRenderer->Matte_Material( Material );
|
||||
}
|
||||
|
||||
float tread_brightness( material_handle const Material, glm::vec2 const &Band ) {
|
||||
material_handle tint_material( material_handle const Material, glm::vec3 const &Colour ) {
|
||||
|
||||
static std::map<std::tuple<material_handle, float, float>, float> brightness;
|
||||
return GfxRenderer->Tinted_Material( Material, Colour );
|
||||
}
|
||||
|
||||
std::pair<float, glm::vec3> const &tread_sample( material_handle const Material, glm::vec2 const &Band ) {
|
||||
|
||||
static std::map<std::tuple<material_handle, float, float>, std::pair<float, glm::vec3>> samples;
|
||||
auto const key { std::make_tuple( Material, Band.x, Band.y ) };
|
||||
if( auto const lookup { brightness.find( key ) }; lookup != brightness.end() ) {
|
||||
if( auto const lookup { samples.find( key ) }; lookup != samples.end() ) {
|
||||
return lookup->second;
|
||||
}
|
||||
auto &result { brightness[ key ] };
|
||||
result = -1.f;
|
||||
auto &result { samples[ key ] };
|
||||
result = { -1.f, glm::vec3{ 0.f } };
|
||||
if( Material == null_handle ) {
|
||||
return result;
|
||||
}
|
||||
@@ -221,9 +226,11 @@ float tread_brightness( material_handle const Material, glm::vec2 const &Band )
|
||||
auto const last { std::clamp( static_cast<int>( 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; };
|
||||
if( type == ".dds" ) {
|
||||
std::ifstream input( file, std::ios::binary );
|
||||
@@ -283,11 +290,21 @@ float tread_brightness( material_handle const Material, glm::vec2 const &Band )
|
||||
}
|
||||
}
|
||||
if( count > 0 ) {
|
||||
result = static_cast<float>( sum / count );
|
||||
result = { static_cast<float>( sum / count ), glm::vec3{ colour / static_cast<double>( count ) } };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float tread_brightness( material_handle const Material, glm::vec2 const &Band ) {
|
||||
|
||||
return tread_sample( Material, Band ).first;
|
||||
}
|
||||
|
||||
glm::vec3 tread_colour( material_handle const Material, glm::vec2 const &Band ) {
|
||||
|
||||
return tread_sample( Material, Band ).second;
|
||||
}
|
||||
|
||||
// in the colour of the top of the rail foot
|
||||
void cap_loft( gfx::vertex_array const &Strip, std::size_t const Ring, bool const Start, bool const End, gfx::vertex_array &Triangles ) {
|
||||
|
||||
@@ -1481,6 +1498,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
|
||||
auto const length { segment.GetLength() };
|
||||
auto const &treads { Layout.treads[ Path ][ Side > 0 ] };
|
||||
material_handle current { null_handle };
|
||||
std::vector<material_handle> tints;
|
||||
auto const sink = [&]( bool const Matte ) -> geometryhandle_sequence & {
|
||||
if( current == null_handle ) {
|
||||
return Matte ? Cuts : Geometry;
|
||||
@@ -1491,11 +1509,12 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
|
||||
if( eType == tt_Switch && current == m_material2 ) {
|
||||
return Matte ? GeometryCut2 : Geometry2;
|
||||
}
|
||||
auto const tinted { std::find( tints.begin(), tints.end(), current ) != tints.end() };
|
||||
auto found { std::find_if( GeometryTreads.begin(), GeometryTreads.end(), [&]( tread_geometry const &Tread ) { return Tread.material == current; } ) };
|
||||
if( found == GeometryTreads.end() ) {
|
||||
found = GeometryTreads.insert( GeometryTreads.end(), { current, cut_material( current ), {}, {} } );
|
||||
found = GeometryTreads.insert( GeometryTreads.end(), { current, tinted ? current : cut_material( current ), {}, {} } );
|
||||
}
|
||||
return Matte ? found->cuts : found->geometry; };
|
||||
return Matte && false == tinted ? found->cuts : found->geometry; };
|
||||
auto const texturelength = [&]() {
|
||||
return ( current == null_handle ? Texturelength : texture_length( current ) ); };
|
||||
auto const aim = [&]( std::vector<rail_tread> const &Spans, double const Middle ) -> rail_tread const * {
|
||||
@@ -1509,10 +1528,10 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
|
||||
return nullptr;
|
||||
};
|
||||
// parts no wheel rolls on go in the matte material of the cut faces
|
||||
auto const piece = [&]( double const Start, double const End, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, bool const Capstart, bool const Capend, gfx::vertex_array const &Shape, bool const Rolled, std::vector<glm::dvec2> const &Inner, std::vector<glm::dvec2> const &Ramp = {}, bool const Walls = false, float const Lift = 0.f, float const Wallu = -1.f ) {
|
||||
auto const piece = [&]( double const Start, double const End, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, bool const Capstart, bool const Capend, gfx::vertex_array const &Shape, bool const Rolled, std::vector<glm::dvec2> const &Inner, std::vector<glm::dvec2> const &Ramp = {}, bool const Walls = false, float const Lift = 0.f, float const Wallu = -1.f, double const Fade = blend ) {
|
||||
gfx::vertex_array vertices;
|
||||
gfx::userdata_array userdata;
|
||||
if( Shape.size() > 1 && segment.RenderLoftSpan( vertices, m_origin, Shape, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, Lift, Inner, Rolled ? nullptr : &userdata, Ramp, Ramp.empty() ? 0.0 : blend, Walls, Wallu ) ) {
|
||||
if( Shape.size() > 1 && segment.RenderLoftSpan( vertices, m_origin, Shape, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, Lift, Inner, Rolled ? nullptr : &userdata, Ramp, Ramp.empty() ? 0.0 : Fade, Walls, Wallu ) ) {
|
||||
cut_faces( vertices, Shape.size(), Capstart, Capend, Drawing.bank, sink( true ) );
|
||||
gfx::vertex_array triangles;
|
||||
gfx::userdata_array triangleuserdata;
|
||||
@@ -1643,8 +1662,40 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
|
||||
}
|
||||
auto const &lower { Span.below ? Span.gauge : Span.edge };
|
||||
auto const &upper { Span.below ? Span.edge : Span.gauge };
|
||||
auto const own { rail_material( Path ) };
|
||||
auto const colour { 0.5f * ( tread_colour( Span.material, tread_band() ) + tread_colour( own, tread_band() ) ) };
|
||||
auto const tint = [&]( material_handle const Material ) {
|
||||
auto const tinted { tint_material( Material, colour ) };
|
||||
if( tinted != Material ) {
|
||||
tints.push_back( tinted );
|
||||
}
|
||||
return tinted; };
|
||||
auto const owntint { tint( own ) };
|
||||
auto const sharedtint { tint( Span.material ) };
|
||||
auto const shifted = []( std::vector<glm::dvec2> Knots, double const Lateral ) {
|
||||
for( auto &knot : Knots ) {
|
||||
knot.y += Lateral;
|
||||
}
|
||||
return Knots; };
|
||||
std::vector<glm::dvec2> centre;
|
||||
{
|
||||
std::vector<double> stations;
|
||||
for( auto const *knots : { &lower, &upper } ) {
|
||||
for( auto const &knot : *knots ) {
|
||||
stations.push_back( knot.x );
|
||||
}
|
||||
}
|
||||
std::sort( stations.begin(), stations.end() );
|
||||
for( auto const station : stations ) {
|
||||
centre.emplace_back( station, 0.5 * ( level( lower, station ) + level( upper, station ) ) );
|
||||
}
|
||||
}
|
||||
auto const beneath { shifted( lower, -tread_fade ) };
|
||||
auto const beyond { shifted( upper, tread_fade ) };
|
||||
auto const lowfade { bound( shifted( lower, tread_fade ), centre, true ) };
|
||||
auto const highfade { bound( shifted( upper, -tread_fade ), centre, false ) };
|
||||
std::vector<double> splits { Start, End };
|
||||
for( auto const *knots : { &lower, &upper } ) {
|
||||
for( auto const *knots : { &lower, &upper, &lowfade, &highfade, &beneath, &beyond } ) {
|
||||
for( auto const edge : { low, high } ) {
|
||||
for( std::size_t i = 1; i < knots->size(); ++i ) {
|
||||
auto const &a { ( *knots )[ i - 1 ] };
|
||||
@@ -1659,22 +1710,41 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
|
||||
}
|
||||
}
|
||||
std::sort( splits.begin(), splits.end() );
|
||||
auto const inside { bound( Extents, upper, true ) };
|
||||
for( std::size_t i = 1; i < splits.size(); ++i ) {
|
||||
auto const middle { 0.5 * ( splits[ i - 1 ] + splits[ i ] ) };
|
||||
auto const a { splits[ i - 1 ] };
|
||||
auto const b { splits[ i ] };
|
||||
auto const middle { 0.5 * ( a + b ) };
|
||||
auto const from { level( lower, middle ) };
|
||||
auto const to { level( upper, middle ) };
|
||||
if( from > low ) {
|
||||
auto const inner { level( lowfade, middle ) };
|
||||
auto const outer { level( highfade, middle ) };
|
||||
if( from - tread_fade > low ) {
|
||||
current = null_handle;
|
||||
piece( splits[ i - 1 ], splits[ i ], Offsets, bound( Extents, lower, true ), false, false, top, true, Inner );
|
||||
piece( a, b, Offsets, bound( Extents, beneath, true ), false, false, top, true, Inner );
|
||||
}
|
||||
if( to > low && from < high ) {
|
||||
if( from > low && from - tread_fade < high ) {
|
||||
current = owntint;
|
||||
piece( a, b, Offsets, bound( Extents, lower, true ), false, false, top, false, bound( Inner, beneath, false ), lower, false, 0.f, -1.f, tread_fade );
|
||||
}
|
||||
if( inner > low && from < high ) {
|
||||
current = sharedtint;
|
||||
piece( a, b, Offsets, bound( Extents, lowfade, true ), false, false, top, false, bound( Inner, lower, false ), lower, false, 0.f, -1.f, -tread_fade );
|
||||
}
|
||||
if( outer > low && inner < high ) {
|
||||
current = Span.material;
|
||||
piece( splits[ i - 1 ], splits[ i ], Offsets, inside, false, false, top, true, bound( Inner, lower, false ) );
|
||||
piece( a, b, Offsets, bound( Extents, highfade, true ), false, false, top, true, bound( Inner, lowfade, false ) );
|
||||
}
|
||||
if( to < high ) {
|
||||
if( to > low && outer < high ) {
|
||||
current = sharedtint;
|
||||
piece( a, b, Offsets, bound( Extents, upper, true ), false, false, top, false, bound( Inner, highfade, false ), upper, false, 0.f, -1.f, tread_fade );
|
||||
}
|
||||
if( to + tread_fade > low && to < high ) {
|
||||
current = owntint;
|
||||
piece( a, b, Offsets, bound( Extents, beyond, true ), false, false, top, false, bound( Inner, upper, false ), upper, false, 0.f, -1.f, -tread_fade );
|
||||
}
|
||||
if( to + tread_fade < high ) {
|
||||
current = null_handle;
|
||||
piece( splits[ i - 1 ], splits[ i ], Offsets, Extents, false, false, top, true, bound( Inner, upper, false ) );
|
||||
piece( a, b, Offsets, Extents, false, false, top, true, bound( Inner, beyond, false ) );
|
||||
}
|
||||
}
|
||||
current = null_handle;
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace track_rails {
|
||||
|
||||
double constexpr flangeway { 0.045 };
|
||||
double constexpr blend { 0.015 };
|
||||
double constexpr tread_fade { 0.02 };
|
||||
double constexpr rolled_band { 0.1 };
|
||||
double constexpr nose_tip { 0.012 };
|
||||
double constexpr middle_angle { 0.35 };
|
||||
@@ -30,6 +31,8 @@ int blade_steps( TTrack const &Track );
|
||||
double blade_length( TTrack const &Track, int const Path );
|
||||
material_handle cut_material( material_handle const Material );
|
||||
float tread_brightness( material_handle const Material, glm::vec2 const &Band );
|
||||
glm::vec3 tread_colour( material_handle const Material, glm::vec2 const &Band );
|
||||
material_handle tint_material( material_handle const Material, glm::vec3 const &Colour );
|
||||
// triangles closing the first and/or the last cross-section of a lofted strip, made of pairs of points for each of Ring profile points, both sides of each
|
||||
void cap_loft( gfx::vertex_array const &Strip, std::size_t const Ring, bool const Start, bool const End, gfx::vertex_array &Triangles );
|
||||
gfx::userdata_array worn_userdata( std::size_t const Count );
|
||||
|
||||
Reference in New Issue
Block a user