diff --git a/DynObj.cpp b/DynObj.cpp index 6c799cbc..7e6a5b6e 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -621,26 +621,26 @@ TDynamicObject::toggle_lights() { if( true == SectionLightsActive ) { // switch all lights off - for( auto §ionlight : SectionLightLevels ) { - sectionlight.level = 0.0f; + for( auto §ion : Sections ) { + section.light_level = 0.0f; } SectionLightsActive = false; } else { // set lights with probability depending on the compartment type. TODO: expose this in .mmd file - for( auto §ionlight : SectionLightLevels ) { + for( auto §ion : Sections ) { - std::string const &compartmentname = sectionlight.compartment->pName; - if( ( compartmentname.find( "corridor" ) == 0 ) - || ( compartmentname.find( "korytarz" ) == 0 ) ) { + auto const sectionname { section.compartment->pName }; + if( ( sectionname.find( "corridor" ) == 0 ) + || ( sectionname.find( "korytarz" ) == 0 ) ) { // corridors are lit 100% of time - sectionlight.level = 0.75f; + section.light_level = 0.75f; } else if( - ( compartmentname.find( "compartment" ) == 0 ) - || ( compartmentname.find( "przedzial" ) == 0 ) ) { + ( sectionname.find( "compartment" ) == 0 ) + || ( sectionname.find( "przedzial" ) == 0 ) ) { // compartments are lit with 75% probability - sectionlight.level = ( Random() < 0.75 ? 0.75f : 0.15f ); + section.light_level = ( Random() < 0.75 ? 0.75f : 0.15f ); } } SectionLightsActive = true; @@ -1024,10 +1024,10 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) // else btHeadSignals23.TurnOff(); } // interior light levels - for( auto const §ion : SectionLightLevels ) { - section.compartment->SetLightLevel( section.level, true ); + for( auto const §ion : Sections ) { + section.compartment->SetLightLevel( section.light_level, true ); if( section.load != nullptr ) { - section.load->SetLightLevel( section.level, true ); + section.load->SetLightLevel( section.light_level, true ); } } // load chunks visibility @@ -2161,35 +2161,21 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" // check the low poly interior for potential compartments of interest, ie ones which can be individually lit // TODO: definition of relevant compartments in the .mmd file TSubModel *submodel { nullptr }; - if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); } - if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); } - if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { Sections.push_back( { submodel, nullptr, 0.0f } ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { Sections.push_back( { submodel, nullptr, 0.0f } ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { Sections.push_back( { submodel, nullptr, 0.0f } ); } // passenger car compartments std::vector nameprefixes = { "corridor", "korytarz", "compartment", "przedzial" }; - int compartmentindex; - std::string compartmentname; for( auto const &nameprefix : nameprefixes ) { - compartmentindex = 0; - do { - compartmentname = - nameprefix + ( - compartmentindex < 10 ? - "0" + std::to_string( compartmentindex ) : - std::to_string( compartmentindex ) ); - submodel = mdLowPolyInt->GetFromName( compartmentname ); - if( submodel != nullptr ) { - SectionLightLevels.push_back( { - submodel, - nullptr, // pointers to load sections are generated afterwards - 0.0f } ); - } - ++compartmentindex; - } while( ( submodel != nullptr ) - || ( compartmentindex < 2 ) ); // chain can start from prefix00 or prefix01 + init_sections( mdLowPolyInt, nameprefix ); } - update_load_sections(); - update_load_visibility(); } + // 'external_load' is an optional special section in the main model, pointing to submodel of external load + if( mdModel ) { + init_sections( mdModel, "external_load" ); + } + update_load_sections(); + update_load_visibility(); // wyszukiwanie zderzakow if( mdModel ) { // jeśli ma w czym szukać @@ -2277,6 +2263,35 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" return MoverParameters->Dim.L; } +int +TDynamicObject::init_sections( TModel3d const *Model, std::string const &Nameprefix ) { + + std::string sectionname; + auto sectioncount = 0; + auto sectionindex = 0; + TSubModel *sectionsubmodel { nullptr }; + + do { + sectionname = + Nameprefix + ( + sectionindex < 10 ? + "0" + std::to_string( sectionindex ) : + std::to_string( sectionindex ) ); + sectionsubmodel = Model->GetFromName( sectionname ); + if( sectionsubmodel != nullptr ) { + Sections.push_back( { + sectionsubmodel, + nullptr, // pointers to load sections are generated afterwards + 0.0f } ); + ++sectioncount; + } + ++sectionindex; + } while( ( sectionsubmodel != nullptr ) + || ( sectionindex < 2 ) ); // chain can start from prefix00 or prefix01 + + return sectioncount; +} + void TDynamicObject::create_controller( std::string const Type, bool const Trainset ) { @@ -2580,6 +2595,9 @@ void TDynamicObject::LoadExchange( int const Disembark, int const Embark, int co // update state of load exchange operation void TDynamicObject::update_exchange( double const Deltatime ) { + // TODO: move offset calculation after initial check, when the loading system is all unified + update_load_offset(); + if( ( m_exchange.unload_count < 0.01 ) && ( m_exchange.load_count < 0.01 ) ) { return; } if( ( MoverParameters->Vel < 2.0 ) @@ -2686,7 +2704,7 @@ TDynamicObject::update_load_sections() { SectionLoadVisibility.clear(); - for( auto §ion : SectionLightLevels ) { + for( auto §ion : Sections ) { section.load = ( mdLoad != nullptr ? @@ -2737,6 +2755,19 @@ TDynamicObject::update_load_visibility() { } } ); } +void +TDynamicObject::update_load_offset() { + + if( MoverParameters->LoadMinOffset == 0.f ) { return; } + + auto const loadpercentage { ( + MoverParameters->MaxLoad == 0.0 ? + 0.0 : + 100.0 * MoverParameters->Load / MoverParameters->MaxLoad ) }; + + LoadOffset = interpolate( MoverParameters->LoadMinOffset, 0.f, clamp( 0.0, 1.0, loadpercentage * 0.01 ) ); +} + void TDynamicObject::shuffle_load_sections() { @@ -2748,6 +2779,8 @@ TDynamicObject::shuffle_load_sections() { return ( ( section.submodel->pName.find( "compartment" ) == 0 ) || ( section.submodel->pName.find( "przedzial" ) == 0 ) ); } ); + // NOTE: potentially we're left with a mix of corridor and external section loads + // but that's not necessarily a wrong outcome, so we leave it this way for the time being } /* @@ -3807,10 +3840,11 @@ bool TDynamicObject::Update(double dt, double dt1) MoverParameters->DerailReason = 0; //żeby tylko raz } - update_exchange( dt ); - if (MoverParameters->LoadStatus) + if( MoverParameters->LoadStatus ) { LoadUpdate(); // zmiana modelu ładunku - + } + update_exchange( dt ); + return true; // Ra: chyba tak? } @@ -3848,9 +3882,11 @@ bool TDynamicObject::FastUpdate(double dt) // ResetdMoveLen(); FastMove(dDOMoveLen); - update_exchange( dt ); - if (MoverParameters->LoadStatus) + if( MoverParameters->LoadStatus ) { LoadUpdate(); // zmiana modelu ładunku + } + update_exchange( dt ); + return true; // Ra: chyba tak? } diff --git a/DynObj.h b/DynObj.h index 6589cd10..094a919a 100644 --- a/DynObj.h +++ b/DynObj.h @@ -196,16 +196,17 @@ public: TModel3d *mdLoad; // model zmiennego ładunku TModel3d *mdKabina; // model kabiny dla użytkownika; McZapkie-030303: to z train.h TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly + float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) + float LoadOffset { 0.f }; glm::vec3 InteriorLight { 0.9f * 255.f / 255.f, 0.9f * 216.f / 255.f, 0.9f * 176.f / 255.f }; // tungsten light. TODO: allow definition of light type? float InteriorLightLevel { 0.0f }; // current level of interior lighting - struct section_light { + struct vehicle_section { TSubModel *compartment; TSubModel *load; - float level; + float light_level; }; - std::vector SectionLightLevels; // table of light levels for specific compartments of associated 3d model + std::vector Sections; // table of recognized vehicle sections bool SectionLightsActive { false }; // flag indicating whether section lights were set. - float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) struct section_visibility { TSubModel *submodel; bool visible; @@ -517,6 +518,7 @@ private: std::string Name, std::string BaseDir, std::string asReplacableSkin, std::string Type_Name, TTrack *Track, double fDist, std::string DriverType, double fVel, std::string TrainName, float Load, std::string LoadType, bool Reversed, std::string); + int init_sections( TModel3d const *Model, std::string const &Nameprefix ); void create_controller( std::string const Type, bool const Trainset ); void AttachPrev(TDynamicObject *Object, int iType = 1); bool UpdateForce(double dt, double dt1, bool FullVer); @@ -525,6 +527,7 @@ private: void LoadUpdate(); void update_load_sections(); void update_load_visibility(); + void update_load_offset(); void shuffle_load_sections(); bool Update(double dt, double dt1); bool FastUpdate(double dt); diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index c4bc99e3..a5512bce 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -947,6 +947,7 @@ public: float MaxLoad = 0.f; /*masa w T lub ilosc w sztukach - ladownosc*/ std::string LoadAccepted; std::string LoadQuantity; /*co moze byc zaladowane, jednostki miary*/ double OverLoadFactor = 0.0; /*ile razy moze byc przekroczona ladownosc*/ + float LoadMinOffset { 0.f }; // offset applied to cargo model when load amount is 0 float LoadSpeed = 0.f; float UnLoadSpeed = 0.f;/*szybkosc na- i rozladunku jednostki/s*/ int DoorOpenCtrl = 0; int DoorCloseCtrl = 0; /*0: przez pasazera, 1: przez maszyniste, 2: samoczynne (zamykanie)*/ double DoorStayOpen = 0.0; /*jak dlugo otwarte w przypadku DoorCloseCtrl=2*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b2ce0b5c..74bc21e5 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -6624,12 +6624,9 @@ TMoverParameters::signal_departure( bool const State, range_t const Notify ) { void TMoverParameters::update_autonomous_doors( double const Deltatime ) { - if( ( DoorCloseCtrl != control_t::autonomous ) - || ( ( false == DoorLeftOpened ) - && ( false == DoorRightOpened ) ) ) { - // nothing to do - return; - } + if( DoorCloseCtrl != control_t::autonomous ) { return; } + if( ( false == DoorLeftOpened ) + && ( false == DoorRightOpened ) ) { return; } if( DoorStayOpen > 0.0 ) { // update door timers if the door close after defined time @@ -6641,18 +6638,19 @@ TMoverParameters::update_autonomous_doors( double const Deltatime ) { if( true == DoorLeftOpened ) { DoorLeftOpenTimer = DoorStayOpen; } if( true == DoorRightOpened ) { DoorRightOpenTimer = DoorStayOpen; } } - // the door are closed if their timer goes below 0, or if the vehicle is moving at > 5 km/h + // the door are closed if their timer goes below 0, or if the vehicle is moving at > 10 km/h + auto const closingspeed { 10.0 }; // NOTE: timer value of 0 is 'special' as it means the door will stay open until vehicle is moving if( true == DoorLeftOpened ) { if( ( ( DoorStayOpen > 0.0 ) && ( DoorLeftOpenTimer < 0.0 ) ) - || ( Vel > 5.0 ) ) { + || ( Vel > closingspeed ) ) { // close the door and set the timer to expired state (closing may happen sooner if vehicle starts moving) DoorLeft( false, range_t::local ); } } if( true == DoorRightOpened ) { if( ( ( DoorStayOpen > 0.0 ) && ( DoorRightOpenTimer < 0.0 ) ) - || ( Vel > 5.0 ) ) { + || ( Vel > closingspeed ) ) { // close the door and set the timer to expired state (closing may happen sooner if vehicle starts moving) DoorRight( false, range_t::local ); } @@ -7576,6 +7574,8 @@ void TMoverParameters::LoadFIZ_Load( std::string const &line ) { extract_value( OverLoadFactor, "OverLoadFactor", line, "" ); extract_value( LoadSpeed, "LoadSpeed", line, "" ); extract_value( UnLoadSpeed, "UnLoadSpeed", line, "" ); + + extract_value( LoadMinOffset, "LoadMinOffset", line, "" ); } void TMoverParameters::LoadFIZ_Dimensions( std::string const &line ) { diff --git a/Model3d.cpp b/Model3d.cpp index 08eb2292..a2010d63 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1154,7 +1154,7 @@ void TModel3d::AddTo(TSubModel *tmp, TSubModel *SubModel) { iFlags |= 0x0200; // submodele są oddzielne }; -TSubModel *TModel3d::GetFromName(std::string const &Name) +TSubModel *TModel3d::GetFromName(std::string const &Name) const { // wyszukanie submodelu po nazwie if (Name.empty()) return Root; // potrzebne do terenu z E3D diff --git a/Model3d.h b/Model3d.h index 157bb992..7c8317a1 100644 --- a/Model3d.h +++ b/Model3d.h @@ -241,7 +241,7 @@ public: Root->m_boundingradius : 0.f ); } inline TSubModel * GetSMRoot() { return (Root); }; - TSubModel * GetFromName(std::string const &Name); + TSubModel * GetFromName(std::string const &Name) const; TSubModel * AddToNamed(const char *Name, TSubModel *SubModel); void AddTo(TSubModel *tmp, TSubModel *SubModel); void LoadFromTextFile(std::string const &FileName, bool dynamic); diff --git a/renderer.cpp b/renderer.cpp index 3a508f7f..a85679c0 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -2151,7 +2151,7 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); if( Dynamic->mdLoad ) // renderowanie nieprzezroczystego ładunku - Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance ); + Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); // post-render cleanup m_renderspecular = false; @@ -3096,7 +3096,7 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { Render_Alpha( Dynamic->mdModel, Dynamic->Material(), squaredistance ); if( Dynamic->mdLoad ) // renderowanie nieprzezroczystego ładunku - Render_Alpha( Dynamic->mdLoad, Dynamic->Material(), squaredistance ); + Render_Alpha( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); // post-render cleanup m_renderspecular = false;