17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-20 23:23:30 +02:00

fix(track): a crossing of grooved rails is one flat block where their lofts overlap; the rail cut by the other covers the roundings of the rail going through, the tops in the block are flat in one texture, the plates of the V reach up to the rail top, the walls of the cuts are lit and textured as walls, and the nose between two flangeways starts blunt at the width of a nose tip instead of a thin sliver standing in the groove

This commit is contained in:
maj00r
2026-10-13 22:03:36 +02:00
parent 9343ebcb13
commit 2f8aeb24c0
5 changed files with 198 additions and 48 deletions

View File

@@ -580,7 +580,7 @@ void TSegment::plan_frame( double const Distance, glm::dvec3 &Point, glm::dvec3
Across = glm::normalize( Across );
}
bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, float const Lift, std::vector<glm::dvec2> const &Inner, gfx::userdata_array *Userdata, std::vector<glm::dvec2> const &Ramp, double const Rampwidth, bool const Walls ) const {
bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, float const Lift, std::vector<glm::dvec2> const &Inner, gfx::userdata_array *Userdata, std::vector<glm::dvec2> const &Ramp, double const Rampwidth, bool const Walls, float const Wallu ) const {
if( fTsBuffer.empty() || To - From < 0.001 || ShapePoints.empty() )
return false;
@@ -662,41 +662,53 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
auto const count { ShapePoints.size() };
auto const reach = [&]( std::size_t const Index ) {
return static_cast<double>( Side * ShapePoints[ Index ].position.x - Halfwidth ); };
auto const twin = [&]( std::size_t const A, std::size_t const B ) {
return ShapePoints[ A ].position.x == ShapePoints[ B ].position.x && ShapePoints[ A ].position.y == ShapePoints[ B ].position.y; };
auto const clamped = [&]( std::size_t const Index ) {
auto const outside { reach( Index ) };
return ( outside > extent ? 1 : outside < innerextent ? -1 : 0 ); };
for( std::size_t j = 0; j < count; ++j ) {
auto shape { ShapePoints[ j ] };
auto outside { reach( j ) };
auto wall { glm::vec3{ 0.f } };
if( outside > extent ) {
wall = outerwall;
for( auto const neighbour : { j - 1, j + 1 } ) {
if( neighbour >= count || reach( neighbour ) > extent ) {
continue;
auto const side { clamped( j ) };
if( side != 0 ) {
auto const limit { side > 0 ? extent : innerextent };
auto const open = [&]( std::size_t const Index ) {
return Index < count && ( side > 0 ? reach( Index ) <= extent : reach( Index ) >= innerextent ); };
auto first { j };
auto last { j };
if( Walls ) {
while( first > 0 && twin( first - 1, j ) ) {
--first;
}
while( last + 1 < count && twin( last + 1, j ) ) {
++last;
}
auto const &inner { ShapePoints[ neighbour ] };
auto const t { static_cast<float>( ( extent - reach( neighbour ) ) / std::max( outside - reach( neighbour ), 1e-6 ) ) };
shape.position.y = glm::mix( inner.position.y, shape.position.y, t );
shape.texture.x = glm::mix( inner.texture.x, shape.texture.x, t );
shape.normal = inner.normal;
wall = glm::vec3{ 0.f };
break;
}
outside = extent;
}
else if( outside < innerextent ) {
wall = innerwall;
for( auto const neighbour : { j - 1, j + 1 } ) {
if( neighbour >= count || reach( neighbour ) < innerextent ) {
auto const paired { last > first && first > 0 && last + 1 < count };
auto const before { paired ? first - 1 : j - 1 };
auto const after { paired ? last + 1 : j + 1 };
auto const partner { j == last ? after : before };
auto moved { count };
for( auto const neighbour : { paired ? partner : before, paired ? ( partner == before ? after : before ) : after } ) {
if( false == open( neighbour ) ) {
continue;
}
auto const &other { ShapePoints[ neighbour ] };
auto const t { static_cast<float>( ( innerextent - reach( neighbour ) ) / std::min( outside - reach( neighbour ), -1e-6 ) ) };
auto const t { static_cast<float>( ( limit - reach( neighbour ) ) / ( side > 0 ? std::max( outside - reach( neighbour ), 1e-6 ) : std::min( outside - reach( neighbour ), -1e-6 ) ) ) };
shape.position.y = glm::mix( other.position.y, shape.position.y, t );
shape.texture.x = glm::mix( other.texture.x, shape.texture.x, t );
shape.normal = other.normal;
wall = glm::vec3{ 0.f };
auto const blend { glm::mix( other.normal, shape.normal, t ) };
shape.normal = ( glm::length2( blend ) > 1e-6f ? blend : other.normal );
moved = neighbour;
break;
}
outside = innerextent;
outside = limit;
auto const onwall { paired ? ( false == open( partner ) && clamped( partner ) == side ) : moved == count };
if( onwall ) {
wall = ( side > 0 ? outerwall : innerwall );
}
}
auto pt { parallel * static_cast<float>( Side * ( Halfwidth + outside + offset ) ) + position };
pt.y += shape.position.y + Lift;
@@ -704,6 +716,9 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
norm.y += shape.normal.y;
if( glm::length2( wall ) > 0.f ) {
norm = wall;
if( Wallu >= 0.f ) {
shape.texture.x = Wallu;
}
}
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 );

View File

@@ -132,7 +132,7 @@ public:
RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, bool const Transition, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, std::pair<float, float> fOffsetX = {0.f, 0.f}, glm::vec3 **p = nullptr, bool bRender = true );
// the profile lofted from distance From to To, moved outwards by Offsets and cut on its outer side to Extents beyond the gauge line, both as (distance, value) knots
bool
RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents = {}, float const Lift = 0.f, std::vector<glm::dvec2> const &Inner = {}, gfx::userdata_array *Userdata = nullptr, std::vector<glm::dvec2> const &Ramp = {}, double const Rampwidth = 0.0, bool const Walls = false ) const;
RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents = {}, float const Lift = 0.f, std::vector<glm::dvec2> const &Inner = {}, gfx::userdata_array *Userdata = nullptr, std::vector<glm::dvec2> const &Ramp = {}, double const Rampwidth = 0.0, bool const Walls = false, float const Wallu = -1.f ) const;
// point of the path and the unit vector across it towards the right rail profile, at a distance from Point1
void
plan_frame( double const Distance, glm::dvec3 &Point, glm::dvec3 &Across ) const;

View File

@@ -2626,6 +2626,7 @@ TTrack::rail_dimensions() const {
result.high = std::max( result.high, static_cast<double>( point.x ) );
}
result.height = top - bottom;
result.crest = top;
result.head = 0.0;
for( std::size_t i = 0; i < count; ++i ) {
auto const &point { profile[ i ].position };
@@ -2633,6 +2634,7 @@ TTrack::rail_dimensions() const {
result.head = std::max( result.head, static_cast<double>( point.x ) );
}
}
result.tread = result.head;
std::vector<std::size_t> heads, lips;
for( std::size_t i = 0; i < count; ++i ) {
auto const &point { profile[ i ].position };
@@ -2663,6 +2665,18 @@ TTrack::rail_dimensions() const {
result.lip = std::max( result.lip, static_cast<double>( profile[ i ].position.x ) );
result.liptop = std::max( result.liptop, static_cast<double>( profile[ i ].position.y ) );
}
result.tread = -1e9;
for( auto const i : heads ) {
if( profile[ i ].position.y > top - 0.0005 ) {
result.tread = std::max( result.tread, static_cast<double>( profile[ i ].position.x ) );
}
}
result.ledge = 1e9;
for( auto const i : lips ) {
if( profile[ i ].position.y > result.liptop - 0.0005 ) {
result.ledge = std::min( result.ledge, static_cast<double>( profile[ i ].position.x ) );
}
}
result.rim = 1e9;
result.crown = -1e9;
auto const reach = [&]( double const X ) {

View File

@@ -330,6 +330,9 @@ public:
double rim { 0.0 };
double crown { 0.0 };
double liptop { 0.0 };
double crest { 0.0 };
double tread { 0.0735 };
double ledge { 0.0 };
};
rail_size rail_dimensions() const;
// a check rail beside a rail, how far its ends turn away from the running rail
@@ -382,11 +385,13 @@ public:
std::vector<glm::dvec2> inner, outer;
bool capfrom { false };
bool capto { false };
double lift { 0.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<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;
@@ -493,6 +498,7 @@ private:
float halfwidth { 0.f };
gfx::vertex_array checkshapes[ 2 ];
gfx::vertex_array plates[ 2 ];
float platetexture { 0.f };
};
rail_drawing make_rail_drawing( gfx::geometrybank_handle const &Bank );
// 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

View File

@@ -158,6 +158,7 @@ void forget() {
++stamp;
}
double wing_reach( double const Slope ) {
return 0.15 / Slope + 0.3;
@@ -925,8 +926,8 @@ TTrack::rail_band groove_band( double const At, double const Into, double const
auto const seam { 0.001 };
TTrack::rail_band band { At, Into, Facing };
band.whole = ( false == Through );
band.from = ( Through ? 0.0 : seam - std::max( Other.head, Other.crown ) );
band.to = ( Through ? ( Other.grooved ? -Other.lip : flangeway ) : -Other.rim - seam );
band.from = ( Through ? 0.0 : seam - Other.tread );
band.to = ( Through ? ( Other.grooved ? -Other.lip : flangeway ) : -Other.ledge - seam );
return band;
}
@@ -935,23 +936,30 @@ void vee_fill( std::vector<TTrack::rail_fill> &Fills, double const At, double co
auto const seam { 0.001 };
auto const sign { Outwards ? 1.0 : -1.0 };
auto const edge { Outwards ? std::max( Own.head, Own.crown ) : Own.rim };
auto const flat { Outwards ? Own.tread : Own.ledge };
auto const inside { Outwards == ( Facing > 0.0 ) };
auto const closest { inside ? -Other.rim : -std::max( Other.head, Other.crown ) };
auto const rounded { inside ? Other.ledge - Other.rim : std::max( Other.head, Other.crown ) - Other.tread };
auto const own { Outwards ? Own.high - edge : edge - Own.low };
auto const other { inside ? Other.rim - Other.low : Other.high - std::max( Other.head, Other.crown ) };
auto const gap { own + other / std::max( std::abs( Facing ), 0.1 ) };
auto const slant { std::max( std::abs( Facing ), 0.1 ) };
auto const gap { own + other / slant };
auto const meets = [&]( double const Width ) {
return At + ( closest + Facing * ( edge + sign * Width ) ) / Into; };
auto const apex { meets( 0.0 ) };
auto const end { meets( gap ) };
auto const reach { seam + rounded / slant };
TTrack::rail_fill fill;
fill.from = std::min( apex, end );
fill.to = std::max( apex, end );
fill.capfrom = ( end < apex );
fill.capto = ( end > apex );
std::vector<glm::dvec2> line { { apex, edge + sign * seam }, { end, edge + sign * ( gap + seam ) } };
if( Own.grooved ) {
fill.lift = std::min( Outwards ? Own.crest : Own.liptop, inside ? Other.liptop : Other.crest ) - Own.liptop;
}
std::vector<glm::dvec2> line { { apex, edge + sign * reach }, { end, edge + sign * ( gap + reach ) } };
std::sort( line.begin(), line.end(), []( glm::dvec2 const &A, glm::dvec2 const &B ) { return A.x < B.x; } );
std::vector<glm::dvec2> fixed { { fill.from, edge - sign * seam }, { fill.to, edge - sign * seam } };
std::vector<glm::dvec2> fixed { { fill.from, flat - sign * seam }, { fill.to, flat - sign * seam } };
fill.outer = ( Outwards ? line : fixed );
fill.inner = ( Outwards ? fixed : line );
Fills.push_back( fill );
@@ -996,9 +1004,15 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
if( size.grooved || theirs.grooved ) {
auto const through { size.grooved != theirs.grooved ? size.grooved : goes_through( crossing.owner ? *crossing.owner : *this, *crossing.track ) };
layout.bands[ crossing.path ][ crossing.side > 0 ].push_back( groove_band( crossing.at, crossing.into, crossing.facing, through, theirs ) );
if( through && size.grooved && theirs.grooved && crossing.angle <= middle_angle ) {
vee_fill( layout.fills[ crossing.path ][ crossing.side > 0 ], crossing.at, crossing.into, crossing.facing, size, theirs, true );
vee_fill( layout.fills[ crossing.path ][ crossing.side > 0 ], crossing.at, crossing.into, crossing.facing, size, theirs, false );
if( size.grooved && theirs.grooved && crossing.angle <= middle_angle ) {
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 ) );
if( through ) {
auto &fills { layout.fills[ crossing.path ][ crossing.side > 0 ] };
fills.insert( fills.end(), vee.begin(), vee.end() );
}
}
continue;
}
@@ -1138,9 +1152,12 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
auto &fills { layout.fills[ path ][ frog.side[ path ] > 0 ] };
if( size.grooved ) {
layout.bands[ path ][ frog.side[ path ] > 0 ].push_back( groove_band( frog.at[ path ], into, facing, path == 0, size ) );
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 ) );
if( path == 0 ) {
vee_fill( fills, frog.at[ path ], into, facing, size, size, true );
vee_fill( fills, frog.at[ path ], into, facing, size, size, false );
fills.insert( fills.end(), vee.begin(), vee.end() );
}
continue;
}
@@ -1191,6 +1208,17 @@ TTrack::rail_drawing TTrack::make_rail_drawing( gfx::geometrybank_handle const &
texture = outline[ i ].texture.s;
}
}
if( drawing.size.grooved ) {
auto sum { 0.f };
auto points { 0 };
for( std::size_t i = 0; i < outline.size() / 2; ++i ) {
if( outline[ i ].position.y > highest - 0.0005f && outline[ i ].position.x > -0.02f ) {
sum += outline[ i ].texture.s;
++points;
}
}
texture = sum / std::max( points, 1 );
}
auto const top { static_cast<float>( ( drawing.size.grooved ? drawing.size.liptop : highest ) - 0.0005 ) };
auto const bottom { static_cast<float>( -drawing.size.height ) };
gfx::vertex_array plate {
@@ -1200,6 +1228,7 @@ TTrack::rail_drawing TTrack::make_rail_drawing( gfx::geometrybank_handle const &
{{ -1.f, top, 0.f }, { 0.f, 1.f, 0.f }, { texture, 0.f }},
{{ -1.f, top, 0.f }, { -1.f, 0.f, 0.f }, { texture, 0.f }},
{{ -1.f, bottom, 0.f }, { -1.f, 0.f, 0.f }, { texture, 0.f }} };
drawing.platetexture = texture;
create_track_rail_profile( drawing.plates[ 0 ], drawing.plates[ 1 ], &plate );
for( auto &shape : drawing.plates ) {
shape.resize( plate.size() );
@@ -1219,10 +1248,10 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
auto const &plan { Layout.plans[ Path ][ Side > 0 ] };
auto const length { segment.GetLength() };
// 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 ) {
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, 0.f, Inner, Rolled ? nullptr : &userdata, Ramp, Ramp.empty() ? 0.0 : blend, Walls ) ) {
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 );
gfx::vertex_array triangles;
gfx::userdata_array triangleuserdata;
@@ -1441,7 +1470,7 @@ 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 ) {
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 );
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 ) );
}
}
auto const &bands { Layout.bands[ Path ][ Side > 0 ] };
@@ -1480,7 +1509,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 ) {
auto const lay = [&]( gfx::vertex_array const &Shape, std::vector<rail_cut> cuts, bool const Wheels, 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 };
@@ -1506,7 +1535,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
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 );
piece( a, b, pieceoffsets, pieceextents, capstart, capend, Shape, true, pieceinner, {}, false == Wheels, 0.f, Wallu );
}
else {
running( a, b, pieceoffsets, pieceextents, pieceinner, rolled, capstart, capend );
@@ -1543,7 +1572,15 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
point.position = glm::mix( Profile[ A ].position, Profile[ B ].position, t );
point.texture = glm::mix( Profile[ A ].texture, Profile[ B ].texture, t );
return point; };
std::vector<gfx::vertex_array> tops;
auto const flattened = [&]( std::size_t const Index ) {
auto point { Profile[ Index ] };
auto const &file { outline[ Side > 0 ? Index : count - 1 - Index ].position };
if( file.y > size.crest - 0.0005 || ( file.x < -0.02 && file.y > size.liptop - 0.0005 ) ) {
point.normal = { 0.f, 1.f, 0.f };
point.texture.x = Drawing.platetexture;
}
return point; };
std::vector<gfx::vertex_array> tops, flats;
gfx::vertex_array body;
std::vector<std::size_t> floors;
auto floortexture { 0.f };
@@ -1562,33 +1599,47 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
while( i + 1 < count && above( i + 1 ) ) {
++i;
}
gfx::vertex_array top;
gfx::vertex_array top, flat;
if( first > 0 ) {
if( height( first - 1 ) > -1e-4 ) {
top.push_back( Profile[ first - 1 ] );
flat.push_back( flattened( first - 1 ) );
}
else {
top.push_back( floorpoint( first - 1, first ) );
flat.push_back( top.back() );
body.push_back( top.back() );
floors.push_back( body.size() );
body.push_back( top.back() );
}
}
top.insert( top.end(), Profile.begin() + first, Profile.begin() + i + 1 );
for( auto k { first }; k <= i; ++k ) {
top.push_back( Profile[ k ] );
flat.push_back( flattened( k ) );
}
if( i + 1 < count ) {
if( height( i + 1 ) > -1e-4 ) {
top.push_back( Profile[ i + 1 ] );
flat.push_back( flattened( i + 1 ) );
}
else {
top.push_back( floorpoint( i, i + 1 ) );
flat.push_back( top.back() );
floors.push_back( body.size() );
body.push_back( top.back() );
body.push_back( top.back() );
}
}
top.insert( top.begin(), top.front() );
top.push_back( top.back() );
for( auto *strip : { &top, &flat } ) {
gfx::vertex_array doubled { strip->front(), strip->front() };
for( std::size_t k = 1; k + 1 < strip->size(); ++k ) {
doubled.insert( doubled.end(), 2, ( *strip )[ k ] );
}
doubled.insert( doubled.end(), 2, strip->back() );
*strip = doubled;
}
tops.push_back( top );
flats.push_back( flat );
}
if( floors.size() > 1 ) {
auto const across { body[ floors.back() ].position - body[ floors.front() ].position };
@@ -1611,13 +1662,77 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
}
}
lay( body, wholecuts, false );
for( auto const &top : tops ) {
auto const reach { reaches( top ) };
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 ] ) };
auto const head { reach.x + reach.y > 0.0 };
auto edge { head ? 1e9 : -1e9 };
for( auto const &point : tops[ k ] ) {
if( point.position.y > ( head ? size.crest : size.liptop ) - 0.0005 ) {
auto const across { static_cast<double>( Side * point.position.x - halfwidth ) };
edge = ( head ? std::min( edge, across ) : std::max( edge, across ) );
}
}
std::vector<rail_cut> cuts { plan.cuts };
for( auto const &band : bands ) {
cuts.push_back( band_cut( band, reach ) );
auto cut { band_cut( band, reach ) };
if( false == band.whole && std::abs( band.facing ) >= 1e-3 && std::abs( edge ) < 1.0 ) {
auto const blunt { edge + ( head ? nose_tip : -nose_tip ) };
if( head == ( band.facing / band.into > 0.0 ) ) {
cut.start = std::max( cut.start, band.at + ( ( band.into > 0.0 ? band.to : band.from ) + band.facing * blunt ) / band.into );
cut.startcap = true;
}
else {
cut.end = std::min( cut.end, band.at + ( ( band.into > 0.0 ? band.from : band.to ) + band.facing * blunt ) / band.into );
cut.endcap = true;
}
}
cuts.push_back( cut );
}
lay( top, cuts, false );
std::vector<glm::dvec2> spans { Layout.blocks[ Path ][ Side > 0 ] };
for( auto const &cut : cuts ) {
for( auto &span : spans ) {
if( cut.at > span.x && cut.at < span.y ) {
span = { std::min( span.x, cut.end ), std::max( span.y, cut.start ) };
}
}
}
std::sort( spans.begin(), spans.end(), byposition );
std::vector<glm::dvec2> merged;
for( auto const &span : spans ) {
if( false == merged.empty() && span.x <= merged.back().y ) {
merged.back().y = std::max( merged.back().y, span.y );
}
else {
merged.push_back( span );
}
}
if( merged.empty() ) {
lay( tops[ k ], cuts, false, wallu );
continue;
}
auto const seam = []( double const At, double const End, double const Start ) {
rail_cut cut;
cut.at = At;
cut.end = End;
cut.start = Start;
cut.endcap = false;
cut.startcap = false;
return cut; };
std::vector<rail_cut> outside, inside;
for( auto const &cut : cuts ) {
auto const within { std::any_of( merged.begin(), merged.end(), [&]( glm::dvec2 const &Span ) { return cut.at > Span.x && cut.at < Span.y; } ) };
( within ? inside : outside ).push_back( cut );
}
inside.push_back( seam( merged.front().x - 0.001, -1e9, merged.front().x ) );
for( std::size_t m = 0; m < merged.size(); ++m ) {
outside.push_back( seam( 0.5 * ( merged[ m ].x + merged[ m ].y ), merged[ m ].x, merged[ m ].y ) );
inside.push_back( m + 1 < merged.size() ?
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 );
}
return true;
}