diff --git a/world/RailLayer.cpp b/world/RailLayer.cpp index 4dfe7f3d..9192dd6f 100644 --- a/world/RailLayer.cpp +++ b/world/RailLayer.cpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "world/TrackRails.h" +#include "world/TrackSleepers.h" #include "world/TrackBlend.h" #include "rendering/renderer.h" @@ -381,6 +382,7 @@ struct rail_piece { bool walls { false }; float lift { 0.f }; float wallu { -1.f }; + knots::line rises; }; struct top_band { @@ -605,6 +607,8 @@ private: TSegment const &m_segment; double const m_length; double const m_guardoffset; + double const m_rise; // of the feet of the wing rails of the low section + glm::dvec2 const m_stock; // the stretch the rail is the stock rail of a blade along std::size_t m_topfirst; std::size_t m_toplast { 0 }; std::vector m_tints; @@ -626,6 +630,8 @@ TTrack::rail_layer::rail_layer( TTrack &Track, rail_layout const &Layout, rail_d m_segment { path_segment( Track, Path ) }, m_length { m_segment.GetLength() }, m_guardoffset { -( flangeway + Drawing.size.head ) }, + m_rise { track_sleepers::wing_rise( Track ) }, + m_stock { track_sleepers::stock_stretch( Track, Path, Side ) }, m_topfirst { Profile.size() } { // the running surface on top of the head is split along the edge of the wheel treads, the part of it out of their reach goes matte; the rest of the profile and the end faces are laid whole for( std::size_t j = 0; j < Profile.size(); ++j ) { @@ -791,6 +797,7 @@ void TTrack::rail_layer::lay_cuts( gfx::vertex_array const &Shape, std::vector 0 && Cuts[ i - 1 ].carved && Cuts[ i ].carved ) }; auto const pieceoffsets { part_offsets( i > 0 ? &Cuts[ i - 1 ] : nullptr, i < Cuts.size() ? &Cuts[ i ] : nullptr ) }; + auto const piecerises { part_rises( i > 0 ? &Cuts[ i - 1 ] : nullptr, i < Cuts.size() ? &Cuts[ i ] : nullptr, Wheels ? m_rise : 0.0, m_stock ) }; // the limits of the head from both ends can overlap along a short piece, as the planed wing rail of one frog and the cut of the next: the tighter of them holds auto const pieceextents { own ? knots::bound( extents, Cuts[ i ].endextents, true ) : extents }; auto const pieceinner { own ? knots::bound( inner, Cuts[ i ].endinner, false ) : inner }; @@ -838,7 +845,8 @@ void TTrack::rail_layer::lay_cuts( gfx::vertex_array const &Shape, std::vectoredge.empty() && m_topfirst < m_toplast ) { treaded( whole, *tread, ends ); } @@ -1270,6 +1278,10 @@ loft_span TTrack::rail_layer::span( rail_piece const &Piece, piece_ends const &E result.rampwidth = ( Piece.ramp.empty() ? 0.0 : Piece.fade ); result.walls = Piece.walls; result.wallu = Piece.wallu; + result.rises = Piece.rises; + if( false == Piece.rises.empty() ) { + result.head = { m_drawing.size.neck, m_drawing.size.crest }; + } if( std::abs( Piece.from - Ends.from ) < 1e-6 ) { result.fromend = Ends.fromend; } diff --git a/world/RailLayout.h b/world/RailLayout.h index ff667b82..4b222fe5 100644 --- a/world/RailLayout.h +++ b/world/RailLayout.h @@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/. #pragma once +#include #include #include #include @@ -49,6 +50,8 @@ double constexpr blade_gap { 0.0005 }; double constexpr blade_lowered { 0.050 }; double constexpr blade_spacing { 0.5 }; double constexpr blade_runon_margin { 0.2 }; // how far short of the end of the plain track a blade running on into it ends +double constexpr wing_low { 1.5 }; // how far before the throat the wing rails are of the low section of the blades, raised on their timbers as the blades +double constexpr wing_forged { 0.5 }; // how long the end of a wing rail forged to the plain rail is, going down to it // where the inner rails of the two paths of a switch cross, found from their gauge lines behind the blades struct switch_frog { @@ -175,6 +178,36 @@ inline knots::line part_offsets( rail_cut const *Before, rail_cut const *After ) knots::sort( result ); return result; } +// how far the foot of a part of a rail between two cuts is raised, Rise along a wing rail of the low section: from wing_low before the throat on, +// down to the plain rail past it along its forged end; the rail whole along Stock, the stock rail of a blade there +inline knots::line part_rises( rail_cut const *Before, rail_cut const *After, double const Rise, glm::dvec2 const &Stock ) { + + knots::line result; + auto const going { Before != nullptr && Before->startkind == rail_end::point }; + auto const coming { After != nullptr && After->endkind == rail_end::point }; + if( Rise <= 0.0 || ( false == going && false == coming ) ) { + return result; + } + auto const lowto { going ? Before->startanchor + wing_low : 0.0 }; + auto const lowfrom { coming ? After->endanchor - wing_low : 0.0 }; + auto const through { going && coming && lowto + 2.0 * wing_forged >= lowfrom }; + auto const stock { Stock.y > Stock.x }; + // the share of the rise: whole along the low section, none along the stock rail, between along the forged ends + auto const share = [&]( double const At ) { + auto const low { through ? 1.0 : std::max( + going ? std::clamp( ( lowto + wing_forged - At ) / wing_forged, 0.0, 1.0 ) : 0.0, + coming ? std::clamp( ( At - lowfrom + wing_forged ) / wing_forged, 0.0, 1.0 ) : 0.0 ) }; + auto const whole { stock ? std::clamp( std::max( Stock.x - At, At - Stock.y ) / wing_forged, 0.0, 1.0 ) : 1.0 }; + return std::min( low, whole ); }; + std::array places { lowto, lowto + wing_forged, lowfrom - wing_forged, lowfrom, Stock.x - wing_forged, Stock.x, Stock.y, Stock.y + wing_forged }; + std::sort( places.begin(), places.end() ); + for( auto const at : places ) { + if( result.empty() || at > result.back().x + 1e-6 ) { + result.emplace_back( at, Rise * share( at ) ); + } + } + return result; +} struct rail_tread { double from { 0.0 }; double to { 0.0 }; diff --git a/world/Segment.cpp b/world/Segment.cpp index afb2f2bc..9a2dc6c2 100644 --- a/world/Segment.cpp +++ b/world/Segment.cpp @@ -717,7 +717,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig for( auto distance { std::ceil( Span.from / 0.5 ) * 0.5 }; distance < std::min( Span.to, m_dense ); distance += 0.5 ) { stations.push_back( distance ); } - for( auto const *line : { &Span.offsets, &Span.extents, &Span.inner, &Span.drops } ) { + for( auto const *line : { &Span.offsets, &Span.extents, &Span.inner, &Span.drops, &Span.rises } ) { for( auto const &knot : *line ) { if( knot.x > Span.from && knot.x < Span.to ) { stations.push_back( knot.x ); @@ -759,11 +759,11 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig for( auto const &point : ShapePoints ) { bottom = std::min( bottom, point.position.y ); } - auto const raised = [&]( float const Height ) { - if( Span.rise <= 0.0 || Height >= Span.head.x || Span.head.x <= bottom ) { + auto const raised = [&]( float const Height, double const Rise ) { + if( Rise <= 0.0 || Height >= Span.head.x || Span.head.x <= bottom ) { return Height; } - return static_cast( Height + Span.rise * ( Span.head.x - Height ) / ( Span.head.x - bottom ) ); }; + return static_cast( Height + Rise * ( Span.head.x - Height ) / ( Span.head.x - bottom ) ); }; auto const ring = [&]( double const Distance, double const Middle, std::vector &Ring, std::vector &Wear ) { glm::dvec3 point; @@ -797,6 +797,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig } auto const worn { knots::sample( Span.ramp, Distance, -1e6 ) }; auto const drop { knots::sample( Span.drops, Distance, 0.0 ) }; + auto const rise { Span.rises.empty() ? Span.rise : knots::sample( Span.rises, Distance, 0.0 ) }; static loft_end const unshaped; auto const &shaped { Distance <= Span.from ? Span.fromend : Distance >= Span.to ? Span.toend : unshaped }; auto const bend { shaped.bend }; @@ -830,7 +831,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig // nothing of the profile between the limits there: the ring shrinks to one point, the step draws nothing auto const outside { std::clamp( 0.5 * ( std::min( extent, farthest ) + std::max( innerextent, nearest ) ), nearest, farthest ) }; auto pt { parallel * static_cast( Span.side * ( Span.halfwidth + outside + offset ) ) + position }; - pt.y += raised( ShapePoints.front().position.y ) + Span.lift; + pt.y += raised( ShapePoints.front().position.y, rise ) + Span.lift; for( std::size_t j = 0; j < count; ++j ) { Ring.emplace_back( pt, glm::vec3{ 0.f, 1.f, 0.f }, glm::vec2{ ShapePoints[ j ].texture.x, 0.f } ); Wear.push_back( 1.f ); @@ -909,7 +910,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig if( drop > 0.0 && shape.position.y > Span.head.x && Span.head.y > Span.head.x ) { shape.position.y -= static_cast( drop * ( shape.position.y - Span.head.x ) / ( Span.head.y - Span.head.x ) ); } - shape.position.y = raised( shape.position.y ); + shape.position.y = raised( shape.position.y, rise ); auto pt { parallel * static_cast( Span.side * ( Span.halfwidth + outside + offset ) ) + position + along * static_cast( outside + offset ) }; pt.y += shape.position.y + Span.lift; auto norm { shape.normal.x * parallel }; diff --git a/world/Segment.h b/world/Segment.h index 69e47e7f..26fb1c35 100644 --- a/world/Segment.h +++ b/world/Segment.h @@ -48,6 +48,7 @@ struct loft_span { knots::line drops; glm::dvec2 head { 0.0 }; double rise { 0.0 }; // how far the foot of the profile is raised, the web below the head (head.x) shortened by as much + knots::line rises; // along the span, in place of rise where given }; struct segment_data { diff --git a/world/SleeperFastenings.cpp b/world/SleeperFastenings.cpp index 9e282efa..8e4f4e1b 100644 --- a/world/SleeperFastenings.cpp +++ b/world/SleeperFastenings.cpp @@ -52,22 +52,17 @@ fastened_rails::fastened_rails( TTrack &Track ) { auto const middle { 0.5 * std::abs( track->fTrackWidth ) + size.web }; auto const cut { track_rails::cut_rails( *track ) }; auto const switched { track->eType == tt_Switch && track->SwitchExtension != nullptr }; - // the side of the blade of the path, and the stretch along it from the tip to the heel + auto const rise { wing_rise( *track ) }; + // the side of the blade of the path auto const bladeside = [&]( int const Path ) { return ( track->SwitchExtension->RightSwitch == ( Path == 0 ) ? 1 : -1 ); }; - auto const bladestretch = [&]( int const Path ) { - auto const &shape { track->blade( Path ) }; - return glm::dvec2 { shape.valid ? shape.tip : 0.0, track_rails::blade_length( *track, Path ) }; }; // what the rail is along the path: a blade, the stock rail of a blade, checked auto const add = [&]( rail &&Line, int const Path, int const Side ) { if( switched ) { if( Side == bladeside( Path ) ) { - Line.blade = { 0.0, bladestretch( Path ).y }; - } - if( Side == bladeside( 1 - Path ) ) { - auto const stretch { bladestretch( 1 - Path ) }; - Line.stock = { std::max( 0.0, stretch.x - chair_lead ), stretch.y }; + Line.blade = { 0.0, track_rails::blade_length( *track, Path ) }; } + Line.stock = stock_stretch( *track, Path, Side ); } if( cut ) { for( auto const &check : track_rails::rail_checks( *track )[ Path ][ Side > 0 ? 1 : 0 ] ) { @@ -100,6 +95,7 @@ fastened_rails::fastened_rails( TTrack &Track ) { line.track = track; line.path = path; line.side = side; + line.rises = track_rails::part_rises( before, after, rise, stock_stretch( *track, path, side ) ); line.foot = size.web - size.low; line.low = glm::dvec2 { std::numeric_limits::max() }; line.high = glm::dvec2 { std::numeric_limits::lowest() }; @@ -159,11 +155,13 @@ std::vector fastened_rails::rails_on( beam const &Beam ) co if( std::abs( height - glm::mix( Beam.from.y, Beam.to.y, u ) ) > level_tolerance ) { continue; } + auto const distance { glm::mix( line.distances[ index - 1 ], line.distances[ index ], v ) }; hits.push_back( { u * length, - line.at( glm::mix( line.distances[ index - 1 ], line.distances[ index ], v ) ), + line.at( distance ), glm::normalize( glm::mix( line.inward[ index - 1 ], line.inward[ index ], v ) ), - line.foot } ); + line.foot, + knots::sample( line.rises, distance, 0.0 ) } ); } } std::sort( hits.begin(), hits.end(), []( hit const &Left, hit const &Right ) { return Left.at < Right.at; } ); @@ -180,11 +178,14 @@ std::vector fastened_rails::rails_on( beam const &Beam ) co for( std::size_t index = 0; index < hits.size(); ) { auto best { hits[ index ] }; auto next { index + 1 }; + auto rise { best.rise }; for( ; next < hits.size() && hits[ next ].at - hits[ next - 1 ].at < fastening_merge; ++next ) { if( rank( hits[ next ].kind ) > rank( best.kind ) ) { best = hits[ next ]; } + rise = std::max( rise, hits[ next ].rise ); } + best.rise = rise; if( best.kind != role::blade ) { rails.push_back( best ); } @@ -324,6 +325,7 @@ std::vector fastened_rails::on( beam const &Beam, beam_model const &M } } fit( result, Beam, Model ); + lift( result, rails, Beam, Model ); return result; } @@ -618,6 +620,81 @@ void fastened_rails::fit( std::vector &Fastenings, beam const &Beam, Fastenings = std::move( fitted ); } +void fastened_rails::lift( std::vector &Fastenings, std::vector const &Rails, beam const &Beam, beam_model const &Model ) { + + if( std::none_of( Rails.begin(), Rails.end(), []( hit const &Rail ) { return Rail.rise > 0.0; } ) ) { + return; + } + auto const start { plan( Beam.from ) }; + auto const axis { glm::normalize( plan( Beam.to - Beam.from ) ) }; + auto const padpart { Model.parts[ part_slide ] != nullptr ? part_slide : part_plate }; + auto const &padspan { Model.spans[ padpart ] }; + auto const &padheights { Model.heights[ padpart ] }; + // a pad on the timber under the fastening, along its across from Low to High, up from Bottom to Top in the model + auto const pad = [&]( fastening const &Over, double const Low, double const High, float const Bottom, float const Top ) { + fastening result { padpart, Over.point, Over.across }; + result.scale = static_cast( ( High - Low ) / ( padspan.y - padspan.x ) ); + result.shift = static_cast( Low - result.scale * padspan.x ); + result.lift = Bottom - padheights.x; + result.height = ( Top - Bottom ) / std::max( 0.001f, padheights.y - padheights.x ); + result.pad = true; + return result; }; + // where along the timber the fastening reaches, from and to + auto const footprint = [&]( fastening const &Fastening ) { + auto const at { glm::dot( plan( Fastening.point ) - start, axis ) }; + auto const turn { glm::dot( Fastening.across, axis ) }; + auto const &span { Model.spans[ Fastening.part ] }; + auto const a { at + turn * Fastening.placed( span.x, Fastening.scale ) }; + auto const b { at + turn * Fastening.placed( span.y, Fastening.scale ) }; + return glm::dvec2 { std::min( a, b ), std::max( a, b ) }; }; + auto const holds = [&]( fastening const &Fastening, hit const &Rail ) { + auto const reach { footprint( Fastening ) }; + return Fastening.part != part_chair && Rail.at >= reach.x && Rail.at <= reach.y; }; + std::vector pads; + std::vector lifted( Fastenings.size(), false ); + for( std::size_t index = 0; index < Rails.size(); ++index ) { + auto const &rail { Rails[ index ] }; + if( rail.rise < pad_least ) { + continue; + } + auto const under { std::find_if( Fastenings.begin(), Fastenings.end(), [&]( fastening const &Fastening ) { return holds( Fastening, rail ); } ) }; + if( under == Fastenings.end() ) { + continue; + } + auto const which { static_cast( under - Fastenings.begin() ) }; + if( lifted[ which ] ) { + continue; + } + auto const alike { std::all_of( Rails.begin(), Rails.end(), [&]( hit const &Other ) { + return false == holds( *under, Other ) || std::abs( Other.rise - rail.rise ) < pad_least; } ) }; + if( alike ) { + // the fastening holding only the rails raised as much, moved up as far on a pad as wide as it + auto const &span { Model.spans[ under->part ] }; + auto const a { under->placed( span.x, under->scale ) }; + auto const b { under->placed( span.y, under->scale ) }; + pads.push_back( pad( *under, std::min( a, b ), std::max( a, b ), Model.bed, Model.heights[ under->part ].x + static_cast( rail.rise ) ) ); + under->lift = static_cast( rail.rise ); + lifted[ which ] = true; + continue; + } + // the rail on a pad of its own on the fastening, as wide as its foot, short of the rails beside + auto low { rail.foot }; + auto high { rail.foot }; + if( index > 0 ) { + low = std::min( low, 0.5 * ( rail.at - Rails[ index - 1 ].at ) - fastening_gap ); + } + if( index + 1 < Rails.size() ) { + high = std::min( high, 0.5 * ( Rails[ index + 1 ].at - rail.at ) - fastening_gap ); + } + if( low + high < pad_narrowest ) { + continue; + } + fastening const at { padpart, Beam.from + ( Beam.to - Beam.from ) * ( rail.at / glm::length( plan( Beam.to - Beam.from ) ) ), axis }; + pads.push_back( pad( at, -low, high, Model.seat, Model.seat + static_cast( rail.rise ) ) ); + } + Fastenings.insert( Fastenings.end(), pads.begin(), pads.end() ); +} + std::vector check_fastenings() { std::vector result; @@ -654,6 +731,9 @@ std::vector check_fastenings() { row.bares.push_back( at ); } for( auto const &fastening : fastenings ) { + if( fastening.pad ) { + continue; + } ++row.parts[ fastening.part ]; auto const at { glm::dot( plan( fastening.point ) - start, axis ) }; auto const turn { glm::dot( fastening.across, axis ) }; @@ -763,6 +843,9 @@ std::vector check_rail_seats() { auto const b { Fastening.placed( model->spans[ Fastening.part ].y, Fastening.scale ) }; return along >= std::min( a, b ) && along <= std::max( a, b ); } ); }; for( auto const &fastening : fastenings ) { + if( fastening.pad ) { + continue; + } seats.push_back( plan( fastening.point ) + fastening.across * fastening.placed( 0.0, fastening.scale ) ); if( fastening.part == part_check ) { seats.push_back( plan( fastening.point ) + fastening.across * fastening.placed( check_seats, fastening.scale ) ); diff --git a/world/SleeperFastenings.h b/world/SleeperFastenings.h index 9ad6ea24..59ebf641 100644 --- a/world/SleeperFastenings.h +++ b/world/SleeperFastenings.h @@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/. #include +#include "world/Knots.h" #include "world/TrackSleepers.h" // the fastenings of the rails on the timbers: where a rail lies on a timber, the part of the model of the timbers which holds it @@ -32,6 +33,9 @@ struct fastening { float shift { 0.f }; // along its x, how far the pivot is moved bool both { false }; // the fastening of two rails close beside each other, neither with a check rail of its own seated bool whole { false }; // holding more rails than its seats, out to its ends: not narrowed + float lift { 0.f }; // how far up it's moved, under a rail of the low section raised as a blade + float height { 1.f }; // along its y, from the bottom of the part: a pad stretched to fill under a raised rail + bool pad { false }; // under a raised rail or its fastening, not holding a rail itself // where an x of the part lands along its across, at the scale double placed( double const X, double const Scale ) const { return pivot + shift + Scale * ( X - pivot ); } @@ -60,6 +64,7 @@ private: TTrack *track { nullptr }; int path { 0 }; int side { 1 }; + knots::line rises; // of its foot, along the path, a wing rail of the low section raised as the blades std::vector points; std::vector heights; std::vector inward; // unit vectors in plan from the rail toward the middle of its path @@ -79,6 +84,7 @@ private: role kind { role::plain }; glm::dvec2 inward { 1.0, 0.0 }; double foot { 0.0 }; + double rise { 0.0 }; }; // the rails lying on the timber, in the order along it, one where several lie on one another, but the blades @@ -86,6 +92,9 @@ private: // the fastenings of rails close by fitted to the room between them: narrowed across, a plate cut short at the fastening of the // rail beside it or left out, the plates of two rails lying over each other made one static void fit( std::vector &Fastenings, beam const &Beam, beam_model const &Model ); + // the fastenings under the raised rails: one holding only rails raised as much moved up as far on a pad, a rail raised + // over a fastening with others on a pad of its own on it + static void lift( std::vector &Fastenings, std::vector const &Rails, beam const &Beam, beam_model const &Model ); std::vector m_rails; }; diff --git a/world/SleeperModels.cpp b/world/SleeperModels.cpp index 1dc13567..003dab2c 100644 --- a/world/SleeperModels.cpp +++ b/world/SleeperModels.cpp @@ -122,13 +122,16 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna continue; } glm::vec2 span { std::numeric_limits::max(), std::numeric_limits::lowest() }; + glm::vec2 heights { std::numeric_limits::max(), std::numeric_limits::lowest() }; auto const timber { part < timber_parts }; vertices_of( model.parts[ part ], [&]( glm::vec3 const &Position ) { span = { std::min( span.x, Position.x ), std::max( span.y, Position.x ) }; + heights = { std::min( heights.x, Position.y ), std::max( heights.y, Position.y ) }; if( timber ) { top = std::max( top, Position.y ); } } ); model.spans[ part ] = span; + model.heights[ part ] = heights; if( timber ) { model.span = { std::min( model.span.x, span.x ), std::max( model.span.y, span.y ) }; } @@ -155,6 +158,7 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna } std::sort( model.bands.begin(), model.bands.end() ); // the rails sit on the seat of the sleeper, below the fastenings sticking up over it, or on the plate of the fastening + model.bed = top; model.seat = ( Seat >= 0.f ? Seat : top ); if( Seat < 0.f && model.fastened() ) { model.seat = std::max( top, surface_at( model.parts[ part_plate ], 0.f, 0.f ).value_or( top ) ); diff --git a/world/SleeperRules.h b/world/SleeperRules.h index 6b823438..4248b4be 100644 --- a/world/SleeperRules.h +++ b/world/SleeperRules.h @@ -73,6 +73,9 @@ double constexpr check_least { 0.8 }; double constexpr chair_lead { 0.3 }; // how far from the stock rail toward its blade the slide plate of a chair is measured double constexpr slide_probe { 0.12 }; +// a rail raised less than this goes without a pad under it, and a pad narrower than the second one is left out +double constexpr pad_least { 0.002 }; +double constexpr pad_narrowest { 0.02 }; // how far two fastenings on a timber may reach over one another before the check counts them double constexpr fastening_overlap { 0.01 }; // how far from the camera the fastenings are drawn at most diff --git a/world/TrackSleepers.cpp b/world/TrackSleepers.cpp index 94f239d5..b4563475 100644 --- a/world/TrackSleepers.cpp +++ b/world/TrackSleepers.cpp @@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "world/SleeperRules.h" #include "world/SleeperWorker.h" #include "world/Track.h" +#include "world/TrackRails.h" #include "simulation/simulation.h" #include "scene/scene.h" #include "utilities/utilities.h" @@ -67,7 +68,21 @@ bool lays( TTrack const &Track ) { double blade_rise( TTrack const &Track ) { - return ( Track.eType == tt_Switch && lays( Track ) ? timber_model( Track )->slide : 0.0 ); + return ( Track.eType == tt_Switch ? wing_rise( Track ) : 0.0 ); +} + +double wing_rise( TTrack const &Track ) { + + return ( lays( Track ) ? timber_model( Track )->slide : 0.0 ); +} + +glm::dvec2 stock_stretch( TTrack const &Track, int const Path, int const Side ) { + + if( Track.eType != tt_Switch || Track.SwitchExtension == nullptr || Side != ( Track.SwitchExtension->RightSwitch == ( Path == 1 ) ? 1 : -1 ) ) { + return glm::dvec2 { -1.0 }; + } + auto const &shape { Track.blade( 1 - Path ) }; + return { std::max( 0.0, ( shape.valid ? shape.tip : 0.0 ) - chair_lead ), track_rails::blade_length( Track, 1 - Path ) }; } std::vector> shared_zones( TTrack const &Track, int const Path ) { @@ -255,9 +270,9 @@ void lay( TTrack &Track ) { place[ 1 ] = glm::vec4 { glm::vec3 { y }, 0.f }; place[ 2 ] = glm::vec4 { glm::vec3 { glm::cross( across, y ) }, 0.f }; place[ 3 ] = glm::vec4 { glm::vec3 { fastening.point - Track.m_origin }, 1.f }; - auto const pivot { glm::vec3 { fastening.pivot, 0.f, 0.f } }; + auto const pivot { glm::vec3 { fastening.pivot, parts.heights[ fastening.part ].x, 0.f } }; laid.instances[ fastening.part ].emplace_back( - glm::translate( glm::scale( glm::translate( place, glm::vec3 { fastening.shift, -parts.seat, 0.f } + pivot ), glm::vec3 { fastening.scale, 1.f, 1.f } ), -pivot ) ); + glm::translate( glm::scale( glm::translate( place, glm::vec3 { fastening.shift, fastening.lift - parts.seat, 0.f } + pivot ), glm::vec3 { fastening.scale, fastening.height, 1.f } ), -pivot ) ); } } runbeams.emplace_back( middle, half ); diff --git a/world/TrackSleepers.h b/world/TrackSleepers.h index 683a451b..7efa1681 100644 --- a/world/TrackSleepers.h +++ b/world/TrackSleepers.h @@ -72,6 +72,8 @@ struct beam_model { glm::vec2 span { -1.3f, 1.3f }; // from and to along x, of the most detailed submodels of the whole model float seat { 0.f }; // the height in the model the feet of the rails go to float slide { 0.f }; // how far over the seat the blades of a switch slide, on the slide plates of the chairs; 0 without chairs + float bed { 0.f }; // the height in the model of the top of the timber + std::array heights {}; // from and to along y, of the parts std::vector bands; // the squared distances the levels of detail of the model are seen from, the nearest first float length() const { return span.y - span.x; } // the model has the fastenings of the rails, put where the rails lie on its timbers @@ -123,6 +125,11 @@ bool lays( TTrack const &Track ); // how far the feet of the blades of the switch ride over those of its other rails, on the slide plates of the chairs of its // timbers; 0 where it lays none with chairs double blade_rise( TTrack const &Track ); +// how far the feet of its wing rails of the low section of the blades are raised as the blades; 0 where it lays no timbers with chairs +double wing_rise( TTrack const &Track ); +// the stretch along the path its rail of Side is the stock rail of the blade of the other path along, with the slide chairs from +// a little before the tip of the blade to its heel; from past to where there's none +glm::dvec2 stock_stretch( TTrack const &Track, int const Path, int const Side ); // where along the path of the track its rails lie on timbers shared with other tracks: where another track runs so near that // the plain sleepers of the two would touch, of the lengths of the models they'd be made of. this goes by the shape of the // tracks alone, so it holds whether the beams are drawn or not. the stretches in the order along the path, none where there