From 4ce975a656aa633209cd59e518777a20dbe3b7fc Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 28 Oct 2017 00:56:10 +0200 Subject: [PATCH] build 171027: bounding area calculation fixes, novice ui obstacle indicator, minor traction render enhancement, shadow calculations for tall enough platforms --- AnimModel.cpp | 9 +++++ AnimModel.h | 5 +++ EvLaunch.cpp | 11 +++++- EvLaunch.h | 5 +++ Ground.cpp | 3 ++ MemCell.cpp | 10 ++--- MemCell.h | 21 ++++++----- Model3d.cpp | 18 ++++++--- Track.cpp | 16 +++++++- Track.h | 11 ++++-- Traction.cpp | 14 ++++++- Traction.h | 7 +++- TractionPower.cpp | 6 +-- Train.cpp | 2 +- World.cpp | 8 +++- renderer.cpp | 82 ++++++++++++++++++++++++++++++++--------- scene.cpp | 86 +++++++++++++++++++++++++------------------ scene.h | 27 +++++++++++--- scenenode.cpp | 22 ++++++++++- scenenode.h | 11 ++++-- simulation.cpp | 94 ++++++++++++++++++++++++++++------------------- simulation.h | 2 +- version.h | 2 +- 23 files changed, 334 insertions(+), 138 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index 07f4aa94..fd994fe4 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -583,6 +583,15 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { m_framestamp = Framestamp; }; +// calculates piece's bounding radius +void +TAnimModel::radius_() { + + if( pModel != nullptr ) { + m_area.radius = pModel->bounding_radius(); + } +} + void TAnimModel::RaPrepare() { // ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza bool state; // stan światła diff --git a/AnimModel.h b/AnimModel.h index 3ced5617..acc9545f 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -157,6 +157,11 @@ public: // members static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania +protected: + // calculates piece's bounding radius + void + radius_(); + private: // methods void RaPrepare(); // ustawienie animacji egzemplarza na wzorcu diff --git a/EvLaunch.cpp b/EvLaunch.cpp index 349a29db..6abe7350 100644 --- a/EvLaunch.cpp +++ b/EvLaunch.cpp @@ -121,7 +121,7 @@ bool TEventLauncher::Load(cParser *parser) *parser >> token; } return true; -}; +} bool TEventLauncher::check_conditions() { //"renderowanie" wyzwalacza @@ -184,6 +184,13 @@ bool TEventLauncher::IsGlobal() const { && ( iHour >= 0 ) && ( iMinute >= 0 ) && ( dRadius < 0.0 ) ); // bez ograniczenia zasięgu -}; +} + +// calculates node's bounding radius +void +TEventLauncher::radius_() { + + m_area.radius = std::sqrt( dRadius ); +} //--------------------------------------------------------------------------- diff --git a/EvLaunch.h b/EvLaunch.h index 1dbeae97..d6fe9324 100644 --- a/EvLaunch.h +++ b/EvLaunch.h @@ -37,6 +37,11 @@ public: int iCheckMask { 0 }; double dRadius { 0.0 }; +protected: + // calculates node's bounding radius + void + radius_(); + private: // members int iKey { 0 }; diff --git a/Ground.cpp b/Ground.cpp index 174b050d..b457259f 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -1562,6 +1562,9 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) break; } #endif + default: { + break; + } } return tmp; } diff --git a/MemCell.cpp b/MemCell.cpp index e55546e1..972f89df 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -29,8 +29,8 @@ http://mozilla.org/MPL/2.0/. TMemCell::TMemCell(vector3 *p) { fValue1 = fValue2 = 0; - m_location = - p ? *p : glm::dvec3(); // ustawienie współrzędnych, bo do TGroundNode nie ma dostępu + location( + p ? *p : glm::dvec3() ); // ustawienie współrzędnych, bo do TGroundNode nie ma dostępu bCommand = false; // komenda wysłana OnSent = NULL; } @@ -110,9 +110,9 @@ bool TMemCell::Load(cParser *parser) #else parser->getTokens( 6, false ); *parser - >> m_location.x - >> m_location.y - >> m_location.z + >> m_area.center.x + >> m_area.center.y + >> m_area.center.z #endif >> szText >> fValue1 diff --git a/MemCell.h b/MemCell.h index 432be51f..a89d7580 100644 --- a/MemCell.h +++ b/MemCell.h @@ -16,16 +16,7 @@ http://mozilla.org/MPL/2.0/. class TMemCell : public editor::basic_node { - private: - // content - std::string szText; - double fValue1 { 0.0 }; - double fValue2 { 0.0 }; - // other - TCommandType eCommand { cm_Unknown }; - bool bCommand { false }; // czy zawiera komendę dla zatrzymanego AI - TEvent *OnSent { nullptr }; // event dodawany do kolejki po wysłaniu komendy zatrzymującej skład - public: +public: std::string asTrackName; // McZapkie-100302 - zeby nazwe toru na ktory jest Putcommand wysylane pamietac TMemCell( scene::node_data const &Nodedata ); @@ -63,6 +54,16 @@ class TMemCell : public editor::basic_node { TCommandType CommandCheck(); bool IsVelocity(); void AssignEvents(TEvent *e); + +private: + // content + std::string szText; + double fValue1 { 0.0 }; + double fValue2 { 0.0 }; + // other + TCommandType eCommand { cm_Unknown }; + bool bCommand { false }; // czy zawiera komendę dla zatrzymanego AI + TEvent *OnSent { nullptr }; // event dodawany do kolejki po wysłaniu komendy zatrzymującej skład }; diff --git a/Model3d.cpp b/Model3d.cpp index 0cbdfb17..a24ebc2e 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1008,7 +1008,10 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const & if( m_geometry != NULL ) { // calculate bounding radius while we're at it // NOTE: doesn't take into account transformation hierarchy TODO: implement it - float squaredradius{ 0.f }; + float squaredradius { 0.f }; + // if this happens to be root node it may already have non-squared radius of the largest child + // since we're comparing squared radii, we need to square it back for correct results + m_boundingradius *= m_boundingradius; for( auto const &vertex : GfxRenderer.Vertices( m_geometry ) ) { squaredradius = static_cast( glm::length2( vertex.position ) ); if( squaredradius > m_boundingradius ) { @@ -1016,12 +1019,15 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const & } } if( m_boundingradius > 0.f ) { m_boundingradius = std::sqrt( m_boundingradius ); } - if( Parent ) { - // propagate radius up the chain - Parent->m_boundingradius = std::max( - Parent->m_boundingradius, - m_boundingradius ); + // adjust overall radius if needed + // NOTE: the method to access root submodel is... less than ideal + auto *root { this }; + while( root->Parent != nullptr ) { + root = root->Parent; } + root->m_boundingradius = std::max( + root->m_boundingradius, + m_boundingradius ); } if( Next ) diff --git a/Track.cpp b/Track.cpp index 0c2b54ce..a560e0b9 100644 --- a/Track.cpp +++ b/Track.cpp @@ -867,11 +867,11 @@ void TTrack::Load(cParser *parser, vector3 pOrigin) } // calculate path location - m_location = glm::dvec3{ ( + m_area.center = ( glm::dvec3{ ( CurrentSegment()->FastGetPoint_0() + CurrentSegment()->FastGetPoint( 0.5 ) + CurrentSegment()->FastGetPoint_1() ) - / 3.0 }; + / 3.0 } ); } // TODO: refactor this mess @@ -2821,6 +2821,18 @@ TTrack::endpoints() const { } } +// calculates path's bounding radius +void +TTrack::radius_() { + + auto const points = endpoints(); + for( auto &point : points ) { + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - point ) ) ); // extra margin to prevent driven vehicle from flicking + } +} + void TTrack::MovedUp1(float const dh) { // poprawienie przechyłki wymaga wydłużenia podsypki fTexHeight1 += dh; diff --git a/Track.h b/Track.h index 8d23c127..8deb6aea 100644 --- a/Track.h +++ b/Track.h @@ -182,9 +182,9 @@ 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) - private: +private: double fVelocity = -1.0; // ograniczenie prędkości // prędkość dla AI (powyżej rośnie prawdopowobieństwo wykolejenia) - public: +public: // McZapkie-100502: double fTrackLength = 100.0; // długość z wpisu, nigdzie nie używana double fRadius = 0.0; // promień, dla zwrotnicy kopiowany z tabeli @@ -287,7 +287,12 @@ public: double VelocityGet(); void ConnectionsLog(); - private: +protected: + // calculates path's bounding radius + void + radius_(); + +private: void EnvironmentSet(); void EnvironmentReset(); }; diff --git a/Traction.cpp b/Traction.cpp index 8bab89bb..a0739107 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -169,7 +169,7 @@ TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) { Init(); // przeliczenie parametrów // calculate traction location - m_location = interpolate( pPoint2, pPoint1, 0.5 ); + location( interpolate( pPoint2, pPoint1, 0.5 ) ); } // retrieves list of the track's end points @@ -543,6 +543,18 @@ double TTraction::VoltageGet(double u, double i) return 0.0; // gdy nie podłączony wcale? }; +// calculates path's bounding radius +void +TTraction::radius_() { + + auto const points = endpoints(); + for( auto &point : points ) { + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - point ) ) ); + } +} + glm::vec3 TTraction::wire_color() const { diff --git a/Traction.h b/Traction.h index d1202768..8ec2131a 100644 --- a/Traction.h +++ b/Traction.h @@ -64,7 +64,6 @@ class TTraction : public editor::basic_node { // retrieves list of the track's end points std::vector endpoints() const; - // creates geometry data in specified geometry bank. returns: number of created elements, or NULL // NOTE: deleting nodes doesn't currently release geometry data owned by the node. TODO: implement erasing individual geometry chunks and banks #ifdef EU07_USE_OLD_GROUNDCODE @@ -79,6 +78,12 @@ class TTraction : public editor::basic_node { void ResistanceCalc(int d = -1, double r = 0, TTractionPowerSource *ps = nullptr); void PowerSet(TTractionPowerSource *ps); double VoltageGet(double u, double i); + +protected: + // calculates piece's bounding radius + void + radius_(); + private: glm::vec3 wire_color() const; }; diff --git a/TractionPower.cpp b/TractionPower.cpp index 0b6ee2c9..e4d60783 100644 --- a/TractionPower.cpp +++ b/TractionPower.cpp @@ -42,9 +42,9 @@ bool TTractionPowerSource::Load(cParser *parser) { #else parser->getTokens( 10, false ); *parser - >> m_location.x - >> m_location.y - >> m_location.z + >> m_area.center.x + >> m_area.center.y + >> m_area.center.z #endif >> NominalVoltage >> VoltageFrequency diff --git a/Train.cpp b/Train.cpp index 3d1c34db..67a641e6 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7029,7 +7029,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con /* TGauge *gg { nullptr }; // roboczy wskaźnik na obiekt animujący gałkę */ - std::unordered_map gauges = { + std::unordered_map const gauges = { { "mainctrl:", ggMainCtrl }, { "scndctrl:", ggScndCtrl }, { "dirkey:" , ggDirKey }, diff --git a/World.cpp b/World.cpp index 9d427367..06be5fff 100644 --- a/World.cpp +++ b/World.cpp @@ -1352,7 +1352,8 @@ TWorld::Update_UI() { uitextline1 += " (paused)"; } - if( Controlled && ( Controlled->Mechanik != nullptr ) ) { + if( ( Controlled != nullptr ) + && ( Controlled->Mechanik != nullptr ) ) { auto const &mover = Controlled->MoverParameters; auto const &driver = Controlled->Mechanik; @@ -1374,6 +1375,11 @@ TWorld::Update_UI() { uitextline3 += " Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa" + " (train pipe: " + to_string( mover->PipePress * 100.0, 2 ) + " kPa)"; + + auto const trackblockdistance{ std::abs( Controlled->Mechanik->TrackBlock() ) }; + if( trackblockdistance <= 75.0 ) { + uitextline4 = " Another vehicle ahead (distance: " + to_string( trackblockdistance, 1 ) + " m)"; + } } } diff --git a/renderer.cpp b/renderer.cpp index f2436148..e00f83a2 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -420,7 +420,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); ::glDisable( GL_LIGHTING ); ::glDisable( GL_TEXTURE_2D ); - if( true == Global::RenderShadows ) { + if( ( true == Global::RenderShadows ) && ( false == Global::bWireFrame ) ) { shadowcamera.draw( m_renderpass.camera.position() - shadowcamera.position() ); } if( DebugCameraFlag ) { @@ -2214,11 +2214,11 @@ opengl_renderer::Render( TAnimModel *Instance ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Instance->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Instance->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -2246,7 +2246,7 @@ opengl_renderer::Render( TAnimModel *Instance ) { Instance->pModel, Instance->Material(), distancesquared, - Instance->m_location - m_renderpass.camera.position(), + Instance->location() - m_renderpass.camera.position(), Instance->vAngle ); } } @@ -2799,6 +2799,17 @@ void opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last ) { ::glColor3fv( glm::value_ptr( colors::white ) ); + // setup + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // NOTE: roads-based platforms tend to miss parts of shadows if rendered with either back or front culling + ::glDisable( GL_CULL_FACE ); + break; + } + default: { + break; + } + } // first pass, material 1 for( auto first { First }; first != Last; ++first ) { @@ -2824,7 +2835,16 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, track->EnvironmentReset(); break; } - case rendermode::shadows: // shadows are calculated only for trackbeds + case rendermode::shadows: { + if( ( std::abs( track->fTexHeight1 ) < 0.35f ) + || ( track->iCategoryFlag != 2 ) ) { + // shadows are only calculated for high enough roads, typically meaning track platforms + continue; + } + Bind_Material( track->m_material1 ); + m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); + break; + } case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach case rendermode::pickcontrols: default: { @@ -2854,9 +2874,10 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, break; } case rendermode::shadows: { - if( ( track->iCategoryFlag != 1 ) - || ( track->eType != tt_Normal ) ) { - // shadows are only calculated for trackbeds + if( ( std::abs( track->fTexHeight1 ) < 0.35f ) + || ( ( track->iCategoryFlag == 1 ) + && ( track->eType != tt_Normal ) ) ) { + // shadows are only calculated for high enough trackbeds continue; } Bind_Material( track->m_material2 ); @@ -2870,6 +2891,16 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, } } } + // post-render reset + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + ::glEnable( GL_CULL_FACE ); + break; + } + default: { + break; + } + } } void @@ -3218,11 +3249,11 @@ opengl_renderer::Render_Alpha( TAnimModel *Instance ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Instance->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Instance->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -3238,7 +3269,7 @@ opengl_renderer::Render_Alpha( TAnimModel *Instance ) { Instance->pModel, Instance->Material(), distancesquared, - Instance->m_location - m_renderpass.camera.position(), + Instance->location() - m_renderpass.camera.position(), Instance->vAngle ); } } @@ -3250,11 +3281,11 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Traction->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Traction->location() - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Traction->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Traction->location() - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -3272,15 +3303,29 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { return; } // setup +/* float const linealpha = static_cast( std::min( 1.25, 5000 * Traction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre ::glLineWidth( linealpha ); +*/ + auto const distance { static_cast( std::sqrt( distancesquared ) ) }; + auto const linealpha { + 20.f * Traction->WireThickness + / std::max( + 0.5f * Traction->radius() + 1.f, + distance - ( 0.5f * Traction->radius() ) ) }; + ::glLineWidth( + clamp( + 0.5f * linealpha + Traction->WireThickness * Traction->radius() / 1000.f, + 1.f, 1.5f ) ); // McZapkie-261102: kolor zalezy od materialu i zasniedzenia - auto const color { Traction->wire_color() }; - ::glColor4f( color.r, color.g, color.b, linealpha ); - + ::glColor4fv( + glm::value_ptr( + glm::vec4{ + Traction->wire_color(), + linealpha } ) ); // render m_geometry.draw( Traction->m_geometry ); // debug data @@ -3318,12 +3363,15 @@ opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) { 0.5f * data.area.radius + 1.f, distance - ( 0.5f * data.area.radius ) ) : 1.f ); // negative width means the lines are always opague + ::glLineWidth( + clamp( + 0.5f * linealpha + data.line_width * data.area.radius / 1000.f, + 1.f, 8.f ) ); ::glColor4fv( glm::value_ptr( glm::vec4{ glm::vec3{ data.lighting.diffuse * Global::DayLight.ambient }, // w zaleznosci od koloru swiatla std::min( 1.f, linealpha ) } ) ); - ::glLineWidth( clamp( 0.5f * linealpha + data.line_width * data.area.radius / 1000.f, 1.f, 8.f ) ); // render m_geometry.draw( data.geometry ); ++m_debugstats.lines; diff --git a/scene.cpp b/scene.cpp index 4cc80791..e9834f2b 100644 --- a/scene.cpp +++ b/scene.cpp @@ -242,12 +242,9 @@ basic_cell::insert( TTrack *Path ) { Path->RaOwnerSet( this ); #endif // re-calculate cell radius, in case track extends outside the cell's boundaries - auto endpoints = Path->endpoints(); - for( auto &endpoint : endpoints ) { - m_area.radius = std::max( - m_area.radius, - glm::length( m_area.center - endpoint ) + 25.f ); // extra margin to prevent driven vehicle from flicking - } + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - Path->location() ) + Path->radius() + 25.f ) ); // extra margin to prevent driven vehicle from flicking } // adds provided traction piece to the cell @@ -258,13 +255,8 @@ basic_cell::insert( TTraction *Traction ) { Traction->origin( m_area.center ); m_traction.emplace_back( Traction ); - // re-calculate cell radius, in case traction piece extends outside the cell's boundaries - auto endpoints = Traction->endpoints(); - for( auto &endpoint : endpoints ) { - m_area.radius = std::max( - m_area.radius, - glm::length( m_area.center - endpoint ) ); // adding arbitrary safety margin - } + // re-calculate cell bounding area, in case traction piece extends outside the cell's boundaries + enclose_area( Traction ); } // adds provided model instance to the cell @@ -289,15 +281,8 @@ basic_cell::insert( TAnimModel *Instance ) { // opaque pieces m_instancesopaque.emplace_back( Instance ); } - // re-calculate cell radius, in case model extends outside the cell's boundaries - if( Instance->Model() ) { - auto const modelradius{ Instance->Model()->bounding_radius() }; - if( modelradius > 0.f ) { - m_area.radius = std::max( - m_area.radius, - glm::length( m_area.center - Instance->location() ) + modelradius ); // adding arbitrary safety margin - } - } + // re-calculate cell bounding area, in case model extends outside the cell's boundaries + enclose_area( Instance ); } // adds provided sound instance to the cell @@ -316,6 +301,8 @@ basic_cell::insert( TEventLauncher *Launcher ) { m_active = true; m_eventlaunchers.emplace_back( Launcher ); + // re-calculate cell bounding area, in case launcher range extends outside the cell's boundaries + enclose_area( Launcher ); } // registers provided path in the lookup directory of the cell @@ -485,6 +472,15 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { m_geometrycreated = true; // helper for legacy animation code, get rid of it after refactoring } +// adjusts cell bounding area to enclose specified node +void +basic_cell::enclose_area( editor::basic_node *Node ) { + + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - Node->location() ) + Node->radius() ) ); +} + // legacy method, updates sounds and polls event launchers within radius around specified point @@ -512,7 +508,7 @@ basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographind auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); - auto const radius { EU07_CELLSIZE * 0.5 }; + auto const radius { EU07_CELLSIZE * 0.5 }; // redius around point of interest for( auto &cell : m_cells ) { // we reject early cells which aren't within our area of interest @@ -548,6 +544,11 @@ basic_section::insert( shape_node Shape ) { } else { // large, opaque shapes are placed on section level + // re-calculate section radius, in case shape geometry extends outside the section's boundaries + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - Shape.data().area.center ) + Shape.data().area.radius ) ); + for( auto &shape : m_shapes ) { // check first if the shape can't be merged with one of the shapes already present in the section if( true == shape.merge( Shape ) ) { @@ -739,6 +740,19 @@ basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographinde } } +// stores content of the class in file with specified name +void +basic_region::serialize( std::string const &Scenariofile ) { + // TODO: implement +} + +// restores content of the class from file with specified name. returns: true on success, false otherwise +bool +basic_region::deserialize( std::string const &Scenariofile ) { + // TODO: implement + return false; +} + // legacy method, links specified path piece with potential neighbours void basic_region::TrackJoin( TTrack *Track ) { @@ -792,9 +806,9 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool con // adjust input if necessary: if( true == Transform ) { // shapes generated from legacy terrain come with world space coordinates and don't need processing - if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { + if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) { // rotate... - auto const rotation = glm::radians( Scratchpad.location_rotation ); + auto const rotation = glm::radians( Scratchpad.location.rotation ); for( auto &vertex : shape.m_data.vertices ) { vertex.position = glm::rotateZ( vertex.position, rotation.z ); vertex.position = glm::rotateX( vertex.position, rotation.x ); @@ -804,10 +818,10 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool con vertex.normal = glm::rotateY( vertex.normal, rotation.y ); } } - if( ( false == Scratchpad.location_offset.empty() ) - && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { - // ...and move - auto const offset = Scratchpad.location_offset.top(); + if( ( false == Scratchpad.location.offset.empty() ) + && ( Scratchpad.location.offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { + // ...and move + auto const offset = Scratchpad.location.offset.top(); for( auto &vertex : shape.m_data.vertices ) { vertex.position += offset; } @@ -850,19 +864,19 @@ basic_region::insert_lines( lines_node Lines, scratch_data &Scratchpad ) { if( Lines.m_data.vertices.empty() ) { return; } // transform point coordinates if needed - if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { + if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) { // rotate... - auto const rotation = glm::radians( Scratchpad.location_rotation ); + auto const rotation = glm::radians( Scratchpad.location.rotation ); for( auto &vertex : Lines.m_data.vertices ) { vertex.position = glm::rotateZ( vertex.position, rotation.z ); vertex.position = glm::rotateX( vertex.position, rotation.x ); vertex.position = glm::rotateY( vertex.position, rotation.y ); } } - if( ( false == Scratchpad.location_offset.empty() ) - && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { + if( ( false == Scratchpad.location.offset.empty() ) + && ( Scratchpad.location.offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { // ...and move - auto const offset = Scratchpad.location_offset.top(); + auto const offset = Scratchpad.location.offset.top(); for( auto &vertex : Lines.m_data.vertices ) { vertex.position += offset; } @@ -1073,7 +1087,7 @@ basic_region::sections( glm::dvec3 const &Point, float const Radius ) { int const originx = centerx - sectioncount / 2; int const originz = centerz - sectioncount / 2; - auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) + Radius, 2 ) }; + auto const padding { 0.0 }; // { EU07_SECTIONSIZE * 0.25 }; // TODO: check if we can get away with padding of 0 for( int row = originz; row <= originz + sectioncount; ++row ) { if( row < 0 ) { continue; } @@ -1084,7 +1098,7 @@ basic_region::sections( glm::dvec3 const &Point, float const Radius ) { auto *section { m_sections[ row * EU07_REGIONSIDESECTIONCOUNT + column ] }; if( ( section != nullptr ) - && ( glm::length2( section->area().center - Point ) < squaredradii ) ) { + && ( glm::length2( section->area().center - Point ) <= ( ( section->area().radius + padding + Radius ) * ( section->area().radius + padding + Radius ) ) ) ) { m_scratchpad.sections.emplace_back( section ); } diff --git a/scene.h b/scene.h index 999d0514..8de91971 100644 --- a/scene.h +++ b/scene.h @@ -29,8 +29,16 @@ int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of sections along a side struct scratch_data { - std::stack location_offset; - glm::vec3 location_rotation; + struct binary_data { + + bool terrain{ false }; + } binary; + + struct location_data { + + std::stack offset; + glm::vec3 rotation; + } location; struct trainset_data { @@ -129,6 +137,9 @@ private: using instance_sequence = std::vector; using sound_sequence = std::vector; using eventlauncher_sequence = std::vector; +// methods + void + enclose_area( editor::basic_node *Node ); // members scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE ) }; bool m_active { false }; // whether the cell holds any actual data @@ -182,9 +193,9 @@ public: auto &targetcell { cell( Node->location() ) }; targetcell.insert( Node ); // some node types can extend bounding area of the target cell - m_area.radius = std::max( + m_area.radius = std::max( m_area.radius, - glm::length( m_area.center - targetcell.area().center ) + targetcell.area().radius ); } + static_cast( glm::length( m_area.center - targetcell.area().center ) + targetcell.area().radius ) ); } // registers provided node in the lookup directory of the section enclosing specified point template void @@ -223,7 +234,7 @@ private: cell( glm::dvec3 const &Location ); // members // placement and visibility - scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) }; + scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_SECTIONSIZE ) }; // content cell_array m_cells; // partitioning scheme shapenode_sequence m_shapes; // large pieces of opaque geometry and (legacy) terrain @@ -250,6 +261,12 @@ public: // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle void update_traction( TDynamicObject *Vehicle, int const Pantographindex ); + // stores content of the class in file with specified name + void + serialize( std::string const &Scenariofile ); + // restores content of the class from file with specified name. returns: true on success, false otherwise + bool + deserialize( std::string const &Scenariofile ); // legacy method, links specified path piece with potential neighbours void TrackJoin( TTrack *Track ); diff --git a/scenenode.cpp b/scenenode.cpp index f72b1cb2..fc20188f 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -259,9 +259,9 @@ shape_node::convert( TSubModel const *Submodel ) { squareradius, glm::length2( vertex.position - m_data.area.center ) ); } - m_data.area.radius = std::max( + m_data.area.radius = std::max( m_data.area.radius, - std::sqrt( squareradius ) ); + static_cast( std::sqrt( squareradius ) ) ); return *this; } @@ -497,5 +497,23 @@ basic_node::basic_node( scene::node_data const &Nodedata ) : std::numeric_limits::max() ); } +float const & +basic_node::radius() { + + if( m_area.radius == -1.0 ) { + // calculate if needed + radius_(); + } + return m_area.radius; +} + +// radius() subclass details, calculates node's bounding radius +// by default nodes are 'virtual don't extend from their center point +void +basic_node::radius_() { + + m_area.radius = 0.f; +} + } // editor //--------------------------------------------------------------------------- diff --git a/scenenode.h b/scenenode.h index 4b23d1ce..d30b420b 100644 --- a/scenenode.h +++ b/scenenode.h @@ -288,10 +288,12 @@ public: return m_name; } void location( glm::dvec3 const Location ) { - m_location = Location; } + m_area.center = Location; } glm::dvec3 const & location() const { - return m_location; }; + return m_area.center; }; + float const & + radius(); void visible( bool const Visible ) { m_visible = Visible; } @@ -300,8 +302,11 @@ public: return m_visible; } protected: +// methods + // radius() subclass details, calculates node's bounding radius + virtual void radius_(); // members - glm::dvec3 m_location; + scene::bounding_area m_area; bool m_visible { true }; double m_rangesquaredmin { 0.0 }; // visibility range, min double m_rangesquaredmax { 0.0 }; // visibility range, max diff --git a/simulation.cpp b/simulation.cpp index 9bd163e5..1a99189c 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -38,11 +38,16 @@ state_manager::deserialize( std::string const &Scenariofile ) { // TODO: check first for presence of serialized binary files // if this fails, fall back on the legacy text format + scene::scratch_data importscratchpad; + importscratchpad.binary.terrain = Region->deserialize( Scenariofile ); + // NOTE: for the time being import from text format is a given, since we don't have full binary serialization cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global::asCurrentSceneryPath, Global::bLoadTraction ); if( false == scenarioparser.ok() ) { return false; } - deserialize( scenarioparser ); + deserialize( scenarioparser, importscratchpad ); + // if we didn't find usable binary version of the scenario files, create them now for future use + if( false == importscratchpad.binary.terrain ) { Region->serialize( Scenariofile ); } Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; @@ -67,9 +72,8 @@ state_manager::update( double const Deltatime, int Iterationcount ) { // restores class data from provided stream void -state_manager::deserialize( cParser &Input ) { +state_manager::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) { - scene::scratch_data importscratchpad; // prepare deserialization function table // since all methods use the same objects, we can have simple, hard-coded binds or lambdas for the task using deserializefunction = void(state_manager::*)(cParser &, scene::scratch_data &); @@ -98,7 +102,7 @@ state_manager::deserialize( cParser &Input ) { std::string, deserializefunctionbind> functionmap; for( auto &function : functionlist ) { - functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( importscratchpad ) ) ); + functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) ); } // deserialize content from the provided input @@ -127,9 +131,9 @@ state_manager::deserialize( cParser &Input ) { token = Input.getToken(); } - if( false == importscratchpad.initialized ) { + if( false == Scratchpad.initialized ) { // manually perform scenario initialization - deserialize_firstinit( Input, importscratchpad ); + deserialize_firstinit( Input, Scratchpad ); } } @@ -236,12 +240,12 @@ state_manager::deserialize_event( cParser &Input, scene::scratch_data &Scratchpa // TODO: refactor event class and its de/serialization. do offset and rotation after deserialization is done auto *event = new TEvent(); Math3D::vector3 offset = ( - Scratchpad.location_offset.empty() ? + Scratchpad.location.offset.empty() ? Math3D::vector3() : Math3D::vector3( - Scratchpad.location_offset.top().x, - Scratchpad.location_offset.top().y, - Scratchpad.location_offset.top().z ) ); + Scratchpad.location.offset.top().x, + Scratchpad.location.offset.top().y, + Scratchpad.location.offset.top().z ) ); event->Load( &Input, offset ); if( false == simulation::Events.insert( event ) ) { @@ -386,20 +390,34 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad || ( nodedata.type == "triangle_strip" ) || ( nodedata.type == "triangle_fan" ) ) { - simulation::Region->insert_shape( - scene::shape_node().deserialize( - Input, nodedata ), - Scratchpad, - true ); + if( false == Scratchpad.binary.terrain ) { + + simulation::Region->insert_shape( + scene::shape_node().deserialize( + Input, nodedata ), + Scratchpad, + true ); + } + else { + // all shapes were already loaded from the binary version of the file + skip_until( Input, "endtri" ); + } } else if( ( nodedata.type == "lines" ) || ( nodedata.type == "line_strip" ) || ( nodedata.type == "line_loop" ) ) { - simulation::Region->insert_lines( - scene::lines_node().deserialize( - Input, nodedata ), - Scratchpad ); + if( false == Scratchpad.binary.terrain ) { + + simulation::Region->insert_lines( + scene::lines_node().deserialize( + Input, nodedata ), + Scratchpad ); + } + else { + // all lines were already loaded from the binary version of the file + skip_until( Input, "endline" ); + } } else if( nodedata.type == "memcell" ) { @@ -448,18 +466,18 @@ state_manager::deserialize_origin( cParser &Input, scene::scratch_data &Scratchp >> offset.y >> offset.z; // sumowanie całkowitego przesunięcia - Scratchpad.location_offset.emplace( + Scratchpad.location.offset.emplace( offset + ( - Scratchpad.location_offset.empty() ? + Scratchpad.location.offset.empty() ? glm::dvec3() : - Scratchpad.location_offset.top() ) ); + Scratchpad.location.offset.top() ) ); } void state_manager::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) { - if( false == Scratchpad.location_offset.empty() ) { - Scratchpad.location_offset.pop(); + if( false == Scratchpad.location.offset.empty() ) { + Scratchpad.location.offset.pop(); } else { ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); @@ -471,9 +489,9 @@ state_manager::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchp Input.getTokens( 3 ); Input - >> Scratchpad.location_rotation.x - >> Scratchpad.location_rotation.y - >> Scratchpad.location_rotation.z; + >> Scratchpad.location.rotation.x + >> Scratchpad.location.rotation.y + >> Scratchpad.location.rotation.z; } void @@ -584,12 +602,12 @@ state_manager::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad // TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done auto *track = new TTrack( Nodedata ); Math3D::vector3 offset = ( - Scratchpad.location_offset.empty() ? + Scratchpad.location.offset.empty() ? Math3D::vector3() : Math3D::vector3( - Scratchpad.location_offset.top().x, - Scratchpad.location_offset.top().y, - Scratchpad.location_offset.top().z ) ); + Scratchpad.location.offset.top().x, + Scratchpad.location.offset.top().y, + Scratchpad.location.offset.top().z ) ); track->Load( &Input, offset ); return track; @@ -605,9 +623,9 @@ state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratc // TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done auto *traction = new TTraction( Nodedata ); auto offset = ( - Scratchpad.location_offset.empty() ? + Scratchpad.location.offset.empty() ? glm::dvec3() : - Scratchpad.location_offset.top() ); + Scratchpad.location.offset.top() ); traction->Load( &Input, offset ); return traction; @@ -670,7 +688,7 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa >> rotation.y; auto *instance = new TAnimModel( Nodedata ); - instance->RaAnglesSet( Scratchpad.location_rotation + rotation ); // dostosowanie do pochylania linii + instance->RaAnglesSet( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii if( false == instance->Load( &Input, false ) ) { // model nie wczytał się - ignorowanie node @@ -838,12 +856,12 @@ state_manager::skip_until( cParser &Input, std::string const &Token ) { glm::dvec3 state_manager::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) { - if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { - auto const rotation = glm::radians( Scratchpad.location_rotation ); + if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) { + auto const rotation = glm::radians( Scratchpad.location.rotation ); Location = glm::rotateY( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji } - if( false == Scratchpad.location_offset.empty() ) { - Location += Scratchpad.location_offset.top(); + if( false == Scratchpad.location.offset.empty() ) { + Location += Scratchpad.location.offset.top(); } return Location; } diff --git a/simulation.h b/simulation.h index 14740ecd..4addbe49 100644 --- a/simulation.h +++ b/simulation.h @@ -40,7 +40,7 @@ public: private: // methods // restores class data from provided stream - void deserialize( cParser &Input ); + void deserialize( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ); diff --git a/version.h b/version.h index e45b45fa..6fe44f69 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 1025 +#define VERSION_MINOR 1027 #define VERSION_REVISION 0