mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-03 16:40:21 +01:00
Merge remote-tracking branch 'origin/feat/track-uneven' into feat/renderers-cleanup
This commit is contained in:
@@ -135,6 +135,10 @@ 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 > 0.0 && std::isfinite( fNewStep ) ) ? fNewStep : fLength;
|
||||
if( m_uneven && pOwner->eType != tt_Switch ) {
|
||||
// dense enough for the waves; a switch keeps its division, the blades depend on it
|
||||
fStep = std::min( fStep, 2.0 );
|
||||
}
|
||||
// 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 ) {
|
||||
@@ -426,7 +430,54 @@ Math3D::vector3 TSegment::GetPoint(double const fDistance) const
|
||||
*/
|
||||
|
||||
void TSegment::RaPositionGet(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const {
|
||||
design_position( fDistance, position, rotation );
|
||||
if( false == m_uneven ) {
|
||||
return;
|
||||
}
|
||||
// direction and slope stay as designed: the physics takes the curve radius and the grade from them
|
||||
auto const gauge { std::max( std::abs( pOwner->fTrackWidth ), 0.1f ) };
|
||||
glm::dvec3 const across { -std::cos( rotation.z ), 0.0, -std::sin( rotation.z ) };
|
||||
auto const amount { pOwner->uneven_at( fDistance ) };
|
||||
rotation.x += static_cast<float>( track_uneven::stray_per_mm * (
|
||||
amount.up * track_uneven::roll( position, across, gauge )
|
||||
+ amount.cross * track_uneven::level( position, across ) / gauge ) );
|
||||
position += stray( position, fDistance );
|
||||
}
|
||||
|
||||
glm::dvec3 TSegment::stray( glm::dvec3 const &Point, double const Distance ) const {
|
||||
auto const amount { pOwner->uneven_at( Distance ) };
|
||||
auto const field { track_uneven::field( Point ) };
|
||||
glm::dvec3 result { field.x * amount.side, field.y * amount.up, field.z * amount.side };
|
||||
result *= track_uneven::stray_per_mm;
|
||||
result.y -= pOwner->joint_dip( Distance );
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::dvec3 TSegment::uneven_bend( double const Distance ) const {
|
||||
if( false == m_uneven ) {
|
||||
return glm::dvec3 { 0.0 };
|
||||
}
|
||||
// over about a bogie; FastGetTFromS extrapolates past the ends of the path
|
||||
auto const reach { 1.5 };
|
||||
auto const strayed = [&]( double const At ) {
|
||||
return stray( FastGetPoint( FastGetTFromS( At ) ), At ); };
|
||||
return ( strayed( Distance + reach ) - 2.0 * strayed( Distance ) + strayed( Distance - reach ) ) / ( reach * reach );
|
||||
}
|
||||
|
||||
void TSegment::settle_vertex( gfx::basic_vertex &Vertex, glm::dvec3 const &Origin, glm::vec3 const &Position, glm::vec3 const &Parallel, double const Distance ) const {
|
||||
if( false == m_uneven ) {
|
||||
return;
|
||||
}
|
||||
// the rails settling apart turn about the middle of the track, what's outside them follows the rail
|
||||
auto const gauge { std::max( std::abs( pOwner->fTrackWidth ), 0.1f ) };
|
||||
auto const across { std::clamp( glm::dot( Vertex.position - Position, Parallel ), -0.5f * gauge, 0.5f * gauge ) };
|
||||
Vertex.position.y -= static_cast<float>( track_uneven::stray_per_mm * pOwner->uneven_at( Distance ).cross * track_uneven::level( Origin + glm::dvec3 { Position }, glm::dvec3 { Parallel } ) * across / gauge );
|
||||
Vertex.position += glm::vec3 { stray( Origin + glm::dvec3 { Vertex.position }, Distance ) };
|
||||
}
|
||||
|
||||
void TSegment::design_position(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const {
|
||||
if( false == m_pieces.empty() ) {
|
||||
// the pieces keep to their design, the unevenness is the one of the whole path
|
||||
auto const piece { piece_at( fDistance ) };
|
||||
m_pieces[ piece ]->RaPositionGet( fDistance - m_starts[ piece ], position, rotation );
|
||||
}
|
||||
@@ -579,6 +630,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
|
||||
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 );
|
||||
settle_vertex( Output.back(), Origin, pos1, parallel1, s - step );
|
||||
}
|
||||
// jeśli jest wskaźnik do tablicy
|
||||
if (p && *p && !j) // to dla pierwszego punktu
|
||||
@@ -598,6 +650,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
|
||||
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 );
|
||||
settle_vertex( Output.back(), Origin, pos2, parallel2, s );
|
||||
}
|
||||
// jeśli jest wskaźnik do tablicy
|
||||
// to dla pierwszego punktu
|
||||
@@ -621,6 +674,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
|
||||
glm::normalize( norm ),
|
||||
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv1 } );
|
||||
roll_vertex( Output.back(), pos1, parallel1, s - step );
|
||||
settle_vertex( Output.back(), Origin, 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;
|
||||
@@ -632,6 +686,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
|
||||
glm::normalize( norm ),
|
||||
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv2 } );
|
||||
roll_vertex( Output.back(), pos2, parallel2, s );
|
||||
settle_vertex( Output.back(), Origin, pos2, parallel2, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -699,7 +754,8 @@ bool TSegment::RenderLoftSteps( gfx::vertex_array &Output, glm::dvec3 const &Ori
|
||||
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 } );
|
||||
roll_vertex( Output.back(), Position, Parallel, Share * fLength ); };
|
||||
roll_vertex( Output.back(), Position, Parallel, Share * fLength );
|
||||
settle_vertex( Output.back(), Origin, Position, Parallel, Share * fLength ); };
|
||||
|
||||
glm::vec3 pos1, pos2, parallel1, parallel2;
|
||||
station( 0, pos1, parallel1 );
|
||||
@@ -1044,6 +1100,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
|
||||
}
|
||||
for( auto &vertex : Ring ) {
|
||||
roll_vertex( vertex, position, parallel, Distance );
|
||||
settle_vertex( vertex, Origin, position, parallel, Distance );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ class TSegment
|
||||
glm::dvec3 Point2;
|
||||
float fRoll1 { 0.f };
|
||||
float fRoll2 { 0.f }; // przechyłka na końcach
|
||||
bool m_uneven { false }; // see TrackUneven.h
|
||||
double fLength { -1.0 }; // długość policzona
|
||||
std::vector<double> fTsBuffer; // wartości parametru krzywej t dla równych odcinków s
|
||||
std::vector<double> fTsSlope; // nachylenie dt/du (= dt/ds * fStep) w tych samych punktach
|
||||
@@ -131,6 +132,12 @@ class TSegment
|
||||
// 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;
|
||||
|
||||
// moves a lofted vertex with the unevenness of the track
|
||||
void settle_vertex( gfx::basic_vertex &Vertex, glm::dvec3 const &Origin, glm::vec3 const &Position, glm::vec3 const &Parallel, double const Distance ) const;
|
||||
// displacement of a point by the unevenness, at a distance along the path
|
||||
glm::dvec3 stray( glm::dvec3 const &Point, double const Distance ) const;
|
||||
void design_position( double const fDistance, glm::dvec3 &position, glm::vec3 &rotation ) const;
|
||||
|
||||
public:
|
||||
bool bCurve = false;
|
||||
|
||||
@@ -179,6 +186,13 @@ public:
|
||||
/// <param name="position">Calculated position</param>
|
||||
/// <param name="rotation">Calculated rotation</param>
|
||||
void RaPositionGet(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const;
|
||||
// call before Init, it sets the density of the geometry
|
||||
void
|
||||
set_uneven( bool const Uneven ) {
|
||||
m_uneven = Uneven; }
|
||||
// second derivative of the displacement along the path; times speed squared: acceleration of an axle
|
||||
glm::dvec3
|
||||
uneven_bend( double const Distance ) const;
|
||||
glm::dvec3 FastGetPoint(double const t) const;
|
||||
inline
|
||||
glm::dvec3
|
||||
|
||||
204
world/Track.cpp
204
world/Track.cpp
@@ -85,6 +85,7 @@ void init_switch_piece( TSegment &Segment, segment_data const &Piece, bool const
|
||||
|
||||
TTrack::profiles_array TTrack::m_profiles;
|
||||
TTrack::profiles_map TTrack::m_profilesmap;
|
||||
int TTrack::s_layoutstamp { 0 };
|
||||
|
||||
TSwitchExtension::TSwitchExtension(TTrack *owner, int const what)
|
||||
{ // na początku wszystko puste
|
||||
@@ -367,6 +368,7 @@ void TTrack::ConnectPrevPrev(TTrack *pTrack, int typ)
|
||||
{ //łączenie torów - Point1 własny do Point1 cudzego
|
||||
if (pTrack)
|
||||
{ //(pTrack) może być zwrotnicą, a (this) tylko zwykłym odcinkiem
|
||||
++s_layoutstamp;
|
||||
trPrev = pTrack;
|
||||
iPrevDirection = pTrack->eType == tt_Switch ? 0 : typ & 2;
|
||||
pTrack->trPrev = this;
|
||||
@@ -377,6 +379,7 @@ void TTrack::ConnectPrevNext(TTrack *pTrack, int typ)
|
||||
{ //łaczenie torów - Point1 własny do Point2 cudzego
|
||||
if (pTrack)
|
||||
{
|
||||
++s_layoutstamp;
|
||||
trPrev = pTrack;
|
||||
iPrevDirection = typ | 1; // 1:zwykły lub pierwszy zwrotnicy, 3:drugi zwrotnicy
|
||||
pTrack->trNext = this;
|
||||
@@ -396,6 +399,7 @@ void TTrack::ConnectNextPrev(TTrack *pTrack, int typ)
|
||||
{ //łaczenie torów - Point2 własny do Point1 cudzego
|
||||
if (pTrack)
|
||||
{
|
||||
++s_layoutstamp;
|
||||
trNext = pTrack;
|
||||
iNextDirection = pTrack->eType == tt_Switch ? 0 : typ & 2;
|
||||
pTrack->trPrev = this;
|
||||
@@ -415,6 +419,7 @@ void TTrack::ConnectNextNext(TTrack *pTrack, int typ)
|
||||
{ //łaczenie torów - Point2 własny do Point2 cudzego
|
||||
if (pTrack)
|
||||
{
|
||||
++s_layoutstamp;
|
||||
trNext = pTrack;
|
||||
iNextDirection = typ | 1; // 1:zwykły lub pierwszy zwrotnicy, 3:drugi zwrotnicy
|
||||
pTrack->trNext = this;
|
||||
@@ -731,6 +736,27 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
ErrorLog( "Bad track: " + str + " at end " + std::to_string( end ) + " of track \"" + m_name + "\" which has no such end" );
|
||||
}
|
||||
}
|
||||
else if( str == "uneven" ) {
|
||||
// uneven <mm>: see TrackUneven.h
|
||||
auto const uneven { parser->getToken<float>() };
|
||||
if( ( eType == tt_Normal || eType == tt_Switch ) && iCategoryFlag == 1 ) {
|
||||
m_uneven = std::clamp( uneven, 0.f, track_uneven::most );
|
||||
}
|
||||
}
|
||||
else if( str == "unevenup" || str == "unevenside" || str == "unevencross" ) {
|
||||
// the components of uneven set apart, mm
|
||||
auto const value { parser->getToken<float>() };
|
||||
if( ( eType == tt_Normal || eType == tt_Switch ) && iCategoryFlag == 1 ) {
|
||||
( str == "unevenup" ? m_unevenup : str == "unevenside" ? m_unevenside : m_unevencross ) = std::clamp( value, 0.f, track_uneven::most );
|
||||
}
|
||||
}
|
||||
else if( str == "jointdip" ) {
|
||||
// jointdip <mm>: depth of the dip at rail joints, with or without uneven
|
||||
auto const jointdip { parser->getToken<float>() };
|
||||
if( eType == tt_Normal && iCategoryFlag == 1 ) {
|
||||
m_jointdip = std::clamp( jointdip, 0.f, track_uneven::most );
|
||||
}
|
||||
}
|
||||
else if( str == "friction" ) {
|
||||
// memory cell holding friction value modifiers
|
||||
m_friction.first = parser->getToken<std::string>();
|
||||
@@ -807,6 +833,159 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
// to a stale origin and the renderer would draw sleepers in the wrong place).
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_declared() const {
|
||||
|
||||
return {
|
||||
m_unevenup >= 0.f ? m_unevenup : m_uneven,
|
||||
m_unevenside >= 0.f ? m_unevenside : m_uneven,
|
||||
eType != tt_Normal ? 0.f : m_unevencross >= 0.f ? m_unevencross : m_uneven };
|
||||
}
|
||||
|
||||
bool TTrack::is_uneven() const {
|
||||
|
||||
auto const declared { uneven_declared() };
|
||||
return declared.up > 0.0 || declared.side > 0.0 || declared.cross > 0.0 || joint_depth() > 0.0;
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_at( double const Distance ) const {
|
||||
|
||||
if( eType == tt_Switch ) {
|
||||
return switch_uneven();
|
||||
}
|
||||
auto const own { uneven_declared() };
|
||||
auto const atend = [&]( TTrack const *Other ) -> track_uneven::amounts {
|
||||
if( Other == nullptr ) {
|
||||
return {};
|
||||
}
|
||||
if( Other->eType == tt_Switch ) {
|
||||
return Other->switch_uneven();
|
||||
}
|
||||
auto const theirs { Other->uneven_declared() };
|
||||
return { std::min( own.up, theirs.up ), std::min( own.side, theirs.side ), std::min( own.cross, theirs.cross ) }; };
|
||||
auto const start { atend( trPrev ) };
|
||||
auto const end { atend( trNext ) };
|
||||
return {
|
||||
track_uneven::along( own.up, start.up, end.up, Distance, Length() ),
|
||||
track_uneven::along( own.side, start.side, end.side, Distance, Length() ),
|
||||
track_uneven::along( own.cross, start.cross, end.cross, Distance, Length() ) };
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::switch_uneven() const {
|
||||
|
||||
if( m_switchstray.stamp == s_layoutstamp ) {
|
||||
return m_switchstray.value;
|
||||
}
|
||||
std::vector<TTrack const *> group { this };
|
||||
auto least { uneven_declared() };
|
||||
for( std::size_t index = 0; index < group.size(); ++index ) {
|
||||
auto const &extension { *group[ index ]->SwitchExtension };
|
||||
for( auto const *other : { extension.pPrevs[ 0 ], extension.pPrevs[ 1 ], extension.pNexts[ 0 ], extension.pNexts[ 1 ] } ) {
|
||||
if( other == nullptr ) {
|
||||
continue;
|
||||
}
|
||||
auto const theirs { other->uneven_declared() };
|
||||
if( other->eType == tt_Switch && other->SwitchExtension ) {
|
||||
least.up = std::min( least.up, theirs.up );
|
||||
least.side = std::min( least.side, theirs.side );
|
||||
if( std::find( group.begin(), group.end(), other ) == group.end() ) {
|
||||
group.push_back( other );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// a track with none of a component keeps to its design in it, any other comes to the value of the switch
|
||||
if( theirs.up <= 0.0 ) { least.up = 0.0; }
|
||||
if( theirs.side <= 0.0 ) { least.side = 0.0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
for( auto const *track : group ) {
|
||||
track->m_switchstray = { least, s_layoutstamp };
|
||||
}
|
||||
return least;
|
||||
}
|
||||
|
||||
double TTrack::joint_depth() const {
|
||||
|
||||
if( eType != tt_Normal ) {
|
||||
return 0.0;
|
||||
}
|
||||
return ( m_jointdip >= 0.f ? m_jointdip : track_uneven::dip_share * uneven_declared().up );
|
||||
}
|
||||
|
||||
double TTrack::joint_dip( double const Distance ) const {
|
||||
|
||||
auto const phase { rail_phase( Distance ) };
|
||||
if( phase < 0.0 ) {
|
||||
return 0.0;
|
||||
}
|
||||
auto const fromjoint { std::min( phase, fSoundDistance - phase ) };
|
||||
if( fromjoint >= track_uneven::dip_reach ) {
|
||||
return 0.0;
|
||||
}
|
||||
auto const depth { joint_depth() };
|
||||
auto const atend = [&]( TTrack const *Other ) {
|
||||
return ( Other == nullptr || Other->fSoundDistance != fSoundDistance ? 0.0 : std::min( depth, Other->joint_depth() ) ); };
|
||||
return 0.001 * track_uneven::dip( fromjoint ) * track_uneven::along( depth, atend( trPrev ), atend( trNext ), Distance, Length() );
|
||||
}
|
||||
|
||||
double TTrack::rail_phase( double const Distance ) const {
|
||||
|
||||
if( joint_depth() <= 0.0 || fSoundDistance <= 2.0 * track_uneven::dip_reach ) {
|
||||
return -1.0;
|
||||
}
|
||||
if( m_railrun.stamp != s_layoutstamp ) {
|
||||
settle_rail_run();
|
||||
}
|
||||
return track_uneven::rail_phase( m_railrun.origin + m_railrun.way * Distance, fSoundDistance );
|
||||
}
|
||||
|
||||
void TTrack::settle_rail_run() const {
|
||||
|
||||
// the next track of the run past an end of a track, and which of its ends is joined there
|
||||
auto const onward = []( TTrack const &Track, int const End, int &Joined ) -> TTrack const * {
|
||||
auto const *other { End == 0 ? Track.trPrev : Track.trNext };
|
||||
if( other == nullptr || other == &Track || other->joint_depth() <= 0.0 || other->fSoundDistance != Track.fSoundDistance ) {
|
||||
return nullptr;
|
||||
}
|
||||
Joined = ( End == 0 ? Track.iPrevDirection : Track.iNextDirection ) & 1;
|
||||
return other; };
|
||||
// walks from this track to an end of the run
|
||||
auto const walk = [&]( int &End ) {
|
||||
auto const *track { this };
|
||||
int joined { 0 };
|
||||
for( int step = 0; step < 1000000; ++step ) {
|
||||
auto const *next { onward( *track, End, joined ) };
|
||||
if( next == nullptr || next == this ) {
|
||||
break;
|
||||
}
|
||||
track = next;
|
||||
End = 1 - joined;
|
||||
}
|
||||
return track; };
|
||||
auto const place = []( TTrack const &Track, int const End ) {
|
||||
auto const point { End == 0 ? Track.Segment->FastGetPoint_0() : Track.Segment->FastGetPoint_1() };
|
||||
return std::make_pair( point.x, point.z ); };
|
||||
|
||||
// start at the end with the lesser coordinates, to get the same joints from any track of the run
|
||||
int end { 0 };
|
||||
int otherend { 1 };
|
||||
auto const *track { walk( end ) };
|
||||
auto const *other { walk( otherend ) };
|
||||
if( place( *other, otherend ) < place( *track, end ) ) {
|
||||
track = other;
|
||||
end = otherend;
|
||||
}
|
||||
auto along { 0.0 };
|
||||
while( track != nullptr && track->m_railrun.stamp != s_layoutstamp ) {
|
||||
auto const length { track->Length() };
|
||||
track->m_railrun = { end == 0 ? along : along + length, end == 0 ? 1.0 : -1.0, s_layoutstamp };
|
||||
along += length;
|
||||
int joined { 0 };
|
||||
track = onward( *track, 1 - end, joined );
|
||||
end = joined;
|
||||
}
|
||||
}
|
||||
|
||||
void TTrack::update_location() {
|
||||
|
||||
location( (
|
||||
@@ -825,6 +1004,14 @@ void TTrack::init_segments( bool const Initial ) {
|
||||
fTexHeight1 -= fTexHeightOffset;
|
||||
fTexHeightOffset = 0.f;
|
||||
iTrapezoid = 0;
|
||||
++s_layoutstamp;
|
||||
if( eType == tt_Switch && SwitchExtension ) {
|
||||
SwitchExtension->Segments[ 0 ]->set_uneven( is_uneven() );
|
||||
SwitchExtension->Segments[ 1 ]->set_uneven( is_uneven() );
|
||||
}
|
||||
else if( Segment ) {
|
||||
Segment->set_uneven( is_uneven() );
|
||||
}
|
||||
|
||||
switch (eType) {
|
||||
// Ra: łuki segmentowane co 5m albo 314-kątem foremnym
|
||||
@@ -1510,6 +1697,8 @@ glm::vec3 TTrack::get_nearest_point(const glm::dvec3 &point) const
|
||||
// wypełnianie tablic VBO
|
||||
void TTrack::rebuild_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
|
||||
++s_layoutstamp;
|
||||
|
||||
track_rails::forget();
|
||||
auto const erase = [ this ]( gfx::geometry_handle const &Chunk ) {
|
||||
if( SwitchExtension
|
||||
@@ -2841,6 +3030,21 @@ TTrack::export_as_text_( std::ostream &Output ) const {
|
||||
&& false == m_profile1.first.empty() ) {
|
||||
Output << "railprofile " << m_profile1.first << ' ';
|
||||
}
|
||||
if( m_uneven > 0.f ) {
|
||||
Output << "uneven " << m_uneven << ' ';
|
||||
}
|
||||
if( m_unevenup >= 0.f ) {
|
||||
Output << "unevenup " << m_unevenup << ' ';
|
||||
}
|
||||
if( m_unevenside >= 0.f ) {
|
||||
Output << "unevenside " << m_unevenside << ' ';
|
||||
}
|
||||
if( m_unevencross >= 0.f ) {
|
||||
Output << "unevencross " << m_unevencross << ' ';
|
||||
}
|
||||
if( m_jointdip >= 0.f ) {
|
||||
Output << "jointdip " << m_jointdip << ' ';
|
||||
}
|
||||
if( false == m_friction.first.empty() ) {
|
||||
Output << "friction " << m_friction.first << ' ';
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "world/RailLayout.h"
|
||||
#include "world/TrackSleepers.h"
|
||||
#include "world/TrackBlend.h"
|
||||
#include "world/TrackUneven.h"
|
||||
#include "model/material.h"
|
||||
#include "scene/scenenode.h"
|
||||
#include "utilities/Names.h"
|
||||
@@ -286,6 +287,12 @@ public:
|
||||
TEnvironmentType eEnvironment = e_flat; // dźwięk i oświetlenie
|
||||
int iAction = 0; // czy modyfikowany eventami (specjalna obsługa przy skanowaniu)
|
||||
float fOverhead = -1.0; // można normalnie pobierać prąd (0 dla jazdy bezprądowej po danym odcinku, >0-z opuszczonym i ograniczeniem prędkości)
|
||||
float m_uneven { 0.f }; // mm, see TrackUneven.h; plain tracks and switches
|
||||
// the components set apart from m_uneven, mm; negative: m_uneven. Cross level on plain tracks only
|
||||
float m_unevenup { -1.f };
|
||||
float m_unevenside { -1.f };
|
||||
float m_unevencross { -1.f };
|
||||
float m_jointdip { -1.f }; // depth of the dip at rail joints, mm; negative: from m_uneven; plain tracks only
|
||||
private:
|
||||
double fVelocity = -1.0; // ograniczenie prędkości // prędkość dla AI (powyżej rośnie prawdopowobieństwo wykolejenia)
|
||||
public:
|
||||
@@ -406,6 +413,15 @@ public:
|
||||
void VelocitySet(float v);
|
||||
double VelocityGet();
|
||||
float Friction() const;
|
||||
// the unevenness the track declares
|
||||
track_uneven::amounts uneven_declared() const;
|
||||
bool is_uneven() const;
|
||||
// the unevenness at a distance along the track, meeting the tracks joined at its ends
|
||||
track_uneven::amounts uneven_at( double const Distance ) const;
|
||||
double joint_depth() const; // mm
|
||||
double joint_dip( double const Distance ) const; // m
|
||||
// distance from the rail joint behind; negative: the track has no joints that dip
|
||||
double rail_phase( double const Distance ) const;
|
||||
void ConnectionsLog();
|
||||
bool DoubleSlip() const;
|
||||
static void fetch_default_profiles();
|
||||
@@ -439,6 +455,22 @@ private:
|
||||
void profile_rolls( float &Roll1, float &Roll2 ) const;
|
||||
void share_joints();
|
||||
void create_track_bed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next );
|
||||
// rail joints run on across joined tracks, however the line is cut into them, so the clatter keeps its rhythm
|
||||
struct rail_run {
|
||||
double origin { 0.0 }; // position along the run of Point1
|
||||
double way { 1.0 }; // direction of the path along the run
|
||||
int stamp { -1 };
|
||||
};
|
||||
void settle_rail_run() const;
|
||||
mutable rail_run m_railrun;
|
||||
// one value along all paths of a switch: the lowest of the switches joined, 0 next to a track with none
|
||||
track_uneven::amounts switch_uneven() const;
|
||||
struct switch_stray {
|
||||
track_uneven::amounts value;
|
||||
int stamp { -1 };
|
||||
};
|
||||
mutable switch_stray m_switchstray;
|
||||
static int s_layoutstamp; // bumped when tracks are joined or changed, invalidates the two above
|
||||
void create_road_profile( gfx::vertex_array &Output, bool const Forcetransition = false );
|
||||
void create_road_side_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition = false );
|
||||
/// <summary>
|
||||
|
||||
129
world/TrackUneven.h
Normal file
129
world/TrackUneven.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
|
||||
// Track unevenness: a displacement field over the plan of the scenery, so everything at one place moves together: joined tracks, the
|
||||
// paths of a switch, sleepers, axles. Waves 5-42 m long, shares after the German track spectrum with the long waves cut down; up and down to 83 m.
|
||||
// The "uneven" value is the peak deviation of a rail on a 10 m chord, as in PLK Id-14 appendix 1; see test_track_uneven.cpp.
|
||||
namespace track_uneven {
|
||||
|
||||
inline constexpr float most { 100.f }; // largest value of the attributes, mm
|
||||
inline constexpr double stray_per_mm { 0.00145 }; // field amplitude per mm of "uneven", m
|
||||
inline constexpr double cross_share { 0.53 }; // share of the field amplitude the rails settle apart by
|
||||
inline constexpr double fade { 20.0 }; // length over which a track goes from the value at its end to its own, m
|
||||
inline constexpr double dip_share { 0.4 }; // joint dip depth per mm of "uneven", mm
|
||||
inline constexpr double dip_reach { 3.0 }; // reach of the dip on each side of a joint, m
|
||||
|
||||
// the value of each component of the unevenness, mm
|
||||
struct amounts {
|
||||
double up { 0.0 };
|
||||
double side { 0.0 };
|
||||
double cross { 0.0 };
|
||||
};
|
||||
|
||||
struct wave {
|
||||
double x;
|
||||
double z;
|
||||
double phase;
|
||||
double share;
|
||||
};
|
||||
|
||||
template <std::size_t Count>
|
||||
using wave_set = std::array<wave, Count>;
|
||||
|
||||
// the waves run in all directions of the plan, so no direction of a track is favoured
|
||||
template <std::size_t Count>
|
||||
wave_set<Count> make_waves( double const Turn, std::array<double, Count> const &Lengths, std::array<double, Count> const &Shares ) {
|
||||
|
||||
auto const golden { glm::pi<double>() * ( 3.0 - std::sqrt( 5.0 ) ) };
|
||||
wave_set<Count> result {};
|
||||
for( std::size_t i = 0; i < Count; ++i ) {
|
||||
auto const number { glm::two_pi<double>() / Lengths[ i ] };
|
||||
auto const heading { Turn + golden * i };
|
||||
result[ i ] = { number * std::cos( heading ), number * std::sin( heading ), golden * ( i + 1 ) * ( 1.0 + 3.0 * Turn ), Shares[ i ] };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr std::array<double, 8> wave_lengths { 5.3, 7.1, 9.7, 13.1, 17.9, 23.9, 31.7, 41.9 };
|
||||
inline constexpr std::array<double, 8> wave_shares { 0.0721, 0.0997, 0.1344, 0.1703, 0.1549, 0.1386, 0.1226, 0.1074 };
|
||||
|
||||
// fewer short ones than sideways: the rails come near the limit in the vertical more often, with the peak still at it.
|
||||
// The long ones hardly count on the 10 m chord: the body rises and falls on them, slowly enough for a pantograph to keep up
|
||||
inline wave_set<8> const up_waves { make_waves<8>( 0.0, { 8.3, 11.9, 16.7, 23.9, 37.1, 47.3, 61.9, 83.3 }, { 0.14, 0.19, 0.22, 0.22, 0.23, 0.23, 0.23, 0.23 } ) };
|
||||
inline wave_set<8> const x_waves { make_waves( 1.1, wave_lengths, wave_shares ) };
|
||||
inline wave_set<8> const z_waves { make_waves( 2.3, wave_lengths, wave_shares ) };
|
||||
// the tilt along each axis of the plan. Long waves only: with the short ones the cross level reaching its limit would break the limit of twist
|
||||
inline wave_set<3> const tilt_x_waves { make_waves<3>( 0.7, { 23.9, 31.7, 41.9 }, { 1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0 } ) };
|
||||
inline wave_set<3> const tilt_z_waves { make_waves<3>( 2.9, { 23.9, 31.7, 41.9 }, { 1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0 } ) };
|
||||
|
||||
template <std::size_t Count>
|
||||
double sum( wave_set<Count> const &Waves, glm::dvec3 const &Point ) {
|
||||
|
||||
auto total { 0.0 };
|
||||
for( auto const &wave : Waves ) {
|
||||
total += wave.share * std::sin( wave.x * Point.x + wave.z * Point.z + wave.phase );
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// displacement at the point for an amplitude of 1, sideways within +-1, up and down within +-1.69
|
||||
inline glm::dvec3 field( glm::dvec3 const &Point ) {
|
||||
|
||||
return { sum( x_waves, Point ), sum( up_waves, Point ), sum( z_waves, Point ) };
|
||||
}
|
||||
|
||||
// how much lower the rail towards Across lies by settling apart, for an amplitude of 1. A tilt of the plan, so tracks laid towards each
|
||||
// other meet at one cant. Follows the middle of a path, so plain tracks only
|
||||
inline double level( glm::dvec3 const &Point, glm::dvec3 const &Across ) {
|
||||
|
||||
return cross_share * ( Across.x * sum( tilt_x_waves, Point ) + Across.z * sum( tilt_z_waves, Point ) );
|
||||
}
|
||||
|
||||
// roll made by the field for an amplitude of 1; positive lowers the rail towards Across, as the roll of a path
|
||||
inline double roll( glm::dvec3 const &Point, glm::dvec3 const &Across, double const Gauge ) {
|
||||
|
||||
return ( sum( up_waves, Point - Across * ( 0.5 * Gauge ) ) - sum( up_waves, Point + Across * ( 0.5 * Gauge ) ) ) / Gauge;
|
||||
}
|
||||
|
||||
// value at a distance along a track: Full inside, exactly Atstart and Atend at its ends where it meets the neighbours
|
||||
inline double along( double const Full, double const Atstart, double const Atend, double const Distance, double const Length ) {
|
||||
|
||||
auto const smooth = []( double const Share ) {
|
||||
auto const share { std::clamp( Share, 0.0, 1.0 ) };
|
||||
return share * share * ( 3.0 - 2.0 * share ); };
|
||||
auto const ends { Length > 0.0 ? Atstart + ( Atend - Atstart ) * smooth( Distance / Length ) : Atstart };
|
||||
return ends + ( Full - ends ) * std::min( smooth( Distance / fade ), smooth( ( Length - Distance ) / fade ) );
|
||||
}
|
||||
|
||||
// distance from the joint behind the point
|
||||
inline double rail_phase( double const Along, double const Rail ) {
|
||||
|
||||
auto const phase { std::fmod( Along, Rail ) };
|
||||
return phase < 0.0 ? phase + Rail : phase;
|
||||
}
|
||||
|
||||
// share of the dip depth at a distance from the joint
|
||||
inline double dip( double const Fromjoint ) {
|
||||
|
||||
if( Fromjoint >= dip_reach ) {
|
||||
return 0.0;
|
||||
}
|
||||
auto const height { std::cos( glm::half_pi<double>() * Fromjoint / dip_reach ) };
|
||||
return height * height;
|
||||
}
|
||||
|
||||
} // namespace track_uneven
|
||||
@@ -386,6 +386,11 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
|
||||
}
|
||||
};
|
||||
|
||||
double TTrackFollower::RailPhase( double const Offset ) const {
|
||||
|
||||
return pCurrentTrack != nullptr ? pCurrentTrack->rail_phase( fCurrentDistance + fDirection * Offset ) : -1.0;
|
||||
}
|
||||
|
||||
bool TTrackFollower::ComputatePosition()
|
||||
{ // ustalenie współrzędnych XYZ
|
||||
if (pCurrentSegment) // o ile jest tor
|
||||
|
||||
@@ -39,6 +39,10 @@ public:
|
||||
// inline double GetLength(vector3 p1, vector3 cp1, vector3 cp2, vector3 p2)
|
||||
//{ return pCurrentSegment->ComputeLength(p1,cp1,cp2,p2); };
|
||||
// inline double GetRadius(double L, double d); //McZapkie-150503
|
||||
inline glm::dvec3 UnevenBend() const {
|
||||
return pCurrentSegment ? pCurrentSegment->uneven_bend( fCurrentDistance ) : glm::dvec3 { 0.0 }; };
|
||||
// TTrack::rail_phase of the point Offset ahead of the axle along the vehicle
|
||||
double RailPhase( double const Offset ) const;
|
||||
bool Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir);
|
||||
void Reset();
|
||||
void Render(float fNr);
|
||||
|
||||
Reference in New Issue
Block a user