diff --git a/rendering/opengl46/opengl46renderer.cpp b/rendering/opengl46/opengl46renderer.cpp index 74a1f62a..d18557ba 100644 --- a/rendering/opengl46/opengl46renderer.cpp +++ b/rendering/opengl46/opengl46renderer.cpp @@ -5889,6 +5889,11 @@ void opengl46_renderer::Render_Timbers( scene::basic_cell::path_sequence::const_ // the distance is taken from the camera of the view, the shadow pass culling against its own frustum auto const camera = ( shadows ? m_renderpass.viewport_camera.position() : m_renderpass.pass_camera.position() ); auto const range = static_cast( shadows ? std::min( Global.SleeperDistance, 50.f ) : Global.SleeperDistance ); + if( m_renderpass.draw_mode == rendermode::color && m_timbersweepframe != m_framestamp ) { + // once a frame the beams of the tracks well past where they're laid and worked out ahead are let go + m_timbersweepframe = m_framestamp; + track_sleepers::release_far( camera, range + 600.0 ); + } glm::mat4 const originmodelview = OpenGLMatrices.data( GL_MODELVIEW ); for( auto &bucket : m_timberbuckets ) { bucket.instances.clear(); diff --git a/rendering/opengl46/opengl46renderer.h b/rendering/opengl46/opengl46renderer.h index d914c283..a278b29e 100644 --- a/rendering/opengl46/opengl46renderer.h +++ b/rendering/opengl46/opengl46renderer.h @@ -598,6 +598,7 @@ class opengl46_renderer : public gfx_renderer { bool m_particlesoft{false}; // the depth for the soft particles is bound in the colour pass being drawn unsigned int m_framestamp; // id of currently rendered gfx frame + unsigned int m_timbersweepframe { 0 }; // the frame the beams of the tracks far away were last let go in float m_framerate; double m_updateaccumulator{0.0}; std::string m_debugtimestext; diff --git a/world/SleeperWorker.cpp b/world/SleeperWorker.cpp index 2930e3dc..96e90489 100644 --- a/world/SleeperWorker.cpp +++ b/world/SleeperWorker.cpp @@ -227,4 +227,10 @@ void settle_background() { worker().settle(); } +void forget_layout( TTrack const &Track ) { + + std::lock_guard lock { clusters_mutex() }; + known_clusters().erase( &Track ); +} + } // namespace track_sleepers diff --git a/world/SleeperWorker.h b/world/SleeperWorker.h index 4cda1c13..03f15619 100644 --- a/world/SleeperWorker.h +++ b/world/SleeperWorker.h @@ -27,5 +27,8 @@ bool in_background(); void ask_layout( TTrack &Track ); // nothing left to work out in the background nor being worked out, the waiting ones dropped void settle_background(); +// the layout of the cluster kept for the track let go, once the track laid its beams or went far; the cluster stays while +// another track of it keeps it +void forget_layout( TTrack const &Track ); } // namespace track_sleepers diff --git a/world/Track.cpp b/world/Track.cpp index ba24fa9d..547c56d3 100644 --- a/world/Track.cpp +++ b/world/Track.cpp @@ -1542,7 +1542,8 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { m_sleepers3d.active = track_sleepers::lays( *this ); // bake per-instance sleeper transforms now that the owning cell has assigned m_origin. // safe to call here even if the track has no sleepermodel (early-outs internally). - if( m_sleeper_enabled && m_sleeper_local_transforms.empty() ) { + // a track laying the 3D sleepers draws its sleeper model with them, the transforms of the model alone wouldn't be drawn + if( m_sleeper_enabled && false == m_sleepers3d.active && m_sleeper_local_transforms.empty() ) { build_sleeper_transforms(); } switch (iCategoryFlag & 15) diff --git a/world/TrackSleepers.cpp b/world/TrackSleepers.cpp index 60ca29d5..85790d85 100644 --- a/world/TrackSleepers.cpp +++ b/world/TrackSleepers.cpp @@ -22,6 +22,7 @@ http://mozilla.org/MPL/2.0/. #include +#include #include #include @@ -111,7 +112,6 @@ void lay( TTrack &Track ) { } laid.whole.clear(); laid.plain.clear(); - laid.beams.clear(); laid.chunks.clear(); laid.timbers = timber_model( Track ); laid.plains = plain_model( Track ); @@ -125,9 +125,10 @@ void lay( TTrack &Track ) { + to_string( panel.second, 2 ) + " m along, where they share the timbers of its panel" ); } laid.laying = true; - laid.beams = ( in_background() ? std::move( placed ) : place( Track ) ); + auto const beams { in_background() ? std::move( placed ) : place( Track ) }; laid.laying = false; - auto const &beams { laid.beams }; + // the layout of the cluster isn't needed by this track any more, the instances below are what's drawn + forget_layout( Track ); auto const partlength = [&]( int const Part ) { return static_cast( parts.spans[ Part ].y - parts.spans[ Part ].x ); }; @@ -233,6 +234,47 @@ void lay( TTrack &Track ) { } } close_run(); + // kept for as long as the track is near, without the room the vectors grew into + for( auto &instances : laid.instances ) { + instances.shrink_to_fit(); + } + laid.whole.shrink_to_fit(); + laid.plain.shrink_to_fit(); + laid.chunks.shrink_to_fit(); +} + +void release_far( glm::dvec3 const &Camera, double const Range ) { + + // the tracks swept in slices, so a big scenery costs little in a frame + static std::size_t next { 0 }; + auto const &tracks { simulation::Paths.sequence() }; + auto const slice { std::min( tracks.size(), 256 ) }; + for( std::size_t count = 0; count < slice; ++count ) { + if( next >= tracks.size() ) { + next = 0; + } + auto *track { tracks[ next++ ] }; + if( track == nullptr || false == track->m_sleepers3d.active || track->m_sleepers3d.laying ) { + continue; + } + if( glm::length( track->location() - Camera ) - track->radius() <= Range ) { + continue; + } + auto &laid { track->m_sleepers3d }; + forget_layout( *track ); + auto const holds { laid.chunks.capacity() > 0 || laid.whole.capacity() > 0 || laid.plain.capacity() > 0 + || std::any_of( laid.instances.begin(), laid.instances.end(), []( std::vector const &Instances ) { return Instances.capacity() > 0; } ) }; + if( false == holds ) { + continue; + } + for( auto &instances : laid.instances ) { + std::vector {}.swap( instances ); + } + std::vector {}.swap( laid.whole ); + std::vector {}.swap( laid.plain ); + std::vector {}.swap( laid.chunks ); + laid.dirty = true; + } } layout_check check() { diff --git a/world/TrackSleepers.h b/world/TrackSleepers.h index 12a3d69a..06fb1c62 100644 --- a/world/TrackSleepers.h +++ b/world/TrackSleepers.h @@ -86,7 +86,6 @@ struct laid { std::vector whole; // transforms of the timbers of a model without parts, drawn whole std::vector plain; // transforms of the plain sleepers, whole models, relative to the origin of the track std::vector chunks; - std::vector beams; // as they're laid }; // how far the ballast of a track with beams is lowered, so they stick out of it @@ -130,6 +129,9 @@ void unsettle_around( TTrack &Track ); void lay_in_background( bool const Background ); // the beams of the track worked out ahead, before it comes in range void prefetch( TTrack &Track ); +// the beams of the tracks far from the camera let go, a slice of the tracks each call, to be laid again as they come near; +// otherwise each track keeps the beams laid since it was first seen +void release_far( glm::dvec3 const &Camera, double const Range ); // what's wrong with the layout of the beams of all the tracks: beams over each other, rails with too few beams under them, // timbers shared by rails out of one plane