From c22f4c900ffb2b17d9c4e9ad5de95292b904799d Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 24 Mar 2020 16:12:25 +0100 Subject: [PATCH] build 200322. coupler adapter, automatic coupling vehicle setting, parked vehicle manual brake activation, ai braking logic enhancement, opengl 3.3 renderer diffuse color visualization fix, minor cab control logic bug fixes --- Driver.cpp | 113 +++++++++++++++++++++++++++----------- DynObj.cpp | 119 +++++++++++++++++++++++++++++++++++++++- DynObj.h | 16 +++++- McZapkie/MOVER.h | 24 +++++++- McZapkie/Mover.cpp | 52 +++++++++++------- Model3d.cpp | 4 ++ Train.cpp | 59 ++++++++++++++++---- Train.h | 2 + command.cpp | 2 + command.h | 2 + driverkeyboardinput.cpp | 2 + driveruipanels.cpp | 13 +++-- opengl33renderer.cpp | 63 ++++++++++++++++----- opengl33renderer.h | 1 + openglrenderer.cpp | 48 +++++++++++++--- openglrenderer.h | 2 + version.h | 2 +- 17 files changed, 431 insertions(+), 93 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 16c234fb..0ddc81ae 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1988,6 +1988,8 @@ void TController::AutoRewident() break; } } + // potentially release manual brake + d->MoverParameters->DecManualBrakeLevel( ManualBrakePosNo ); } d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) } @@ -2347,6 +2349,10 @@ bool TController::CheckVehicles(TOrders user) break; } } // switch + if( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) { + // nastawianie hamulca do jazdy pociągowej + AutoRewident(); + } } // Ra 2014-09: tymczasowo prymitywne ustawienie warunku pod kątem SN61 if( ( mvOccupied->TrainType == dt_EZT ) @@ -2767,7 +2773,14 @@ bool TController::ReleaseEngine() { } // gasimy światła Lights( 0, 0 ); + // activate parking brake + // TBD: do it earlier? mvOccupied->SpringBrakeActivate(true); + if( ( mvOccupied->LocalBrake == TLocalBrake::ManualBrake ) + || ( mvOccupied->MBrake == true ) ) { + mvOccupied->IncManualBrakeLevel( ManualBrakePosNo ); + } + // switch off remaining power mvOccupied->BatterySwitch( false ); } } @@ -2959,7 +2972,14 @@ bool TController::IncBrakeEIM() switch (mvOccupied->EIMCtrlType) { case 0: { - OK = mvOccupied->IncLocalBrakeLevel( 1 ); + if( mvOccupied->MED_amax != 9.81 ) { + auto const brakeposition{ clamp( -1.0 * mvOccupied->AIHintLocalBrakeAccFactor * AccDesired / mvOccupied->MED_amax, 0.0, 1.0 ) }; + OK = ( brakeposition != mvOccupied->LocalBrakePosA ); + mvOccupied->LocalBrakePosA = brakeposition; + } + else { + OK = mvOccupied->IncLocalBrakeLevel( 1 ); + } break; } case 1: { @@ -3067,24 +3087,35 @@ bool TController::DecBrakeEIM() bool OK = false; switch (mvOccupied->EIMCtrlType) { - case 0: - OK = mvOccupied->DecLocalBrakeLevel(1); - break; - case 1: - OK = mvOccupied->MainCtrlPos < 2; - if (OK) - mvOccupied->MainCtrlPos = 2; - break; - case 2: - OK = mvOccupied->MainCtrlPos < 3; - if (OK) - mvOccupied->MainCtrlPos = 3; - break; - case 3: - OK = mvOccupied->MainCtrlPos < mvOccupied->MainCtrlMaxDirChangePos; - if( OK ) - mvOccupied->MainCtrlPos = mvOccupied->MainCtrlMaxDirChangePos; - break; + case 0: { + if( mvOccupied->MED_amax != 9.81 ) { + auto const brakeposition { clamp( -1.0 * mvOccupied->AIHintLocalBrakeAccFactor * AccDesired / mvOccupied->MED_amax, 0.0, 1.0 ) }; + OK = ( brakeposition != mvOccupied->LocalBrakePosA ); + mvOccupied->LocalBrakePosA = brakeposition; + } + else { + OK = mvOccupied->DecLocalBrakeLevel( 1 ); + } + break; + } + case 1: { + OK = mvOccupied->MainCtrlPos < 2; + if( OK ) + mvOccupied->MainCtrlPos = 2; + break; + } + case 2: { + OK = mvOccupied->MainCtrlPos < 3; + if( OK ) + mvOccupied->MainCtrlPos = 3; + break; + } + case 3: { + OK = mvOccupied->MainCtrlPos < mvOccupied->MainCtrlMaxDirChangePos; + if( OK ) + mvOccupied->MainCtrlPos = mvOccupied->MainCtrlMaxDirChangePos; + break; + } } return OK; } @@ -4468,6 +4499,7 @@ void TController::PhysicsLog() void TController::UpdateSituation(double dt) { // uruchamiać przynajmniej raz na sekundę + if( false == simulation::is_ready ) { return; } if( ( iDrivigFlags & movePrimary ) == 0 ) { return; } // pasywny nic nie robi // update timers @@ -5068,18 +5100,30 @@ TController::UpdateSituation(double dt) { int const end { ( vehicle->DirectionGet() > 0 ? end::front : end::rear ) }; auto const &neighbour { vehicleparameters->Neighbours[ end ] }; if( neighbour.vehicle != nullptr ) { - // próba podczepienia - vehicleparameters->Attach( - end, neighbour.vehicle_end, - neighbour.vehicle->MoverParameters, - iCoupler ); - if( vehicleparameters->Couplers[ end ].CouplingFlag == iCoupler ) { - // jeżeli został podłączony - iCoupler = 0; // dalsza jazda manewrowa już bez łączenia - iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania - SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie - CheckVehicles( Connect ); // sprawdzić światła nowego składu - JumpToNextOrder(); // wykonanie następnej komendy + if( neighbour.distance < 10 ) { + // check whether we don't need to attach coupler adapter + auto &coupler{ vehicleparameters->Couplers[ end ] }; + if( coupler.type() != TCouplerType::Automatic ) { + auto &othercoupler = neighbour.vehicle->MoverParameters->Couplers[ ( neighbour.vehicle_end != 2 ? neighbour.vehicle_end : coupler.ConnectedNr ) ]; + if( othercoupler.type() == TCouplerType::Automatic ) { + vehicle->attach_coupler_adapter( end ); + } + } + if( neighbour.distance < 2 ) { + // próba podczepienia + vehicleparameters->Attach( + end, neighbour.vehicle_end, + neighbour.vehicle->MoverParameters, + iCoupler ); + if( vehicleparameters->Couplers[ end ].CouplingFlag == iCoupler ) { + // jeżeli został podłączony + iCoupler = 0; // dalsza jazda manewrowa już bez łączenia + iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania + SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie + CheckVehicles( Connect ); // sprawdzić światła nowego składu + JumpToNextOrder(); // wykonanie następnej komendy + } + } } } } // if (AIControllFlag) //koniec zblokowania, bo była zmienna lokalna @@ -5463,7 +5507,12 @@ TController::UpdateSituation(double dt) { // tylko jeśli odepnie WriteLog( mvOccupied->Name + " odczepiony." ); iVehicleCount = -2; - CheckVehicles( Disconnect ); // update trainset state + // potentially remove coupler adapter + if( p->MoverParameters->Couplers[ d ].has_adapter() ) { + p->remove_coupler_adapter( d ); + } + // update trainset state + CheckVehicles( Disconnect ); } // a jak nie, to dociskać dalej } if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress)) diff --git a/DynObj.cpp b/DynObj.cpp index b3521ea8..52343cec 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -747,14 +747,16 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) // ABu-240105: Dodatkowy warunek: if (...).Render, zeby rysowal tylko // jeden // z polaczonych sprzegow - if ((TestFlag(MoverParameters->Couplers[end::front].CouplingFlag, ctrain_coupler)) && + if ((false == MoverParameters->Couplers[ end::front ].has_adapter()) && + (TestFlag(MoverParameters->Couplers[end::front].CouplingFlag, ctrain_coupler)) && (MoverParameters->Couplers[end::front].Render)) { btCoupler1.Turn( true ); btnOn = true; } // else btCoupler1.TurnOff(); - if ((TestFlag(MoverParameters->Couplers[end::rear].CouplingFlag, ctrain_coupler)) && + if ((false == MoverParameters->Couplers[ end::rear ].has_adapter()) && + (TestFlag(MoverParameters->Couplers[end::rear].CouplingFlag, ctrain_coupler)) && (MoverParameters->Couplers[end::rear].Render)) { btCoupler2.Turn( true ); @@ -903,6 +905,12 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) // uginanie zderzakow for (int i = 0; i < 2; ++i) { + if( MoverParameters->Couplers[ i ].has_adapter() ) { + // HACK: if there's coupler adapter on this side, we presume there's additional distance put between vehicles + // which prevents buffers from clashing against each other (or the other vehicle doesn't have buffers to begin with) + continue; + } + auto const dist { clamp( MoverParameters->Couplers[ i ].Dist / 2.0, -MoverParameters->Couplers[ i ].DmaxB, 0.0 ) }; if( dist >= 0.0 ) { continue; } @@ -1539,6 +1547,64 @@ TDynamicObject::uncouple( int const Side ) { return couplingflag; } +bool +TDynamicObject::attach_coupler_adapter( int const Side ) { + + auto &coupler { MoverParameters->Couplers[ Side ] }; + // sanity check(s) + if( coupler.type() == TCouplerType::Automatic ) { return false; } + auto const *neighbour { MoverParameters->Neighbours[ Side ].vehicle }; + if( ( neighbour == nullptr ) + || ( MoverParameters->Neighbours[ Side ].distance > 25.0 ) ) { + // can only acquire the adapter from a nearby enough vehicle + return false; + } + // TBD: empty struct instead of fallback defaults, to allow vehicles without adapter? + auto adapterdata { + coupleradapter_data { + { 0.085f, 0.95f }, + "tabor/polsprzeg" } }; + if( false == neighbour->m_coupleradapter.model.empty() ) { + // explicit coupler adapter definition overrides default parameters + adapterdata = neighbour->m_coupleradapter; + } + if( MoverParameters->Neighbours[ Side ].distance - adapterdata.position.x < 0.5 ) { + // arbitrary amount of free room required to install the adapter + // NOTE: this also covers cases with established physical connection + return false; + } + + coupler.adapter_type = TCouplerType::Automatic; + coupler.adapter_length = adapterdata.position.x; + coupler.adapter_height = adapterdata.position.y; + // audio flag, visuals update + coupler.sounds |= sound::attachadapter; + m_coupleradapters[ Side ] = TModelsManager::GetModel( adapterdata.model ); + + return true; +} + +bool +TDynamicObject::remove_coupler_adapter( int const Side ) { + + auto &coupler{ MoverParameters->Couplers[ Side ] }; + + if( coupler.adapter_type == TCouplerType::NoCoupler ) { return false; } + // TODO: sanity check(s) + if( coupler.Connected != nullptr ) { + // TBD: disallow instead adapter removal if it's coupled with another vehicle? + uncouple( Side ); + } + coupler.adapter_type = TCouplerType::NoCoupler; + coupler.adapter_length = 0.0; + coupler.adapter_height = 0.0; + // audio flag, visuals update + coupler.sounds |= sound::removeadapter; + m_coupleradapters[ Side ] = nullptr; + + return true; +} + TDynamicObject::TDynamicObject() { modelShake = Math3D::vector3(0, 0, 0); // fTrackBlock = 10000.0; // brak przeszkody na drodze @@ -3367,7 +3433,7 @@ bool TDynamicObject::Update(double dt, double dt1) double fCurrent = ( ( MoverParameters->DynamicBrakeFlag && MoverParameters->ResistorsFlag ) ? 0 : - std::abs( MoverParameters->Itot ) ) + std::abs( MoverParameters->Itot ) * MoverParameters->IsVehicleEIMBrakingFactor() ) + MoverParameters->TotalCurrent; // prąd pobierany przez pojazd - bez sensu z tym (TotalCurrent) // TotalCurrent to bedzie prad nietrakcyjny (niezwiazany z napedem) // fCurrent+=fabs(MoverParameters->Voltage)*1e-6; //prąd płynący przez woltomierz, rozładowuje kondensator orgromowy 4µF @@ -4394,6 +4460,12 @@ void TDynamicObject::RenderSounds() { m_couplersounds[ couplerindex ].dsbCouplerAttach.play(); m_couplersounds[ couplerindex ].dsbCouplerDetach.play(); } + if( true == TestFlag( coupler.sounds, sound::attachadapter ) ) { + m_couplersounds[ couplerindex ].dsbAdapterAttach.play(); + } + if( true == TestFlag( coupler.sounds, sound::removeadapter ) ) { + m_couplersounds[ couplerindex ].dsbAdapterRemove.play(); + } ++couplerindex; coupler.sounds = 0; @@ -4566,6 +4638,17 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co mdLowPolyInt = TModelsManager::GetModel(asModel, true); } + else if(token == "coupleradapter:") { + // coupling adapter data + parser.getTokens( 3 ); + parser + >> m_coupleradapter.model + >> m_coupleradapter.position.x + >> m_coupleradapter.position.y; + replace_slashes( m_coupleradapter.model ); + erase_leading_slashes( m_coupleradapter.model ); + } + else if(token == "attachments:") { // additional 3d models attached to main body // content provided as a series of values together enclosed in "{}" @@ -5784,6 +5867,24 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co couplersounds.dsbBufferClamp_loud = bufferclash; } } + else if( token == "coupleradapterattach:" ) { + // laczenie: + sound_source adapterattach { sound_placement::external }; + adapterattach.deserialize( parser, sound_type::single ); + adapterattach.owner( this ); + for( auto &couplersounds : m_couplersounds ) { + couplersounds.dsbAdapterAttach = adapterattach; + } + } + else if( token == "coupleradapterremove:" ) { + // rozlaczanie: + sound_source adapterremove { sound_placement::external }; + adapterremove.deserialize( parser, sound_type::single ); + adapterremove.owner( this ); + for( auto &couplersounds : m_couplersounds ) { + couplersounds.dsbAdapterRemove = adapterremove; + } + } else if( token == "startjolt:" ) { // movement start jolt m_startjolt.deserialize( parser, sound_type::single ); @@ -5972,6 +6073,8 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co m_couplersounds[ end::front ].dsbCouplerStretch_loud.offset( frontcoupleroffset ); m_couplersounds[ end::front ].dsbBufferClamp.offset( frontcoupleroffset ); m_couplersounds[ end::front ].dsbBufferClamp_loud.offset( frontcoupleroffset ); + m_couplersounds[ end::front ].dsbAdapterAttach.offset( frontcoupleroffset ); + m_couplersounds[ end::front ].dsbAdapterRemove.offset( frontcoupleroffset ); auto const rearcoupleroffset { glm::vec3{ 0.f, 1.f, MoverParameters->Dim.L * -0.5f } }; m_couplersounds[ end::rear ].dsbCouplerAttach.offset( rearcoupleroffset ); m_couplersounds[ end::rear ].dsbCouplerDetach.offset( rearcoupleroffset ); @@ -5979,6 +6082,8 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co m_couplersounds[ end::rear ].dsbCouplerStretch_loud.offset( rearcoupleroffset ); m_couplersounds[ end::rear ].dsbBufferClamp.offset( rearcoupleroffset ); m_couplersounds[ end::rear ].dsbBufferClamp_loud.offset( rearcoupleroffset ); + m_couplersounds[ end::rear ].dsbAdapterAttach.offset( rearcoupleroffset ); + m_couplersounds[ end::rear ].dsbAdapterRemove.offset( rearcoupleroffset ); } TModel3d * @@ -6258,6 +6363,10 @@ TDynamicObject::update_neighbours() { // neighbour.vehicle = coupler.Connected; // neighbour.vehicle_end = coupler.ConnectedNr; neighbour.distance = TMoverParameters::CouplerDist( MoverParameters, coupler.Connected ); + // take into account potential adapters attached to the couplers + auto const &othercoupler { neighbour.vehicle->MoverParameters->Couplers[ neighbour.vehicle_end ] }; + neighbour.distance -= coupler.adapter_length; + neighbour.distance -= othercoupler.adapter_length; } else { // if there's no connected vehicle check for potential collision sources in the vicinity @@ -6276,6 +6385,10 @@ TDynamicObject::update_neighbours() { if( neighbour.distance < ( neighbour.vehicle->MoverParameters->CategoryFlag == 2 ? 50 : 100 ) ) { // at short distances (re)calculate range between couplers directly neighbour.distance = TMoverParameters::CouplerDist( MoverParameters, neighbour.vehicle->MoverParameters ); + // take into account potential adapters attached to the couplers + auto const &othercoupler { neighbour.vehicle->MoverParameters->Couplers[ neighbour.vehicle_end ] }; + neighbour.distance -= coupler.adapter_length; + neighbour.distance -= othercoupler.adapter_length; } } } diff --git a/DynObj.h b/DynObj.h index 8319fad2..22a6339c 100644 --- a/DynObj.h +++ b/DynObj.h @@ -311,6 +311,11 @@ private: float time { 0.f }; // time spent on the operation }; + struct coupleradapter_data { + glm::vec2 position; // adapter placement; offset from vehicle end and height + std::string model; // 3d model of the adapter + }; + struct coupler_sounds { sound_source dsbCouplerAttach { sound_placement::external }; // moved from cab sound_source dsbCouplerDetach { sound_placement::external }; // moved from cab @@ -318,6 +323,8 @@ private: sound_source dsbCouplerStretch_loud { sound_placement::external }; sound_source dsbBufferClamp { sound_placement::external }; // moved from cab sound_source dsbBufferClamp_loud { sound_placement::external }; + sound_source dsbAdapterAttach { sound_placement::external }; + sound_source dsbAdapterRemove { sound_placement::external }; }; struct pantograph_sounds { @@ -394,6 +401,7 @@ private: // members TButton btCoupler1; // sprzegi TButton btCoupler2; + std::array m_coupleradapters = { nullptr, nullptr }; TAirCoupler btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki TAirCoupler btCPneumatic2; TAirCoupler btCPneumatic1r; // ABu: to zeby nie bylo problemow przy laczeniu wagonow, @@ -469,12 +477,14 @@ private: sound_source m_outernoise { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; #endif sound_source m_wheelflat { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; - sound_source rscurve { sound_placement::external, 2 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; // youBy + sound_source rscurve { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; // youBy sound_source rsDerailment { sound_placement::external, 2 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; // McZapkie-051202 exchange_data m_exchange; // state of active load exchange procedure, if any exchange_sounds m_exchangesounds; // sounds associated with the load exchange + coupleradapter_data m_coupleradapter; + bool renderme; // yB - czy renderowac float ModCamRot; int iInventory[ 2 ] { 0, 0 }; // flagi bitowe posiadanych submodeli (np. świateł) @@ -583,6 +593,8 @@ private: // pobranie współrzędnych tyłu inline Math3D::vector3 RearPosition() { return vCoulpler[iDirection]; }; + inline Math3D::vector3 CouplerPosition( end const End ) const { + return vCoulpler[ End ]; } inline Math3D::vector3 AxlePositionGet() { return iAxleFirst ? Axle1.pPosition : @@ -647,6 +659,8 @@ private: void couple( int const Side ); int uncouple( int const Side ); + bool attach_coupler_adapter( int const Side ); + bool remove_coupler_adapter( int const Side ); void RadioStop(); void Damage(char flag); void RaLightsSet(int head, int rear); diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 9394e8fc..b6834cea 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -240,7 +240,9 @@ enum sound { attachmainhose = 1 << 10, attachcontrol = 1 << 11, attachgangway = 1 << 12, - attachheating = 1 << 13 + attachheating = 1 << 13, + attachadapter = 1 << 14, + removeadapter = 1 << 15, }; //szczególne typy pojazdów (inna obsługa) dla zmiennej TrainType @@ -673,7 +675,8 @@ struct TCoupling { double FmaxC = 1000.0; double beta = 0.0; TCouplerType CouplerType = TCouplerType::NoCoupler; /*typ sprzegu*/ - int AllowedFlag = 3; //Ra: maska dostępnych + int AutomaticCouplingFlag = coupling::coupler; + int AllowedFlag = coupling::coupler | coupling::brakehose; //Ra: maska dostępnych /*zmienne*/ int CouplingFlag = 0; /*0 - wirtualnie, 1 - sprzegi, 2 - pneumatycznie, 4 - sterowanie, 8 - kabel mocy*/ class TMoverParameters *Connected = nullptr; /*co jest podlaczone*/ @@ -682,12 +685,26 @@ struct TCoupling { double Dist = 0.0; /*strzalka ugiecia zderzaków*/ bool CheckCollision = false; /*czy sprawdzac sile czy pedy*/ float stretch_duration { 0.f }; // seconds, elapsed time with excessive force applied to the coupler + // optional adapter piece + double adapter_length { 0.0 }; // meters, value added on the given end to standard vehicle (half)length + double adapter_height { 0.0 }; // meters, distance from rail level + TCouplerType adapter_type = TCouplerType::NoCoupler; // CouplerType override if other than NoCoupler power_coupling power_high; // power_coupling power_low; // TODO: implement this int sounds { 0 }; // sounds emitted by the coupling devices bool Render = false; /*ABu: czy rysowac jak zaczepiony sprzeg*/ + + inline bool + has_adapter() const { + return ( adapter_type != TCouplerType::NoCoupler ); } + inline TCouplerType const + type() const { + return ( + adapter_type == TCouplerType::NoCoupler ? + CouplerType : + adapter_type ); } }; struct neighbour_data { @@ -1543,6 +1560,7 @@ public: int iLights[2]; // bity zapalonych świateł tutaj, żeby dało się liczyć pobór prądu int AIHintPantstate{ 0 }; // suggested pantograph setup + double AIHintLocalBrakeAccFactor{ 1.05 }; // suggested acceleration weight for local brake operation public: TMoverParameters(double VelInitial, std::string TypeNameInit, std::string NameInit, int Cab); @@ -1557,6 +1575,8 @@ public: bool DirectionForward(); bool DirectionBackward( void );/*! kierunek ruchu*/ bool EIMDirectionChangeAllow( void ) const; + inline double IsVehicleEIMBrakingFactor() { + return eimv[ eimv_Ipoj ] < 0 ? -1.0 : 1.0; } void BrakeLevelSet(double b); bool BrakeLevelAdd(double b); bool IncBrakeLevel(); // wersja na użytek AI diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b59ce564..0714413f 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -22,7 +22,7 @@ http://mozilla.org/MPL/2.0/. // Jeśli jakieś zmienne nie są używane w mover.pas, też można je przenosić. // Przeniesienie wszystkiego na raz zrobiło by zbyt wielki chaos do ogarnięcia. -const double dEpsilon = 0.01; // 1cm (zależy od typu sprzęgu...) +const double dEpsilon = 0.025; // 1cm (zależy od typu sprzęgu...) const double CouplerTune = 0.1; // skalowanie tlumiennosci int ConversionError = 0; @@ -438,13 +438,13 @@ bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters * auto &coupler { Couplers[ ConnectNo ] }; auto &othercoupler = ConnectTo->Couplers[ ( ConnectToNr != 2 ? ConnectToNr : coupler.ConnectedNr ) ]; - auto const distance { CouplerDist( this, ConnectTo ) }; + auto const distance { CouplerDist( this, ConnectTo ) - ( coupler.adapter_length + othercoupler.adapter_length ) }; auto const couplercheck { ( Forced ) || ( ( distance <= dEpsilon ) - && ( coupler.CouplerType != TCouplerType::NoCoupler ) - && ( coupler.CouplerType == othercoupler.CouplerType ) ) }; + && ( coupler.type() != TCouplerType::NoCoupler ) + && ( coupler.type() == othercoupler.type() ) ) }; if( false == couplercheck ) { return false; } @@ -495,7 +495,7 @@ int TMoverParameters::DettachStatus(int ConnectNo) if (TestFlag(DamageFlag, dtrain_coupling)) return -Couplers[ConnectNo].CouplingFlag; // hak urwany - rozłączanie jest OK // CouplerDist(ConnectNo); - if (Couplers[ConnectNo].CouplerType == TCouplerType::Screw ? Neighbours[ConnectNo].distance < 0.01 : true) + if ( (Couplers[ConnectNo].type() != TCouplerType::Screw) || (Neighbours[ConnectNo].distance < 0.01) ) return -Couplers[ConnectNo].CouplingFlag; // można rozłączać, jeśli dociśnięty return (Neighbours[ConnectNo].distance > 0.2) ? -Couplers[ConnectNo].CouplingFlag : Couplers[ConnectNo].CouplingFlag; @@ -1102,7 +1102,7 @@ TMoverParameters::damage_coupler( int const End ) { auto &coupler{ Couplers[ End ] }; - if( coupler.CouplerType == TCouplerType::Articulated ) { return; } // HACK: don't break articulated couplings no matter what + if( coupler.type() == TCouplerType::Articulated ) { return; } // HACK: don't break articulated couplings no matter what if( SetFlag( DamageFlag, dtrain_coupling ) ) EventFlag = true; @@ -1595,7 +1595,7 @@ void TMoverParameters::PowerCouplersCheck( double const Deltatime ) { // bez napiecia... if( couplervoltage != 0.0 ) { // ...ale jest cos na sprzegach: - coupler.power_high.current = ( std::abs( Itot ) + TotalCurrent ) * coupler.power_high.voltage / couplervoltage; // obciążenie rozkladane stosownie do napiec + coupler.power_high.current = ( std::abs( Itot ) * IsVehicleEIMBrakingFactor() + TotalCurrent ) * coupler.power_high.voltage / couplervoltage; // obciążenie rozkladane stosownie do napiec if( true == coupler.power_high.is_live ) { coupler.power_high.current += connectedothercoupler.power_high.current; } @@ -4837,7 +4837,8 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { // potentially generate sounds on clash or stretch if( ( newdistance < 0.0 ) && ( coupler.Dist > newdistance ) - && ( dV < -0.1 ) ) { + && ( dV < -0.1 ) + && ( false == coupler.has_adapter() ) ) { // HACK: with adapter present we presume buffers won't clash // 090503: dzwieki pracy zderzakow SetFlag( coupler.sounds, @@ -4866,7 +4867,7 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { if( ( coupler.CouplingFlag != coupling::faux ) || ( initialdistance < 0 ) ) { - coupler.Dist = clamp( newdistance, -coupler.DmaxB, coupler.DmaxC ); + coupler.Dist = clamp( newdistance, ( coupler.has_adapter() ? 0 : -coupler.DmaxB ), coupler.DmaxC ); double BetaAvg = 0; double Fmax = 0; @@ -4923,16 +4924,19 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { - Fmax * dV * BetaAvg; } // liczenie sily ze sprezystosci zderzaka - if( -newdistance > ( coupler.DmaxB + othercoupler.DmaxB ) ) { + auto const collisiondistance { ( + ( coupler.has_adapter() || othercoupler.has_adapter() ) ? + std::min( coupler.DmaxB, othercoupler.DmaxB ) : // HACK: only take into account buffering ability of automatic coupler + coupler.DmaxB + othercoupler.DmaxB + ) }; + if( -newdistance > collisiondistance ) { // zderzenie coupler.CheckCollision = true; - if( ( coupler.CouplerType == TCouplerType::Automatic ) - && ( coupler.CouplerType == othercoupler.CouplerType ) + if( ( coupler.type() == TCouplerType::Automatic ) + && ( coupler.type() == othercoupler.type() ) && ( coupler.CouplingFlag == coupling::faux ) ) { // sprzeganie wagonow z samoczynnymi sprzegami - // EN57 - // TBD, TODO: configurable flag for automatic coupling - coupler.CouplingFlag = coupling::coupler /*| coupling::brakehose | coupling::mainhose | coupling::control*/; + coupler.CouplingFlag = ( coupler.AutomaticCouplingFlag & othercoupler.AutomaticCouplingFlag ); SetFlag( coupler.sounds, sound::attachcoupler ); } } @@ -5898,10 +5902,10 @@ double TMoverParameters::TractionForce( double dt ) { else if ((std::abs(EngineVoltage) < EnginePowerSource.CollectorParameters.MaxV)) Vadd *= (1.0 - dt); else - Vadd = Max0R( + Vadd = std::max( Vadd * (1.0 - 0.2 * dt), 0.007 * (std::abs(EngineVoltage) - (EnginePowerSource.CollectorParameters.MaxV - 100))); - Itot = eimv[eimv_Ipoj] * (0.01 + Min0R(0.99, 0.99 - Vadd)); + Itot = eimv[eimv_Ipoj] * (0.01 + std::min(0.99, 0.99 - Vadd)); EnginePower = abs(eimv[eimv_Ic] * eimv[eimv_U] * NPoweredAxles) / 1000; // power inverters @@ -9498,8 +9502,8 @@ void TMoverParameters::LoadFIZ_BuffCoupl( std::string const &line, int const Ind auto lookup = couplertypes.find( extract_value( "CType", line ) ); coupler->CouplerType = ( lookup != couplertypes.end() ? - lookup->second : - TCouplerType::NoCoupler ); + lookup->second : + TCouplerType::NoCoupler ); extract_value( coupler->SpringKC, "kC", line, "" ); extract_value( coupler->DmaxC, "DmaxC", line, "" ); @@ -9508,6 +9512,7 @@ void TMoverParameters::LoadFIZ_BuffCoupl( std::string const &line, int const Ind extract_value( coupler->DmaxB, "DmaxB", line, "" ); extract_value( coupler->FmaxB, "FmaxB", line, "" ); extract_value( coupler->beta, "beta", line, "" ); + extract_value( coupler->AutomaticCouplingFlag, "AutomaticFlag", line, "" ); extract_value( coupler->AllowedFlag, "AllowedFlag", line, "" ); if( coupler->AllowedFlag < 0 ) { @@ -10255,6 +10260,8 @@ void TMoverParameters::LoadFIZ_Circuit( std::string const &Input ) { void TMoverParameters::LoadFIZ_AI( std::string const &Input ) { extract_value( AIHintPantstate, "Pantstate", Input, "" ); + extract_value( AIHintLocalBrakeAccFactor, "LocalBrakeAccFactor", Input, "" ); + } void TMoverParameters::LoadFIZ_RList( std::string const &Input ) { @@ -10763,6 +10770,13 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir) LocalBrakePosA = 0.0; BrakeCtrlPos = static_cast( Handle->GetPos( bh_NP ) ); LimPipePress = LowPipePress; + if( ( LocalBrake == TLocalBrake::ManualBrake ) + || ( MBrake == true ) ) { + IncManualBrakeLevel( ManualBrakePosNo ); + } + if( SpringBrake.MaxBrakeForce > 0.0 ) { + SpringBrake.Activate = true; + } } ActFlowSpeed = 0.0; diff --git a/Model3d.cpp b/Model3d.cpp index b04b7fa4..d8e653e9 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -448,9 +448,13 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic */ { // if material has opacity set, replace submodel opacity with it + // NOTE: reverted to use base opacity, this allows to define opacity threshold in material + // without it causing the translucent models to become opaque auto const opacity { ( +/* false == std::isnan( mat.opacity ) ? mat.opacity : +*/ Opacity ) }; iFlags &= ~0x30; iFlags |= ( diff --git a/Train.cpp b/Train.cpp index 682b5d1b..75417b8c 100644 --- a/Train.cpp +++ b/Train.cpp @@ -360,6 +360,8 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::doormodetoggle, &TTrain::OnCommand_doormodetoggle }, { user_command::carcouplingincrease, &TTrain::OnCommand_carcouplingincrease }, { user_command::carcouplingdisconnect, &TTrain::OnCommand_carcouplingdisconnect }, + { user_command::carcoupleradapterattach, &TTrain::OnCommand_carcoupleradapterattach }, + { user_command::carcoupleradapterremove, &TTrain::OnCommand_carcoupleradapterremove }, { user_command::departureannounce, &TTrain::OnCommand_departureannounce }, { user_command::hornlowactivate, &TTrain::OnCommand_hornlowactivate }, { user_command::hornhighactivate, &TTrain::OnCommand_hornhighactivate }, @@ -2430,22 +2432,20 @@ void TTrain::OnCommand_pantographcompressoractivate( TTrain *Train, command_data return; } - if( ( Train->mvControlled->PantPress > 4.8 ) - || ( false == Train->mvControlled->Battery ) ) { - // needs live power source and low enough pressure to work - return; - } - if( Command.action != GLFW_RELEASE ) { // press or hold to activate - Train->mvControlled->PantCompFlag = true; - // visual feedback: + if( ( Train->mvControlled->PantPress < 4.8 ) + && ( true == Train->mvControlled->Battery ) ) { + // needs live power source and low enough pressure to work + Train->mvControlled->PantCompFlag = true; + } + // visual feedback Train->ggPantCompressorButton.UpdateValue( 1.0 ); } else { // release to disable Train->mvControlled->PantCompFlag = false; - // visual feedback: + // visual feedback Train->ggPantCompressorButton.UpdateValue( 0.0 ); } } @@ -2480,6 +2480,8 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com OnCommand_linebreakerclose( Train, Command ); } } + // HACK: ignition key ignores lack of submodel, so we can start vehicles without any modeled controls + Train->ggIgnitionKey.UpdateValue( 0.0 ); } } @@ -2543,6 +2545,8 @@ void TTrain::OnCommand_linebreakerclose( TTrain *Train, command_data const &Comm } else { // no switch capable of doing the job + // HACK: ignition key ignores lack of submodel, so we can start vehicles without any modeled controls + Train->ggIgnitionKey.UpdateValue( 1.0 ); return; } // the actual closing of the line breaker is handled in the train update routine @@ -5400,6 +5404,40 @@ void TTrain::OnCommand_carcouplingdisconnect( TTrain *Train, command_data const } } +void TTrain::OnCommand_carcoupleradapterattach( TTrain *Train, command_data const &Command ) { + + if( ( true == FreeFlyModeFlag ) + && ( Command.action == GLFW_PRESS ) ) { + // tryb freefly, press only + auto *vehicle { std::get( simulation::Region->find_vehicle( Command.location, 50, false, true ) ) }; + if( vehicle == nullptr ) { return; } + + auto const coupler = ( + glm::length2( glm::vec3 { vehicle->CouplerPosition( end::front ) } - Command.location ) < glm::length2( glm::vec3 { vehicle->CouplerPosition( end::rear ) } - Command.location ) ? + end::front : + end::rear ); + + vehicle->attach_coupler_adapter( coupler ); + } +} + +void TTrain::OnCommand_carcoupleradapterremove( TTrain *Train, command_data const &Command ) { + + if( ( true == FreeFlyModeFlag ) + && ( Command.action == GLFW_PRESS ) ) { + // tryb freefly, press only + auto *vehicle { std::get( simulation::Region->find_vehicle( Command.location, 50, false, true ) ) }; + if( vehicle == nullptr ) { return; } + + auto const coupler = ( + glm::length2( glm::vec3 { vehicle->CouplerPosition( end::front ) } - Command.location ) < glm::length2( glm::vec3 { vehicle->CouplerPosition( end::rear ) } - Command.location ) ? + end::front : + end::rear ); + + vehicle->remove_coupler_adapter( coupler ); + } +} + void TTrain::OnCommand_departureannounce( TTrain *Train, command_data const &Command ) { if( Train->ggDepartureSignalButton.SubModel == nullptr ) { @@ -5797,7 +5835,8 @@ bool TTrain::Update( double const Deltatime ) } if( ( ( ggMainButton.SubModel != nullptr ) && ( ggMainButton.GetDesiredValue() > 0.95 ) ) - || ( ( ggMainOnButton.SubModel != nullptr ) && ( ggMainOnButton.GetDesiredValue() > 0.95 ) ) ) { + || ( ( ggMainOnButton.SubModel != nullptr ) && ( ggMainOnButton.GetDesiredValue() > 0.95 ) + || ( ggIgnitionKey.GetDesiredValue() > 0.95 ) ) ) { // HACK: fallback // keep track of period the line breaker button is held down, to determine when/if circuit closes if( ( mvControlled->MainsInitTimeCountdown <= 0.0 ) && ( ( fHVoltage > 0.5 * mvControlled->EnginePowerSource.MaxVoltage ) diff --git a/Train.h b/Train.h index 8acbf339..be8af815 100644 --- a/Train.h +++ b/Train.h @@ -386,6 +386,8 @@ class TTrain { static void OnCommand_doormodetoggle( TTrain *Train, command_data const &Command ); static void OnCommand_carcouplingincrease( TTrain *Train, command_data const &Command ); static void OnCommand_carcouplingdisconnect( TTrain *Train, command_data const &Command ); + static void OnCommand_carcoupleradapterattach( TTrain *Train, command_data const &Command ); + static void OnCommand_carcoupleradapterremove( TTrain *Train, command_data const &Command ); static void OnCommand_departureannounce( TTrain *Train, command_data const &Command ); static void OnCommand_hornlowactivate( TTrain *Train, command_data const &Command ); static void OnCommand_hornhighactivate( TTrain *Train, command_data const &Command ); diff --git a/command.cpp b/command.cpp index d4ea7974..a452e9d7 100644 --- a/command.cpp +++ b/command.cpp @@ -155,6 +155,8 @@ commanddescription_sequence Commands_descriptions = { // TBD, TODO: make coupling controls entity-centric { "carcouplingincrease", command_target::vehicle }, { "carcouplingdisconnect", command_target::vehicle }, + { "carcoupleradapterattach", command_target::vehicle }, + { "carcoupleradapterremove", command_target::vehicle }, { "doortoggleleft", command_target::vehicle }, { "doortoggleright", command_target::vehicle }, { "doorpermitleft", command_target::vehicle }, diff --git a/command.h b/command.h index 0987a421..1ea9877e 100644 --- a/command.h +++ b/command.h @@ -146,6 +146,8 @@ enum class user_command { carcouplingincrease, carcouplingdisconnect, + carcoupleradapterattach, + carcoupleradapterremove, doortoggleleft, doortoggleright, doorpermitleft, diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index 3ba33ddd..96bc3ab6 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -155,6 +155,8 @@ driverkeyboard_input::default_bindings() { { user_command::movedown, GLFW_KEY_PAGE_DOWN }, { user_command::carcouplingincrease, GLFW_KEY_INSERT }, { user_command::carcouplingdisconnect, GLFW_KEY_DELETE }, + { user_command::carcoupleradapterattach, GLFW_KEY_INSERT | keymodifier::control }, + { user_command::carcoupleradapterremove, GLFW_KEY_DELETE | keymodifier::control }, { user_command::doortoggleleft, GLFW_KEY_COMMA }, { user_command::doortoggleright, GLFW_KEY_PERIOD }, { user_command::doorpermitleft, GLFW_KEY_COMMA | keymodifier::shift }, diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 40e9cc5f..3e32f61b 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -797,18 +797,23 @@ debug_panel::update_section_vehicle( std::vector &Output ) { std::string debug_panel::update_vehicle_coupler( int const Side ) { // NOTE: mover and vehicle are guaranteed to be valid by the caller + auto const &mover { *( m_input.mover ) }; + std::string couplerstatus { locale::strings[ locale::string::debug_vehicle_none ] }; + std::string const adapterstatus { ( mover.Couplers[ Side ].adapter_type == TCouplerType::NoCoupler ? "" : "[A]" ) }; auto const *connected { m_input.vehicle->MoverParameters->Neighbours[ Side ].vehicle }; - if( connected == nullptr ) { return couplerstatus; } - - auto const &mover { *( m_input.mover ) }; + if( connected == nullptr ) { + + return couplerstatus + " " + adapterstatus; + } std::snprintf( m_buffer.data(), m_buffer.size(), - "%s [%d] (%.1f m)", + "%s %s[%d] (%.1f m)", connected->name().c_str(), + adapterstatus.c_str(), mover.Couplers[ Side ].CouplingFlag, mover.Neighbours[ Side ].distance ); diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index 5ef8297a..f179c21d 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -1083,6 +1083,30 @@ bool opengl33_renderer::Render_lowpoly( TDynamicObject *Dynamic, float const Squ return true; } +bool opengl33_renderer::Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha ) { + + if( Dynamic->m_coupleradapters[ End ] == nullptr ) { return false; } + + auto const position { Math3D::vector3 { + 0.f, + Dynamic->MoverParameters->Couplers[ End ].adapter_height, + ( Dynamic->MoverParameters->Couplers[ End ].adapter_length + Dynamic->MoverParameters->Dim.L * 0.5 ) * ( End == end::front ? 1 : -1 ) } }; + + auto const angle { glm::vec3{ + 0, + ( End == end::front ? 0 : 180 ), + 0 } }; + + if( Alpha ) { + Render_Alpha( Dynamic->m_coupleradapters[ End ], Dynamic->Material(), Squaredistance, position, angle ); + } + else { + Render( Dynamic->m_coupleradapters[ End ], Dynamic->Material(), Squaredistance, position, angle ); + } + + return true; +} + // creates dynamic environment cubemap bool opengl33_renderer::Render_reflections(viewport_config &vp) { @@ -1813,13 +1837,17 @@ void opengl33_renderer::Bind_Material( material_handle const Material, TSubModel // material-based parameters switch( entry.defaultparam ) { case gl::shader::defaultparam_e::glossiness: { - src = glm::vec4( material.glossiness ); + src = glm::vec4 { material.glossiness }; } default: { break; } } + if( entry.size == 1 ) { + // HACK: convert color to luminosity, if it's passed as single value + src == glm::vec4 { colors::RGBtoHSV( glm::vec3 { src } ).s }; + } for (size_t j = 0; j < entry.size; j++) model_ubs.param[entry.location][entry.offset + j] = src[j]; } @@ -2487,8 +2515,11 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) for( auto *attachment : Dynamic->mdAttachments ) { Render( attachment, Dynamic->Material(), squaredistance ); } + // optional coupling adapters + Render_coupler_adapter( Dynamic, squaredistance, end::front ); + Render_coupler_adapter( Dynamic, squaredistance, end::rear ); - // post-render cleanup + // post-render cleanup if (Dynamic->fShade > 0.0f) { // restore regular light level @@ -2510,6 +2541,9 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) for( auto *attachment : Dynamic->mdAttachments ) { Render( attachment, Dynamic->Material(), squaredistance ); } + // optional coupling adapters + Render_coupler_adapter( Dynamic, squaredistance, end::front ); + Render_coupler_adapter( Dynamic, squaredistance, end::rear ); if( Dynamic->mdLoad ) { // renderowanie nieprzezroczystego ładunku Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); @@ -2687,11 +2721,6 @@ void opengl33_renderer::Render(TSubModel *Submodel) if ((Submodel->iVisible) && (TSubModel::fSquareDist >= Submodel->fSquareMinDist) && (TSubModel::fSquareDist < Submodel->fSquareMaxDist)) { - - // debug data - ++m_renderpass.draw_stats.submodels; - ++m_renderpass.draw_stats.drawcalls; - glm::mat4 future_stack = model_ubs.future; if (Submodel->iFlags & 0xC000) @@ -2714,6 +2743,11 @@ void opengl33_renderer::Render(TSubModel *Submodel) if (Submodel->iAlpha & Submodel->iFlags & 0x1F) { // rysuj gdy element nieprzezroczysty + + // debug data + ++m_renderpass.draw_stats.submodels; + ++m_renderpass.draw_stats.drawcalls; + switch (m_renderpass.draw_mode) { case rendermode::color: @@ -3440,6 +3474,9 @@ bool opengl33_renderer::Render_Alpha(TDynamicObject *Dynamic) for( auto *attachment : Dynamic->mdAttachments ) { Render_Alpha( attachment, Dynamic->Material(), squaredistance ); } + // optional coupling adapters + Render_coupler_adapter( Dynamic, squaredistance, end::front, true ); + Render_coupler_adapter( Dynamic, squaredistance, end::rear, true ); if( Dynamic->mdLoad ) { // renderowanie nieprzezroczystego ładunku Render_Alpha( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); @@ -3512,11 +3549,6 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) // renderowanie przezroczystych przez DL if ((Submodel->iVisible) && (TSubModel::fSquareDist >= Submodel->fSquareMinDist) && (TSubModel::fSquareDist < Submodel->fSquareMaxDist)) { - - // debug data - ++m_renderpass.draw_stats.submodels; - ++m_renderpass.draw_stats.drawcalls; - glm::mat4 future_stack = model_ubs.future; if (Submodel->iFlags & 0xC000) @@ -3538,7 +3570,12 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) // renderowanie obiektów OpenGL if (Submodel->iAlpha & Submodel->iFlags & 0x2F) { - // rysuj gdy element przezroczysty + // rysuj gdy element przezroczysty + + // debug data + ++m_renderpass.draw_stats.submodels; + ++m_renderpass.draw_stats.drawcalls; + switch (m_renderpass.draw_mode) { case rendermode::color: diff --git a/opengl33renderer.h b/opengl33renderer.h index 7e2030f4..8b7d3db3 100644 --- a/opengl33renderer.h +++ b/opengl33renderer.h @@ -242,6 +242,7 @@ class opengl33_renderer : public gfx_renderer { bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false); bool Render_interior( bool const Alpha = false ); bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false ); + bool Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false ); void Render(TMemCell *Memcell); void Render_particles(); void Render_precipitation(); diff --git a/openglrenderer.cpp b/openglrenderer.cpp index a0fd386c..8932a82a 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -845,6 +845,27 @@ bool opengl_renderer::Render_lowpoly( TDynamicObject *Dynamic, float const Squar return true; } +bool opengl_renderer::Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha ) { + + if( Dynamic->m_coupleradapters[ End ] == nullptr ) { return false; } + + auto const position { Math3D::vector3 { + 0.f, + Dynamic->MoverParameters->Couplers[ End ].adapter_height, + ( Dynamic->MoverParameters->Couplers[ End ].adapter_length + Dynamic->MoverParameters->Dim.L * 0.5 ) * ( End == end::front ? 1 : -1 ) } }; + + auto const angle{ glm::vec3{ 0, ( End == end::front ? 0 : 180 ), 0 } }; + + if( Alpha ) { + Render_Alpha( Dynamic->m_coupleradapters[ End ], Dynamic->Material(), Squaredistance, position, angle ); + } + else { + Render( Dynamic->m_coupleradapters[ End ], Dynamic->Material(), Squaredistance, position, angle ); + } + + return true; +} + // creates dynamic environment cubemap bool opengl_renderer::Render_reflections() { @@ -2318,6 +2339,9 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { for( auto *attachment : Dynamic->mdAttachments ) { Render( attachment, Dynamic->Material(), squaredistance ); } + // optional coupling adapters + Render_coupler_adapter( Dynamic, squaredistance, end::front ); + Render_coupler_adapter( Dynamic, squaredistance, end::rear ); // post-render cleanup m_renderspecular = false; if( Dynamic->fShade > 0.0f ) { @@ -2342,6 +2366,9 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { for( auto *attachment : Dynamic->mdAttachments ) { Render( attachment, Dynamic->Material(), squaredistance ); } + // optional coupling adapters + Render_coupler_adapter( Dynamic, squaredistance, end::front ); + Render_coupler_adapter( Dynamic, squaredistance, end::rear ); if( Dynamic->mdLoad ) { // renderowanie nieprzezroczystego ładunku Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); @@ -2524,10 +2551,6 @@ opengl_renderer::Render( TSubModel *Submodel ) { && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { - // debug data - ++m_renderpass.draw_stats.submodels; - ++m_renderpass.draw_stats.drawcalls; - if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); if( Submodel->fMatrix ) @@ -2540,6 +2563,11 @@ opengl_renderer::Render( TSubModel *Submodel ) { // renderowanie obiektów OpenGL if( Submodel->iAlpha & Submodel->iFlags & 0x1F ) { // rysuj gdy element nieprzezroczysty + + // debug data + ++m_renderpass.draw_stats.submodels; + ++m_renderpass.draw_stats.drawcalls; + switch( m_renderpass.draw_mode ) { case rendermode::color: case rendermode::reflections: { @@ -3491,6 +3519,9 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { for( auto *attachment : Dynamic->mdAttachments ) { Render_Alpha( attachment, Dynamic->Material(), squaredistance ); } + // optional coupling adapters + Render_coupler_adapter( Dynamic, squaredistance, end::front, true ); + Render_coupler_adapter( Dynamic, squaredistance, end::rear, true ); if( Dynamic->mdLoad ) { // renderowanie nieprzezroczystego ładunku Render_Alpha( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); @@ -3569,10 +3600,6 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { - // debug data - ++m_renderpass.draw_stats.submodels; - ++m_renderpass.draw_stats.drawcalls; - if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); if( Submodel->fMatrix ) @@ -3585,6 +3612,11 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // renderowanie obiektów OpenGL if( Submodel->iAlpha & Submodel->iFlags & 0x2F ) { // rysuj gdy element przezroczysty + + // debug data + ++m_renderpass.draw_stats.submodels; + ++m_renderpass.draw_stats.drawcalls; + switch( m_renderpass.draw_mode ) { case rendermode::color: { diff --git a/openglrenderer.h b/openglrenderer.h index d80c7793..deec0011 100644 --- a/openglrenderer.h +++ b/openglrenderer.h @@ -223,6 +223,8 @@ private: Render_interior( bool const Alpha = false ); bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false ); + bool + Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false ); void Render( TMemCell *Memcell ); void diff --git a/version.h b/version.h index 85e730d5..ead6fcb4 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 20 -#define VERSION_MINOR 222 +#define VERSION_MINOR 322 #define VERSION_REVISION 0