17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-24 03:36:14 +02:00

feat(track): where two rails share a running surface it takes the texture whose running surface is brighter; the brightness is read from the texture file over the u range of the rail head top, the shared parts are the block of crossing grooved rails, the nose of an acute frog from its tip to the end of the V, the through rail across a wide or grooved crossing, the frog of a switch, and the stock rails of a switch with frogs up to half the blade length where the blade grows to a full head

This commit is contained in:
maj00r
2026-10-13 22:36:09 +02:00
parent 2f8aeb24c0
commit 9448bd8c70
7 changed files with 303 additions and 27 deletions

View File

@@ -196,7 +196,13 @@ void editor_mode::selftest_step()
std::uint64_t hash{1469598103934665603ull};
std::size_t count{0};
int chunks{0};
for (auto const *sequence : {&track->Geometry1, &track->Geometry2, &track->GeometryCut1, &track->GeometryCut2})
std::vector<TTrack::geometryhandle_sequence const *> sequences{&track->Geometry1, &track->Geometry2, &track->GeometryCut1, &track->GeometryCut2};
for (auto const &tread : track->GeometryTreads)
{
sequences.push_back(&tread.geometry);
sequences.push_back(&tread.cuts);
}
for (auto const *sequence : sequences)
{
for (auto const &geometry : *sequence)
{

View File

@@ -4235,6 +4235,8 @@ void opengl33_renderer::Render(TTrack *Track)
draw(std::begin(Track->Geometry1), std::end(Track->Geometry1));
draw(std::begin(Track->Geometry2), std::end(Track->Geometry2));
for (auto &tread : Track->GeometryTreads)
draw(std::begin(tread.geometry), std::end(tread.geometry));
if (Track->eType == tt_Switch)
draw(Track->SwitchExtension->Geometry3);
break;
@@ -4493,7 +4495,13 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
{
continue;
}
for (auto const &cuts : {std::make_pair(track->m_materialcut1, &track->GeometryCut1), std::make_pair(track->m_materialcut2, &track->GeometryCut2)})
std::vector<std::pair<material_handle, TTrack::geometryhandle_sequence *>> parts{{track->m_materialcut1, &track->GeometryCut1}, {track->m_materialcut2, &track->GeometryCut2}};
for (auto &tread : track->GeometryTreads)
{
parts.emplace_back(tread.material, &tread.geometry);
parts.emplace_back(tread.matte, &tread.cuts);
}
for (auto const &cuts : parts)
{
if ((cuts.first == null_handle) || cuts.second->empty())
{

View File

@@ -3140,6 +3140,10 @@ opengl_renderer::Render( TTrack *Track ) {
Bind_Material( Track->m_material2 );
m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) );
}
for( auto const &tread : Track->GeometryTreads ) {
Bind_Material( tread.material );
m_geometry.draw( std::begin( tread.geometry ), std::end( tread.geometry ) );
}
if( ( Track->eType == tt_Switch )
&& ( Track->SwitchExtension->m_material3 != 0 ) ) {
Bind_Material( Track->SwitchExtension->m_material3 );
@@ -3337,7 +3341,12 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator 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 ) } ) {
std::vector<std::pair<material_handle, TTrack::geometryhandle_sequence const *>> parts { { track->m_materialcut1, &track->GeometryCut1 }, { track->m_materialcut2, &track->GeometryCut2 } };
for( auto const &tread : track->GeometryTreads ) {
parts.emplace_back( tread.material, &tread.geometry );
parts.emplace_back( tread.matte, &tread.cuts );
}
for( auto const &cuts : parts ) {
if( ( cuts.first == null_handle )
|| ( cuts.second->empty() ) ) {
continue;

View File

@@ -1368,6 +1368,7 @@ void TTrack::rebuild_geometry( gfx::geometrybank_handle const &Bank ) {
Geometry2.clear();
GeometryCut1.clear();
GeometryCut2.clear();
GeometryTreads.clear();
if( SwitchExtension ) {
SwitchExtension->Geometry3 = gfx::geometry_handle{};
}
@@ -1531,6 +1532,17 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
}
};
gfx::vertex_array vertices;
for( int path = 0; path < 2; ++path ) {
auto const material { path ? m_material2 : m_material1 };
if( material == 0 ) {
continue;
}
auto const side { blade_side( path ) };
loft_blade( path, side > 0 ? rpts1 : rpts2, side > 0 ? rpts3 : rpts4, texture_length( material ), vertices );
cut_faces( vertices, nnumPts, true, false, path ? GeometryCut2 : GeometryCut1 );
( path ? Geometry2 : Geometry1 ).emplace_back( GfxRenderer->Insert( vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
}
for( int path = 0; path < 2; ++path ) {
auto const material { path ? m_material2 : m_material1 };
if( material == 0 ) {
@@ -1541,10 +1553,6 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
auto const texturelength { texture_length( material ) };
auto const side { blade_side( path ) };
auto const &rail { side > 0 ? rpts1 : rpts2 };
loft_blade( path, rail, side > 0 ? rpts3 : rpts4, texturelength, vertices );
cut_faces( vertices, nnumPts, true, false, cuts );
geometry.emplace_back( GfxRenderer->Insert( vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
// fixed parts
// szyna za iglicą
fixed_rail( path, rail, side, texturelength, geometry, cuts );

View File

@@ -189,6 +189,13 @@ public:
material_handle m_materialcut2 = 0; // matte variant of texture 2, for the cut faces of rails of the other switch path
geometryhandle_sequence GeometryCut1; // cut faces of rails textured with texture 1, the blade tip first for a switch
geometryhandle_sequence GeometryCut2; // cut faces of rails textured with texture 2, the blade tip first
struct tread_geometry {
material_handle material { 0 };
material_handle matte { 0 };
geometryhandle_sequence geometry;
geometryhandle_sequence cuts;
};
std::vector<tread_geometry> GeometryTreads;
std::vector<segment_data> m_paths; // source data for owned paths
bool m_editorremoved { false };
@@ -387,11 +394,17 @@ public:
bool capto { false };
double lift { 0.0 };
};
struct rail_tread {
double from { 0.0 };
double to { 0.0 };
material_handle material { 0 };
};
struct rail_layout {
rail_plan plans[ 2 ][ 2 ]; // path, right side
std::vector<rail_band> bands[ 2 ][ 2 ];
std::vector<rail_fill> fills[ 2 ][ 2 ];
std::vector<glm::dvec2> blocks[ 2 ][ 2 ];
std::vector<rail_tread> treads[ 2 ][ 2 ];
std::vector<guard_rail> guards[ 2 ][ 2 ];
std::vector<glm::dvec2> wingends[ 2 ][ 2 ]; // ends of the wing rails of other rails running along each rail, and which way they run there
check_rail_set checks;
@@ -504,6 +517,9 @@ private:
// a rail along a path from a distance on, in parts where other rails cross it, and the check rails beside it. false: no crossing, the rail is laid whole as before
bool lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, int const Path, gfx::vertex_array const &Profile, int const Side, float const Texturelength, double const From, bool const Openstart, bool const Openend, geometryhandle_sequence &Geometry, geometryhandle_sequence &Cuts );
int blade_side( int const Path ) const;
material_handle rail_material( int const Path ) const;
glm::vec2 tread_band() const;
material_handle shared_tread( int const Path, TTrack const &Other, int const Otherpath ) const;
void loft_blade( int const Path, gfx::vertex_array const &Rail, gfx::vertex_array const &Blade, float const Texturelength, gfx::vertex_array &Vertices ) const;
// members
static profiles_array m_profiles; // shared database of path element profiles

View File

@@ -14,6 +14,7 @@ http://mozilla.org/MPL/2.0/.
#include "simulation/simulation.h"
#include "scene/scene.h"
#include "rendering/renderer.h"
#include "stb/stb_image.h"
namespace {
@@ -196,6 +197,97 @@ 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 ) {
static std::map<std::tuple<material_handle, float, float>, float> brightness;
auto const key { std::make_tuple( Material, Band.x, Band.y ) };
if( auto const lookup { brightness.find( key ) }; lookup != brightness.end() ) {
return lookup->second;
}
auto &result { brightness[ key ] };
result = -1.f;
if( Material == null_handle ) {
return result;
}
auto const texture { GfxRenderer->Material( Material )->GetTexture( 0 ) };
if( texture == null_handle ) {
return result;
}
auto const &image { GfxRenderer->Texture( texture ) };
auto const type { std::string( image.get_type() ) };
auto const file { std::string( image.get_name() ) + type };
auto const columns = [&]( int const Width ) {
auto const first { std::clamp( static_cast<int>( std::floor( std::min( Band.x, Band.y ) * Width ) ), 0, Width - 1 ) };
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 };
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;
++count; };
if( type == ".dds" ) {
std::ifstream input( file, std::ios::binary );
std::array<unsigned char, 128> header {};
if( input.read( reinterpret_cast<char *>( header.data() ), header.size() ) && std::equal( header.begin(), header.begin() + 4, "DDS " ) ) {
auto const word = [&]( std::size_t const Offset ) {
return static_cast<std::uint32_t>( header[ Offset ] ) | static_cast<std::uint32_t>( header[ Offset + 1 ] ) << 8 | static_cast<std::uint32_t>( header[ Offset + 2 ] ) << 16 | static_cast<std::uint32_t>( header[ Offset + 3 ] ) << 24; };
auto const height { static_cast<int>( word( 12 ) ) };
auto const width { static_cast<int>( word( 16 ) ) };
auto const fourcc { word( 84 ) };
auto const blocksize { fourcc == 0x31545844u ? 8 : ( fourcc == 0x33545844u || fourcc == 0x35545844u ) ? 16 : 0 };
if( blocksize > 0 && width > 0 && height > 0 ) {
auto const wide { ( width + 3 ) / 4 };
auto const high { ( height + 3 ) / 4 };
std::vector<unsigned char> data( static_cast<std::size_t>( wide ) * high * blocksize );
if( input.read( reinterpret_cast<char *>( data.data() ), data.size() ) ) {
auto const [ first, last ] { columns( width ) };
auto const unpack = []( unsigned const Color ) {
return glm::vec3{ ( ( Color >> 11 ) & 31 ) * 255.f / 31.f, ( ( Color >> 5 ) & 63 ) * 255.f / 63.f, ( Color & 31 ) * 255.f / 31.f }; };
for( int row = 0; row < high; ++row ) {
for( int column = first / 4; column <= last / 4; ++column ) {
auto const *block { &data[ ( static_cast<std::size_t>( row ) * wide + column ) * blocksize + ( blocksize - 8 ) ] };
auto const low { static_cast<unsigned>( block[ 0 ] | block[ 1 ] << 8 ) };
auto const top { static_cast<unsigned>( block[ 2 ] | block[ 3 ] << 8 ) };
auto const opaque { blocksize == 16 || low > top };
glm::vec3 const colors[ 4 ] {
unpack( low ),
unpack( top ),
opaque ? ( 2.f * unpack( low ) + unpack( top ) ) / 3.f : 0.5f * ( unpack( low ) + unpack( top ) ),
opaque ? ( unpack( low ) + 2.f * unpack( top ) ) / 3.f : glm::vec3{ 0.f } };
auto const bits { static_cast<std::uint32_t>( block[ 4 ] ) | static_cast<std::uint32_t>( block[ 5 ] ) << 8 | static_cast<std::uint32_t>( block[ 6 ] ) << 16 | static_cast<std::uint32_t>( block[ 7 ] ) << 24 };
for( int texel = 0; texel < 16; ++texel ) {
auto const x { column * 4 + texel % 4 };
auto const index { ( bits >> ( 2 * texel ) ) & 3 };
if( x < first || x > last || ( false == opaque && index == 3 ) ) {
continue;
}
add( colors[ index ].r, colors[ index ].g, colors[ index ].b );
}
}
}
}
}
}
}
else {
int width, height, components;
if( auto *pixels { stbi_load( file.c_str(), &width, &height, &components, 4 ) } ) {
auto const [ first, last ] { columns( width ) };
for( int y = 0; y < height; ++y ) {
for( int x = first; x <= last; ++x ) {
auto const *pixel { &pixels[ ( static_cast<std::size_t>( y ) * width + x ) * 4 ] };
add( pixel[ 0 ], pixel[ 1 ], pixel[ 2 ] );
}
}
stbi_image_free( pixels );
}
}
if( count > 0 ) {
result = static_cast<float>( sum / count );
}
return result;
}
// 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 ) {
@@ -978,6 +1070,47 @@ struct frame_corner {
} // namespace
material_handle TTrack::rail_material( int const Path ) const {
return ( Path == 1 && eType == tt_Switch ? m_material2 : m_material1 );
}
glm::vec2 TTrack::tread_band() const {
glm::vec2 band { 0.45f, 0.55f };
if( m_profiles.empty() ) {
return band;
}
auto const &outline { track_rail_profile( m_profile1.second ) };
auto const count { outline.size() / 2 };
auto top { -1e9f };
for( std::size_t i = 0; i < count; ++i ) {
top = std::max( top, outline[ i ].position.y );
}
glm::vec2 found { 1e9f, -1e9f };
for( std::size_t i = 0; i < count; ++i ) {
if( outline[ i ].position.y > top - 0.0005f ) {
found = { std::min( found.x, outline[ i ].texture.s ), std::max( found.y, outline[ i ].texture.s ) };
}
}
if( found.x > found.y ) {
return band;
}
auto const middle { 0.5f * ( found.x + found.y ) };
auto const half { std::max( 0.5f * ( found.y - found.x ), 0.01f ) };
return { middle - half, middle + half };
}
material_handle TTrack::shared_tread( int const Path, TTrack const &Other, int const Otherpath ) const {
auto const own { rail_material( Path ) };
auto const theirs { Other.rail_material( Otherpath ) };
if( theirs == null_handle || theirs == own ) {
return own;
}
return ( track_rails::tread_brightness( theirs, Other.tread_band() ) > track_rails::tread_brightness( own, tread_band() ) ? theirs : own );
}
TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
rail_layout layout;
@@ -993,6 +1126,21 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
auto &guards { layout.guards };
auto &wingends { layout.wingends };
auto const guardoffset { -( flangeway + railhead ) };
auto const tread = [&]( int const Path, int const Side, glm::dvec2 const &Span, TTrack const &Other, int const Otherpath ) {
auto const material { shared_tread( Path, Other, Otherpath ) };
if( material != rail_material( Path ) && Span.y > Span.x ) {
layout.treads[ Path ][ Side > 0 ].push_back( { Span.x, Span.y, material } );
}
};
auto const across = [&]( rail_crossing const &Crossing, rail_size const &Other ) {
glm::dvec2 span { 1e9, -1e9 };
for( auto const theirs : { Other.grooved ? Other.ledge : 0.0, Other.grooved ? Other.tread : Other.head } ) {
for( auto const ours : { size.grooved ? size.ledge : 0.0, size.grooved ? size.tread : size.head } ) {
auto const distance { Crossing.at + ( Crossing.facing * ours - theirs ) / Crossing.into };
span = { std::min( span.x, distance ), std::max( span.y, distance ) };
}
}
return span; };
std::vector<frame_corner> corners;
if( Crossings ) {
for( auto const &crossing : crossings ) {
@@ -1008,12 +1156,17 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
std::vector<rail_fill> vee;
vee_fill( vee, crossing.at, crossing.into, crossing.facing, size, theirs, true );
vee_fill( vee, crossing.at, crossing.into, crossing.facing, size, theirs, false );
layout.blocks[ crossing.path ][ crossing.side > 0 ].emplace_back( std::min( vee[ 0 ].from, vee[ 1 ].from ), std::max( vee[ 0 ].to, vee[ 1 ].to ) );
glm::dvec2 const block { std::min( vee[ 0 ].from, vee[ 1 ].from ), std::max( vee[ 0 ].to, vee[ 1 ].to ) };
layout.blocks[ crossing.path ][ crossing.side > 0 ].push_back( block );
tread( crossing.path, crossing.side, block, *crossing.track, crossing.otherpath );
if( through ) {
auto &fills { layout.fills[ crossing.path ][ crossing.side > 0 ] };
fills.insert( fills.end(), vee.begin(), vee.end() );
}
}
else if( through ) {
tread( crossing.path, crossing.side, across( crossing, theirs ), *crossing.track, crossing.otherpath );
}
continue;
}
if( crossing.angle > middle_angle && crossing.angle <= wide_angle ) {
@@ -1023,6 +1176,9 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
auto const other { crossing.track->rail_dimensions() };
auto const through { goes_through( *this, *crossing.track ) };
wide( plan, crossing.at, crossing.into, crossing.facing, through ? 0.0 : -other.head, flangeway, 0.0, railhead );
if( through ) {
tread( crossing.path, crossing.side, across( crossing, other ), *crossing.track, crossing.otherpath );
}
auto const reach { ( flangeway + railhead + 0.3 ) / std::abs( crossing.into ) };
auto &spans { guards[ crossing.path ][ crossing.side > 0 ] };
TTrack::guard_rail guard { crossing.at - reach, crossing.at + reach };
@@ -1047,8 +1203,12 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
wingends[ crossing.path ][ crossing.side > 0 ].emplace_back( crossing.at - toward * wing_reach( slope ), -toward );
acute( plan, crossing.at, toward, slope, crossing.nose,
wing_extension( *this, checks, crossing.path, crossing.side, crossing.at, toward, slope, *crossing.track, crossing.otherpath, crossing.otherside, crossing.otherat, crossing.aligned ? -toward : toward ), size );
auto const nose { toward < 0.0 ? plan.cuts.back().start : plan.cuts.back().end };
std::vector<rail_fill> vee;
vee_fill( vee, crossing.at, crossing.into, crossing.facing, size, crossing.track->rail_dimensions(), true );
tread( crossing.path, crossing.side, { std::min( nose, vee.front().from ), std::max( nose, vee.front().to ) }, *crossing.track, crossing.otherpath );
if( crossing.nose ) {
vee_fill( layout.fills[ crossing.path ][ crossing.side > 0 ], crossing.at, crossing.into, crossing.facing, size, crossing.track->rail_dimensions(), true );
layout.fills[ crossing.path ][ crossing.side > 0 ].push_back( vee.front() );
}
}
else {
@@ -1135,6 +1295,11 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
}
}
}
if( eType == tt_Switch && cut_rails( *this ) ) {
for( int path = 0; path < 2; ++path ) {
tread( path, -blade_side( path ), { 0.0, 0.5 * blade_length( *this, path ) }, *this, 1 - path );
}
}
if( eType == tt_Switch ) {
auto const frog { this->frog() };
if( frog.valid ) {
@@ -1155,7 +1320,9 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
std::vector<rail_fill> vee;
vee_fill( vee, frog.at[ path ], into, facing, size, size, true );
vee_fill( vee, frog.at[ path ], into, facing, size, size, false );
layout.blocks[ path ][ frog.side[ path ] > 0 ].emplace_back( std::min( vee[ 0 ].from, vee[ 1 ].from ), std::max( vee[ 0 ].to, vee[ 1 ].to ) );
glm::dvec2 const block { std::min( vee[ 0 ].from, vee[ 1 ].from ), std::max( vee[ 0 ].to, vee[ 1 ].to ) };
layout.blocks[ path ][ frog.side[ path ] > 0 ].push_back( block );
tread( path, frog.side[ path ], block, *this, 1 - path );
if( path == 0 ) {
fills.insert( fills.end(), vee.begin(), vee.end() );
}
@@ -1164,8 +1331,12 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
wingends[ path ][ frog.side[ path ] > 0 ].emplace_back( frog.at[ path ] + wing_reach( slope ), 1.0 );
acute( plans[ path ][ frog.side[ path ] > 0 ], frog.at[ path ], -1.0, slope, path == 0,
wing_extension( *this, checks, path, frog.side[ path ], frog.at[ path ], -1.0, slope, *this, 1 - path, frog.side[ 1 - path ], frog.at[ 1 - path ], 1.0 ), size );
auto const nose { plans[ path ][ frog.side[ path ] > 0 ].cuts.back().start };
std::vector<rail_fill> vee;
vee_fill( vee, frog.at[ path ], into, facing, size, size, true );
tread( path, frog.side[ path ], { std::min( nose, vee.front().from ), std::max( nose, vee.front().to ) }, *this, 1 - path );
if( path == 0 ) {
vee_fill( fills, frog.at[ path ], into, facing, size, size, true );
fills.push_back( vee.front() );
}
}
}
@@ -1247,12 +1418,40 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
auto const &segment { path_segment( *this, Path ) };
auto const &plan { Layout.plans[ Path ][ Side > 0 ] };
auto const length { segment.GetLength() };
auto const &treads { Layout.treads[ Path ][ Side > 0 ] };
material_handle current { null_handle };
auto const sink = [&]( bool const Matte ) -> geometryhandle_sequence & {
if( current == null_handle ) {
return Matte ? Cuts : Geometry;
}
if( current == m_material1 ) {
return Matte ? GeometryCut1 : Geometry1;
}
if( eType == tt_Switch && current == m_material2 ) {
return Matte ? GeometryCut2 : Geometry2;
}
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 ), {}, {} } );
}
return Matte ? 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 ) {
current = null_handle;
for( auto const &span : Spans ) {
if( Middle > span.from && Middle < span.to ) {
current = span.material;
break;
}
}
};
// 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 ) {
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 ) ) {
cut_faces( vertices, Shape.size(), Capstart, Capend, Drawing.bank, Cuts );
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 ) ) {
cut_faces( vertices, Shape.size(), Capstart, Capend, Drawing.bank, sink( true ) );
gfx::vertex_array triangles;
gfx::userdata_array triangleuserdata;
auto const ring { 2 * Shape.size() };
@@ -1267,7 +1466,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
}
}
}
( Rolled ? Geometry : Cuts ).emplace_back( GfxRenderer->Insert( triangles, Rolled ? empty_userdata : triangleuserdata, Drawing.bank, GL_TRIANGLES ) );
sink( false == Rolled ).emplace_back( GfxRenderer->Insert( triangles, Rolled ? empty_userdata : triangleuserdata, Drawing.bank, GL_TRIANGLES ) );
}
};
auto const level = []( std::vector<glm::dvec2> const &Knots, double const Distance ) {
@@ -1320,8 +1519,8 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
return gfx::vertex_array( Profile.begin() + First, Profile.begin() + Last + 1 ); };
auto const running = [&]( double const Start, double const End, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, std::vector<glm::dvec2> const &Inner, std::vector<glm::dvec2> const &Rolled, bool const Capstart, bool const Capend ) {
gfx::vertex_array vertices;
if( segment.RenderLoftSpan( vertices, m_origin, Profile, Side, halfwidth, Texturelength, Start, End, Offsets, Extents, 0.f, Inner ) ) {
cut_faces( vertices, Profile.size(), Capstart, Capend, Drawing.bank, Cuts );
if( segment.RenderLoftSpan( vertices, m_origin, Profile, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, 0.f, Inner ) ) {
cut_faces( vertices, Profile.size(), Capstart, Capend, Drawing.bank, sink( true ) );
}
piece( Start, End, Offsets, Extents, false, false, strip( 0, topfirst ), true, Inner );
piece( Start, End, Offsets, Extents, false, false, strip( toplast, Profile.size() - 1 ), true, Inner );
@@ -1470,11 +1669,13 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
auto const a { std::max( fill.from, From ) };
auto const b { std::min( fill.to, length ) };
if( b - a > 0.01 ) {
aim( treads, 0.5 * ( a + b ) );
piece( a, b, {}, fill.outer, a == fill.from && fill.capfrom, b == fill.to && fill.capto, Drawing.plates[ Side > 0 ? 0 : 1 ], true, fill.inner, {}, true, static_cast<float>( fill.lift ) );
current = null_handle;
}
}
auto const &bands { Layout.bands[ Path ][ Side > 0 ] };
if( plan.cuts.empty() && bands.empty() ) {
if( plan.cuts.empty() && bands.empty() && treads.empty() ) {
return false;
}
auto const byposition = []( glm::dvec2 const &A, glm::dvec2 const &B ) { return A.x < B.x; };
@@ -1509,7 +1710,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
( outwards ? cut.endinner : cut.endextents ) = endknots;
( outwards ? cut.startextents : cut.startinner ) = startknots;
return cut; };
auto const lay = [&]( gfx::vertex_array const &Shape, std::vector<rail_cut> cuts, bool const Wheels, float const Wallu = -1.f ) {
auto const lay = [&]( gfx::vertex_array const &Shape, std::vector<rail_cut> cuts, bool const Wheels, std::vector<rail_tread> const &Spans, float const Wallu = -1.f ) {
std::sort( cuts.begin(), cuts.end(), []( rail_cut const &A, rail_cut const &B ) { return A.at < B.at; } );
auto start { -1e9 };
auto startcap { true };
@@ -1534,12 +1735,30 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
auto const capstart { start > From ? startcap : Openstart };
auto const capend { end < length ? cuts[ i ].endcap : Openend };
if( b - a > 0.01 ) {
if( false == Wheels || rolled.empty() || topfirst >= toplast ) {
piece( a, b, pieceoffsets, pieceextents, capstart, capend, Shape, true, pieceinner, {}, false == Wheels, 0.f, Wallu );
std::vector<double> splits;
for( auto const &span : Spans ) {
for( auto const edge : { span.from, span.to } ) {
if( edge > a + 0.01 && edge < b - 0.01 ) {
splits.push_back( edge );
}
}
}
else {
running( a, b, pieceoffsets, pieceextents, pieceinner, rolled, capstart, capend );
std::sort( splits.begin(), splits.end() );
splits.erase( std::unique( splits.begin(), splits.end(), []( double const A, double const B ) { return B - A < 0.002; } ), splits.end() );
splits.insert( splits.begin(), a );
splits.push_back( b );
for( std::size_t k = 1; k < splits.size(); ++k ) {
auto const first { k == 1 };
auto const last { k + 1 == splits.size() };
aim( Spans, 0.5 * ( splits[ k - 1 ] + splits[ k ] ) );
if( false == Wheels || rolled.empty() || topfirst >= toplast ) {
piece( splits[ k - 1 ], splits[ k ], pieceoffsets, pieceextents, first && capstart, last && capend, Shape, true, pieceinner, {}, false == Wheels, 0.f, Wallu );
}
else {
running( splits[ k - 1 ], splits[ k ], pieceoffsets, pieceextents, pieceinner, rolled, first && capstart, last && capend );
}
}
current = null_handle;
}
if( i < cuts.size() ) {
rolledfrom = cuts[ i ].startrolled;
@@ -1559,7 +1778,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
for( auto const &band : bands ) {
cuts.push_back( band_cut( band, reach ) );
}
lay( Profile, cuts, true );
lay( Profile, cuts, true, treads );
return true;
}
auto const height = [&]( std::size_t const Index ) {
@@ -1661,7 +1880,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
wholecuts.push_back( band_cut( band, bodyreach ) );
}
}
lay( body, wholecuts, false );
lay( body, wholecuts, false, {} );
auto const wallu { floortextures > 0 ? floortexture / floortextures : -1.f };
for( std::size_t k = 0; k < tops.size(); ++k ) {
auto const reach { reaches( tops[ k ] ) };
@@ -1708,9 +1927,18 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
}
}
if( merged.empty() ) {
lay( tops[ k ], cuts, false, wallu );
lay( tops[ k ], cuts, false, treads, wallu );
continue;
}
std::vector<rail_tread> blocktreads;
for( auto const &span : merged ) {
for( auto const &tread : treads ) {
if( tread.from < span.y && tread.to > span.x ) {
blocktreads.push_back( { span.x, span.y, tread.material } );
break;
}
}
}
auto const seam = []( double const At, double const End, double const Start ) {
rail_cut cut;
cut.at = At;
@@ -1731,8 +1959,8 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
seam( 0.5 * ( merged[ m ].y + merged[ m + 1 ].x ), merged[ m ].y, merged[ m + 1 ].x ) :
seam( merged[ m ].y + 0.001, merged[ m ].y, 1e9 ) );
}
lay( tops[ k ], outside, false, wallu );
lay( flats[ k ], inside, false, wallu );
lay( tops[ k ], outside, false, treads, wallu );
lay( flats[ k ], inside, false, blocktreads, wallu );
}
return true;
}

View File

@@ -29,6 +29,7 @@ bool cut_rails( TTrack const &Track );
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 );
// 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 );