From 8d910e434ffb8b1fedb25a4ee65292053612aecb Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 12 Nov 2018 23:26:29 +0100 Subject: [PATCH 1/6] compartment lighting system enhancement, lowpoly system tweaks, minor bug fixes --- Button.cpp | 27 ++- Button.h | 4 +- DynObj.cpp | 55 ++++- DynObj.h | 4 +- Gauge.cpp | 21 +- Gauge.h | 2 +- Model3d.cpp | 9 +- Model3d.h | 2 +- Train.cpp | 527 ++++++++++++++++++++++++------------------- Train.h | 20 +- drivermode.cpp | 101 ++++++--- drivermode.h | 7 + drivermouseinput.cpp | 1 + renderer.cpp | 64 ++++-- renderer.h | 2 +- 15 files changed, 507 insertions(+), 339 deletions(-) diff --git a/Button.cpp b/Button.cpp index 3d8d5511..219c1f64 100644 --- a/Button.cpp +++ b/Button.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "Button.h" #include "parser.h" #include "Model3d.h" +#include "DynObj.h" #include "Console.h" #include "Logs.h" #include "renderer.h" @@ -25,17 +26,19 @@ void TButton::Clear(int i) Update(); // kasowanie bitu Feedback, o ile jakiś ustawiony }; -void TButton::Init( std::string const &asName, TModel3d *pModel, bool bNewOn ) { +bool TButton::Init( std::string const &asName, TModel3d const *pModel, bool bNewOn ) { - if( pModel == nullptr ) { return; } + if( pModel == nullptr ) { return false; } pModelOn = pModel->GetFromName( asName + "_on" ); pModelOff = pModel->GetFromName( asName + "_off" ); m_state = bNewOn; Update(); + + return( ( pModelOn != nullptr ) || ( pModelOff != nullptr ) ); }; -void TButton::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pModel1, TModel3d *pModel2 ) { +void TButton::Load( cParser &Parser, TDynamicObject const *Owner ) { std::string submodelname; @@ -57,21 +60,17 @@ void TButton::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pMod m_soundfxincrease.owner( Owner ); m_soundfxdecrease.owner( Owner ); - if( pModel1 ) { - // poszukiwanie submodeli w modelu - Init( submodelname, pModel1, false ); + std::array sources { Owner->mdKabina, Owner->mdLowPolyInt, Owner->mdModel }; + for( auto const *source : sources ) { + if( true == Init( submodelname, source, false ) ) { + // got what we wanted, bail out + break; + } } - if( ( pModelOn == nullptr ) - && ( pModelOff == nullptr ) - && ( pModel2 != nullptr ) ) { - // poszukiwanie submodeli w modelu - Init( submodelname, pModel2, false ); - } - if( ( pModelOn == nullptr ) && ( pModelOff == nullptr ) ) { // if we failed to locate even one state submodel, cry - ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + ( pModel1 != nullptr ? pModel1->NameGet() : pModel2 != nullptr ? pModel2->NameGet() : "NULL" ) + "\"", logtype::model ); + ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model(s) of \"" + Owner->name() + "\"", logtype::model ); } // pass submodel location to defined sounds diff --git a/Button.h b/Button.h index bfb993ac..67bd312d 100644 --- a/Button.h +++ b/Button.h @@ -31,8 +31,8 @@ public: return ( ( pModelOn != nullptr ) || ( pModelOff != nullptr ) ); } void Update(); - void Init( std::string const &asName, TModel3d *pModel, bool bNewOn = false ); - void Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pModel1, TModel3d *pModel2 = nullptr ); + bool Init( std::string const &asName, TModel3d const *pModel, bool bNewOn = false ); + void Load( cParser &Parser, TDynamicObject const *Owner ); void AssignBool(bool const *bValue); // returns offset of submodel associated with the button from the model centre glm::vec3 model_offset() const; diff --git a/DynObj.cpp b/DynObj.cpp index dc067d34..058e66a0 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -648,16 +648,14 @@ TDynamicObject::toggle_lights() { } void -TDynamicObject::set_cab_lights( float const Level ) { - - if( Level == InteriorLightLevel ) { return; } - - InteriorLightLevel = Level; +TDynamicObject::set_cab_lights( int const Cab, float const Level ) { for( auto §ion : Sections ) { // cab compartments are placed at the beginning of the list, so we can bail out as soon as we find different compartment type auto const sectionname { section.compartment->pName }; + if( sectionname.size() < 4 ) { return; } if( sectionname.find( "cab" ) != 0 ) { return; } + if( sectionname[ 3 ] != Cab + '0' ) { continue; } // match the cab with correct index section.light_level = Level; } @@ -985,7 +983,8 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) btnOn = true; } - if( ( Mechanik != nullptr ) + if( ( false == bDisplayCab ) // edge case, lowpoly may act as a stand-in for the hi-fi cab, so make sure not to show the driver when inside + && ( Mechanik != nullptr ) && ( ( Mechanik->GetAction() != TAction::actSleep ) || ( MoverParameters->Battery ) ) ) { // rysowanie figurki mechanika @@ -1040,10 +1039,19 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) // else btHeadSignals23.TurnOff(); } // interior light levels + auto sectionlightcolor { glm::vec4( 1.f ) }; for( auto const §ion : Sections ) { - section.compartment->SetLightLevel( section.light_level, true ); + /* + sectionlightcolor = glm::vec4( InteriorLight, section.light_level ); + */ + sectionlightcolor = glm::vec4( + ( ( ( section.light_level == 0.f ) || ( Global.fLuminance > section.compartment->fLight ) ) ? + glm::vec3( 240.f / 255.f ) : // TBD: save and restore initial submodel diffuse instead of enforcing one? + InteriorLight ), // TODO: per-compartment (type) light color + section.light_level ); + section.compartment->SetLightLevel( sectionlightcolor, true ); if( section.load != nullptr ) { - section.load->SetLightLevel( section.light_level, true ); + section.load->SetLightLevel( sectionlightcolor, true ); } } // load chunks visibility @@ -1062,7 +1070,17 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) sectionchunk = sectionchunk->NextGet(); } } - + // driver cabs visibility + for( int cabidx = 0; cabidx < LowPolyIntCabs.size(); ++cabidx ) { + if( LowPolyIntCabs[ cabidx ] == nullptr ) { continue; } + LowPolyIntCabs[ cabidx ]->iVisible = ( + mdKabina == nullptr ? true : // there's no hi-fi cab + bDisplayCab == false ? true : // we're in external view + simulation::Train == nullptr ? true : // not a player-driven vehicle, implies external view + simulation::Train->Dynamic() != this ? true : // not a player-driven vehicle, implies external view + JointCabs ? false : // internal view, all cabs share the model so hide them 'all' + ( simulation::Train->iCabn != cabidx ) ); // internal view, hide occupied cab and show others + } } // ABu 29.01.05 koniec przeklejenia ************************************* @@ -2185,9 +2203,18 @@ 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 ) { 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 } ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { + Sections.push_back( { submodel, nullptr, 0.0f } ); + LowPolyIntCabs[ 0 ] = submodel; + } + if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { + Sections.push_back( { submodel, nullptr, 0.0f } ); + LowPolyIntCabs[ 1 ] = submodel; + } + if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { + Sections.push_back( { submodel, nullptr, 0.0f } ); + LowPolyIntCabs[ 2 ] = submodel; + } // passenger car compartments std::vector nameprefixes = { "corridor", "korytarz", "compartment", "przedzial" }; for( auto const &nameprefix : nameprefixes ) { @@ -5899,6 +5926,10 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co >> HuntingShake.fadein_end; } + else if( token == "jointcabs:" ) { + parser.getTokens(); + parser >> JointCabs; + } } while( token != "" ); diff --git a/DynObj.h b/DynObj.h index f6308ab7..7725f03b 100644 --- a/DynObj.h +++ b/DynObj.h @@ -197,6 +197,8 @@ 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 + std::array LowPolyIntCabs {}; // pointers to low fidelity version of individual driver cabs + bool JointCabs{ false }; // flag for vehicles with multiple virtual 'cabs' sharing location and 3d model(s) 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? @@ -616,7 +618,7 @@ private: void Damage(char flag); void RaLightsSet(int head, int rear); int LightList( side const Side ) const { return iInventory[ Side ]; } - void set_cab_lights( float const Level ); + void set_cab_lights( int const Cab, float const Level ); TDynamicObject * FirstFind(int &coupler_nr, int cf = 1); float GetEPP(); // wyliczanie sredniego cisnienia w PG int DirectionSet(int d); // ustawienie kierunku w składzie diff --git a/Gauge.cpp b/Gauge.cpp index c9c09490..79218ce8 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "Gauge.h" #include "parser.h" #include "Model3d.h" +#include "DynObj.h" #include "Timer.h" #include "Logs.h" #include "renderer.h" @@ -76,7 +77,7 @@ void TGauge::Init(TSubModel *Submodel, TGaugeAnimation Type, float Scale, float } }; -bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2, double mul ) { +void TGauge::Load( cParser &Parser, TDynamicObject const *Owner, double const mul ) { std::string submodelname, gaugetypename; float scale, endscale, endvalue, offset, friction; @@ -138,13 +139,17 @@ bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, if( interpolatescale ) { endscale *= mul; } - TSubModel *submodel = md1->GetFromName( submodelname ); - if (submodel) // jeśli nie znaleziony - md2 = nullptr; // informacja, że znaleziony - else if (md2) // a jest podany drugi model (np. zewnętrzny) - submodel = md2->GetFromName(submodelname); // to może tam będzie, co za różnica gdzie + TSubModel *submodel { nullptr }; + std::array sources { Owner->mdKabina, Owner->mdLowPolyInt }; + for( auto const *source : sources ) { + if( ( source != nullptr ) + && ( submodel = source->GetFromName( submodelname ) ) != nullptr ) { + // got what we wanted, bail out + break; + } + } if( submodel == nullptr ) { - ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\"", logtype::model ); + ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model(s) of \"" + Owner->name() + "\"", logtype::model ); } std::map gaugetypes { @@ -163,7 +168,7 @@ bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, Init( submodel, type, scale, offset, friction, 0, endvalue, endscale, interpolatescale ); - return md2 != nullptr; // true, gdy podany model zewnętrzny, a w kabinie nie było +// return md2 != nullptr; // true, gdy podany model zewnętrzny, a w kabinie nie było }; bool diff --git a/Gauge.h b/Gauge.h index 82de24af..e812831b 100644 --- a/Gauge.h +++ b/Gauge.h @@ -38,7 +38,7 @@ public: void Clear() { *this = TGauge(); } void Init(TSubModel *Submodel, TGaugeAnimation Type, float Scale = 1, float Offset = 0, float Friction = 0, float Value = 0, float const Endvalue = -1.0, float const Endscale = -1.0, bool const Interpolate = false ); - bool Load(cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2 = nullptr, double mul = 1.0); + void Load(cParser &Parser, TDynamicObject const *Owner, double const mul = 1.0); void UpdateValue( float fNewDesired ); void UpdateValue( float fNewDesired, sound_source &Fallbacksound ); void PutValue(float fNewDesired); diff --git a/Model3d.cpp b/Model3d.cpp index 92a40e31..2977fa30 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -109,9 +109,12 @@ TSubModel::SetVisibilityLevel( float const Level, bool const Includechildren, bo // sets light level (alpha component of illumination color) to specified value void -TSubModel::SetLightLevel( float const Level, bool const Includechildren, bool const Includesiblings ) { - - f4Emision.a = Level; +TSubModel::SetLightLevel( glm::vec4 const &Level, bool const Includechildren, bool const Includesiblings ) { + /* + f4Emision = Level; + */ + f4Diffuse = { Level.r, Level.g, Level.b, f4Diffuse.a }; + f4Emision.a = Level.a; if( true == Includesiblings ) { auto sibling { this }; while( ( sibling = sibling->Next ) != nullptr ) { diff --git a/Model3d.h b/Model3d.h index af52323b..884dca3f 100644 --- a/Model3d.h +++ b/Model3d.h @@ -198,7 +198,7 @@ public: // sets visibility level (alpha component) to specified value void SetVisibilityLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); // sets light level (alpha component of illumination color) to specified value - void SetLightLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); + void SetLightLevel( glm::vec4 const &Level, bool const Includechildren = false, bool const Includesiblings = false ); inline float3 Translation1Get() { return fMatrix ? *(fMatrix->TranslationGet()) + v_TransVector : v_TransVector; } inline float3 Translation2Get() { diff --git a/Train.cpp b/Train.cpp index 02959338..38f1294e 100644 --- a/Train.cpp +++ b/Train.cpp @@ -30,13 +30,13 @@ http://mozilla.org/MPL/2.0/. #include "Console.h" #include "application.h" #include "renderer.h" - +/* namespace input { extern user_command command; } - +*/ void control_mapper::insert( TGauge const &Gauge, std::string const &Label ) { @@ -67,9 +67,10 @@ void TCab::Load(cParser &Parser) std::string token; Parser.getTokens(); Parser >> token; - if (token == "cablight") + if (token == "cablight:") { Parser.getTokens( 9, false ); +/* Parser >> dimm.r >> dimm.g @@ -80,6 +81,7 @@ void TCab::Load(cParser &Parser) >> intlitlow.r >> intlitlow.g >> intlitlow.b; +*/ Parser.getTokens(); Parser >> token; } CabPos1.x = std::stod( token ); @@ -369,10 +371,6 @@ TTrain::TTrain() { fBlinkTimer = 0; fHaslerTimer = 0; DynamicSet(NULL); // ustawia wszystkie mv* - iCabLightFlag = 0; - // hunter-091012 - bCabLight = false; - bCabLightDim = false; //----- pMechSittingPosition = Math3D::vector3(0, 0, 0); // ABu: 180404 fTachoTimer = 0.0; // włączenie skoków wskazań prędkościomierza @@ -765,6 +763,11 @@ void TTrain::OnCommand_mastercontrollerdecrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold Train->mvControlled->DecMainCtrl( 1 ); + Train->m_mastercontrollerinuse = true; + } + else { + // release + Train->m_mastercontrollerinuse = false; } } @@ -781,6 +784,11 @@ void TTrain::OnCommand_mastercontrollerset( TTrain *Train, command_data const &C if( Command.action != GLFW_RELEASE ) { // on press or hold Train->set_master_controller( Command.param1 ); + Train->m_mastercontrollerinuse = true; + } + else { + // release + Train->m_mastercontrollerinuse = false; } } @@ -797,6 +805,11 @@ void TTrain::OnCommand_secondcontrollerincrease( TTrain *Train, command_data con else { Train->mvControlled->IncScndCtrl( 1 ); } + Train->m_mastercontrollerinuse = true; + } + else { + // release + Train->m_mastercontrollerinuse = false; } } @@ -1028,17 +1041,39 @@ void TTrain::OnCommand_trainbrakedecrease( TTrain *Train, command_data const &Co Train->set_train_brake( Train->mvOccupied->BrakeCtrlPos - Global.fBrakeStep ); } } + else { + // release + if( ( Train->mvOccupied->BrakeCtrlPos == -1 ) + && ( Train->mvOccupied->BrakeHandle == TBrakeHandle::FVel6 ) + && ( Train->DynamicObject->Controller != AIdriver ) + && ( Global.iFeedbackMode < 3 ) ) { + // Odskakiwanie hamulce EP + Train->set_train_brake( 0 ); + } + } } void TTrain::OnCommand_trainbrakeset( TTrain *Train, command_data const &Command ) { - Train->mvOccupied->BrakeLevelSet( - interpolate( - Train->mvOccupied->Handle->GetPos( bh_MIN ), - Train->mvOccupied->Handle->GetPos( bh_MAX ), - clamp( - Command.param1, - 0.0, 1.0 ) ) ); + if( Command.action != GLFW_RELEASE ) { + // press or hold + Train->mvOccupied->BrakeLevelSet( + interpolate( + Train->mvOccupied->Handle->GetPos( bh_MIN ), + Train->mvOccupied->Handle->GetPos( bh_MAX ), + clamp( + Command.param1, + 0.0, 1.0 ) ) ); + } else { + // release + if( ( Train->mvOccupied->BrakeCtrlPos == -1 ) + && ( Train->mvOccupied->BrakeHandle == TBrakeHandle::FVel6 ) + && ( Train->DynamicObject->Controller != AIdriver ) + && ( Global.iFeedbackMode < 3 ) ) { + // Odskakiwanie hamulce EP + Train->set_train_brake( 0 ); + } + } } void TTrain::OnCommand_trainbrakecharging( TTrain *Train, command_data const &Command ) { @@ -3758,7 +3793,7 @@ void TTrain::OnCommand_interiorlighttoggle( TTrain *Train, command_data const &C if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->bCabLight ) { + if( false == Train->Cabine[Train->iCabn].bLight ) { // turn on OnCommand_interiorlightenable( Train, Command ); } @@ -3780,11 +3815,18 @@ void TTrain::OnCommand_interiorlightenable( TTrain *Train, command_data const &C } // visual feedback Train->ggCabLightButton.UpdateValue( 1.0, Train->dsbSwitch ); - - if( true == Train->bCabLight ) { return; } // already enabled - - Train->bCabLight = true; Train->btCabLight.Turn( true ); + // store lighting switch states + if( false == Train->DynamicObject->JointCabs ) { + // vehicles with separate cabs get separate lighting switch states + Train->Cabine[ Train->iCabn ].bLight = true; + } + else { + // joint virtual cabs share lighting switch states + for( auto &cab : Train->Cabine ) { + cab.bLight = true; + } + } } } @@ -3799,11 +3841,18 @@ void TTrain::OnCommand_interiorlightdisable( TTrain *Train, command_data const & } // visual feedback Train->ggCabLightButton.UpdateValue( 0.0, Train->dsbSwitch ); - - if( false == Train->bCabLight ) { return; } // already disabled - - Train->bCabLight = false; Train->btCabLight.Turn( false ); + // store lighting switch states + if( false == Train->DynamicObject->JointCabs ) { + // vehicles with separate cabs get separate lighting switch states + Train->Cabine[ Train->iCabn ].bLight = false; + } + else { + // joint virtual cabs share lighting switch states + for( auto &cab : Train->Cabine ) { + cab.bLight = false; + } + } } } @@ -3811,7 +3860,7 @@ void TTrain::OnCommand_interiorlightdimtoggle( TTrain *Train, command_data const if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->bCabLightDim ) { + if( false == Train->Cabine[ Train->iCabn ].bLightDim ) { // turn on OnCommand_interiorlightdimenable( Train, Command ); } @@ -3833,10 +3882,17 @@ void TTrain::OnCommand_interiorlightdimenable( TTrain *Train, command_data const } // visual feedback Train->ggCabLightDimButton.UpdateValue( 1.0, Train->dsbSwitch ); - - if( true == Train->bCabLightDim ) { return; } // already enabled - - Train->bCabLightDim = true; + // store lighting switch states + if( false == Train->DynamicObject->JointCabs ) { + // vehicles with separate cabs get separate lighting switch states + Train->Cabine[ Train->iCabn ].bLightDim = true; + } + else { + // joint virtual cabs share lighting switch states + for( auto &cab : Train->Cabine ) { + cab.bLightDim = true; + } + } } } @@ -3851,10 +3907,17 @@ void TTrain::OnCommand_interiorlightdimdisable( TTrain *Train, command_data cons } // visual feedback Train->ggCabLightDimButton.UpdateValue( 0.0, Train->dsbSwitch ); - - if( false == Train->bCabLightDim ) { return; } // already disabled - - Train->bCabLightDim = false; + // store lighting switch states + if( false == Train->DynamicObject->JointCabs ) { + // vehicles with separate cabs get separate lighting switch states + Train->Cabine[ Train->iCabn ].bLightDim = false; + } + else { + // joint virtual cabs share lighting switch states + for( auto &cab : Train->Cabine ) { + cab.bLightDim = false; + } + } } } @@ -4808,10 +4871,13 @@ bool TTrain::Update( double const Deltatime ) || ( mvOccupied->TrainType == dt_EP05 ) ) { // dla ET40 i EU05 automatyczne cofanie nastawnika - i tak nie będzie to działać dobrze... // TODO: use deltatime to stabilize speed +/* if( false == ( ( input::command == user_command::mastercontrollerset ) || ( input::command == user_command::mastercontrollerincrease ) || ( input::command == user_command::mastercontrollerdecrease ) ) ) { +*/ + if( false == m_mastercontrollerinuse ) { if( mvOccupied->MainCtrlPos > mvOccupied->MainCtrlActualPos ) { mvOccupied->DecMainCtrl( 1 ); } @@ -4821,7 +4887,7 @@ bool TTrain::Update( double const Deltatime ) } } } - +/* if( ( mvOccupied->BrakeHandle == TBrakeHandle::FVel6 ) && ( mvOccupied->fBrakeCtrlPos < 0.0 ) && ( Global.iFeedbackMode < 3 ) ) { @@ -4833,6 +4899,7 @@ bool TTrain::Update( double const Deltatime ) set_train_brake( 0 ); } } +*/ } // McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol @@ -5021,17 +5088,6 @@ bool TTrain::Update( double const Deltatime ) Console::ValueSet(6, fTachoVelocity); } #endif - // hunter-091012: swiatlo - if (bCabLight == true) - { - if (bCabLightDim == true) - iCabLightFlag = 1; - else - iCabLightFlag = 2; - } - else - iCabLightFlag = 0; - //------------------ // hunter-261211: nadmiarowy przetwornicy i ogrzewania // Ra 15-01: to musi stąd wylecieć - zależności nie mogą być w kabinie @@ -5770,43 +5826,34 @@ bool TTrain::Update( double const Deltatime ) btHaslerCurrent.Turn(DynamicObject->MoverParameters->Im != 0.0); // prąd na silnikach // calculate current level of interior illumination - // TODO: organize it along with rest of train update in a more sensible arrangement - auto interiorlightlevel { 0.f }; - switch( iCabLightFlag ) // Ra: uzeleżnic od napięcia w obwodzie sterowania - { // hunter-091012: uzaleznienie jasnosci od przetwornicy - case 0: { - //światło wewnętrzne zgaszone - interiorlightlevel = 0.0f; - break; - } - case 1: { - //światło wewnętrzne przygaszone (255 216 176) - auto const converteractive { ( - ( mvOccupied->ConverterFlag ) - || ( ( ( mvOccupied->Couplers[ side::front ].CouplingFlag & coupling::permanent ) != 0 ) && mvOccupied->Couplers[ side::front ].Connected->ConverterFlag ) - || ( ( ( mvOccupied->Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && mvOccupied->Couplers[ side::rear ].Connected->ConverterFlag ) ) }; + { + // TODO: organize it along with rest of train update in a more sensible arrangement + auto const converteractive{ ( + ( mvOccupied->ConverterFlag ) + || ( ( ( mvOccupied->Couplers[ side::front ].CouplingFlag & coupling::permanent ) != 0 ) && mvOccupied->Couplers[ side::front ].Connected->ConverterFlag ) + || ( ( ( mvOccupied->Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && mvOccupied->Couplers[ side::rear ].Connected->ConverterFlag ) ) }; + // Ra: uzeleżnic od napięcia w obwodzie sterowania + // hunter-091012: uzaleznienie jasnosci od przetwornicy + int cabidx { 0 }; + for( auto &cab : Cabine ) { - interiorlightlevel = ( - converteractive ? - 0.4f : - 0.2f ); - break; - } - case 2: { - //światło wewnętrzne zapalone (255 216 176) - auto const converteractive { ( - ( mvOccupied->ConverterFlag ) - || ( ( ( mvOccupied->Couplers[ side::front ].CouplingFlag & coupling::permanent ) != 0 ) && mvOccupied->Couplers[ side::front ].Connected->ConverterFlag ) - || ( ( ( mvOccupied->Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && mvOccupied->Couplers[ side::rear ].Connected->ConverterFlag ) ) }; + auto const cablightlevel = + ( ( cab.bLight == false ) ? 0.f : + ( cab.bLightDim == true ) ? 0.4f : + 1.f ) + * ( converteractive ? 1.f : 0.5f ); - interiorlightlevel = ( - converteractive ? - 1.0f : - 0.5f ); - break; + if( cab.LightLevel != cablightlevel ) { + cab.LightLevel = cablightlevel; + DynamicObject->set_cab_lights( cabidx, cab.LightLevel ); + } + if( cabidx == iCabn ) { + DynamicObject->InteriorLightLevel = cablightlevel; + } + + ++cabidx; } } - DynamicObject->set_cab_lights( interiorlightlevel ); // anti slip system activation, maintained while the control button is down if( mvOccupied->BrakeSystem != TBrakeSystem::ElectroPneumatic ) { @@ -6192,15 +6239,16 @@ bool TTrain::CabChange(int iDirection) else { // jeśli pojazd prowadzony ręcznie albo wcale (wagon) DynamicObject->MoverParameters->CabDeactivisation(); - if (DynamicObject->MoverParameters->ChangeCab(iDirection)) - if (InitializeCab(DynamicObject->MoverParameters->ActiveCab, - DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + - ".mmd")) - { // zmiana kabiny w ramach tego samego pojazdu + if( DynamicObject->MoverParameters->ChangeCab( iDirection ) ) { + if( InitializeCab( + DynamicObject->MoverParameters->ActiveCab, + DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + ".mmd" ) ) { + // zmiana kabiny w ramach tego samego pojazdu DynamicObject->MoverParameters->CabActivisation(); // załączenie rozrządu (wirtualne kabiny) DynamicObject->Mechanik->CheckVehicles( Change_direction ); return true; // udało się zmienić kabinę } + } // aktywizacja poprzedniej, bo jeszcze nie wiadomo, czy jakiś pojazd jest DynamicObject->MoverParameters->CabActivisation(); } @@ -6496,10 +6544,10 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) >> viewangle.y // yaw first, then pitch >> viewangle.x; pMechViewAngle = glm::radians( viewangle ); -/* - Global.pCamera.Pitch = pMechViewAngle.x; - Global.pCamera.Yaw = pMechViewAngle.y; -*/ + + Global.pCamera.Angle.x = pMechViewAngle.x; + Global.pCamera.Angle.y = pMechViewAngle.y; + parser.getTokens(); parser >> token; } @@ -6579,11 +6627,13 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) } clear_cab_controls(); } +/* if (nullptr == DynamicObject->mdKabina) { // don't bother with other parts until the cab is initialised continue; } +*/ else if (true == initialize_gauge(parser, token, cabindex)) { // matched the token, grab the next one @@ -6603,7 +6653,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) >> submodelname >> renderername; - auto const *submodel { DynamicObject->mdKabina->GetFromName( submodelname ) }; + auto const *submodel { ( DynamicObject->mdKabina ? DynamicObject->mdKabina->GetFromName( submodelname ) : nullptr ) }; if( submodel == nullptr ) { WriteLog( "Python Screen: submodel " + submodelname + " not found - Ignoring screen" ); continue; @@ -6628,8 +6678,10 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) { return false; } +/* if (DynamicObject->mdKabina) { +*/ // configure placement of sound emitters which aren't bound with any device model, and weren't placed manually // try first to bind sounds to location of possible devices if( dsbReverserKey.offset() == nullvector ) { @@ -6681,28 +6733,37 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) } } - DynamicObject->mdKabina->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego - set_cab_controls(); + if( DynamicObject->mdKabina ) + DynamicObject->mdKabina->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego + set_cab_controls( NewCabNo < 0 ? 2 : NewCabNo ); +/* return true; } return (token == "none"); +*/ + return true; } Math3D::vector3 TTrain::MirrorPosition(bool lewe) { // zwraca współrzędne widoku kamery z lusterka switch (iCabn) { - case 1: // przednia (1) - return DynamicObject->mMatrix * - Math3D::vector3(lewe ? Cabine[iCabn].CabPos2.x : Cabine[iCabn].CabPos1.x, - 1.5 + Cabine[iCabn].CabPos1.y, Cabine[iCabn].CabPos2.z); case 2: // tylna (-1) return DynamicObject->mMatrix * - Math3D::vector3(lewe ? Cabine[iCabn].CabPos1.x : Cabine[iCabn].CabPos2.x, - 1.5 + Cabine[iCabn].CabPos1.y, Cabine[iCabn].CabPos1.z); + Math3D::vector3( + mvOccupied->Dim.W * ( lewe ? -0.5 : 0.5 ) + 0.2 * ( lewe ? -1 : 1 ), + 1.5 + Cabine[iCabn].CabPos1.y, + Cabine[iCabn].CabPos1.z); + case 1: // przednia (1) + default: + return DynamicObject->mMatrix * + Math3D::vector3( + mvOccupied->Dim.W * ( lewe ? 0.5 : -0.5 ) + 0.2 * ( lewe ? 1 : -1 ), + 1.5 + Cabine[iCabn].CabPos1.y, + Cabine[iCabn].CabPos2.z); } - return DynamicObject->GetPosition(); // współrzędne środka pojazdu +// return DynamicObject->GetPosition(); // współrzędne środka pojazdu }; void TTrain::DynamicSet(TDynamicObject *d) @@ -7052,20 +7113,20 @@ void TTrain::clear_cab_controls() } // NOTE: we can get rid of this function once we have per-cab persistent state -void TTrain::set_cab_controls() { +void TTrain::set_cab_controls( int const Cab ) { // switches // battery if( true == mvOccupied->Battery ) { - ggBatteryButton.PutValue( 1.0 ); + ggBatteryButton.PutValue( 1.f ); } // motor connectors ggStLinOffButton.PutValue( ( mvControlled->StLinSwitchOff ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); // radio if( true == mvOccupied->Radio ) { - ggRadioButton.PutValue( 1.0 ); + ggRadioButton.PutValue( 1.f ); } ggRadioChannelSelector.PutValue( iRadioChannel - 1 ); // pantographs @@ -7073,96 +7134,96 @@ void TTrain::set_cab_controls() { if( ggPantFrontButton.SubModel ) { ggPantFrontButton.PutValue( ( mvControlled->PantFrontUp ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); } if( ggPantFrontButtonOff.SubModel ) { ggPantFrontButtonOff.PutValue( ( mvControlled->PantFrontUp ? - 0.0 : - 1.0 ) ); + 0.f : + 1.f ) ); } // NOTE: currently we animate the selectable pantograph control for both pantographs // TODO: implement actual selection control, and refactor handling this control setup in a separate method if( ggPantSelectedButton.SubModel ) { ggPantSelectedButton.PutValue( ( mvControlled->PantFrontUp ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); } if( ggPantSelectedDownButton.SubModel ) { ggPantSelectedDownButton.PutValue( ( mvControlled->PantFrontUp ? - 0.0 : - 1.0 ) ); + 0.f : + 1.f ) ); } } if( mvOccupied->PantSwitchType != "impulse" ) { if( ggPantRearButton.SubModel ) { ggPantRearButton.PutValue( ( mvControlled->PantRearUp ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); } if( ggPantRearButtonOff.SubModel ) { ggPantRearButtonOff.PutValue( ( mvControlled->PantRearUp ? - 0.0 : - 1.0 ) ); + 0.f : + 1.f ) ); } // NOTE: currently we animate the selectable pantograph control for both pantographs // TODO: implement actual selection control, and refactor handling this control setup in a separate method if( ggPantSelectedButton.SubModel ) { ggPantSelectedButton.PutValue( ( mvControlled->PantRearUp ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); } if( ggPantSelectedDownButton.SubModel ) { ggPantSelectedDownButton.PutValue( ( mvControlled->PantRearUp ? - 0.0 : - 1.0 ) ); + 0.f : + 1.f ) ); } } // auxiliary compressor ggPantCompressorValve.PutValue( mvControlled->bPantKurek3 ? - 0.0 : // default setting is pantographs connected with primary tank - 1.0 ); + 0.f : // default setting is pantographs connected with primary tank + 1.f ); ggPantCompressorButton.PutValue( mvControlled->PantCompFlag ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // converter if( mvOccupied->ConvSwitchType != "impulse" ) { ggConverterButton.PutValue( mvControlled->ConverterAllow ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } ggConverterLocalButton.PutValue( mvControlled->ConverterAllowLocal ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // compressor ggCompressorButton.PutValue( mvControlled->CompressorAllow ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); ggCompressorLocalButton.PutValue( mvControlled->CompressorAllowLocal ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // motor overload relay threshold / shunt mode ggMaxCurrentCtrl.PutValue( ( true == mvControlled->ShuntModeAllow ? ( true == mvControlled->ShuntMode ? - 1.0 : - 0.0 ) : + 1.f : + 0.f ) : ( mvControlled->Imax == mvControlled->ImaxHi ? - 1.0 : - 0.0 ) ) ); + 1.f : + 0.f ) ) ); // lights ggLightsButton.PutValue( mvOccupied->LightsPos - 1 ); @@ -7172,84 +7233,84 @@ void TTrain::set_cab_controls() { side::rear ); if( ( DynamicObject->iLights[ vehicleside ] & light::headlight_left ) != 0 ) { - ggLeftLightButton.PutValue( 1.0 ); + ggLeftLightButton.PutValue( 1.f ); } if( ( DynamicObject->iLights[ vehicleside ] & light::headlight_right ) != 0 ) { - ggRightLightButton.PutValue( 1.0 ); + ggRightLightButton.PutValue( 1.f ); } if( ( DynamicObject->iLights[ vehicleside ] & light::headlight_upper ) != 0 ) { - ggUpperLightButton.PutValue( 1.0 ); + ggUpperLightButton.PutValue( 1.f ); } if( ( DynamicObject->iLights[ vehicleside ] & light::redmarker_left ) != 0 ) { if( ggLeftEndLightButton.SubModel != nullptr ) { - ggLeftEndLightButton.PutValue( 1.0 ); + ggLeftEndLightButton.PutValue( 1.f ); } else { - ggLeftLightButton.PutValue( -1.0 ); + ggLeftLightButton.PutValue( -1.f ); } } if( ( DynamicObject->iLights[ vehicleside ] & light::redmarker_right ) != 0 ) { if( ggRightEndLightButton.SubModel != nullptr ) { - ggRightEndLightButton.PutValue( 1.0 ); + ggRightEndLightButton.PutValue( 1.f ); } else { - ggRightLightButton.PutValue( -1.0 ); + ggRightLightButton.PutValue( -1.f ); } } if( true == DynamicObject->DimHeadlights ) { - ggDimHeadlightsButton.PutValue( 1.0 ); + ggDimHeadlightsButton.PutValue( 1.f ); } // cab lights - if( true == bCabLight ) { - ggCabLightButton.PutValue( 1.0 ); + if( true == Cabine[Cab].bLight ) { + ggCabLightButton.PutValue( 1.f ); } - if( true == bCabLightDim ) { - ggCabLightDimButton.PutValue( 1.0 ); + if( true == Cabine[Cab].bLightDim ) { + ggCabLightDimButton.PutValue( 1.f ); } ggInstrumentLightButton.PutValue( ( InstrumentLightActive ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); ggDashboardLightButton.PutValue( ( DashboardLightActive ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); ggTimetableLightButton.PutValue( ( TimetableLightActive ? - 1.0 : - 0.0 ) ); + 1.f : + 0.f ) ); // doors // NOTE: we're relying on the cab models to have switches reversed for the rear cab(?) - ggDoorLeftButton.PutValue( mvOccupied->DoorLeftOpened ? 1.0 : 0.0 ); - ggDoorRightButton.PutValue( mvOccupied->DoorRightOpened ? 1.0 : 0.0 ); + ggDoorLeftButton.PutValue( mvOccupied->DoorLeftOpened ? 1.f : 0.f ); + ggDoorRightButton.PutValue( mvOccupied->DoorRightOpened ? 1.f : 0.f ); // door lock ggDoorSignallingButton.PutValue( mvOccupied->DoorLockEnabled ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // heating if( true == mvControlled->Heating ) { - ggTrainHeatingButton.PutValue( 1.0 ); + ggTrainHeatingButton.PutValue( 1.f ); } // brake acting time if( ggBrakeProfileCtrl.SubModel != nullptr ) { ggBrakeProfileCtrl.PutValue( ( ( mvOccupied->BrakeDelayFlag & bdelay_R ) != 0 ? - 2.0 : + 2.f : mvOccupied->BrakeDelayFlag - 1 ) ); } if( ggBrakeProfileG.SubModel != nullptr ) { ggBrakeProfileG.PutValue( mvOccupied->BrakeDelayFlag == bdelay_G ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } if( ggBrakeProfileR.SubModel != nullptr ) { ggBrakeProfileR.PutValue( ( mvOccupied->BrakeDelayFlag & bdelay_R ) != 0 ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } if (ggBrakeOperationModeCtrl.SubModel != nullptr) { ggBrakeOperationModeCtrl.PutValue( @@ -7260,75 +7321,75 @@ void TTrain::set_cab_controls() { // alarm chain ggAlarmChain.PutValue( mvControlled->AlarmChainFlag ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // brake signalling ggSignallingButton.PutValue( mvControlled->Signalling ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // multiple-unit current indicator source ggNextCurrentButton.PutValue( ShowNextCurrent ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // water pump ggWaterPumpBreakerButton.PutValue( mvControlled->WaterPump.breaker ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); if( ggWaterPumpButton.type() != TGaugeType::push ) { ggWaterPumpButton.PutValue( mvControlled->WaterPump.is_enabled ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } // water heater ggWaterHeaterBreakerButton.PutValue( mvControlled->WaterHeater.breaker ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); ggWaterHeaterButton.PutValue( mvControlled->WaterHeater.is_enabled ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); ggWaterCircuitsLinkButton.PutValue( mvControlled->WaterCircuitsLink ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); // fuel pump if( ggFuelPumpButton.type() != TGaugeType::push ) { ggFuelPumpButton.PutValue( mvControlled->FuelPump.is_enabled ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } // oil pump if( ggOilPumpButton.type() != TGaugeType::push ) { ggOilPumpButton.PutValue( mvControlled->OilPump.is_enabled ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } // traction motor fans if( ggMotorBlowersFrontButton.type() != TGaugeType::push ) { ggMotorBlowersFrontButton.PutValue( mvControlled->MotorBlowers[side::front].is_enabled ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } if( ggMotorBlowersRearButton.type() != TGaugeType::push ) { ggMotorBlowersRearButton.PutValue( mvControlled->MotorBlowers[side::rear].is_enabled ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } if( ggMotorBlowersAllOffButton.type() != TGaugeType::push ) { ggMotorBlowersAllOffButton.PutValue( ( mvControlled->MotorBlowers[side::front].is_disabled || mvControlled->MotorBlowers[ side::front ].is_disabled ) ? - 1.0 : - 0.0 ); + 1.f : + 0.f ); } // we reset all indicators, as they're set during the update pass @@ -7421,33 +7482,33 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co }; auto lookup = lights.find( Label ); if( lookup != lights.end() ) { - lookup->second.Load( Parser, DynamicObject, DynamicObject->mdKabina ); + lookup->second.Load( Parser, DynamicObject ); } else if( Label == "i-instrumentlight:" ) { - btInstrumentLight.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); + btInstrumentLight.Load( Parser, DynamicObject ); InstrumentLightType = 0; } else if( Label == "i-instrumentlight_M:" ) { - btInstrumentLight.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); + btInstrumentLight.Load( Parser, DynamicObject ); InstrumentLightType = 1; } else if( Label == "i-instrumentlight_C:" ) { - btInstrumentLight.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); + btInstrumentLight.Load( Parser, DynamicObject ); InstrumentLightType = 2; } else if (Label == "i-doors:") { int i = Parser.getToken() - 1; auto &button = Cabine[Cabindex].Button(-1); // pierwsza wolna lampka - button.Load(Parser, DynamicObject, DynamicObject->mdKabina); + button.Load(Parser, DynamicObject); button.AssignBool(bDoors[0] + 3 * i); } /* else if( Label == "i-malfunction:" ) { // generic malfunction indicator auto &button = Cabine[ Cabindex ].Button( -1 ); // pierwsza wolna gałka - button.Load( Parser, DynamicObject, DynamicObject->mdKabina ); + button.Load( Parser, DynamicObject ); button.AssignBool( &mvOccupied->dizel_heat.PA ); } */ @@ -7567,19 +7628,19 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con }; auto lookup = gauges.find( Label ); if( lookup != gauges.end() ) { - lookup->second.Load( Parser, DynamicObject, DynamicObject->mdKabina ); + lookup->second.Load( Parser, DynamicObject); m_controlmapper.insert( lookup->second, lookup->first ); } // ABu 090305: uniwersalne przyciski lub inne rzeczy else if( Label == "mainctrlact:" ) { - ggMainCtrlAct.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); + ggMainCtrlAct.Load( Parser, DynamicObject); } // SEKCJA WSKAZNIKOW else if ((Label == "tachometer:") || (Label == "tachometerb:")) { // predkosciomierz wskaźnikowy z szarpaniem auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(&fTachoVelocityJump); // bind tachometer sound location to the meter if( dsbHasler.offset() == glm::vec3() ) { @@ -7590,7 +7651,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { // predkosciomierz wskaźnikowy bez szarpania auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(&fTachoVelocity); // bind tachometer sound location to the meter if( dsbHasler.offset() == glm::vec3() ) { @@ -7601,7 +7662,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { // predkosciomierz cyfrowy auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(&fTachoVelocity); // bind tachometer sound location to the meter if( dsbHasler.offset() == glm::vec3() ) { @@ -7612,28 +7673,28 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { // 1szy amperomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(fHCurrent + 1); } else if ((Label == "hvcurrent2:") || (Label == "hvcurrent2b:")) { // 2gi amperomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(fHCurrent + 2); } else if ((Label == "hvcurrent3:") || (Label == "hvcurrent3b:")) { // 3ci amperomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałska - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(fHCurrent + 3); } else if ((Label == "hvcurrent:") || (Label == "hvcurrentb:")) { // amperomierz calkowitego pradu auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(fHCurrent); } else if (Label == "eimscreen:") @@ -7643,7 +7704,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con Parser.getTokens(2, false); Parser >> i >> j; auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(&fEIMParams[i][j]); } else if (Label == "brakes:") @@ -7653,7 +7714,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con Parser.getTokens(2, false); Parser >> i >> j; auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(&fPress[i - 1][j]); } else if ((Label == "brakepress:") || (Label == "brakepressb:")) @@ -7661,79 +7722,79 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con // manometr cylindrow hamulcowych // Ra 2014-08: przeniesione do TCab auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, 0.1); gauge.AssignDouble(&mvOccupied->BrakePress); } else if ((Label == "pipepress:") || (Label == "pipepressb:")) { // manometr przewodu hamulcowego auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, 0.1); gauge.AssignDouble(&mvOccupied->PipePress); } else if (Label == "limpipepress:") { // manometr zbiornika sterujacego zaworu maszynisty - ggZbS.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); + ggZbS.Load(Parser, DynamicObject, 0.1); } else if (Label == "cntrlpress:") { // manometr zbiornika kontrolnego/rorzďż˝du auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, 0.1); gauge.AssignDouble(&mvControlled->PantPress); } else if ((Label == "compressor:") || (Label == "compressorb:")) { // manometr sprezarki/zbiornika glownego auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, 0.1); gauge.AssignDouble(&mvOccupied->Compressor); } else if( Label == "oilpress:" ) { // oil pressure auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka - gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); + gauge.Load( Parser, DynamicObject ); gauge.AssignFloat( &mvControlled->OilPump.pressure ); } else if( Label == "oiltemp:" ) { // oil temperature auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka - gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); + gauge.Load( Parser, DynamicObject ); gauge.AssignFloat( &mvControlled->dizel_heat.To ); } else if( Label == "water1temp:" ) { // main circuit water temperature auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka - gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); + gauge.Load( Parser, DynamicObject ); gauge.AssignFloat( &mvControlled->dizel_heat.temperatura1 ); } else if( Label == "water2temp:" ) { // auxiliary circuit water temperature auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka - gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); + gauge.Load( Parser, DynamicObject ); gauge.AssignFloat( &mvControlled->dizel_heat.temperatura2 ); } // yB - dla drugiej sekcji else if (Label == "hvbcurrent1:") { // 1szy amperomierz - ggI1B.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggI1B.Load(Parser, DynamicObject); } else if (Label == "hvbcurrent2:") { // 2gi amperomierz - ggI2B.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggI2B.Load(Parser, DynamicObject); } else if (Label == "hvbcurrent3:") { // 3ci amperomierz - ggI3B.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggI3B.Load(Parser, DynamicObject); } else if (Label == "hvbcurrent:") { // amperomierz calkowitego pradu - ggItotalB.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggItotalB.Load(Parser, DynamicObject); } //************************************************************* else if (Label == "clock:") @@ -7741,76 +7802,78 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con // zegar analogowy if (Parser.getToken() == "analog") { - // McZapkie-300302: zegarek - ggClockSInd.Init(DynamicObject->mdKabina->GetFromName("ClockShand"), TGaugeAnimation::gt_Rotate, 1.0/60.0); - ggClockMInd.Init(DynamicObject->mdKabina->GetFromName("ClockMhand"), TGaugeAnimation::gt_Rotate, 1.0/60.0); - ggClockHInd.Init(DynamicObject->mdKabina->GetFromName("ClockHhand"), TGaugeAnimation::gt_Rotate, 1.0/12.0); + if( DynamicObject->mdKabina ) { + // McZapkie-300302: zegarek + ggClockSInd.Init( DynamicObject->mdKabina->GetFromName( "ClockShand" ), TGaugeAnimation::gt_Rotate, 1.0 / 60.0 ); + ggClockMInd.Init( DynamicObject->mdKabina->GetFromName( "ClockMhand" ), TGaugeAnimation::gt_Rotate, 1.0 / 60.0 ); + ggClockHInd.Init( DynamicObject->mdKabina->GetFromName( "ClockHhand" ), TGaugeAnimation::gt_Rotate, 1.0 / 12.0 ); + } } } else if (Label == "evoltage:") { // woltomierz napiecia silnikow - ggEngineVoltage.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggEngineVoltage.Load(Parser, DynamicObject); } else if (Label == "hvoltage:") { // woltomierz wysokiego napiecia auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(&fHVoltage); } else if (Label == "lvoltage:") { // woltomierz niskiego napiecia - ggLVoltage.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggLVoltage.Load(Parser, DynamicObject); } else if (Label == "enrot1m:") { // obrotomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(fEngine + 1); } // ggEnrot1m.Load(Parser,DynamicObject->mdKabina); else if (Label == "enrot2m:") { // obrotomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(fEngine + 2); } // ggEnrot2m.Load(Parser,DynamicObject->mdKabina); else if (Label == "enrot3m:") { // obrotomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignFloat(fEngine + 3); } // ggEnrot3m.Load(Parser,DynamicObject->mdKabina); else if (Label == "engageratio:") { // np. ciśnienie sterownika sprzęgła auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignDouble(&mvControlled->dizel_engage); } // ggEngageRatio.Load(Parser,DynamicObject->mdKabina); else if (Label == "maingearstatus:") { // np. ciśnienie sterownika skrzyni biegów - ggMainGearStatus.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggMainGearStatus.Load(Parser, DynamicObject); } else if (Label == "ignitionkey:") { - ggIgnitionKey.Load(Parser, DynamicObject, DynamicObject->mdKabina); + ggIgnitionKey.Load(Parser, DynamicObject); } else if (Label == "distcounter:") { // Ra 2014-07: licznik kilometrów auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignDouble(&mvControlled->DistCounter); } else if( Label == "shuntmodepower:" ) { // shunt mode power slider auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject); gauge.AssignDouble(&mvControlled->AnPos); m_controlmapper.insert( gauge, "shuntmodepower:" ); } diff --git a/Train.h b/Train.h index 144f05ae..78e3c3ff 100644 --- a/Train.h +++ b/Train.h @@ -33,16 +33,21 @@ public: // methods void Load(cParser &Parser); void Update(); + TGauge &Gauge( int n = -1 ); // pobranie adresu obiektu + TButton &Button( int n = -1 ); // pobranie adresu obiektu // members Math3D::vector3 CabPos1 { 0, 1, 1 }; Math3D::vector3 CabPos2 { 0, 1, -1 }; bool bEnabled { false }; bool bOccupied { true }; +/* glm::vec3 dimm; // McZapkie-120503: tlumienie swiatla glm::vec3 intlit; // McZapkie-120503: oswietlenie kabiny glm::vec3 intlitlow; // McZapkie-120503: przyciemnione oswietlenie kabiny - TGauge &Gauge( int n = -1 ); // pobranie adresu obiektu - TButton &Button( int n = -1 ); // pobranie adresu obiektu +*/ + bool bLight { false }; // hunter-091012: czy swiatlo jest zapalone? + bool bLightDim { false }; // hunter-091012: czy przyciemnienie kabiny jest zapalone? + float LightLevel{ 0.f }; // last calculated interior light level private: // members @@ -121,7 +126,7 @@ class TTrain void clear_cab_controls(); // sets cabin controls based on current state of the vehicle // NOTE: we can get rid of this function once we have per-cab persistent state - void set_cab_controls(); + void set_cab_controls( int const Cab ); // initializes a gauge matching provided label. returns: true if the label was found, false otherwise bool initialize_gauge(cParser &Parser, std::string const &Label, int const Cabindex); // initializes a button matching provided label. returns: true if the label was found, false otherwise @@ -589,14 +594,14 @@ public: // reszta może by?publiczna sound_source m_radiosound { sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // cached template for radio messages std::vector>> m_radiomessages; // list of currently played radio messages sound_source m_radiostop { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; - +/* int iCabLightFlag; // McZapkie:120503: oswietlenie kabiny (0: wyl, 1: przyciemnione, 2: pelne) bool bCabLight; // hunter-091012: czy swiatlo jest zapalone? bool bCabLightDim; // hunter-091012: czy przyciemnienie kabiny jest zapalone? - +*/ // McZapkie: opis kabiny - obszar poruszania sie mechanika oraz zajetosc - TCab Cabine[ maxcab + 1 ]; // przedzial maszynowy, kabina 1 (A), kabina 2 (B) - int iCabn; + std::array Cabine; // przedzial maszynowy, kabina 1 (A), kabina 2 (B) + int iCabn { 0 }; // McZapkie: do poruszania sie po kabinie Math3D::vector3 pMechSittingPosition; // ABu 180404 Math3D::vector3 MirrorPosition( bool lewe ); @@ -638,6 +643,7 @@ private: bool bHeat[8]; // grzanie // McZapkie: do syczenia float fPPress, fNPress; + bool m_mastercontrollerinuse { false }; int iRadioChannel { 1 }; // numer aktualnego kana?u radiowego std::vector> m_screens; diff --git a/drivermode.cpp b/drivermode.cpp index d8996095..6bcc62ad 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -27,13 +27,13 @@ http://mozilla.org/MPL/2.0/. #include "renderer.h" #include "utilities.h" #include "Logs.h" - +/* namespace input { user_command command; // currently issued control command, if any } - +*/ void driver_mode::drivermode_input::poll() { @@ -47,11 +47,13 @@ driver_mode::drivermode_input::poll() { if( uart != nullptr ) { uart->poll(); } +/* // TBD, TODO: wrap current command in object, include other input sources? input::command = ( mouse.command() != user_command::none ? mouse.command() : keyboard.command() ); +*/ } bool @@ -468,6 +470,15 @@ driver_mode::update_camera( double const Deltatime ) { simulation::Train->pMechViewAngle = { Camera.Angle.x, Camera.Angle.y }; simulation::Train->pMechOffset = Camera.m_owneroffset; } + + if( ( true == FreeFlyModeFlag ) + && ( Camera.m_owner != nullptr ) ) { + // cache external view config + auto &externalviewconfig { m_externalviewconfigs[ m_externalviewmode ] }; + externalviewconfig.owner = Camera.m_owner; + externalviewconfig.offset = Camera.m_owneroffset; + externalviewconfig.angle = Camera.Angle; + } } else { // debug camera @@ -567,13 +578,13 @@ driver_mode::update_camera( double const Deltatime ) { // gdy w korytarzu Camera.LookAt = Camera.m_owner->GetWorldPosition( Camera.m_owneroffset ) - + simulation::Train->GetDirection() * 5.0; + + Camera.m_owner->VectorFront() * 5.0; } else { // patrzenie w kierunku osi pojazdu, z uwzględnieniem kabiny Camera.LookAt = Camera.m_owner->GetWorldPosition( Camera.m_owneroffset ) - + simulation::Train->GetDirection() * 5.0 + + Camera.m_owner->VectorFront() * 5.0 * simulation::Train->Occupied()->ActiveCab; //-1 albo 1 } Camera.vUp = simulation::Train->GetUp(); @@ -914,18 +925,26 @@ driver_mode::ExternalView() { Camera.m_owner = owner; - auto const offsetflip { - ( vehicle->MoverParameters->ActiveCab == 0 ? 1 : vehicle->MoverParameters->ActiveCab ) - * ( vehicle->MoverParameters->ActiveDir == 0 ? 1 : vehicle->MoverParameters->ActiveDir ) }; + auto const &viewconfig { m_externalviewconfigs[ m_externalviewmode ] }; + if( owner == viewconfig.owner ) { + // restore view config for previous owner + Camera.m_owneroffset = viewconfig.offset; + Camera.Angle = viewconfig.angle; + } + else { + // default view setup + auto const offsetflip{ + ( vehicle->MoverParameters->ActiveCab == 0 ? 1 : vehicle->MoverParameters->ActiveCab ) + * ( vehicle->MoverParameters->ActiveDir == 0 ? 1 : vehicle->MoverParameters->ActiveDir ) }; - Camera.m_owneroffset = { - 1.5 * owner->MoverParameters->Dim.W * offsetflip, - std::max( 5.0, 1.25 * owner->MoverParameters->Dim.H ), - - 0.4 * owner->MoverParameters->Dim.L * offsetflip }; + Camera.m_owneroffset = { + 1.5 * owner->MoverParameters->Dim.W * offsetflip, + std::max( 5.0, 1.25 * owner->MoverParameters->Dim.H ), + -0.4 * owner->MoverParameters->Dim.L * offsetflip }; - Camera.Angle.y = glm::radians( ( vehicle->MoverParameters->ActiveDir < 0 ? 180.0 : 0.0 ) ); - - auto const shakeangles { owner->shake_angles() }; + Camera.Angle.y = glm::radians( ( vehicle->MoverParameters->ActiveDir < 0 ? 180.0 : 0.0 ) ); + } + auto const shakeangles{ owner->shake_angles() }; Camera.Angle.x -= 0.5 * shakeangles.second; // hustanie kamery przod tyl Camera.Angle.z = shakeangles.first; // hustanie kamery na boki @@ -937,18 +956,26 @@ driver_mode::ExternalView() { Camera.m_owner = owner; - auto const offsetflip { - ( vehicle->MoverParameters->ActiveCab == 0 ? 1 : vehicle->MoverParameters->ActiveCab ) - * ( vehicle->MoverParameters->ActiveDir == 0 ? 1 : vehicle->MoverParameters->ActiveDir ) - * -1 }; + auto const &viewconfig{ m_externalviewconfigs[ m_externalviewmode ] }; + if( owner == viewconfig.owner ) { + // restore view config for previous owner + Camera.m_owneroffset = viewconfig.offset; + Camera.Angle = viewconfig.angle; + } + else { + // default view setup + auto const offsetflip{ + ( vehicle->MoverParameters->ActiveCab == 0 ? 1 : vehicle->MoverParameters->ActiveCab ) + * ( vehicle->MoverParameters->ActiveDir == 0 ? 1 : vehicle->MoverParameters->ActiveDir ) + * -1 }; - Camera.m_owneroffset = { - 1.5 * owner->MoverParameters->Dim.W * offsetflip, - std::max( 5.0, 1.25 * owner->MoverParameters->Dim.H ), - 0.2 * owner->MoverParameters->Dim.L * offsetflip }; - - Camera.Angle.y = glm::radians( ( vehicle->MoverParameters->ActiveDir < 0 ? 0.0 : 180.0 ) ); + Camera.m_owneroffset = { + 1.5 * owner->MoverParameters->Dim.W * offsetflip, + std::max( 5.0, 1.25 * owner->MoverParameters->Dim.H ), + 0.2 * owner->MoverParameters->Dim.L * offsetflip }; + Camera.Angle.y = glm::radians( ( vehicle->MoverParameters->ActiveDir < 0 ? 0.0 : 180.0 ) ); + } auto const shakeangles { owner->shake_angles() }; Camera.Angle.x -= 0.5 * shakeangles.second; // hustanie kamery przod tyl Camera.Angle.z = shakeangles.first; // hustanie kamery na boki @@ -959,17 +986,25 @@ driver_mode::ExternalView() { Camera.m_owner = owner; - auto const offsetflip { - ( vehicle->MoverParameters->ActiveCab == 0 ? 1 : vehicle->MoverParameters->ActiveCab ) - * ( vehicle->MoverParameters->ActiveDir == 0 ? 1 : vehicle->MoverParameters->ActiveDir ) }; + auto const &viewconfig{ m_externalviewconfigs[ m_externalviewmode ] }; + if( owner == viewconfig.owner ) { + // restore view config for previous owner + Camera.m_owneroffset = viewconfig.offset; + Camera.Angle = viewconfig.angle; + } + else { + // default view setup + auto const offsetflip{ + ( vehicle->MoverParameters->ActiveCab == 0 ? 1 : vehicle->MoverParameters->ActiveCab ) + * ( vehicle->MoverParameters->ActiveDir == 0 ? 1 : vehicle->MoverParameters->ActiveDir ) }; - Camera.m_owneroffset = { - - 0.65 * owner->MoverParameters->Dim.W * offsetflip, - 0.90, - 0.15 * owner->MoverParameters->Dim.L * offsetflip }; - - Camera.Angle.y = glm::radians( ( vehicle->MoverParameters->ActiveDir < 0 ? 180.0 : 0.0 ) ); + Camera.m_owneroffset = { + -0.65 * owner->MoverParameters->Dim.W * offsetflip, + 0.90, + 0.15 * owner->MoverParameters->Dim.L * offsetflip }; + Camera.Angle.y = glm::radians( ( vehicle->MoverParameters->ActiveDir < 0 ? 180.0 : 0.0 ) ); + } auto const shakeangles { owner->shake_angles() }; Camera.Angle.x -= 0.5 * shakeangles.second; // hustanie kamery przod tyl Camera.Angle.z = shakeangles.first; // hustanie kamery na boki diff --git a/drivermode.h b/drivermode.h index 19ec4493..0f4f1a81 100644 --- a/drivermode.h +++ b/drivermode.h @@ -60,6 +60,12 @@ private: count_ }; + struct view_config { + TDynamicObject const *owner { nullptr }; + Math3D::vector3 offset {}; + Math3D::vector3 angle {}; + }; + struct drivermode_input { gamepad_input gamepad; @@ -91,6 +97,7 @@ private: TCamera DebugCamera; int m_externalviewmode { view::consistfront }; // selected external view mode bool m_externalview { false }; + std::array m_externalviewconfigs; TDynamicObject *pDynamicNearest { nullptr }; // vehicle nearest to the active camera. TODO: move to camera double fTime50Hz { 0.0 }; // bufor czasu dla komunikacji z PoKeys double const m_primaryupdaterate { 1.0 / 100.0 }; diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index a725481d..5c4c6503 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -319,6 +319,7 @@ drivermouse_input::button( int const Button, int const Action ) { else { if( Button == GLFW_MOUSE_BUTTON_LEFT ) { if( m_slider.command() != user_command::none ) { + m_relay.post( m_slider.command(), 0, 0, Action, 0 ); m_slider.release(); } } diff --git a/renderer.cpp b/renderer.cpp index 769c651a..229c0647 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -562,7 +562,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( vehicle->InteriorLightLevel > 0.f ) { setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); } - Render_cab( vehicle, false ); + Render_cab( vehicle, vehicle->InteriorLightLevel, false ); if( vehicle->InteriorLightLevel > 0.f ) { setup_shadow_color( shadowcolor ); } @@ -581,17 +581,17 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); // cache shadow colour in case we need to account for cab light auto const shadowcolor{ m_shadowcolor }; - auto const *vehicle{ simulation::Train->Dynamic() }; + auto const *vehicle { simulation::Train->Dynamic() }; if( vehicle->InteriorLightLevel > 0.f ) { setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); } if( Global.Overcast > 1.f ) { // with active precipitation draw the opaque cab parts here to mask rain/snow placed 'inside' the cab setup_drawing( false ); - Render_cab( vehicle, false ); + Render_cab( vehicle, vehicle->InteriorLightLevel, false ); setup_drawing( true ); } - Render_cab( vehicle, true ); + Render_cab( vehicle, vehicle->InteriorLightLevel, true ); if( vehicle->InteriorLightLevel > 0.f ) { setup_shadow_color( shadowcolor ); } @@ -681,8 +681,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #else setup_units( false, false, false ); #endif - Render_cab( simulation::Train->Dynamic(), false ); - Render_cab( simulation::Train->Dynamic(), true ); + Render_cab( simulation::Train->Dynamic(), 0.f, false ); + Render_cab( simulation::Train->Dynamic(), 0.f, true ); m_cabshadowpass = m_renderpass; // post-render restore @@ -745,7 +745,10 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_drawing( false ); setup_units( false, false, false ); // cab render skips translucent parts, so we can do it here - if( simulation::Train != nullptr ) { Render_cab( simulation::Train->Dynamic() ); } + if( simulation::Train != nullptr ) { + Render_cab( simulation::Train->Dynamic(), 0.f ); + Render( simulation::Train->Dynamic() ); + } // post-render cleanup } break; @@ -816,7 +819,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f switch( Mode ) { case rendermode::color: { Config.draw_range = Global.BaseDrawRange; break; } case rendermode::shadows: { Config.draw_range = Global.BaseDrawRange * 0.5f; break; } - case rendermode::cabshadows: { Config.draw_range = ( simulation::Train->Occupied()->ActiveCab != 0 ? 10.f : 20.f ); break; } + case rendermode::cabshadows: { Config.draw_range = simulation::Train->Occupied()->Dim.L; break; } case rendermode::reflections: { Config.draw_range = Global.BaseDrawRange; break; } case rendermode::pickcontrols: { Config.draw_range = 50.f; break; } case rendermode::pickscenery: { Config.draw_range = Global.BaseDrawRange * 0.5f; break; } @@ -2158,7 +2161,10 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { // render if( Dynamic->mdLowPolyInt ) { // low poly interior - if( FreeFlyModeFlag ? true : !Dynamic->mdKabina || !Dynamic->bDisplayCab ) { + /* + if( ( true == FreeFlyModeFlag ) + || ( ( Dynamic->mdKabina == nullptr ) || ( false == Dynamic->bDisplayCab ) ) ) { + */ /* // enable cab light if needed if( Dynamic->InteriorLightLevel > 0.0f ) { @@ -2174,7 +2180,9 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( m_baseambient ) ); } */ + /* } + */ } if( Dynamic->mdModel ) Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); @@ -2191,9 +2199,9 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { case rendermode::shadows: { if( Dynamic->mdLowPolyInt ) { // low poly interior - if( FreeFlyModeFlag ? true : !Dynamic->mdKabina || !Dynamic->bDisplayCab ) { +// if( FreeFlyModeFlag ? true : !Dynamic->mdKabina || !Dynamic->bDisplayCab ) { Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); - } +// } } if( Dynamic->mdModel ) Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); @@ -2202,7 +2210,13 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { // post-render cleanup break; } - case rendermode::pickcontrols: + case rendermode::pickcontrols: { + if( Dynamic->mdLowPolyInt ) { + // low poly interior + Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); + } + break; + } case rendermode::pickscenery: default: { break; @@ -2220,7 +2234,7 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { // rendering kabiny gdy jest oddzielnym modelem i ma byc wyswietlana bool -opengl_renderer::Render_cab( TDynamicObject const *Dynamic, bool const Alpha ) { +opengl_renderer::Render_cab( TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha ) { if( Dynamic == nullptr ) { @@ -2252,9 +2266,9 @@ opengl_renderer::Render_cab( TDynamicObject const *Dynamic, bool const Alpha ) { // change light level based on light level of the occupied track m_sunlight.apply_intensity( Dynamic->fShade ); } - if( Dynamic->InteriorLightLevel > 0.f ) { + if( Lightlevel > 0.f ) { // crude way to light the cabin, until we have something more complete in place - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Dynamic->InteriorLightLevel ) ); + ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Lightlevel ) ); } // render if( true == Alpha ) { @@ -2270,7 +2284,7 @@ opengl_renderer::Render_cab( TDynamicObject const *Dynamic, bool const Alpha ) { // change light level based on light level of the occupied track m_sunlight.apply_intensity(); } - if( Dynamic->InteriorLightLevel > 0.0f ) { + if( Lightlevel > 0.f ) { // reset the overall ambient ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr(m_baseambient) ); } @@ -2435,9 +2449,10 @@ opengl_renderer::Render( TSubModel *Submodel ) { } // ...luminance auto const unitstate = m_unitstate; - if( Global.fLuminance < Submodel->fLight ) { + auto const isemissive { ( Submodel->f4Emision.a > 0.f ) && ( Global.fLuminance < Submodel->fLight ) }; + if( isemissive ) { // zeby swiecilo na kolorowo - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); + ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( /* Submodel->f4Emision */ Submodel->f4Diffuse * Submodel->f4Emision.a ) ); // disable shadows so they don't obstruct self-lit items /* setup_shadow_color( colors::white ); @@ -2466,7 +2481,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( colors::none ) ); } - if( Global.fLuminance < Submodel->fLight ) { + if( isemissive ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); /* @@ -3245,7 +3260,7 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { // render if( Dynamic->mdLowPolyInt ) { // low poly interior - if( FreeFlyModeFlag ? true : !Dynamic->mdKabina || !Dynamic->bDisplayCab ) { +// if( FreeFlyModeFlag ? true : !Dynamic->mdKabina || !Dynamic->bDisplayCab ) { /* // enable cab light if needed if( Dynamic->InteriorLightLevel > 0.0f ) { @@ -3261,7 +3276,7 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( m_baseambient ) ); } */ - } +// } } if( Dynamic->mdModel ) @@ -3403,9 +3418,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { } // ...luminance auto const unitstate = m_unitstate; - if( Global.fLuminance < Submodel->fLight ) { + auto const isemissive { ( Submodel->f4Emision.a > 0.f ) && ( Global.fLuminance < Submodel->fLight ) }; + if( isemissive ) { // zeby swiecilo na kolorowo - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); + ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( /* Submodel->f4Emision */ Submodel->f4Diffuse * Submodel->f4Emision.a ) ); // disable shadows so they don't obstruct self-lit items /* setup_shadow_color( colors::white ); @@ -3420,7 +3436,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( colors::none ) ); } - if( Global.fLuminance < Submodel->fLight ) { + if( isemissive ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); /* diff --git a/renderer.h b/renderer.h index 64ed1625..6bdc8777 100644 --- a/renderer.h +++ b/renderer.h @@ -292,7 +292,7 @@ private: void Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last ); bool - Render_cab( TDynamicObject const *Dynamic, bool const Alpha = false ); + Render_cab( TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false ); void Render( TMemCell *Memcell ); void From 18766b11db2b476cc0bbc33335216bfab766e992 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 15 Nov 2018 15:49:55 +0100 Subject: [PATCH 2/6] daylight saving time date calculation fix --- simulationtime.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++--- simulationtime.h | 18 ++++++++++--- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/simulationtime.cpp b/simulationtime.cpp index 46c64947..b2574756 100644 --- a/simulationtime.cpp +++ b/simulationtime.cpp @@ -68,6 +68,9 @@ scenario_time::init() { ::RegQueryValueEx( timezonekey, "TZI", NULL, NULL, (BYTE *)®istrytimezoneinfo, &size ); } #endif + convert_transition_time( registrytimezoneinfo.StandardDate ); + convert_transition_time( registrytimezoneinfo.DaylightDate ); + TIME_ZONE_INFORMATION timezoneinfo { 0 }; timezoneinfo.Bias = registrytimezoneinfo.Bias; timezoneinfo.DaylightBias = registrytimezoneinfo.DaylightBias; @@ -118,7 +121,7 @@ scenario_time::update( double const Deltatime ) { } m_time.wHour -= 24; } - int leap = ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ); + int leap { ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ) }; while( m_time.wDay > m_monthdaycounts[ leap ][ m_time.wMonth ] ) { m_time.wDay -= m_monthdaycounts[ leap ][ m_time.wMonth ]; @@ -141,7 +144,7 @@ scenario_time::year_day( int Day, const int Month, const int Year ) const { { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; - int leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; + int const leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; for( int i = 1; i < Month; ++i ) Day += daytab[ leap ][ i ]; @@ -156,7 +159,7 @@ scenario_time::daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yea { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; - int leap = ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ); + int const leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; WORD idx = 1; while( ( idx < 13 ) && ( Yearday >= daytab[ leap ][ idx ] ) ) { @@ -189,4 +192,60 @@ scenario_time::julian_day() const { return JD; } +// calculates day of week for provided date +int +scenario_time::day_of_week( int const Day, int const Month, int const Year ) const { + + // using Zeller's congruence, http://en.wikipedia.org/wiki/Zeller%27s_congruence + int const q = Day; + int const m = Month > 2 ? Month : Month + 12; + int const y = Month > 2 ? Year : Year - 1; + + int const h = ( q + ( 26 * ( m + 1 ) / 10 ) + y + ( y / 4 ) + 6 * ( y / 100 ) + ( y / 400 ) ) % 7; + +/* return ( (h + 5) % 7 ) + 1; // iso week standard, with monday = 1 +*/ return ( (h + 6) % 7 ) + 1; // sunday = 1 numbering method, used in north america, japan +} + +// calculates day of month for specified weekday of specified month of the year +int +scenario_time::day_of_month( int const Week, int const Weekday, int const Month, int const Year ) const { + + int day = 0; + int dayoffset = weekdays( day_of_week( 1, Month, Year ), Weekday ); + + day = ( Week - 1 ) * 7 + 1 + dayoffset; + + if( Week == 5 ) { + // 5th week potentially indicates last week in the month, not necessarily actual 5th + char const daytab[ 2 ][ 13 ] = { + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + int const leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; + + while( day > daytab[ leap ][ Month ] ) { + day -= 7; + } + } + + return day; +} + +// returns number of days between specified days of week +int +scenario_time::weekdays( int const First, int const Second ) const { + + if( Second >= First ) { return Second - First; } + else { return 7 - First + Second; } +} + +// helper, converts provided time transition date to regular date +void +scenario_time::convert_transition_time( SYSTEMTIME &Time ) const { + + // NOTE: windows uses 0-6 range for days of week numbering, our methods use 1-7 + Time.wDay = day_of_month( Time.wDay, Time.wDayOfWeek + 1, Time.wMonth, m_time.wYear ); +} + //--------------------------------------------------------------------------- diff --git a/simulationtime.h b/simulationtime.h index 069a66ca..45f4352e 100644 --- a/simulationtime.h +++ b/simulationtime.h @@ -35,9 +35,6 @@ public: int year_day() const { return m_yearday; } - // helper, calculates day of year from given date - int - year_day( int Day, int const Month, int const Year ) const; int julian_day() const; inline @@ -46,9 +43,24 @@ public: return m_timezonebias; } private: + // converts provided time transition date to regular date + void + convert_transition_time( SYSTEMTIME &Time ) const; // calculates day and month from given day of year void daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yearday ); + // calculates day of year from given date + int + year_day( int Day, int const Month, int const Year ) const; + // calculates day of week for provided date + int + day_of_week( int const Day, int const Month, int const Year ) const; + // calculates day of month for specified weekday of specified month of the year + int + day_of_month( int const Week, int const Weekday, int const Month, int const Year ) const; + // returns number of days between specified days of week + int + weekdays( int const First, int const Second ) const; SYSTEMTIME m_time; double m_milliseconds{ 0.0 }; From 45119e64bdb5d4013b0dd6d2a52afaad4a2fce5f Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 15 Nov 2018 15:51:37 +0100 Subject: [PATCH 3/6] maintenance: minor track geometry generation code refactoring --- Segment.cpp | 7 +- Segment.h | 2 +- Track.cpp | 211 ++++++++++++++++++++++++++-------------------------- 3 files changed, 108 insertions(+), 112 deletions(-) diff --git a/Segment.cpp b/Segment.cpp index c9b04fca..a3f63708 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -374,7 +374,7 @@ Math3D::vector3 TSegment::FastGetPoint(double const t) const interpolate( Point1, Point2, t ) ); } -bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, const gfx::vertex_array &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, std::pair fOffsetX, glm::vec3 **p, bool bRender) +bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, const gfx::vertex_array &ShapePoints, bool const Transition, double fTextureLength, double Texturescale, int iSkip, int iEnd, std::pair fOffsetX, glm::vec3 **p, bool bRender) { // generowanie trójkątów dla odcinka trajektorii ruchu // standardowo tworzy triangle_strip dla prostego albo ich zestaw dla łuku // po modyfikacji - dla ujemnego (iNumShapePoints) w dodatkowych polach tabeli podany jest przekrój końcowy @@ -384,8 +384,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori glm::vec3 pos1, pos2, dir, parallel1, parallel2, pt, norm; float s, step, fOffset, tv1, tv2, t, fEnd; - bool const trapez = iNumShapePoints < 0; // sygnalizacja trapezowatości - iNumShapePoints = std::abs( iNumShapePoints ); + auto const iNumShapePoints = Transition ? ShapePoints.size() / 2 : ShapePoints.size(); float const texturelength = fTextureLength * Texturescale; float const texturescale = Texturescale; @@ -450,7 +449,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori parallel2 = glm::normalize( parallel2 ); // TODO: refactor the loop, there's no need to calculate starting points for each segment when we can copy the end points of the previous one - if( trapez ) { + if( Transition ) { for( int j = 0; j < iNumShapePoints; ++j ) { pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m1 * ( ShapePoints[ j + iNumShapePoints ].position.x - fOffsetX.second ) ) + pos1; pt.y += jmm1 * ShapePoints[ j ].position.y + m1 * ShapePoints[ j + iNumShapePoints ].position.y; diff --git a/Segment.h b/Segment.h index 06d0290a..9ea8020e 100644 --- a/Segment.h +++ b/Segment.h @@ -116,7 +116,7 @@ public: r2 = fRoll2; } bool - RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, gfx::vertex_array const &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, std::pair fOffsetX = {0.f, 0.f}, glm::vec3 **p = nullptr, bool bRender = true ); + RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, gfx::vertex_array const &ShapePoints, bool const Transition, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, std::pair fOffsetX = {0.f, 0.f}, glm::vec3 **p = nullptr, bool bRender = true ); /* void Render(); diff --git a/Track.cpp b/Track.cpp index ad19950e..1a576a6a 100644 --- a/Track.cpp +++ b/Track.cpp @@ -186,8 +186,7 @@ void TTrack::Init() bool TTrack::sort_by_material( TTrack const *Left, TTrack const *Right ) { - return ( ( Left->m_material1 < Right->m_material1 ) - && ( Left->m_material2 < Right->m_material2 ) ); + return std::tie( Left->m_material1, Left->m_material2 ) < std::tie( Right->m_material1, Right->m_material2 ); } TTrack * TTrack::Create400m(int what, double dx) @@ -580,7 +579,7 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin) if (eType == tt_Table) // obrotnica ma doklejkę { // SwitchExtension=new TSwitchExtension(this,1); //dodatkowe zmienne dla obrotnicy - SwitchExtension->Segments[0]->Init(p1, p2, segsize); // kopia oryginalnego toru + SwitchExtension->Segments[0]->Init(p1, p2, segsize, r1, r2 ); // kopia oryginalnego toru } else if (iCategoryFlag & 2) if (m_material1 && fTexLength) @@ -1110,11 +1109,10 @@ void TTrack::RaAssign( TAnimModel *am, basic_event *done, basic_event *joined ) { SwitchExtension->pModel = am; SwitchExtension->evMinus = done; // event zakończenia animacji (zadanie nowej przedłuża) - SwitchExtension->evPlus = - joined; // event potwierdzenia połączenia (gdy nie znajdzie, to się nie połączy) - if (am) - if (am->GetContainer()) // może nie być? - am->GetContainer()->EventAssign(done); // zdarzenie zakończenia animacji + SwitchExtension->evPlus = joined; // event potwierdzenia połączenia (gdy nie znajdzie, to się nie połączy) + if( ( am != nullptr ) && ( am->GetContainer() ) ) {// może nie być? + am->GetContainer()->EventAssign( done ); // zdarzenie zakończenia animacji + } } }; @@ -1138,7 +1136,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { create_track_bed_profile( bpts1, trPrev, trNext ); auto const texturelength { texture_length( m_material2 ) }; gfx::vertex_array vertices; - Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -5 : 5, texturelength); + Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid > 0, texturelength); if( ( Bank != 0 ) && ( true == Geometry2.empty() ) ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } @@ -1153,18 +1151,18 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { auto const texturelength { texture_length( m_material1 ) }; gfx::vertex_array vertices; if( ( Bank != 0 ) && ( true == Geometry1.empty() ) ) { - Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, texturelength ); + Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, texturelength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // reuse the scratchpad - Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, texturelength ); + Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, texturelength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } if( ( Bank == 0 ) && ( false == Geometry1.empty() ) ) { // special variant, replace existing data for a turntable track - Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, texturelength ); + Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, texturelength ); GfxRenderer.Replace( vertices, Geometry1[ 0 ] ); vertices.clear(); // reuse the scratchpad - Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, texturelength ); + Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, texturelength ); GfxRenderer.Replace( vertices, Geometry1[ 1 ] ); } } @@ -1175,8 +1173,12 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { gfx::vertex_array rpts3, rpts4; create_track_blade_profile( rpts3, rpts4 ); // TODO, TBD: change all track geometry to triangles, to allow packing data in less, larger buffers - auto const bladelength { static_cast( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) }; auto const nnumPts { track_rail_profile( m_profile1.second ).size() / 2 }; + auto const bladelength { static_cast( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) }; + // positive jointlength: the switch is typically used along the main track, negative: along the diverging track + // TODO: determine this from names of textures assigned to the tracks +// auto const jointlength { static_cast( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.15 ) ) }; + auto const jointlength { 0 }; // temporary until implementation of the above if (SwitchExtension->RightSwitch) { // nowa wersja z SPKS, ale odwrotnie lewa/prawa gfx::vertex_array vertices; @@ -1184,15 +1186,22 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { auto const texturelength { texture_length( m_material1 ) }; // left blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } ); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // fixed parts - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength ); + if( jointlength > 0 ) { + // part of the diverging rail touched by wheels of vehicle going straight + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, 0, jointlength ); + Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + vertices.clear(); + } + // other rail, full length + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -1200,15 +1209,16 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { auto const texturelength { texture_length( m_material2 ) }; // right blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } ); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // fixed parts - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength ); + // diverging rail, potentially minus part touched by wheels of vehicle going straight + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, jointlength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -1220,15 +1230,23 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { auto const texturelength { texture_length( m_material1 ) }; // right blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } ); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // fixed parts - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength ); // prawa szyna za iglicą + // prawa szyna za iglicą + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength ); // lewa szyna normalna cała + if( jointlength > 0 ) { + // part of the diverging rail touched by wheels of vehicle going straight + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, 0, jointlength ); + Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + vertices.clear(); + } + // other rail, full length + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -1236,15 +1254,17 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { auto const texturelength { texture_length( m_material2 ) }; // left blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } ); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // fixed parts - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength ); // lewa szyna za iglicą + // lewa szyna za iglicą + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength ); // prawa szyna normalnie cała + // diverging rail, potentially minus part touched by wheels of vehicle going straight + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, jointlength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -1276,7 +1296,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { { // tworzenie trójkątów nawierzchni szosy auto const texturelength { texture_length( m_material1 ) }; gfx::vertex_array vertices; - Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -2 : 2, texturelength); + Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid > 0, texturelength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } if (m_material2) @@ -1287,31 +1307,17 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony create_road_side_profile( rpts1, rpts2, bpts1 ); gfx::vertex_array vertices; - if( iTrapezoid ) // trapez albo przechyłki - { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony - // odcinka - if( ( fTexHeight1 >= 0.0 ) || ( slop != 0.0 ) ) { - Segment->RenderLoft( vertices, m_origin, rpts1, -3, texturelength ); // tylko jeśli jest z prawej - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - } - if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - Segment->RenderLoft( vertices, m_origin, rpts2, -3, texturelength ); // tylko jeśli jest z lewej - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - } + if( ( fTexHeight1 >= 0.0 ) || ( slop != 0.0 ) ) { + // tylko jeśli jest z prawej + Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, texturelength ); + Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + vertices.clear(); } - else { // pobocza zwykłe, brak przechyłki - if( ( fTexHeight1 >= 0.0 ) || ( slop != 0.0 ) ) { - Segment->RenderLoft( vertices, m_origin, rpts1, 3, texturelength ); - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - } - if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - Segment->RenderLoft( vertices, m_origin, rpts2, 3, texturelength ); - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - } + if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { + // tylko jeśli jest z lewej + Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, texturelength ); + Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + vertices.clear(); } } break; @@ -1385,22 +1391,22 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if (SwitchExtension->iRoads == 4) { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); + SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 3 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); + SwitchExtension->Segments[ 3 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 4 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); + SwitchExtension->Segments[ 4 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 5 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); + SwitchExtension->Segments[ 5 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); @@ -1410,17 +1416,17 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { else { // punkt 3 pokrywa się z punktem 1, jak w zwrotnicy; połączenie 1->2 nie musi być prostoliniowe if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); // z P2 do P4 + SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); // z P2 do P4 if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); // z P4 do P3=P1 (odwrócony) + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); // z P4 do P3=P1 (odwrócony) if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); // z P1 do P2 + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); // z P1 do P2 if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); @@ -1495,7 +1501,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy gfx::vertex_array vertices; - Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); + Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid > 0, fTexLength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } if (m_material2) @@ -1503,24 +1509,12 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony create_road_side_profile( rpts1, rpts2, bpts1 ); gfx::vertex_array vertices; - if (iTrapezoid) // trapez albo przechyłki - { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - Segment->RenderLoft(vertices, m_origin, rpts1, -3, fTexLength); - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - Segment->RenderLoft(vertices, m_origin, rpts2, -3, fTexLength); - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - } - else - { // pobocza zwykłe, brak przechyłki - Segment->RenderLoft(vertices, m_origin, rpts1, 3, fTexLength); - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - Segment->RenderLoft(vertices, m_origin, rpts2, 3, fTexLength); - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); - } + Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, fTexLength ); + Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + vertices.clear(); + Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, fTexLength ); + Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + vertices.clear(); } } } @@ -1795,8 +1789,8 @@ TTrack * TTrack::RaAnimate() auto const texturelength { texture_length( m_material1 ) }; // left blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } ); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } ); GfxRenderer.Replace( vertices, Geometry1[ 0 ] ); vertices.clear(); } @@ -1804,8 +1798,8 @@ TTrack * TTrack::RaAnimate() auto const texturelength { texture_length( m_material2 ) }; // right blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } ); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } ); GfxRenderer.Replace( vertices, Geometry2[ 0 ] ); vertices.clear(); } @@ -1815,8 +1809,8 @@ TTrack * TTrack::RaAnimate() auto const texturelength { texture_length( m_material1 ) }; // right blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } ); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } ); GfxRenderer.Replace( vertices, Geometry1[ 0 ] ); vertices.clear(); } @@ -1824,8 +1818,8 @@ TTrack * TTrack::RaAnimate() auto const texturelength { texture_length( m_material2 ) }; // left blade // composed from two parts: transition from blade to regular rail, and regular rail - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } ); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } ); GfxRenderer.Replace( vertices, Geometry2[ 0 ] ); vertices.clear(); } @@ -1842,22 +1836,23 @@ TTrack * TTrack::RaAnimate() SwitchExtension->pModel ? SwitchExtension->pModel->GetContainer() : // pobranie głównego submodelu nullptr ); - if (ac) - if ((ac->AngleGet() != SwitchExtension->fOffset) || - !(ac->TransGet() == - SwitchExtension->vTrans)) // czy przemieściło się od ostatniego sprawdzania - { - double hlen = 0.5 * SwitchExtension->Segments[0]->GetLength(); // połowa - // długości - SwitchExtension->fOffset = ac->AngleGet(); // pobranie kąta z submodelu - double sina = -hlen * std::sin(glm::radians(SwitchExtension->fOffset)), - cosa = -hlen * std::cos(glm::radians(SwitchExtension->fOffset)); + if( ac ) { + if( ( ac->AngleGet() != SwitchExtension->fOffset ) + || !( ac->TransGet() == SwitchExtension->vTrans ) ) { // czy przemieściło się od ostatniego sprawdzania + + double hlen = 0.5 * SwitchExtension->Segments[ 0 ]->GetLength(); // połowa długości + SwitchExtension->fOffset = + SwitchExtension->pModel->Angles().y // take into account orientation of the model + + ac->AngleGet(); // pobranie kąta z submodelu + double + sina = -hlen * std::sin( glm::radians( SwitchExtension->fOffset ) ), + cosa = -hlen * std::cos( glm::radians( SwitchExtension->fOffset ) ); SwitchExtension->vTrans = ac->TransGet(); - auto middle = - location() + - SwitchExtension->vTrans; // SwitchExtension->Segments[0]->FastGetPoint(0.5); - Segment->Init(middle + Math3D::vector3(sina, 0.0, cosa), - middle - Math3D::vector3(sina, 0.0, cosa), 10.0); // nowy odcinek + auto middle = location() + SwitchExtension->vTrans; // SwitchExtension->Segments[0]->FastGetPoint(0.5); + Segment->Init( + middle + Math3D::vector3( sina, 0.0, cosa ), + middle - Math3D::vector3( sina, 0.0, cosa ), + 10.0 ); // nowy odcinek for( auto dynamic : Dynamics ) { // minimalny ruch, aby przeliczyć pozycję dynamic->Move( 0.000001 ); @@ -1865,6 +1860,7 @@ TTrack * TTrack::RaAnimate() // NOTE: passing empty handle is a bit of a hack here. could be refactored into something more elegant create_geometry( {} ); } // animacja trwa nadal + } } else m = false; // koniec animacji albo w ogóle nie połączone z modelem @@ -3059,10 +3055,10 @@ TTrack::create_switch_trackbed( gfx::vertex_array &Output ) { gfx::vertex_array trackbedvertices1, trackbedvertices2; // main trackbed create_track_bed_profile( trackbedprofile, SwitchExtension->pPrevs[ 0 ], SwitchExtension->pNexts[ 0 ] ); - SwitchExtension->Segments[ 0 ]->RenderLoft( trackbedvertices1, m_origin, trackbedprofile, -5, texturelength ); + SwitchExtension->Segments[ 0 ]->RenderLoft( trackbedvertices1, m_origin, trackbedprofile, true, texturelength ); // side trackbed create_track_bed_profile( trackbedprofile, SwitchExtension->pPrevs[ 1 ], SwitchExtension->pNexts[ 1 ] ); - SwitchExtension->Segments[ 1 ]->RenderLoft( trackbedvertices2, m_origin, trackbedprofile, -5, texturelength ); + SwitchExtension->Segments[ 1 ]->RenderLoft( trackbedvertices2, m_origin, trackbedprofile, true, texturelength ); // ...then combine them into a single geometry sequence auto const segmentsize { 10 }; auto const segmentcount { trackbedvertices1.size() / segmentsize }; @@ -3142,8 +3138,9 @@ TTrack::copy_adjacent_trackbed_material( TTrack const *Exclude ) { break; } case tt_Switch: { - // only check the neighbour on the joint side + // only check the neighbours of the main track adjacents.emplace_back( SwitchExtension->pPrevs[ 0 ] ); + adjacents.emplace_back( SwitchExtension->pNexts[ 0 ] ); break; } default: { From 97d60a9b0ea71c8df0ffb4ad73f241c3f5a14d65 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 15 Nov 2018 23:02:21 +0100 Subject: [PATCH 4/6] contextual interception of user input by imgui user interface implementation --- drivermode.cpp | 7 +++++-- driveruilayer.cpp | 8 +++----- driveruilayer.h | 18 +++++++++--------- editormode.cpp | 14 +++++++++++++- editormode.h | 2 +- editoruilayer.cpp | 7 ------- editoruilayer.h | 3 --- editoruipanels.cpp | 23 +++++++++++++++++++++++ editoruipanels.h | 7 +++++++ uilayer.cpp | 26 +++++++++++++++++++++----- uilayer.h | 24 +++++++++++++++++++----- 11 files changed, 101 insertions(+), 38 deletions(-) diff --git a/drivermode.cpp b/drivermode.cpp index 6bcc62ad..94029c02 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -322,7 +322,7 @@ driver_mode::on_key( int const Key, int const Scancode, int const Action, int co Global.altState = ( Mods & GLFW_MOD_ALT ) ? true : false; // give the ui first shot at the input processing... - if( true == m_userinterface->on_key( Key, Action ) ) { return; } + if( true == m_userinterface->on_key( Key, Scancode, Action, Mods ) ) { return; } // ...if the input is left untouched, pass it on if( true == m_input.keyboard.key( Key, Action ) ) { return; } @@ -370,7 +370,7 @@ void driver_mode::on_mouse_button( int const Button, int const Action, int const Mods ) { // give the ui first shot at the input processing... - if( true == m_userinterface->on_mouse_button( Button, Action ) ) { return; } + if( true == m_userinterface->on_mouse_button( Button, Action, Mods ) ) { return; } // give the potential event recipient a shot at it, in the virtual z order m_input.mouse.button( Button, Action ); @@ -379,6 +379,9 @@ driver_mode::on_mouse_button( int const Button, int const Action, int const Mods void driver_mode::on_scroll( double const Xoffset, double const Yoffset ) { + // give the ui first shot at the input processing... + if( true == m_userinterface->on_scroll( Xoffset, Yoffset ) ) { return; } + m_input.mouse.scroll( Xoffset, Yoffset ); } diff --git a/driveruilayer.cpp b/driveruilayer.cpp index d2ac22b9..7a30dcde 100644 --- a/driveruilayer.cpp +++ b/driveruilayer.cpp @@ -40,9 +40,7 @@ driver_ui::driver_ui() { // potentially processes provided input key. returns: true if key was processed, false otherwise bool -driver_ui::on_key( int const Key, int const Action ) { - // TODO: pass the input first through an active ui element if there's any - // if the ui element shows no interest or we don't have one, try to interpret the input yourself: +driver_ui::on_key_( int const Key, int const Scancode, int const Action, int const Mods ) { if( Key == GLFW_KEY_ESCAPE ) { // toggle pause @@ -130,14 +128,14 @@ driver_ui::on_key( int const Key, int const Action ) { // potentially processes provided mouse movement. returns: true if the input was processed, false otherwise bool -driver_ui::on_cursor_pos( double const Horizontal, double const Vertical ) { +driver_ui::on_cursor_pos_( double const Horizontal, double const Vertical ) { // intercept mouse movement when the pause window is on return m_paused; } // potentially processes provided mouse button. returns: true if the input was processed, false otherwise bool -driver_ui::on_mouse_button( int const Button, int const Action ) { +driver_ui::on_mouse_button_( int const Button, int const Action, int const Mods ) { // intercept mouse movement when the pause window is on return m_paused; } diff --git a/driveruilayer.h b/driveruilayer.h index 28a6d94b..f90beccb 100644 --- a/driveruilayer.h +++ b/driveruilayer.h @@ -18,15 +18,6 @@ public: // constructors driver_ui(); // methods - // potentially processes provided input key. returns: true if the input was processed, false otherwise - bool - on_key( int const Key, int const Action ) override; - // potentially processes provided mouse movement. returns: true if the input was processed, false otherwise - bool - on_cursor_pos( double const Horizontal, double const Vertical ) override; - // potentially processes provided mouse button. returns: true if the input was processed, false otherwise - bool - on_mouse_button( int const Button, int const Action ) override; // updates state of UI elements void update() override; @@ -39,6 +30,15 @@ private: // render() subclass details void render_() override; + // on_key() subclass details + bool + on_key_( int const Key, int const Scancode, int const Action, int const Mods ) override; + // on_cursor_pos() subclass details + bool + on_cursor_pos_( double const Horizontal, double const Vertical ) override; + // on_mouse_button() subclass details + bool + on_mouse_button_( int const Button, int const Action, int const Mods ) override; // members drivingaid_panel m_aidpanel { "Driving Aid", true }; timetable_panel m_timetablepanel { "Timetable", false }; diff --git a/editormode.cpp b/editormode.cpp index 30bb12cf..eb34f510 100644 --- a/editormode.cpp +++ b/editormode.cpp @@ -139,7 +139,7 @@ editor_mode::on_key( int const Key, int const Scancode, int const Action, int co Global.altState = ( Mods & GLFW_MOD_ALT ) ? true : false; // give the ui first shot at the input processing... - if( true == m_userinterface->on_key( Key, Action ) ) { return; } + if( true == m_userinterface->on_key( Key, Scancode, Action, Mods ) ) { return; } // ...if the input is left untouched, pass it on if( true == m_input.keyboard.key( Key, Action ) ) { return; } @@ -222,6 +222,9 @@ editor_mode::on_cursor_pos( double const Horizontal, double const Vertical ) { void editor_mode::on_mouse_button( int const Button, int const Action, int const Mods ) { + // give the ui first shot at the input processing... + if( true == m_userinterface->on_mouse_button( Button, Action, Mods ) ) { return; } + if( Button == GLFW_MOUSE_BUTTON_LEFT ) { if( Action == GLFW_PRESS ) { @@ -245,6 +248,15 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods m_input.mouse.button( Button, Action ); } +void +editor_mode::on_scroll( double const Xoffset, double const Yoffset ) { + + // give the ui first shot at the input processing... + if( true == m_userinterface->on_scroll( Xoffset, Yoffset ) ) { return; } + + // TBD, TODO: implement scroll wheel handling +} + void editor_mode::on_event_poll() { diff --git a/editormode.h b/editormode.h index ef856eb1..a801492c 100644 --- a/editormode.h +++ b/editormode.h @@ -42,7 +42,7 @@ public: void on_mouse_button( int const Button, int const Action, int const Mods ) override; void - on_scroll( double const Xoffset, double const Yoffset ) override { ; } + on_scroll( double const Xoffset, double const Yoffset ) override; void on_event_poll() override; diff --git a/editoruilayer.cpp b/editoruilayer.cpp index 342bd20e..b0e75e87 100644 --- a/editoruilayer.cpp +++ b/editoruilayer.cpp @@ -20,13 +20,6 @@ editor_ui::editor_ui() { push_back( &m_itempropertiespanel ); } -// potentially processes provided input key. returns: true if key was processed, false otherwise -bool -editor_ui::on_key( int const Key, int const Action ) { - - return false; -} - // updates state of UI elements void editor_ui::update() { diff --git a/editoruilayer.h b/editoruilayer.h index 95b06169..daa050ef 100644 --- a/editoruilayer.h +++ b/editoruilayer.h @@ -24,9 +24,6 @@ public: // constructors editor_ui(); // methods - // potentially processes provided input key. returns: true if the input was processed, false otherwise - bool - on_key( int const Key, int const Action ) override; // updates state of UI elements void update() override; diff --git a/editoruipanels.cpp b/editoruipanels.cpp index 0d1ded79..34f1eb30 100644 --- a/editoruipanels.cpp +++ b/editoruipanels.cpp @@ -17,10 +17,13 @@ http://mozilla.org/MPL/2.0/. #include "Event.h" #include "renderer.h" #include "utilities.h" +#include "scenenodegroups.h" void itemproperties_panel::update( scene::basic_node const *Node ) { + m_node = Node; + if( false == is_open ) { return; } text_lines.clear(); @@ -196,6 +199,26 @@ itemproperties_panel::render() { for( auto const &line : text_lines ) { ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); } + // group section + render_group(); } ImGui::End(); } + +bool +itemproperties_panel::render_group() { + + if( m_node == nullptr ) { return false; } + if( m_node->group() == null_handle ) { return false; } + + if( false == ImGui::CollapsingHeader( "Parent Group" ) ) { return false; } + + auto const &nodegroup { scene::Groups.group( m_node->group() ) }; + auto const &linecolor { Global.UITextColor }; + + ImGui::TextColored( ImVec4( linecolor.r, linecolor.g, linecolor.b, linecolor.a ), + ( "Nodes: " + to_string( static_cast( nodegroup.nodes.size() ) ) + + "\nEvents: " + to_string( static_cast( nodegroup.events.size() ) ) ).c_str() ); + + return true; +} diff --git a/editoruipanels.h b/editoruipanels.h index 3a18c4d8..5f4c325b 100644 --- a/editoruipanels.h +++ b/editoruipanels.h @@ -21,4 +21,11 @@ public: void update( scene::basic_node const *Node ); void render() override; + +private: +// methods + bool render_group(); + +// members + scene::basic_node const *m_node { nullptr }; // scene node bound to the panel }; diff --git a/uilayer.cpp b/uilayer.cpp index 0784c6ca..6ad6929f 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -137,21 +137,37 @@ ui_layer::shutdown() { } bool -ui_layer::on_key( int const Key, int const Action ) { +ui_layer::on_key( int const Key, int const Scancode, int const Action, int const Mods ) { - return false; + ImGui_ImplGlfw_KeyCallback( m_window, Key, Scancode, Action, Mods ); + if( m_imguiio->WantTextInput ) { return true; } + + return on_key_( Key, Scancode, Action, Mods ); } bool ui_layer::on_cursor_pos( double const Horizontal, double const Vertical ) { - return false; + return on_cursor_pos_( Horizontal, Vertical ); } bool -ui_layer::on_mouse_button( int const Button, int const Action ) { +ui_layer::on_mouse_button( int const Button, int const Action, int const Mods ) { - return false; + ImGui_ImplGlfw_MouseButtonCallback( m_window, Button, Action, Mods ); + if( m_imguiio->WantCaptureMouse ) { return true; } + + return on_mouse_button_( Button, Action, Mods ); +} + +// potentially processes provided mouse scroll event. returns: true if the input was processed, false otherwise +bool +ui_layer::on_scroll( double const Xoffset, double const Yoffset ) { + + ImGui_ImplGlfw_ScrollCallback( m_window, Xoffset, Yoffset ); + if( m_imguiio->WantCaptureMouse ) { return true; } + + return on_scroll_( Xoffset, Yoffset ); } void diff --git a/uilayer.h b/uilayer.h index 9d120827..ee524d0a 100644 --- a/uilayer.h +++ b/uilayer.h @@ -66,17 +66,17 @@ public: void shutdown(); // potentially processes provided input key. returns: true if the input was processed, false otherwise - virtual bool - on_key( int const Key, int const Action ); + on_key( int const Key, int const Scancode, int const Action, int const Mods ); // potentially processes provided mouse movement. returns: true if the input was processed, false otherwise - virtual bool on_cursor_pos( double const Horizontal, double const Vertical ); // potentially processes provided mouse button. returns: true if the input was processed, false otherwise - virtual bool - on_mouse_button( int const Button, int const Action ); + on_mouse_button( int const Button, int const Action, int const Mods ); + // potentially processes provided mouse scroll event. returns: true if the input was processed, false otherwise + bool + on_scroll( double const Xoffset, double const Yoffset ); // updates state of UI elements virtual void @@ -135,6 +135,20 @@ private: // draws a quad between coordinates x,y and z,w with uv-coordinates spanning 0-1 void quad( glm::vec4 const &Coordinates, glm::vec4 const &Color ); + // input methods subclass details + virtual + bool + on_key_( int const Key, int const Scancode, int const Action, int const Mods ) { return false; } + virtual + bool + on_cursor_pos_( double const Horizontal, double const Vertical ) { return false; } + virtual + bool + on_mouse_button_( int const Button, int const Action, int const Mods ) { return false; } + virtual + bool + on_scroll_( double const Xoffset, double const Yoffset ) { return false; } + // members static GLint m_textureunit; From 1d6f75322b2c8b214cb626d3e6f5e16ae7372340 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 17 Nov 2018 21:14:36 +0100 Subject: [PATCH 5/6] build 181117. load exchange time calculation improvement, pantograph tank pressure cab control, ui info panel vehicle selection tweak --- Driver.cpp | 19 +++++++++++++++++-- DynObj.cpp | 38 ++++++++++++++++++++++++++++++++------ DynObj.h | 6 +++++- Train.cpp | 6 ++++++ driveruipanels.cpp | 18 +++++++++--------- station.cpp | 14 ++------------ version.h | 2 +- 7 files changed, 72 insertions(+), 31 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 599f9940..e6e5301b 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -966,8 +966,8 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // perform loading/unloading auto const platformside = static_cast( std::floor( std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ) ) ) % 10; - auto const exchangetime = std::max( 5.0, simulation::Station.update_load( pVehicles[ 0 ], *TrainParams, platformside ) ); - WaitingSet( std::max( -fStopTime, exchangetime ) ); // na końcu rozkładu się ustawia 60s i tu by było skrócenie + auto const exchangetime = simulation::Station.update_load( pVehicles[ 0 ], *TrainParams, platformside ); + WaitingSet( exchangetime ); if( TrainParams->CheckTrainLatency() < 0.0 ) { // odnotowano spóźnienie @@ -1009,6 +1009,21 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN continue; } } + else { + // sitting at passenger stop + if( fStopTime < 0 ) { + // verify progress of load exchange + auto exchangetime { 0.f }; + auto *vehicle { pVehicles[ 0 ] }; + while( vehicle != nullptr ) { + exchangetime = std::max( exchangetime, vehicle->LoadExchangeTime() ); + vehicle = vehicle->Next(); + } + if( exchangetime > 0 ) { + WaitingSet( exchangetime ); + } + } + } if (OrderCurrentGet() & Shunt) { OrderNext(Obey_train); // uruchomić jazdę pociągową diff --git a/DynObj.cpp b/DynObj.cpp index 058e66a0..fff3b8cb 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2636,13 +2636,39 @@ void TDynamicObject::LoadExchange( int const Disembark, int const Embark, int co } m_exchange.unload_count += Disembark; m_exchange.load_count += Embark; - m_exchange.speed_factor = ( - Platform == 3 ? - 2.0 : - 1.0 ); + m_exchange.platforms = Platform; m_exchange.time = 0.0; } +// calculates time needed to complete current load change +float TDynamicObject::LoadExchangeTime() const { + + if( ( m_exchange.unload_count < 0.01 ) && ( m_exchange.load_count < 0.01 ) ) { return 0.f; } + + auto const baseexchangetime { m_exchange.unload_count / MoverParameters->UnLoadSpeed + m_exchange.load_count / MoverParameters->LoadSpeed }; + auto const nominalexchangespeedfactor { ( m_exchange.platforms == 3 ? 2.f : 1.f ) }; + auto const actualexchangespeedfactor { LoadExchangeSpeed() }; + + return baseexchangetime / ( actualexchangespeedfactor > 0.f ? actualexchangespeedfactor : nominalexchangespeedfactor ); +} + +// calculates current load exchange rate +float TDynamicObject::LoadExchangeSpeed() const { + // platforms (1:left, 2:right, 3:both) + // with exchange performed on both sides waiting times are halved + auto exchangespeedfactor { 0.f }; + auto const lewe { ( DirectionGet() > 0 ) ? 1 : 2 }; + auto const prawe { 3 - lewe }; + if( m_exchange.platforms & lewe ) { + exchangespeedfactor += ( MoverParameters->DoorLeftOpened ? 1.f : 0.f ); + } + if( m_exchange.platforms & prawe ) { + exchangespeedfactor += ( MoverParameters->DoorRightOpened ? 1.f : 0.f ); + } + + return exchangespeedfactor; +} + // update state of load exchange operation void TDynamicObject::update_exchange( double const Deltatime ) { @@ -2660,7 +2686,7 @@ void TDynamicObject::update_exchange( double const Deltatime ) { && ( m_exchange.time >= 1.0 ) ) { m_exchange.time -= 1.0; - auto const exchangesize = std::min( m_exchange.unload_count, MoverParameters->UnLoadSpeed * m_exchange.speed_factor ); + auto const exchangesize = std::min( m_exchange.unload_count, MoverParameters->UnLoadSpeed * LoadExchangeSpeed() ); m_exchange.unload_count -= exchangesize; MoverParameters->LoadStatus = 1; MoverParameters->LoadAmount = std::max( 0.f, MoverParameters->LoadAmount - exchangesize ); @@ -2674,7 +2700,7 @@ void TDynamicObject::update_exchange( double const Deltatime ) { && ( m_exchange.time >= 1.0 ) ) { m_exchange.time -= 1.0; - auto const exchangesize = std::min( m_exchange.load_count, MoverParameters->LoadSpeed * m_exchange.speed_factor ); + auto const exchangesize = std::min( m_exchange.load_count, MoverParameters->LoadSpeed * LoadExchangeSpeed() ); m_exchange.load_count -= exchangesize; MoverParameters->LoadStatus = 2; MoverParameters->LoadAmount = std::min( MoverParameters->MaxLoad, MoverParameters->LoadAmount + exchangesize ); // std::max not strictly needed but, eh diff --git a/DynObj.h b/DynObj.h index 7725f03b..8bcb242a 100644 --- a/DynObj.h +++ b/DynObj.h @@ -289,7 +289,7 @@ private: struct exchange_data { float unload_count { 0.f }; // amount to unload float load_count { 0.f }; // amount to load - float speed_factor { 1.f }; // operation speed modifier + int platforms { 0 }; // platforms which may take part in the exchange float time { 0.f }; // time spent on the operation }; @@ -529,6 +529,10 @@ private: bool UpdateForce(double dt, double dt1, bool FullVer); // initiates load change by specified amounts, with a platform on specified side void LoadExchange( int const Disembark, int const Embark, int const Platform ); + // calculates time needed to complete current load change + float LoadExchangeTime() const; + // calculates current load exchange factor, where 1 = nominal rate, higher = faster + float LoadExchangeSpeed() const; // TODO: make private when cleaning up void LoadUpdate(); void update_load_sections(); void update_load_visibility(); diff --git a/Train.cpp b/Train.cpp index 38f1294e..76ab8258 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7775,6 +7775,12 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con gauge.Load( Parser, DynamicObject ); gauge.AssignFloat( &mvControlled->dizel_heat.temperatura2 ); } + else if( Label == "pantpress:" ) { + // pantograph tank pressure + auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka + gauge.Load( Parser, DynamicObject, 0.1 ); + gauge.AssignDouble( &mvOccupied->PantPress ); + } // yB - dla drugiej sekcji else if (Label == "hvbcurrent1:") { diff --git a/driveruipanels.cpp b/driveruipanels.cpp index d8ee2ccb..4c0d9384 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -117,7 +117,7 @@ drivingaid_panel::update() { { // alerter, hints std::string expandedtext; if( is_expanded ) { - auto const stoptime { static_cast( -1.0 * controlled->Mechanik->fStopTime ) }; + auto const stoptime { static_cast( std::ceil( -1.0 * controlled->Mechanik->fStopTime ) ) }; if( stoptime > 0 ) { std::snprintf( m_buffer.data(), m_buffer.size(), @@ -175,10 +175,10 @@ timetable_panel::update() { text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); } - auto *vehicle { - ( FreeFlyModeFlag ? - std::get( simulation::Region->find_vehicle( camera.Pos, 20, false, false ) ) : - controlled ) }; // w trybie latania lokalizujemy wg mapy + auto *vehicle { ( + false == FreeFlyModeFlag ? controlled : + camera.m_owner != nullptr ? camera.m_owner : + std::get( simulation::Region->find_vehicle( camera.Pos, 20, false, false ) ) ) }; // w trybie latania lokalizujemy wg mapy if( vehicle == nullptr ) { return; } // if the nearest located vehicle doesn't have a direct driver, try to query its owner @@ -288,10 +288,10 @@ debug_panel::update() { m_input.train = simulation::Train; m_input.controlled = ( m_input.train ? m_input.train->Dynamic() : nullptr ); m_input.camera = &( Global.pCamera ); - m_input.vehicle = - ( FreeFlyModeFlag ? - std::get( simulation::Region->find_vehicle( m_input.camera->Pos, 20, false, false ) ) : - m_input.controlled ); // w trybie latania lokalizujemy wg mapy + m_input.vehicle = ( + false == FreeFlyModeFlag ? m_input.controlled : + m_input.camera->m_owner != nullptr ? m_input.camera->m_owner : + std::get( simulation::Region->find_vehicle( m_input.camera->Pos, 20, false, false ) ) ); // w trybie latania lokalizujemy wg mapy m_input.mover = ( m_input.vehicle != nullptr ? m_input.vehicle->MoverParameters : diff --git a/station.cpp b/station.cpp index 88a1c928..7e9d4654 100644 --- a/station.cpp +++ b/station.cpp @@ -37,12 +37,6 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch // go through all vehicles and update their load // NOTE: for the time being we limit ourselves to passenger-carrying cars only auto exchangetime { 0.f }; - // platform (1:left, 2:right, 3:both) - // with exchange performed on both sides waiting times are halved - auto const exchangetimemodifier { ( - Platform == 3 ? - 0.5f : - 1.0f ) }; auto *vehicle { First }; while( vehicle != nullptr ) { @@ -69,18 +63,14 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch 0 : Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) ); if( true == firststop ) { - // slightly larger group at the initial station + // larger group at the initial station loadcount *= 2; } if( ( unloadcount > 0 ) || ( loadcount > 0 ) ) { vehicle->LoadExchange( unloadcount, loadcount, Platform ); -/* - vehicle->LoadUpdate(); - vehicle->update_load_visibility(); -*/ - exchangetime = std::max( exchangetime, exchangetimemodifier * ( unloadcount / parameters.UnLoadSpeed + loadcount / parameters.LoadSpeed ) ); + exchangetime = std::max( exchangetime, vehicle->LoadExchangeTime() ); } } vehicle = vehicle->Next(); diff --git a/version.h b/version.h index 84844c62..f42fbdb5 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 1030 +#define VERSION_MINOR 1117 #define VERSION_REVISION 0 From 3fbf5bf894b7dca125f7d593c822dfb24193dd1e Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 28 Nov 2018 23:29:35 +0100 Subject: [PATCH 6/6] build 181128. coupler sounds enhancement, minor ai logic tweaks, minor bug fixes --- Classes.h | 1 + Driver.cpp | 10 +++- DynObj.cpp | 46 +++++++++--------- Event.cpp | 2 +- McZapkie/MOVER.h | 23 +++++---- McZapkie/Mover.cpp | 39 +++++++++++++-- audiorenderer.cpp | 17 ++++++- audiorenderer.h | 6 +-- editormode.h | 2 +- editoruipanels.cpp | 90 +++++++++++++++++++++++++++++------ editoruipanels.h | 4 ++ scenenode.h | 2 +- simulationstateserializer.cpp | 1 + sound.cpp | 2 +- utilities.cpp | 12 +++++ utilities.h | 3 ++ version.h | 2 +- 17 files changed, 199 insertions(+), 63 deletions(-) diff --git a/Classes.h b/Classes.h index ae514622..ecbfd1b4 100644 --- a/Classes.h +++ b/Classes.h @@ -44,6 +44,7 @@ struct light_array; namespace scene { struct node_data; class basic_node; +using group_handle = std::size_t; } namespace Mtable diff --git a/Driver.cpp b/Driver.cpp index e6e5301b..2cd38bc7 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2072,6 +2072,14 @@ bool TController::CheckVehicles(TOrders user) p = pVehicles[0]; while (p) { + // HACK: wagony muszą mieć baterię załączoną do otwarcia drzwi... + if( ( p != pVehicle ) + && ( ( p->MoverParameters->Couplers[ side::front ].CouplingFlag & ( coupling::control | coupling::permanent ) ) == 0 ) + && ( ( p->MoverParameters->Couplers[ side::rear ].CouplingFlag & ( coupling::control | coupling::permanent ) ) == 0 ) ) { + // NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly + p->MoverParameters->BatterySwitch( true ); + } + if (p->asDestination == "none") p->DestinationSet(TrainParams->Relation2, TrainParams->TrainName); // relacja docelowa, jeśli nie było if (AIControllFlag) // jeśli prowadzi komputer @@ -3212,8 +3220,6 @@ void TController::Doors( bool const Open, int const Side ) { // tu będzie jeszcze długość peronu zaokrąglona do 10m (20m bezpieczniej, bo nie modyfikuje bitu 1) auto *vehicle = pVehicles[0]; // pojazd na czole składu while( vehicle != nullptr ) { - // wagony muszą mieć baterię załączoną do otwarcia drzwi... - vehicle->MoverParameters->BatterySwitch( true ); // otwieranie drzwi w pojazdach - flaga zezwolenia była by lepsza if( vehicle->MoverParameters->DoorOpenCtrl != control_t::passenger ) { // if the door are controlled by the driver, we let the user operate them... diff --git a/DynObj.cpp b/DynObj.cpp index fff3b8cb..62bdc746 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1374,8 +1374,6 @@ TDynamicObject::couple( int const Side ) { Side, 2, MoverParameters->Couplers[ Side ].Connected, coupling::coupler ) ) { - // tmp->MoverParameters->Couplers[CouplNr].Render=true; //podłączony sprzęg będzie widoczny - m_couplersounds[ Side ].dsbCouplerAttach.play(); // one coupling type per key press return; } @@ -1393,9 +1391,6 @@ TDynamicObject::couple( int const Side ) { Side, 2, MoverParameters->Couplers[ Side ].Connected, ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::brakehose ) ) ) { - // TODO: dedicated sound for connecting cable-type connections - m_couplersounds[ Side ].dsbCouplerDetach.play(); - SetPneumatic( Side != 0, true ); if( Side == side::front ) { PrevConnected->SetPneumatic( Side != 0, true ); @@ -1417,9 +1412,6 @@ TDynamicObject::couple( int const Side ) { Side, 2, MoverParameters->Couplers[ Side ].Connected, ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::mainhose ) ) ) { - // TODO: dedicated sound for connecting cable-type connections - m_couplersounds[ Side ].dsbCouplerDetach.play(); - SetPneumatic( Side != 0, false ); if( Side == side::front ) { PrevConnected->SetPneumatic( Side != 0, false ); @@ -1441,8 +1433,6 @@ TDynamicObject::couple( int const Side ) { Side, 2, MoverParameters->Couplers[ Side ].Connected, ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::control ) ) ) { - // TODO: dedicated sound for connecting cable-type connections - m_couplersounds[ Side ].dsbCouplerAttach.play(); // one coupling type per key press return; } @@ -1457,8 +1447,6 @@ TDynamicObject::couple( int const Side ) { Side, 2, MoverParameters->Couplers[ Side ].Connected, ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::gangway ) ) ) { - // TODO: dedicated gangway sound - m_couplersounds[ Side ].dsbCouplerAttach.play(); // one coupling type per key press return; } @@ -1473,9 +1461,6 @@ TDynamicObject::couple( int const Side ) { Side, 2, MoverParameters->Couplers[ Side ].Connected, ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::heating ) ) ) { - - // TODO: dedicated 'click' sound for connecting cable-type connections - m_couplersounds[ Side ].dsbCouplerDetach.play(); // one coupling type per key press return; } @@ -1494,11 +1479,6 @@ TDynamicObject::uncouple( int const Side ) { } // jeżeli sprzęg niezablokowany, jest co odczepić i się da auto const couplingflag { Dettach( Side ) }; - if( couplingflag == coupling::faux ) { - // dźwięk odczepiania - m_couplersounds[ Side ].dsbCouplerAttach.play(); - m_couplersounds[ Side ].dsbCouplerDetach.play(); - } return couplingflag; } @@ -2565,9 +2545,9 @@ void TDynamicObject::AttachPrev(TDynamicObject *Object, int iType) loc.Z=Object->vPosition.y; Object->MoverParameters->Loc=loc; //ustawienie dodawanego pojazdu */ - MoverParameters->Attach(iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, true); + MoverParameters->Attach(iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, true, false); MoverParameters->Couplers[iDirection].Render = false; - Object->MoverParameters->Attach(Object->iDirection ^ 1, iDirection, MoverParameters, iType, true); + Object->MoverParameters->Attach(Object->iDirection ^ 1, iDirection, MoverParameters, iType, true, false); Object->MoverParameters->Couplers[Object->iDirection ^ 1].Render = true; // rysowanie sprzęgu w dołączanym if (iDirection) { //łączenie standardowe @@ -4503,6 +4483,11 @@ void TDynamicObject::RenderSounds() { auto &coupler { MoverParameters->Couplers[ couplerindex ] }; + if( coupler.sounds == sound::none ) { + ++couplerindex; + continue; + } + if( true == TestFlag( coupler.sounds, sound::bufferclash ) ) { // zderzaki uderzaja o siebie if( true == TestFlag( coupler.sounds, sound::loud ) ) { @@ -4527,6 +4512,7 @@ void TDynamicObject::RenderSounds() { .play( sound_flags::exclusive ); } } + if( true == TestFlag( coupler.sounds, sound::couplerstretch ) ) { // sprzegi sie rozciagaja if( true == TestFlag( coupler.sounds, sound::loud ) ) { @@ -4552,8 +4538,22 @@ void TDynamicObject::RenderSounds() { } } - coupler.sounds = 0; + // TODO: dedicated sound for each connection type + // until then, play legacy placeholders: + if( ( coupler.sounds & ( sound::attachcoupler | sound::attachcontrol | sound::attachgangway ) ) != 0 ) { + m_couplersounds[ couplerindex ].dsbCouplerAttach.play(); + } + if( ( coupler.sounds & ( sound::attachbrakehose | sound::attachmainhose | sound::attachheating ) ) != 0 ) { + m_couplersounds[ couplerindex ].dsbCouplerDetach.play(); + } + if( true == TestFlag( coupler.sounds, sound::detachall ) ) { + // TODO: dedicated disconnect sounds + m_couplersounds[ couplerindex ].dsbCouplerAttach.play(); + m_couplersounds[ couplerindex ].dsbCouplerDetach.play(); + } + ++couplerindex; + coupler.sounds = 0; } MoverParameters->SoundFlag = 0; diff --git a/Event.cpp b/Event.cpp index 76d3aa68..80c116fd 100644 --- a/Event.cpp +++ b/Event.cpp @@ -1019,7 +1019,7 @@ multi_event::init() { auto const conditiontchecksmemcell { m_conditions.flags & ( flags::text | flags::value_1 | flags::value_2 ) }; // not all multi-events have memory cell checks, for the ones which don't we can keep quiet about it - init_targets( simulation::Memory, "memory cell", ( false == conditiontchecksmemcell ) ); + init_targets( simulation::Memory, "memory cell", conditiontchecksmemcell ); if( m_ignored ) { // legacy compatibility behaviour, instead of disabling the event we disable the memory cell comparison test m_conditions.flags &= ~( flags::text | flags::value_1 | flags::value_2 ); diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 57d948a1..4612283b 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -209,13 +209,20 @@ static int const s_CAtest = 128; /*dzwieki*/ enum sound { none, - loud = 0x1, - couplerstretch = 0x2, - bufferclash = 0x4, - relay = 0x10, - parallel = 0x20, - shuntfield = 0x40, - pneumatic = 0x80 + loud = 1 << 0, + couplerstretch = 1 << 1, + bufferclash = 1 << 2, + relay = 1 << 3, + parallel = 1 << 4, + shuntfield = 1 << 5, + pneumatic = 1 << 6, + detachall = 1 << 7, + attachcoupler = 1 << 8, + attachbrakehose = 1 << 9, + attachmainhose = 1 << 10, + attachcontrol = 1 << 11, + attachgangway = 1 << 12, + attachheating = 1 << 13 }; //szczególne typy pojazdów (inna obsługa) dla zmiennej TrainType @@ -1245,7 +1252,7 @@ public: double Distance(const TLocation &Loc1, const TLocation &Loc2, const TDimension &Dim1, const TDimension &Dim2); /* double Distance(const vector3 &Loc1, const vector3 &Loc2, const vector3 &Dim1, const vector3 &Dim2); */ //bool AttachA(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced = false); - bool Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced = false); + bool Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced = false, bool Audible = true); int DettachStatus(int ConnectNo); bool Dettach(int ConnectNo); void SetCoupleDist(); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 29c0255d..5cb45bc4 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -406,7 +406,7 @@ double TMoverParameters::CouplerDist(int Coupler) return Couplers[ Coupler ].CoupleDist; }; -bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced) +bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced, bool Audible) { //łączenie do swojego sprzęgu (ConnectNo) pojazdu (ConnectTo) stroną (ConnectToNr) // Ra: zwykle wykonywane dwukrotnie, dla każdego pojazdu oddzielnie // Ra: trzeba by odróżnić wymóg dociśnięcia od uszkodzenia sprzęgu przy podczepianiu AI do @@ -433,12 +433,32 @@ bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters * coupler.Render = true; // tego rysować othercoupler.Render = false; // a tego nie }; + auto const couplingchange { CouplingType ^ coupler.CouplingFlag }; coupler.CouplingFlag = CouplingType; // ustawienie typu sprzęgu // if (CouplingType!=ctrain_virtual) //Ra: wirtualnego nie łączymy zwrotnie! //{//jeśli łączenie sprzęgiem niewirtualnym, ustawiamy połączenie zwrotne othercoupler.CouplingFlag = CouplingType; othercoupler.Connected = this; othercoupler.CoupleDist = coupler.CoupleDist; + + if( ( true == Audible ) && ( couplingchange != 0 ) ) { + // set sound event flag + int soundflag{ sound::none }; + std::vector> const soundmappings = { + { coupling::coupler, sound::attachcoupler }, + { coupling::brakehose, sound::attachbrakehose }, + { coupling::mainhose, sound::attachmainhose }, + { coupling::control, sound::attachcontrol}, + { coupling::gangway, sound::attachgangway}, + { coupling::heating, sound::attachheating} }; + for( auto const &soundmapping : soundmappings ) { + if( ( couplingchange & soundmapping.first ) != 0 ) { + soundflag |= soundmapping.second; + } + } + SetFlag( coupler.sounds, soundflag ); + } + return true; //} // podłączenie nie udało się - jest wirtualne @@ -489,6 +509,10 @@ bool TMoverParameters::Dettach(int ConnectNo) Couplers[ConnectNo].Connected->Couplers[Couplers[ConnectNo].ConnectedNr].CouplingFlag = 0; // pozostaje sprzęg wirtualny Couplers[ConnectNo].CouplingFlag = 0; // pozostaje sprzęg wirtualny + + // set sound event flag + SetFlag( Couplers[ ConnectNo ].sounds, sound::detachall ); + return true; } else if (i > 0) @@ -4395,7 +4419,8 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt) // sprzeganie wagonow z samoczynnymi sprzegami} // CouplingFlag:=ctrain_coupler+ctrain_pneumatic+ctrain_controll+ctrain_passenger+ctrain_scndpneumatic; // EN57 - Couplers[ CouplerN ].CouplingFlag = coupling::coupler | coupling::brakehose | coupling::mainhose | coupling::control; + Couplers[ CouplerN ].CouplingFlag = coupling::coupler /*| coupling::brakehose | coupling::mainhose | coupling::control*/; + SetFlag( Couplers[ CouplerN ].sounds, sound::attachcoupler ); } } } @@ -4847,9 +4872,13 @@ double TMoverParameters::TractionForce( double dt ) { EnginePower = Voltage * Im / 1000.0; /* - // NOTE: this part is experimentally disabled, as it generated early traction force drop for undetermined purpose - if ((tmpV > 2) && (EnginePower < tmp)) - Ft = Ft * EnginePower / tmp; + // power curve drop + // NOTE: disabled for the time being due to side-effects + if( ( tmpV > 1 ) && ( EnginePower < tmp ) ) { + Ft = interpolate( + Ft, EnginePower / tmp, + clamp( tmpV - 1.0, 0.0, 1.0 ) ); + } */ } diff --git a/audiorenderer.cpp b/audiorenderer.cpp index 409337f3..c9689fbf 100644 --- a/audiorenderer.cpp +++ b/audiorenderer.cpp @@ -81,7 +81,13 @@ openal_source::update( double const Deltatime, glm::vec3 const &Listenervelocity if( sound_range < 0.0 ) { sound_velocity = Listenervelocity; // cached for doppler shift calculation } - +/* + // HACK: if the application gets stuck for long time loading assets the audio can gone awry. + // terminate all sources when it happens to stay on the safe side + if( Deltatime > 1.0 ) { + stop(); + } +*/ if( id != audio::null_resource ) { sound_change = false; @@ -314,7 +320,6 @@ openal_renderer::erase( sound_source const *Controller ) { // updates state of all active emitters void openal_renderer::update( double const Deltatime ) { - // update listener // gain ::alListenerf( AL_GAIN, clamp( Global.AudioVolume, 0.f, 2.f ) * ( Global.iPause == 0 ? 1.f : 0.15f ) ); @@ -411,6 +416,14 @@ openal_renderer::fetch_source() { } } + if( newsource.id == audio::null_resource ) { + // for sources with functional emitter reset emitter parameters from potential last use + ::alSourcef( newsource.id, AL_PITCH, 1.f ); + ::alSourcef( newsource.id, AL_GAIN, 1.f ); + ::alSourcefv( newsource.id, AL_POSITION, glm::value_ptr( glm::vec3{ 0.f } ) ); + ::alSourcefv( newsource.id, AL_VELOCITY, glm::value_ptr( glm::vec3{ 0.f } ) ); + } + return newsource; } diff --git a/audiorenderer.h b/audiorenderer.h index c6492dbf..22ec13f7 100644 --- a/audiorenderer.h +++ b/audiorenderer.h @@ -89,11 +89,11 @@ struct openal_source { private: // members - double update_deltatime; // time delta of most current update + double update_deltatime { 0.0 }; // time delta of most current update float pitch_variation { 1.f }; // emitter-specific variation of the base pitch float sound_range { 50.f }; // cached audible range of the emitted samples - glm::vec3 sound_distance; // cached distance between sound and the listener - glm::vec3 sound_velocity; // sound movement vector + glm::vec3 sound_distance { 0.f }; // cached distance between sound and the listener + glm::vec3 sound_velocity { 0.f }; // sound movement vector bool is_in_range { false }; // helper, indicates the source was recently within audible range bool is_multipart { false }; // multi-part sounds are kept alive at longer ranges }; diff --git a/editormode.h b/editormode.h index a801492c..df146561 100644 --- a/editormode.h +++ b/editormode.h @@ -75,12 +75,12 @@ private: bool mode_snap() const; // members + state_backup m_statebackup; // helper, cached variables to be restored on mode exit editormode_input m_input; TCamera Camera; double fTime50Hz { 0.0 }; // bufor czasu dla komunikacji z PoKeys scene::basic_editor m_editor; scene::basic_node *m_node; // currently selected scene node bool m_takesnapshot { true }; // helper, hints whether snapshot of selected node(s) should be taken before modification - state_backup m_statebackup; // helper, cached variables to be restored on mode exit }; diff --git a/editoruipanels.cpp b/editoruipanels.cpp index 34f1eb30..7ddf59e7 100644 --- a/editoruipanels.cpp +++ b/editoruipanels.cpp @@ -27,6 +27,8 @@ itemproperties_panel::update( scene::basic_node const *Node ) { if( false == is_open ) { return; } text_lines.clear(); + m_grouplines.clear(); + std::string textline; // scenario inspector @@ -41,7 +43,7 @@ itemproperties_panel::update( scene::basic_node const *Node ) { } textline = - "node name: " + node->name() + "name: " + ( node->name().empty() ? "(none)" : node->name() ) + "\nlocation: [" + to_string( node->location().x, 2 ) + ", " + to_string( node->location().y, 2 ) + ", " + to_string( node->location().z, 2 ) + "]" + " (distance: " + to_string( glm::length( glm::dvec3{ node->location().x, 0.0, node->location().z } -glm::dvec3{ camera.Pos.x, 0.0, camera.Pos.z } ), 1 ) + " m)"; text_lines.emplace_back( textline, Global.UITextColor ); @@ -65,7 +67,7 @@ itemproperties_panel::update( scene::basic_node const *Node ) { textline += ']'; } else { - textline += "none"; + textline += "(none)"; } text_lines.emplace_back( textline, Global.UITextColor ); @@ -73,7 +75,7 @@ itemproperties_panel::update( scene::basic_node const *Node ) { auto modelfile { ( ( subnode->pModel != nullptr ) ? subnode->pModel->NameGet() : - "none" ) }; + "(none)" ) }; if( modelfile.find( szModelPath ) == 0 ) { // don't include 'models/' in the path modelfile.erase( 0, std::string{ szModelPath }.size() ); @@ -82,7 +84,7 @@ itemproperties_panel::update( scene::basic_node const *Node ) { auto texturefile { ( ( subnode->Material()->replacable_skins[ 1 ] != null_handle ) ? GfxRenderer.Material( subnode->Material()->replacable_skins[ 1 ] ).name : - "none" ) }; + "(none)" ) }; if( texturefile.find( szTexturePath ) == 0 ) { // don't include 'textures/' in the path texturefile.erase( 0, std::string{ szTexturePath }.size() ); @@ -95,7 +97,7 @@ itemproperties_panel::update( scene::basic_node const *Node ) { auto const *subnode = static_cast( node ); // basic attributes textline = - "isolated: " + ( ( subnode->pIsolated != nullptr ) ? subnode->pIsolated->asName : "none" ) + "isolated: " + ( ( subnode->pIsolated != nullptr ) ? subnode->pIsolated->asName : "(none)" ) + "\nvelocity: " + to_string( subnode->SwitchExtension ? subnode->SwitchExtension->fVelocity : subnode->fVelocity ) + "\nwidth: " + to_string( subnode->fTrackWidth ) + " m" + "\nfriction: " + to_string( subnode->fFriction, 2 ) @@ -105,14 +107,14 @@ itemproperties_panel::update( scene::basic_node const *Node ) { auto texturefile { ( ( subnode->m_material1 != null_handle ) ? GfxRenderer.Material( subnode->m_material1 ).name : - "none" ) }; + "(none)" ) }; if( texturefile.find( szTexturePath ) == 0 ) { texturefile.erase( 0, std::string{ szTexturePath }.size() ); } auto texturefile2{ ( ( subnode->m_material2 != null_handle ) ? GfxRenderer.Material( subnode->m_material2 ).name : - "none" ) }; + "(none)" ) }; if( texturefile2.find( szTexturePath ) == 0 ) { texturefile2.erase( 0, std::string{ szTexturePath }.size() ); } @@ -167,11 +169,72 @@ itemproperties_panel::update( scene::basic_node const *Node ) { + " [" + to_string( subnode->Value1(), 2 ) + "]" + " [" + to_string( subnode->Value2(), 2 ) + "]"; text_lines.emplace_back( textline, Global.UITextColor ); - textline = "track: " + ( subnode->asTrackName.empty() ? "none" : subnode->asTrackName ); + textline = "track: " + ( subnode->asTrackName.empty() ? "(none)" : subnode->asTrackName ); text_lines.emplace_back( textline, Global.UITextColor ); } + + update_group(); } +void +itemproperties_panel::update_group() { + + auto const grouphandle { m_node->group() }; + + if( grouphandle == null_handle ) { + m_grouphandle = null_handle; + m_groupprefix.clear(); + return; + } + + auto const &nodegroup { scene::Groups.group( grouphandle ) }; + + if( m_grouphandle != grouphandle ) { + // calculate group name from shared prefix of item names + std::vector> names; + // build list of custom item and event names + for( auto const *node : nodegroup.nodes ) { + auto const &name { node->name() }; + if( name.empty() || name == "none" ) { continue; } + names.emplace_back( name ); + } + for( auto const *event : nodegroup.events ) { + auto const &name { event->m_name }; + if( name.empty() || name == "none" ) { continue; } + names.emplace_back( name ); + } + // find the common prefix + if( names.size() > 1 ) { + m_groupprefix = names.front(); + for( auto const &name : names ) { + // NOTE: first calculation runs over two instances of the same name, but, eh + auto const prefixlength{ len_common_prefix( m_groupprefix, name ) }; + if( prefixlength > 0 ) { + m_groupprefix = m_groupprefix.substr( 0, prefixlength ); + } + else { + m_groupprefix.clear(); + break; + } + } + } + else { + // less than two names to compare means no prefix + m_groupprefix.clear(); + } + m_grouphandle = grouphandle; + } + + m_grouplines.emplace_back( + "nodes: " + to_string( static_cast( nodegroup.nodes.size() ) ) + + "\nevents: " + to_string( static_cast( nodegroup.events.size() ) ), + Global.UITextColor ); + m_grouplines.emplace_back( + "names prefix: " + ( m_groupprefix.empty() ? "(none)" : m_groupprefix ), + Global.UITextColor ); +} + + void itemproperties_panel::render() { @@ -209,16 +272,13 @@ bool itemproperties_panel::render_group() { if( m_node == nullptr ) { return false; } - if( m_node->group() == null_handle ) { return false; } + if( m_grouplines.empty() ) { return false; } if( false == ImGui::CollapsingHeader( "Parent Group" ) ) { return false; } - auto const &nodegroup { scene::Groups.group( m_node->group() ) }; - auto const &linecolor { Global.UITextColor }; - - ImGui::TextColored( ImVec4( linecolor.r, linecolor.g, linecolor.b, linecolor.a ), - ( "Nodes: " + to_string( static_cast( nodegroup.nodes.size() ) ) - + "\nEvents: " + to_string( static_cast( nodegroup.events.size() ) ) ).c_str() ); + for( auto const &line : m_grouplines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } return true; } diff --git a/editoruipanels.h b/editoruipanels.h index 5f4c325b..efd6f8cd 100644 --- a/editoruipanels.h +++ b/editoruipanels.h @@ -24,8 +24,12 @@ public: private: // methods + void update_group(); bool render_group(); // members scene::basic_node const *m_node { nullptr }; // scene node bound to the panel + scene::group_handle m_grouphandle { null_handle }; // scene group bound to the panel + std::string m_groupprefix; + std::vector m_grouplines; }; diff --git a/scenenode.h b/scenenode.h index 31a8c938..2bd0d2c2 100644 --- a/scenenode.h +++ b/scenenode.h @@ -64,7 +64,7 @@ struct bounding_area { deserialize( std::istream &Input ); }; -using group_handle = std::size_t; +//using group_handle = std::size_t; struct node_data { diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index 905c31aa..5be31c89 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -323,6 +323,7 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch >> nodedata.range_min >> nodedata.name >> nodedata.type; + if( nodedata.name == "none" ) { nodedata.name.clear(); } // type-based deserialization. not elegant but it'll do if( nodedata.type == "dynamic" ) { diff --git a/sound.cpp b/sound.cpp index d2fb5bff..702c31f7 100644 --- a/sound.cpp +++ b/sound.cpp @@ -466,7 +466,7 @@ sound_source::stop( bool const Skipend ) { if( ( false == Skipend ) && ( sound( sound_id::end ).buffer != null_handle ) /* && ( sound( sound_id::end ).buffer != sound( sound_id::main ).buffer ) */ // end == main can happen in malformed legacy cases -/* && ( sound( sound_id::end ).playing == 0 ) */ ) { + && ( sound( sound_id::end ).playing < 2 ) ) { // allows potential single extra instance to account for longer overlapping sounds // spawn potentially defined sound end sample, if the emitter is currently active insert( sound_id::end ); } diff --git a/utilities.cpp b/utilities.cpp index 0183399d..971b034d 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -411,6 +411,18 @@ substr_path( std::string const &Filename ) { "" ); } +// returns length of common prefix between two provided strings +std::ptrdiff_t +len_common_prefix( std::string const &Left, std::string const &Right ) { + + auto const *left { Left.data() }; + auto const *right { Right.data() }; + // compare up to the length of the shorter string + return ( Right.size() <= Left.size() ? + std::distance( right, std::mismatch( right, right + Right.size(), left ).first ) : + std::distance( left, std::mismatch( left, left + Left.size(), right ).first ) ); +} + // helper, restores content of a 3d vector from provided input stream // TODO: review and clean up the helper routines, there's likely some redundant ones glm::dvec3 LoadPoint( cParser &Input ) { diff --git a/utilities.h b/utilities.h index 16b488c1..400f12df 100644 --- a/utilities.h +++ b/utilities.h @@ -200,6 +200,9 @@ replace_slashes( std::string &Filename ); // returns potential path part from provided file name std::string substr_path( std::string const &Filename ); +// returns common prefix of two provided strings +std::ptrdiff_t len_common_prefix( std::string const &Left, std::string const &Right ); + template void SafeDelete( Type_ &Pointer ) { delete Pointer; diff --git a/version.h b/version.h index f42fbdb5..a2bda4f1 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 1117 +#define VERSION_MINOR 1128 #define VERSION_REVISION 0