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

Merge remote-tracking branch 'origin/feat/switch-blades' into feat/renderers-cleanup

This commit is contained in:
2026-10-29 23:55:04 +01:00
145 changed files with 36946 additions and 3791 deletions

View File

@@ -96,6 +96,28 @@ bool cut_rails( TTrack const &Track ) {
Track.eType == tt_Switch && Track.SwitchExtension && Track.SwitchExtension->m_frogs && Track.SwitchExtension->Segments[ 0 ] && Track.SwitchExtension->Segments[ 1 ] );
}
// a track with rails laid, cut or whole
bool railed( TTrack const &Track ) {
return ( Track.iCategoryFlag & 15 ) == 1
&& Track.m_material1 != null_handle
&& false == Track.m_editorremoved
&& ( Track.eType == tt_Normal ? Track.Segment != nullptr :
Track.eType == tt_Switch && Track.SwitchExtension && Track.SwitchExtension->Segments[ 0 ] && Track.SwitchExtension->Segments[ 1 ] );
}
bool shaped_blades( TTrack const &Track ) {
if( cut_rails( Track ) ) {
return true;
}
return track_features::blades
&& ( Track.iCategoryFlag & 15 ) == 1
&& Track.m_material1 != null_handle
&& false == Track.m_editorremoved
&& Track.eType == tt_Switch && Track.SwitchExtension && Track.SwitchExtension->m_frogs && Track.SwitchExtension->Segments[ 0 ] && Track.SwitchExtension->Segments[ 1 ];
}
int blade_steps( TTrack const &Track, int const Path ) {
return static_cast<int>( std::ceil( Track.SwitchExtension->Segments[ Path ]->RaSegCount() * 0.65 ) );
@@ -131,7 +153,7 @@ TTrack const *runon_track( TTrack const &Switch, int const Path ) {
double blade_runon( TTrack const &Track, int const Side ) {
auto const *owner { Track.trPrev };
if( Track.eType != tt_Normal || Track.Segment == nullptr || owner == nullptr || owner->eType != tt_Switch || false == cut_rails( *owner ) ) {
if( Track.eType != tt_Normal || Track.Segment == nullptr || owner == nullptr || owner->eType != tt_Switch || false == shaped_blades( *owner ) ) {
return 0.0;
}
for( int path = 0; path < 2; ++path ) {
@@ -239,7 +261,8 @@ struct rail_run {
double length { 0.0 };
};
std::vector<TTrack *> nearby_rails( TTrack &Track ) {
// the tracks with rails around the track: the ones whose rails are cut, or with Whole all of them
std::vector<TTrack *> nearby_rails( TTrack &Track, bool const Whole = false ) {
std::vector<TTrack *> others;
auto const sections { simulation::Region->sections( Track.location(), static_cast<float>( Track.radius() + 50.0 ) ) };
@@ -249,7 +272,7 @@ std::vector<TTrack *> nearby_rails( TTrack &Track ) {
continue;
}
for( auto *other : cell.m_directories.paths ) {
if( other == nullptr || other == &Track || false == cut_rails( *other ) ) {
if( other == nullptr || other == &Track || false == ( Whole ? railed( *other ) : cut_rails( *other ) ) ) {
continue;
}
if( glm::length( other->location() - Track.location() ) > other->radius() + Track.radius() + 1.0 ) {
@@ -414,11 +437,12 @@ std::vector<rail_crossing> own_crossings( TTrack &Track ) {
// with the blades of the other parts left out of the account, as they're yet to be made as short; at the latest short of their throats,
// the check rails then cut to begin past the heels
// crossings of the rails of the track with the rails of the tracks around, with the blades of switches left out of the account
std::vector<rail_crossing> unbladed_crossings( TTrack const &Track ) {
// Whole: the rails of the tracks around taken whole too, where they aren't cut
std::vector<rail_crossing> unbladed_crossings( TTrack const &Track, bool const Whole = false ) {
auto &track { const_cast<TTrack &>( Track ) };
std::vector<rail_crossing> found;
for( auto *other : nearby_rails( track ) ) {
for( auto *other : nearby_rails( track, Whole ) ) {
for( int otherpath = 0; otherpath < path_count( *other ); ++otherpath ) {
rail_run theirs { other, otherpath };
theirs.length = path_segment( *other, otherpath ).GetLength();
@@ -440,7 +464,8 @@ std::pair<double, double> slip_blade_reach( TTrack const &Track ) {
auto reach { 1e9 };
auto latest { 1e9 };
for( auto const &crossing : unbladed_crossings( Track ) ) {
// the crossings of the diamond end the blades whether its rails are cut there or laid whole
for( auto const &crossing : unbladed_crossings( Track, true ) ) {
if( crossing.angle <= middle_angle ) {
auto const slope { std::tan( crossing.angle ) };
reach = std::min( reach, crossing.at - 0.25 - 0.5 * check_length( slope ) - 0.3 );
@@ -831,7 +856,7 @@ track_rails::switch_blade const &TTrack::blade( int const Path ) const {
}
cached = switch_blade {};
auto &result { *cached };
if( eType != tt_Switch || false == cut_rails( *this ) || m_profiles.empty() ) {
if( eType != tt_Switch || false == shaped_blades( *this ) || m_profiles.empty() ) {
return result;
}
auto const side { blade_side( Path ) };

View File

@@ -121,6 +121,11 @@ bool TSegment::Init(glm::dvec3 &NewPoint1, glm::dvec3 NewCPointOut, glm::dvec3 N
else {
fLength = glm::length(Point1 - Point2);
}
if( false == std::isfinite( fLength ) ) {
ErrorLog( "Bad track: spline \"" + pOwner->name() + "\" with no finite length (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
fLength = 0.01;
}
if (fLength <= 0) {
ErrorLog( "Bad track: zero length spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
@@ -129,7 +134,7 @@ bool TSegment::Init(glm::dvec3 &NewPoint1, glm::dvec3 NewCPointOut, glm::dvec3 N
fStoop = std::atan2(Point2.y - Point1.y, fLength); // pochylenie toru prostego, żeby nie liczyć wielokrotnie
fStep = fNewStep;
fStep = ( fNewStep > 0.0 && std::isfinite( fNewStep ) ) ? fNewStep : fLength;
// NOTE: optionally replace this part with the commented version, after solving geometry issues with double switches
if( pOwner->eType == tt_Switch
&& fStep * (3.0 * Global.SplineFidelity) > fLength ) {
@@ -137,7 +142,7 @@ bool TSegment::Init(glm::dvec3 &NewPoint1, glm::dvec3 NewCPointOut, glm::dvec3 N
fStep = fLength / ( 3.0 * Global.SplineFidelity );
}
// iSegCount = static_cast<int>( std::ceil( fLength / fStep ) ); // potrzebne do VBO
iSegCount = pOwner->eType == tt_Switch ? static_cast<int>(6 * Global.SplineFidelity) : static_cast<int>(std::ceil(fLength / fStep)); // potrzebne do VBO
iSegCount = pOwner->eType == tt_Switch ? static_cast<int>(6 * Global.SplineFidelity) : static_cast<int>(std::clamp(std::ceil(fLength / fStep), 1.0, 100000.0)); // potrzebne do VBO
fStep = fLength / iSegCount; // update step to equalize size of individual pieces
@@ -573,6 +578,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
pt,
glm::normalize( norm ),
glm::vec2 { ( jmm1 * ShapePoints[ j ].texture.x + m1 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv1 } );
roll_vertex( Output.back(), pos1, parallel1, s - step );
}
// jeśli jest wskaźnik do tablicy
if (p && *p && !j) // to dla pierwszego punktu
@@ -591,6 +597,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
pt,
glm::normalize( norm ),
glm::vec2 { ( jmm2 * ShapePoints[ j ].texture.x + m2 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv2 } );
roll_vertex( Output.back(), pos2, parallel2, s );
}
// jeśli jest wskaźnik do tablicy
// to dla pierwszego punktu
@@ -613,6 +620,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
pt,
glm::normalize( norm ),
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv1 } );
roll_vertex( Output.back(), pos1, parallel1, s - step );
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m2 * ( ShapePoints[ j ].position.x - fOffsetX.second ) ) + pos2;
pt.y += ShapePoints[ j ].position.y;
@@ -623,6 +631,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
pt,
glm::normalize( norm ),
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv2 } );
roll_vertex( Output.back(), pos2, parallel2, s );
}
}
}
@@ -633,6 +642,25 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
return true;
};
void TSegment::roll_vertex( gfx::basic_vertex &Vertex, glm::vec3 const &Position, glm::vec3 const &Parallel, double const Distance ) const {
if( false == m_baked_roll.has_value() ) {
return;
}
auto const delta { GetRoll( std::clamp( Distance, 0.0, fLength ) ) - *m_baked_roll };
if( std::abs( delta ) < 1e-6f ) {
return;
}
auto const c { std::cos( delta ) };
auto const s { std::sin( delta ) };
// across the path and up turned the way the profiles are turned by the roll, along the path as it was
auto const turn = [&]( glm::vec3 const &Vector ) {
auto const across { glm::dot( Vector, Parallel ) };
auto const along { Vector - Parallel * across - glm::vec3 { 0.f, Vector.y, 0.f } };
return along + Parallel * ( across * c + Vector.y * s ) + glm::vec3 { 0.f, -across * s + Vector.y * c, 0.f }; };
Vertex.position = Position + turn( Vertex.position - Position );
Vertex.normal = turn( Vertex.normal );
}
bool TSegment::RenderLoftSteps( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, double fTextureLength, int const Steps ) const
{
if( fTsBuffer.empty() || Steps < 1 || fLength <= 0.0 )
@@ -670,7 +698,8 @@ bool TSegment::RenderLoftSteps( gfx::vertex_array &Output, glm::dvec3 const &Ori
point.y += rest * from.position.y + Share * to.position.y;
auto normal { ( rest * from.normal.x + Share * to.normal.x ) * Parallel };
normal.y += rest * from.normal.y + Share * to.normal.y;
Output.emplace_back( point, glm::normalize( normal ), glm::vec2 { rest * from.texture.x + Share * to.texture.x, V } ); };
Output.emplace_back( point, glm::normalize( normal ), glm::vec2 { rest * from.texture.x + Share * to.texture.x, V } );
roll_vertex( Output.back(), Position, Parallel, Share * fLength ); };
glm::vec3 pos1, pos2, parallel1, parallel2;
station( 0, pos1, parallel1 );
@@ -894,6 +923,11 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
auto const onwall { paired ? ( false == open( partner ) && clamped( partner ) == side ) : moved == count };
if( onwall ) {
wall = ( side > 0 ? outerwall : innerwall );
// a face of the profile squeezed onto the wall from the other side of the rail keeps facing that side: the thin plate a
// planed blade gets to near its tip is lit from the middle of the track as its face there, not as the one against the stock rail
if( static_cast<float>( Span.side * side ) * ShapePoints[ j ].normal.x < 0.f ) {
wall = -wall;
}
}
}
if( drop > 0.0 && shape.position.y > Span.head.x && Span.head.y > Span.head.x ) {
@@ -912,6 +946,9 @@ 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( Span.rampwidth != 0.0 ? static_cast<float>( std::clamp( ( Span.rampwidth > 0.0 ? outside - worn : worn - outside ) / std::abs( Span.rampwidth ) + 1.0, 0.0, 1.0 ) ) : 1.f );
}
for( auto &vertex : Ring ) {
roll_vertex( vertex, position, parallel, Distance );
}
};
auto const texturelength { static_cast<float>( Span.texturelength ) };

View File

@@ -102,6 +102,9 @@ class TSegment
std::vector<double> m_starts; // distance from Point1 to the start of each part
std::optional<std::pair<glm::dvec3, glm::dvec3>> m_joints[ 2 ];
double m_dense { 0.0 };
// a switch makes the profiles of its rails and its bed at the mean of the rolls of its paths: lofted along a path, they're turned
// from it to the roll of the path there, so a switch on a cant ramp follows it; none for the other paths
std::optional<float> m_baked_roll;
glm::dvec3 GetFirstDerivative(double const fTime) const;
double RombergIntegral(double const fA, double const fB) const;
@@ -111,6 +114,8 @@ class TSegment
glm::dvec3 RaInterpolate(double const t) const;
glm::dvec3 RaInterpolate0(double const t) const;
std::size_t piece_at(double const s) const;
// the vertex of a profile lofted at the point and across the path there, turned from the baked roll to the one of the path at the distance
void roll_vertex( gfx::basic_vertex &Vertex, glm::vec3 const &Position, glm::vec3 const &Parallel, double const Distance ) const;
public:
bool bCurve = false;
@@ -200,6 +205,9 @@ public:
void
set_dense( double const Distance ) {
m_dense = Distance; }
void
set_baked_roll( std::optional<float> const Roll ) {
m_baked_roll = Roll; }
/*
void
Render();

View File

@@ -60,8 +60,10 @@ void init_switch_piece( TSegment &Segment, segment_data const &Piece, bool const
p1.y += 0.18;
p2.y += 0.18;
}
if( glm::length2( ( p1 + p1 + p2 ) / 3.0 - p1 - cp1 ) < sq( 0.02 )
|| glm::length2( ( p1 + p2 + p2 ) / 3.0 - p2 - cp2 ) < sq( 0.02 ) ) {
// one with its radius given is a curve however gently it bends, as a short path bent with the curve it's laid into
if( Piece.radius == 0.f
&& ( glm::length2( ( p1 + p1 + p2 ) / 3.0 - p1 - cp1 ) < sq( 0.02 )
|| glm::length2( ( p1 + p2 + p2 ) / 3.0 - p2 - cp2 ) < sq( 0.02 ) ) ) {
cp1 = cp2 = glm::dvec3( 0, 0, 0 );
}
auto const straight { cp1 == glm::dvec3() && cp2 == glm::dvec3() };
@@ -426,6 +428,7 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
std::string str;
size_t i; //,state; //Ra: teraz już nie ma początkowego stanu zwrotnicy we wpisie
std::string token;
bool tributary{false};
parser->getTokens();
*parser >> str; // typ toru
@@ -467,11 +470,17 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
}
else if (str == "tributary")
{
eType = tt_Tributary;
ErrorLog("Bad track: type \"tributary\" of track \"" + m_name + "\" in file \"" + parser->Name() + "\" (line " + std::to_string(parser->Line()) + ") isn't supported, loaded as a river with its first path");
eType = tt_Normal;
iCategoryFlag = 4;
tributary = true;
}
else
eType = tt_Unknown;
{
ErrorLog("Bad track: unknown type \"" + str + "\" of track \"" + m_name + "\" in file \"" + parser->Name() + "\" (line " + std::to_string(parser->Line()) + "), loaded as a normal track");
eType = tt_Normal;
iCategoryFlag = 1;
}
if (Global.iWriteLogEnabled & 4)
WriteLog(str);
parser->getTokens(4);
@@ -544,6 +553,10 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
// all subtypes contain at least one path
m_paths.emplace_back();
m_paths.back().deserialize( *parser, pOrigin );
if( tributary ) {
segment_data ignored;
ignored.deserialize( *parser, pOrigin );
}
switch( eType ) {
case tt_Switch:
case tt_Cross:
@@ -842,9 +855,12 @@ void TTrack::init_segments( bool const Initial ) {
}
// length2 is better than length for comparing because it does not require sqrt function
if( glm::length2((p1 + p1 + p2) / 3.0 - p1 - cp1) < sq(0.02)
|| glm::length2((p1 + p2 + p2) / 3.0 - p2 + cp1) < sq(0.02) ) {
// "prostowanie" prostych z kontrolnymi, dokładność 2cm
// "prostowanie" prostych z kontrolnymi, dokładność 2cm: a straight only where both control vectors lie along it, or aren't
// given; one of them moved off it, the other one left where a straight has it, makes a curve. One with its radius given is a
// curve however gently it bends, as a short path bent with the curve it's laid into
auto const startalong { cp1 == glm::dvec3() || glm::length2( ( p1 + p1 + p2 ) / 3.0 - p1 - cp1 ) < sq( 0.02 ) };
auto const endalong { cp2 == glm::dvec3() || glm::length2( ( p1 + p2 + p2 ) / 3.0 - p2 - cp2 ) < sq( 0.02 ) };
if( startalong && endalong && fRadius == 0 ) {
cp1 = cp2 = glm::dvec3(0, 0, 0);
}
@@ -936,8 +952,8 @@ void TTrack::init_segments( bool const Initial ) {
// na przechyłce doliczyć jeszcze pół przechyłki?
}
if( eType != tt_Cross ) {
// dla skrzyżowań muszą być podane kontrolne
if( eType != tt_Cross && fRadiusTable[ 0 ] == 0 ) {
// dla skrzyżowań muszą być podane kontrolne; a path with its radius given is a curve however gently it bends
// length2 is better than length for comparing because it does not require sqrt function
if( glm::length2(( p1 + p1 + p2 ) / 3.0 - p1 - cp1 ) < sq(0.02)
|| glm::length2(( p1 + p2 + p2 ) / 3.0 - p2 + cp1 ) < sq(0.02) ) {
@@ -1001,8 +1017,8 @@ void TTrack::init_segments( bool const Initial ) {
// na przechyłce doliczyć jeszcze pół przechyłki?
}
if( eType != tt_Cross ) {
// dla skrzyżowań muszą być podane kontrolne
if( eType != tt_Cross && fRadiusTable[ 1 ] == 0 ) {
// dla skrzyżowań muszą być podane kontrolne; a path with its radius given is a curve however gently it bends
// length2 is better than length for comparing because it does not require sqrt function
if( glm::length2((p3 + p3 + p4) / 3.0 - p3 - cp3) < sq(0.02)
|| glm::length2((p3 + p4 + p4) / 3.0 - p4 + cp3) < sq(0.02) ) {
@@ -1092,6 +1108,13 @@ void TTrack::init_segments( bool const Initial ) {
fTexHeightOffset = texheightoffset;
SwitchExtension->Segments[ index ]->InitChain( std::move( chain ) );
}
// the profiles of the switch are made at the mean of the rolls of its paths; lofted, they're turned from it to the roll of
// each path along it, so a switch on a cant ramp follows the ramp to its ends
float mean1, mean2;
profile_rolls( mean1, mean2 );
for( std::size_t index = 0; index < 2; ++index ) {
SwitchExtension->Segments[ index ]->set_baked_roll( mean1 );
}
}
if( Initial ) {
@@ -1488,12 +1511,27 @@ glm::vec3 TTrack::get_nearest_point(const glm::dvec3 &point) const
void TTrack::rebuild_geometry( gfx::geometrybank_handle const &Bank ) {
track_rails::forget();
auto const erase = [ this ]( gfx::geometry_handle const &Chunk ) {
if( SwitchExtension
&& std::any_of( SwitchExtension->m_nosechunks.begin(), SwitchExtension->m_nosechunks.end(), [ & ]( auto const &Pooled ) { return Pooled.handle == Chunk; } ) ) {
return;
}
GfxRenderer->Erase( Chunk );
};
for( auto const *geometry : { &Geometry1, &Geometry2, &GeometryCut1, &GeometryCut2 } ) {
std::for_each( geometry->begin(), geometry->end(), erase );
}
for( auto const &tread : GeometryTreads ) {
std::for_each( tread.geometry.begin(), tread.geometry.end(), erase );
std::for_each( tread.cuts.begin(), tread.cuts.end(), erase );
}
Geometry1.clear();
Geometry2.clear();
GeometryCut1.clear();
GeometryCut2.clear();
GeometryTreads.clear();
if( SwitchExtension ) {
erase( SwitchExtension->Geometry3 );
SwitchExtension->Geometry3 = gfx::geometry_handle{};
}
m_sleeper_local_transforms.clear();
@@ -1640,7 +1678,13 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
continue;
}
vertices.clear(); // reuse the scratchpad
Segment->RenderLoft( vertices, m_origin, profile, iTrapezoid > 0, texturelength );
// the rail starts behind the heel of a blade running on into the track from the switch before it
if( auto const runon { eType == tt_Normal && iTrapezoid == 0 ? track_rails::blade_runon( *this, side ) : 0.0 }; runon > 0.0 ) {
Segment->RenderLoftSpan( vertices, m_origin, profile, { .side = side, .halfwidth = 0.5f * std::abs( fTrackWidth ), .texturelength = texturelength, .from = runon, .to = Segment->GetLength() } );
}
else {
Segment->RenderLoft( vertices, m_origin, profile, iTrapezoid > 0, texturelength );
}
if( open ) {
cut_faces( vertices, nnumPts, trPrev == nullptr, trNext == nullptr, GeometryCut1 );
}
@@ -2014,6 +2058,59 @@ bool TTrack::SetConnections(int i)
return false;
}
void TTrack::join_switches()
{
if( eType != tt_Switch || SwitchExtension == nullptr || simulation::Region == nullptr ) {
return;
}
// the ends of the paths as the codes of the track joined to them have them: 0 the start of a switch, where both paths begin, 1 the end of
// its first path, 3 the end of its second one; a vehicle going on onto a start keeps its direction, onto an end it turns
auto const code = []( int const Endpoint ) { return Endpoint == 3 ? 1 : Endpoint == 5 ? 3 : 0; };
// the paths of the switch joined to its neighbours as it's set, as Switch() takes them
auto const bind = []( TTrack &Switch ) {
auto const &extension { *Switch.SwitchExtension };
auto const state { extension.CurrentIndex & 1 };
Switch.trNext = extension.pNexts[ state ];
Switch.trPrev = extension.pPrevs[ state ];
Switch.iNextDirection = extension.iNextDirection[ state ];
Switch.iPrevDirection = extension.iPrevDirection[ state ];
};
auto &own { *SwitchExtension };
for( int path = 0; path < 2; ++path ) {
for( int end = 0; end < 2; ++end ) {
if( ( end ? own.pNexts[ path ] : own.pPrevs[ path ] ) != nullptr || own.Segments[ path ] == nullptr ) {
continue;
}
auto const point { end ? own.Segments[ path ]->FastGetPoint_1() : own.Segments[ path ]->FastGetPoint_0() };
auto const [ other, endpoint ] { simulation::Region->find_path( point, this ) };
if( other == nullptr || other == this || other->eType != tt_Switch || other->SwitchExtension == nullptr || endpoint < 2 || endpoint > 5 ) {
continue;
}
auto &theirs { *other->SwitchExtension };
if( end == 0 ) {
own.pPrevs[ 0 ] = own.pPrevs[ 1 ] = other;
own.iPrevDirection[ 0 ] = own.iPrevDirection[ 1 ] = code( endpoint );
}
else {
own.pNexts[ path ] = other;
own.iNextDirection[ path ] = code( endpoint );
}
auto const ours { end == 0 ? 0 : path ? 3 : 1 };
if( endpoint == 2 || endpoint == 4 ) {
theirs.pPrevs[ 0 ] = theirs.pPrevs[ 1 ] = this;
theirs.iPrevDirection[ 0 ] = theirs.iPrevDirection[ 1 ] = ours;
}
else {
auto const theirpath { endpoint == 5 ? 1 : 0 };
theirs.pNexts[ theirpath ] = this;
theirs.iNextDirection[ theirpath ] = ours;
}
bind( *other );
}
}
bind( *this );
}
bool TTrack::Switch(int i, float const t, float const d)
{ // przełączenie torów z uruchomieniem animacji
if (SwitchExtension) // tory przełączalne mają doklejkę
@@ -3954,6 +4051,13 @@ path_table::InitTracks() {
}
}
// the plain tracks are joined to the switches above, a switch right after another one only here, once they are
for( auto *track : m_items ) {
if( track->eType == tt_Switch ) {
track->join_switches();
}
}
if( Global.CreateSwitchTrackbeds ) {
// do this after all connections are established, otherwise missing switch connections
// may prevent us from obtaining texture data for basic track from 'across' a switch

View File

@@ -324,6 +324,9 @@ public:
material_handle rail_material( int const Path ) const;
glm::vec2 tread_band() const;
bool SetConnections(int i);
// joins the free ends of the switch to the free ends of other switches in the same points, which the plain tracks joined to switches
// leave out: a switch right after another one, at the end of either path of it, two facing at their starts or meeting at their ends
void join_switches();
bool Switch(int i, float const t = -1.f, float const d = -1.f);
bool SwitchForced(int i, TDynamicObject *o);
int CrossSegment(int from, int into);

View File

@@ -17,5 +17,8 @@ namespace track_features {
bool constexpr crossings { true };
// the 3D sleepers and switch timbers of sleepers3d; off, the tracks keep their ballast whatever eu07.ini sets
bool constexpr sleepers { true };
// the blades of the switches with the key frogs shaped as their templates have them, from the tip their planed nose ends at to the heel,
// with the rails left whole where the crossings are off; off with the crossings too, they're laid from the start of the switch as before
bool constexpr blades { true };
} // namespace track_features

View File

@@ -33,6 +33,9 @@ double check_length( double const Slope );
double movable_nose_length( double const Slope );
// the rails of the track are cut where they cross other rails: plain tracks, and switches whose frog is made from the rails
bool cut_rails( TTrack const &Track );
// the blades of the switch are shaped from the tip to the heel: with the rails cut, or alone where only the blades are on, but for the
// switches of a double slip, whose blades end short of crossings found only with the rails cut
bool shaped_blades( TTrack const &Track );
int blade_steps( TTrack const &Track, int const Path );
double blade_length( TTrack const &Track, int const Path );
// the plain track a path of a switch goes on into at its end, laid the same way, which the blade of the path may run on into