diff --git a/Camera.cpp b/Camera.cpp index 6ba1f7c6..28405e8a 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -14,44 +14,30 @@ http://mozilla.org/MPL/2.0/. #include "utilities.h" #include "Console.h" #include "Timer.h" +#include "DynObj.h" #include "MOVER.h" //--------------------------------------------------------------------------- -void TCamera::Init( Math3D::vector3 const &NPos, Math3D::vector3 const &NAngle, TCameraType const NType ) { +void TCamera::Init( Math3D::vector3 const &NPos, Math3D::vector3 const &NAngle/*, TCameraType const NType*/, TDynamicObject *Owner ) { vUp = { 0, 1, 0 }; Velocity = { 0, 0, 0 }; - Pitch = NAngle.x; - Yaw = NAngle.y; - Roll = NAngle.z; + Angle = NAngle; Pos = NPos; - Type = NType; + m_owner = Owner; }; void TCamera::Reset() { - Pitch = Yaw = Roll = 0; + Angle = {}; m_rotationoffsets = {}; }; void TCamera::OnCursorMove(double x, double y) { -/* - Yaw -= x; - while( Yaw > M_PI ) { - Yaw -= 2 * M_PI; - } - while( Yaw < -M_PI ) { - Yaw += 2 * M_PI; - } - Pitch -= y; - if (Type == tp_Follow) { - // jeżeli jazda z pojazdem ograniczenie kąta spoglądania w dół i w górę - Pitch = clamp( Pitch, -M_PI_4, M_PI_4 ); - } -*/ + m_rotationoffsets += glm::dvec3 { y, x, 0.0 }; } @@ -76,19 +62,21 @@ TCamera::OnCommand( command_data const &Command ) { case user_command::movehorizontalfast: { auto const movespeed = ( - Type == TCameraType::tp_Free ? runspeed : - Type == TCameraType::tp_Follow ? walkspeed : - 0.0 ); + m_owner == nullptr ? runspeed : // free roam + false == FreeFlyModeFlag ? walkspeed : // vehicle cab + 0.0 ); // vehicle external + +// if( movespeed == 0.0 ) { break; } // enable to fix external cameras in place auto const speedmultiplier = ( - ( ( Type == TCameraType::tp_Free ) && ( Command.command == user_command::movehorizontalfast ) ) ? + ( ( m_owner == nullptr ) && ( Command.command == user_command::movehorizontalfast ) ) ? 30.0 : 1.0 ); // left-right auto const movexparam { Command.param1 }; // 2/3rd of the stick range enables walk speed, past that we lerp between walk and run speed - auto const movex { walkspeed + ( std::max( 0.0, std::abs( movexparam ) - 0.65 ) / 0.35 ) * ( movespeed - walkspeed ) }; + auto const movex { walkspeed + ( std::max( 0.0, std::abs( movexparam ) - 0.65 ) / 0.35 ) * std::max( 0.0, movespeed - walkspeed ) }; m_moverate.x = ( movexparam > 0.0 ? movex * speedmultiplier : @@ -97,7 +85,7 @@ TCamera::OnCommand( command_data const &Command ) { // forward-back double const movezparam { Command.param2 }; - auto const movez { walkspeed + ( std::max( 0.0, std::abs( movezparam ) - 0.65 ) / 0.35 ) * ( movespeed - walkspeed ) }; + auto const movez { walkspeed + ( std::max( 0.0, std::abs( movezparam ) - 0.65 ) / 0.35 ) * std::max( 0.0, movespeed - walkspeed ) }; // NOTE: z-axis is flipped given world coordinate system m_moverate.z = ( movezparam > 0.0 ? -movez * speedmultiplier : @@ -111,15 +99,16 @@ TCamera::OnCommand( command_data const &Command ) { case user_command::moveverticalfast: { auto const movespeed = ( - Type == TCameraType::tp_Free ? runspeed * 0.5 : - Type == TCameraType::tp_Follow ? walkspeed : - 0.0 ); + m_owner == nullptr ? runspeed * 0.5 : // free roam + false == FreeFlyModeFlag ? walkspeed : // vehicle cab + 0.0 ); // vehicle external + +// if( movespeed == 0.0 ) { break; } // enable to fix external cameras in place auto const speedmultiplier = ( - ( ( Type == TCameraType::tp_Free ) && ( Command.command == user_command::moveverticalfast ) ) ? + ( ( m_owner == nullptr ) && ( Command.command == user_command::moveverticalfast ) ) ? 10.0 : 1.0 ); - // up-down auto const moveyparam { Command.param1 }; // 2/3rd of the stick range enables walk speed, past that we lerp between walk and run speed @@ -145,9 +134,6 @@ TCamera::OnCommand( command_data const &Command ) { void TCamera::Update() { - if( FreeFlyModeFlag == true ) { Type = TCameraType::tp_Free; } - else { Type = TCameraType::tp_Follow; } - // check for sent user commands // NOTE: this is a temporary arrangement, for the transition period from old command setup to the new one // ultimately we'll need to track position of camera/driver for all human entities present in the scenario @@ -161,8 +147,28 @@ void TCamera::Update() auto const deltatime { Timer::GetDeltaRenderTime() }; // czas bez pauzy + // update rotation + auto const rotationfactor { std::min( 1.0, 20 * deltatime ) }; + + Angle.y -= m_rotationoffsets.y * rotationfactor; + m_rotationoffsets.y *= ( 1.0 - rotationfactor ); + while( Angle.y > M_PI ) { + Angle.y -= 2 * M_PI; + } + while( Angle.y < -M_PI ) { + Angle.y += 2 * M_PI; + } + + Angle.x -= m_rotationoffsets.x * rotationfactor; + m_rotationoffsets.x *= ( 1.0 - rotationfactor ); + + if( m_owner != nullptr ) { + // jeżeli jazda z pojazdem ograniczenie kąta spoglądania w dół i w górę + Angle.x = clamp( Angle.x, -M_PI_4, M_PI_4 ); + } + // update position - if( ( Type == TCameraType::tp_Free ) + if( ( m_owner == nullptr ) || ( false == Global.ctrlState ) || ( true == DebugCameraFlag ) ) { // ctrl is used for mirror view, so we ignore the controls when in vehicle if ctrl is pressed @@ -171,46 +177,34 @@ void TCamera::Update() Velocity.z = clamp( Velocity.z + m_moverate.z * 10.0 * deltatime, -std::abs( m_moverate.z ), std::abs( m_moverate.z ) ); Velocity.y = clamp( Velocity.y + m_moverate.y * 10.0 * deltatime, -std::abs( m_moverate.y ), std::abs( m_moverate.y ) ); } - - if( ( Type == TCameraType::tp_Free ) + if( ( m_owner == nullptr ) || ( true == DebugCameraFlag ) ) { - // free movement position update is handled here, movement while in vehicle is handled by train update - Math3D::vector3 Vec = Velocity; - Vec.RotateY( Yaw ); - Pos += Vec * 5.0 * deltatime; + // free movement position update + auto movement { Velocity }; + movement.RotateY( Angle.y ); + Pos += movement * 5.0 * deltatime; } - // update rotation - auto const rotationfactor { std::min( 1.0, 20 * deltatime ) }; + else { + // attached movement position update + auto movement { Velocity * -2.0 }; + movement.y = -movement.y; + if( m_owner->MoverParameters->ActiveCab < 0 ) { + movement *= -1.f; + movement.y = -movement.y; + } + movement.RotateY( Angle.y ); - Yaw -= rotationfactor * m_rotationoffsets.y; - m_rotationoffsets.y *= ( 1.0 - rotationfactor ); - while( Yaw > M_PI ) { - Yaw -= 2 * M_PI; + m_owneroffset += movement * deltatime; } - while( Yaw < -M_PI ) { - Yaw += 2 * M_PI; - } - - Pitch -= rotationfactor * m_rotationoffsets.x; - m_rotationoffsets.x *= ( 1.0 - rotationfactor ); - if( Type == TCameraType::tp_Follow ) { - // jeżeli jazda z pojazdem ograniczenie kąta spoglądania w dół i w górę - Pitch = clamp( Pitch, -M_PI_4, M_PI_4 ); - } -} - -Math3D::vector3 TCamera::GetDirection() { - - return glm::normalize( glm::rotateY( glm::vec3{ 0.f, 0.f, 1.f }, Yaw ) ); } bool TCamera::SetMatrix( glm::dmat4 &Matrix ) { - Matrix = glm::rotate( Matrix, -Roll, glm::dvec3( 0.0, 0.0, 1.0 ) ); // po wyłączeniu tego kręci się pojazd, a sceneria nie - Matrix = glm::rotate( Matrix, -Pitch, glm::dvec3( 1.0, 0.0, 0.0 ) ); - Matrix = glm::rotate( Matrix, -Yaw, glm::dvec3( 0.0, 1.0, 0.0 ) ); // w zewnętrznym widoku: kierunek patrzenia + Matrix = glm::rotate( Matrix, -Angle.z, glm::dvec3( 0.0, 0.0, 1.0 ) ); // po wyłączeniu tego kręci się pojazd, a sceneria nie + Matrix = glm::rotate( Matrix, -Angle.x, glm::dvec3( 1.0, 0.0, 0.0 ) ); + Matrix = glm::rotate( Matrix, -Angle.y, glm::dvec3( 0.0, 1.0, 0.0 ) ); // w zewnętrznym widoku: kierunek patrzenia - if( ( Type == TCameraType::tp_Follow ) && ( false == DebugCameraFlag ) ) { + if( ( m_owner != nullptr ) && ( false == DebugCameraFlag ) ) { Matrix *= glm::lookAt( glm::dvec3{ Pos }, @@ -226,14 +220,14 @@ bool TCamera::SetMatrix( glm::dmat4 &Matrix ) { void TCamera::RaLook() { // zmiana kierunku patrzenia - przelicza Yaw - Math3D::vector3 where = LookAt - Pos + Math3D::vector3(0, 3, 0); // trochę w górę od szyn + Math3D::vector3 where = LookAt - Pos /*+ Math3D::vector3(0, 3, 0)*/; // trochę w górę od szyn if( ( where.x != 0.0 ) || ( where.z != 0.0 ) ) { - Yaw = atan2( -where.x, -where.z ); // kąt horyzontalny + Angle.y = atan2( -where.x, -where.z ); // kąt horyzontalny m_rotationoffsets.y = 0.0; } double l = Math3D::Length3(where); if( l > 0.0 ) { - Pitch = asin( where.y / l ); // kąt w pionie + Angle.x = asin( where.y / l ); // kąt w pionie m_rotationoffsets.x = 0.0; } }; diff --git a/Camera.h b/Camera.h index b2792b18..18cda482 100644 --- a/Camera.h +++ b/Camera.h @@ -13,34 +13,27 @@ http://mozilla.org/MPL/2.0/. #include "command.h" //--------------------------------------------------------------------------- -enum class TCameraType -{ // tryby pracy kamery - tp_Follow, // jazda z pojazdem - tp_Free, // stoi na scenerii - tp_Satelite // widok z góry (nie używany) -}; class TCamera { public: // McZapkie: potrzebuje do kiwania na boki - void Init( Math3D::vector3 const &Location, Math3D::vector3 const &Angle, TCameraType const Type ); + void Init( Math3D::vector3 const &Location, Math3D::vector3 const &Angle, TDynamicObject *Owner ); void Reset(); void OnCursorMove(double const x, double const y); bool OnCommand( command_data const &Command ); void Update(); - Math3D::vector3 GetDirection(); bool SetMatrix(glm::dmat4 &Matrix); void RaLook(); - TCameraType Type; - double Pitch; - double Yaw; // w środku: 0=do przodu; na zewnątrz: 0=na południe - double Roll; + Math3D::vector3 Angle; // pitch, yaw, roll Math3D::vector3 Pos; // współrzędne obserwatora Math3D::vector3 LookAt; // współrzędne punktu, na który ma patrzeć Math3D::vector3 vUp; Math3D::vector3 Velocity; + TDynamicObject *m_owner { nullptr }; // TODO: change to const when shake calculations are part of vehicles update + Math3D::vector3 m_owneroffset {}; + private: glm::dvec3 m_moverate; glm::dvec3 m_rotationoffsets; // requested changes to pitch, yaw and roll diff --git a/Driver.h b/Driver.h index de54094d..c0f50fbf 100644 --- a/Driver.h +++ b/Driver.h @@ -428,4 +428,6 @@ private: return iDirection; } TDynamicObject const *Vehicle() const { return pVehicle; } + TDynamicObject *Vehicle( side const Side ) const { + return pVehicles[ Side ]; } }; diff --git a/DynObj.cpp b/DynObj.cpp index 2c88a302..dc067d34 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2109,6 +2109,10 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" create_controller( DriverType, !TrainName.empty() ); + ShakeSpring.Init( 125.0 ); + BaseShake = baseshake_config {}; + ShakeState = shake_state {}; + // McZapkie-250202 /* iAxles = std::min( MoverParameters->NAxles, MaxAxles ); // ilość osi @@ -4403,7 +4407,8 @@ void TDynamicObject::RenderSounds() { 0.0, 1.0, clamp( MoverParameters->Vel / 40.0, - 0.0, 1.0 ) ); + 0.0, 1.0 ) ) + + ( MyTrack->eType == tt_Switch ? 0.25 : 0.0 ); } else { volume = 0; @@ -5855,6 +5860,46 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co m_startjolt.owner( this ); } + else if (token == "mechspring:") + { + // parametry bujania kamery: + double ks, kd; + parser.getTokens(2, false); + parser + >> ks + >> kd; + ShakeSpring.Init(ks, kd); + parser.getTokens(6, false); + parser + >> BaseShake.jolt_scale.x + >> BaseShake.jolt_scale.y + >> BaseShake.jolt_scale.z + >> BaseShake.jolt_limit + >> BaseShake.angle_scale.x + >> BaseShake.angle_scale.z; + } + else if( token == "enginespring:" ) { + parser.getTokens( 5, false ); + parser + >> EngineShake.scale + >> EngineShake.fadein_offset + >> EngineShake.fadein_factor + >> EngineShake.fadeout_offset + >> EngineShake.fadeout_factor; + // offsets values are provided as rpm for convenience + EngineShake.fadein_offset /= 60.f; + EngineShake.fadeout_offset /= 60.f; + } + else if( token == "huntingspring:" ) { + parser.getTokens( 4, false ); + parser + >> HuntingShake.scale + >> HuntingShake.frequency + >> HuntingShake.fadein_begin + >> HuntingShake.fadein_end; + } + + } while( token != "" ); } // internaldata: @@ -6468,6 +6513,102 @@ TDynamicObject::ConnectedEnginePowerSource( TDynamicObject const *Caller ) const return MoverParameters->EnginePowerSource.SourceType; } +void +TDynamicObject::update_shake( double const Timedelta ) { + // Ra: mechanik powinien być telepany niezależnie od pozycji pojazdu + // Ra: trzeba zrobić model bujania głową i wczepić go do pojazdu + + // Ra: tu by się przydało uwzględnić rozkład sił: + // - na postoju horyzont prosto, kabina skosem + // - przy szybkiej jeździe kabina prosto, horyzont pochylony + + // McZapkie: najpierw policzę pozycję w/m kabiny + + // ABu: rzucamy kabina tylko przy duzym FPS! + // Mala histereza, zeby bez przerwy nie przelaczalo przy FPS~17 + // Granice mozna ustalic doswiadczalnie. Ja proponuje 14:20 + if( false == Global.iSlowMotion ) { // musi być pełna prędkość + + Math3D::vector3 shakevector; + if( ( MoverParameters->EngineType == TEngineType::DieselElectric ) + || ( MoverParameters->EngineType == TEngineType::DieselEngine ) ) { + if( std::abs( MoverParameters->enrot ) > 0.0 ) { + // engine vibration + shakevector.x += + ( std::sin( MoverParameters->eAngle * 4.0 ) * Timedelta * EngineShake.scale ) + // fade in with rpm above threshold + * clamp( + ( MoverParameters->enrot - EngineShake.fadein_offset ) * EngineShake.fadein_factor, + 0.0, 1.0 ) + // fade out with rpm above threshold + * interpolate( + 1.0, 0.0, + clamp( + ( MoverParameters->enrot - EngineShake.fadeout_offset ) * EngineShake.fadeout_factor, + 0.0, 1.0 ) ); + } + } + + if( ( HuntingShake.fadein_begin > 0.f ) + && ( true == MoverParameters->TruckHunting ) ) { + // hunting oscillation + HuntingAngle = clamp_circular( HuntingAngle + 4.0 * HuntingShake.frequency * Timedelta * MoverParameters->Vel, 360.0 ); + auto const huntingamount = + interpolate( + 0.0, 1.0, + clamp( + ( MoverParameters->Vel - HuntingShake.fadein_begin ) / ( HuntingShake.fadein_end - HuntingShake.fadein_begin ), + 0.0, 1.0 ) ); + shakevector.x += + ( std::sin( glm::radians( HuntingAngle ) ) * Timedelta * HuntingShake.scale ) + * huntingamount; + IsHunting = ( huntingamount > 0.025 ); + } + + auto const iVel { std::min( GetVelocity(), 150.0 ) }; + if( iVel > 0.5 ) { + // acceleration-driven base shake + shakevector += Math3D::vector3( + -MoverParameters->AccN * Timedelta * 5.0, // highlight side sway + -MoverParameters->AccVert * Timedelta, + -MoverParameters->AccSVBased * Timedelta * 1.25 ); // accent acceleration/deceleration + } + + auto shake { 1.25 * ShakeSpring.ComputateForces( shakevector, ShakeState.offset ) }; + + if( Random( iVel ) > 25.0 ) { + // extra shake at increased velocity + shake += ShakeSpring.ComputateForces( + Math3D::vector3( + ( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.x, + ( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.y, + ( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.z ) +// * (( 200 - DynamicObject->MyTrack->iQualityFlag ) * 0.0075 ) // scale to 75-150% based on track quality + * 1.25, + ShakeState.offset ); + } + shake *= 0.85; + + ShakeState.velocity -= ( shake + ShakeState.velocity * 100 ) * ( BaseShake.jolt_scale.x + BaseShake.jolt_scale.y + BaseShake.jolt_scale.z ) / ( 200 ); + + // McZapkie: + ShakeState.offset += ShakeState.velocity * Timedelta; + if( std::abs( ShakeState.offset.y ) > std::abs( BaseShake.jolt_limit ) ) { + ShakeState.velocity.y = -ShakeState.velocity.y; + } + } + else { // hamowanie rzucania przy spadku FPS + ShakeState.offset -= ShakeState.offset * std::min( Timedelta, 1.0 ); // po tym chyba potrafią zostać jakieś ułamki, które powodują zjazd + } +} + +std::pair +TDynamicObject::shake_angles() const { + + return { + std::atan( ShakeState.velocity.x * BaseShake.angle_scale.x ), + std::atan( ShakeState.velocity.z * BaseShake.angle_scale.z ) }; +} void TDynamicObject::powertrain_sounds::position( glm::vec3 const Location ) { diff --git a/DynObj.h b/DynObj.h index 8fc06a3d..f6308ab7 100644 --- a/DynObj.h +++ b/DynObj.h @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "Button.h" #include "AirCoupler.h" #include "sound.h" +#include "Spring.h" //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -444,8 +445,6 @@ private: exchange_data m_exchange; // state of active load exchange procedure, if any exchange_sounds m_exchangesounds; // sounds associated with the load exchange - Math3D::vector3 modelShake; - bool renderme; // yB - czy renderowac float ModCamRot; int iInventory[ 2 ] { 0, 0 }; // flagi bitowe posiadanych submodeli (np. świateł) @@ -540,6 +539,9 @@ private: void RenderSounds(); inline Math3D::vector3 GetPosition() const { return vPosition; }; + // converts location from vehicle coordinates frame to world frame + inline Math3D::vector3 GetWorldPosition( Math3D::vector3 const &Location ) const { + return vPosition + mMatrix * Location; } // pobranie współrzędnych czoła inline Math3D::vector3 HeadPosition() { return vCoulpler[iDirection ^ 1]; }; @@ -638,6 +640,41 @@ private: double MEDLogTime = 0; double MEDLogInactiveTime = 0; int MEDLogCount = 0; + +// vehicle shaking calculations +// TBD, TODO: make an object out of it +public: +// methods + void update_shake( double const Timedelta ); + std::pair shake_angles() const; +// members + struct baseshake_config { + Math3D::vector3 angle_scale { 0.05, 0.0, 0.1 }; // roll, yaw, pitch + Math3D::vector3 jolt_scale { 0.2, 0.2, 0.1 }; + double jolt_limit { 0.15 }; + } BaseShake; + struct engineshake_config { + float scale { 2.f }; + float fadein_offset { 1.5f }; // 90 rpm + float fadein_factor { 0.3f }; + float fadeout_offset { 10.f }; // 600 rpm + float fadeout_factor { 0.5f }; + } EngineShake; + struct huntingshake_config { + float scale { 1.f }; + float frequency { 1.f }; + float fadein_begin { 0.f }; // effect start speed in km/h + float fadein_end { 0.f }; // full effect speed in km/h + } HuntingShake; + float HuntingAngle { 0.f }; // crude approximation of hunting oscillation; current angle of sine wave + bool IsHunting { false }; + TSpring ShakeSpring; + struct shake_state { + Math3D::vector3 velocity {}; // current shaking vector + Math3D::vector3 offset {}; // overall shake-driven offset from base position + } ShakeState; + + Math3D::vector3 modelShake; }; diff --git a/Train.cpp b/Train.cpp index ddfaee0d..02959338 100644 --- a/Train.cpp +++ b/Train.cpp @@ -365,8 +365,6 @@ TTrain::TTrain() { fPPress = fNPress = 0; // asMessage=""; - pMechShake = Math3D::vector3(0, 0, 0); - vMechMovement = Math3D::vector3(0, 0, 0); pMechOffset = Math3D::vector3(0, 0, 0); fBlinkTimer = 0; fHaslerTimer = 0; @@ -415,15 +413,6 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) DynamicObject->MechInside = true; - MechSpring.Init(125.0); - vMechVelocity = Math3D::vector3(0, 0, 0); - pMechOffset = Math3D::vector3( 0, 0, 0 ); - fMechSpringX = 0.2; - fMechSpringY = 0.2; - fMechSpringZ = 0.1; - fMechMaxSpring = 0.15; - fMechRoll = 0.05; - fMechPitch = 0.1; fMainRelayTimer = 0; // Hunter, do k...y nędzy, ustawiaj wartości początkowe zmiennych! if( false == LoadMMediaFile( DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + ".mmd" ) ) { @@ -4708,138 +4697,25 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com } // cab movement update, fixed step part -void TTrain::UpdateMechPosition(double dt) -{ // Ra: mechanik powinien być telepany niezależnie od pozycji pojazdu - // Ra: trzeba zrobić model bujania głową i wczepić go do pojazdu +void TTrain::UpdateCab() { - // DynamicObject->vFront=DynamicObject->GetDirection(); //to jest już policzone + // Ra: przesiadka, jeśli AI zmieniło kabinę (a człon?)... + if( ( DynamicObject->Mechanik ) // może nie być? + && ( DynamicObject->Mechanik->AIControllFlag ) ) { + + if( iCabn != ( // numer kabiny (-1: kabina B) + DynamicObject->MoverParameters->ActiveCab == -1 ? + 2 : + DynamicObject->MoverParameters->ActiveCab ) ) { - // Ra: tu by się przydało uwzględnić rozkład sił: - // - na postoju horyzont prosto, kabina skosem - // - przy szybkiej jeździe kabina prosto, horyzont pochylony - - Math3D::vector3 shake; - // McZapkie: najpierw policzę pozycję w/m kabiny - - // ABu: rzucamy kabina tylko przy duzym FPS! - // Mala histereza, zeby bez przerwy nie przelaczalo przy FPS~17 - // Granice mozna ustalic doswiadczalnie. Ja proponuje 14:20 - double const iVel = std::min( DynamicObject->GetVelocity(), 150.0 ); - - if( ( false == Global.iSlowMotion ) // musi być pełna prędkość - && ( pMechOffset.y < 4.0 ) ) // Ra 15-01: przy oglądaniu pantografu bujanie przeszkadza - { - Math3D::vector3 shakevector; - if( ( mvOccupied->EngineType == TEngineType::DieselElectric ) - || ( mvOccupied->EngineType == TEngineType::DieselEngine ) ) { - if( std::abs( mvOccupied->enrot ) > 0.0 ) { - // engine vibration - shakevector.x += - ( std::sin( mvOccupied->eAngle * 4.0 ) * dt * EngineShake.scale ) - // fade in with rpm above threshold - * clamp( - ( mvOccupied->enrot - EngineShake.fadein_offset ) * EngineShake.fadein_factor, - 0.0, 1.0 ) - // fade out with rpm above threshold - * interpolate( - 1.0, 0.0, - clamp( - ( mvOccupied->enrot - EngineShake.fadeout_offset ) * EngineShake.fadeout_factor, - 0.0, 1.0 ) ); - } + InitializeCab( + DynamicObject->MoverParameters->ActiveCab, + DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + ".mmd" ); } - - if( ( HuntingShake.fadein_begin > 0.f ) - && ( true == mvOccupied->TruckHunting ) ) { - // hunting oscillation - HuntingAngle = clamp_circular( HuntingAngle + 4.0 * HuntingShake.frequency * dt * mvOccupied->Vel, 360.0 ); - auto const huntingamount = - interpolate( - 0.0, 1.0, - clamp( - ( mvOccupied->Vel - HuntingShake.fadein_begin ) / ( HuntingShake.fadein_end - HuntingShake.fadein_begin ), - 0.0, 1.0 ) ); - shakevector.x += - ( std::sin( glm::radians( HuntingAngle ) ) * dt * HuntingShake.scale ) - * huntingamount; - IsHunting = ( huntingamount > 0.025 ); - } - - if( iVel > 0.5 ) { - // acceleration-driven base shake - shakevector += Math3D::vector3( - -mvOccupied->AccN * dt * 5.0, // highlight side sway - -mvOccupied->AccVert * dt, - -mvOccupied->AccSVBased * dt * 1.25 ); // accent acceleration/deceleration - } - - shake += 1.25 * MechSpring.ComputateForces( shakevector, pMechShake ); - - if( Random( iVel ) > 25.0 ) { - // extra shake at increased velocity - shake += MechSpring.ComputateForces( - Math3D::vector3( - ( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringX, - ( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringY, - ( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringZ ) - * 1.25, - pMechShake ); - // * (( 200 - DynamicObject->MyTrack->iQualityFlag ) * 0.0075 ); // scale to 75-150% based on track quality - } - shake *= 0.85; - - vMechVelocity -= ( shake + vMechVelocity * 100 ) * ( fMechSpringX + fMechSpringY + fMechSpringZ ) / ( 200 ); - - // McZapkie: - pMechShake += vMechVelocity * dt; - if( ( pMechShake.y > fMechMaxSpring ) - || ( pMechShake.y < -fMechMaxSpring ) ) { - vMechVelocity.y = -vMechVelocity.y; - } - // Ra 2015-01: dotychczasowe rzucanie - pMechOffset += vMechMovement * dt; - // ABu011104: 5*pMechShake.y, zeby ladnie pudlem rzucalo :) - pMechPosition = pMechOffset + Math3D::vector3( 1.5 * pMechShake.x, 2.0 * pMechShake.y, 1.5 * pMechShake.z ); } - else { // hamowanie rzucania przy spadku FPS - pMechShake -= pMechShake * std::min( dt, 1.0 ); // po tym chyba potrafią zostać jakieś ułamki, które powodują zjazd - pMechOffset += vMechMovement * dt; - vMechVelocity.y = 0.5 * vMechVelocity.y; - pMechPosition = pMechOffset + Math3D::vector3( pMechShake.x, 5 * pMechShake.y, pMechShake.z ); - } - // numer kabiny (-1: kabina B) - if( DynamicObject->Mechanik ) // może nie być? - if( DynamicObject->Mechanik->AIControllFlag ) // jeśli prowadzi AI - { // Ra: przesiadka, jeśli AI zmieniło kabinę (a człon?)... - if( iCabn != ( DynamicObject->MoverParameters->ActiveCab == -1 ? - 2 : - DynamicObject->MoverParameters->ActiveCab ) ) - InitializeCab( DynamicObject->MoverParameters->ActiveCab, - DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + - ".mmd" ); - } iCabn = ( DynamicObject->MoverParameters->ActiveCab == -1 ? 2 : DynamicObject->MoverParameters->ActiveCab ); - if( !DebugModeFlag ) { // sprawdzaj więzy //Ra: nie tu! - - pMechPosition.x = clamp( pMechPosition.x, Cabine[ iCabn ].CabPos1.x, Cabine[ iCabn ].CabPos2.x ); - pMechPosition.y = clamp( pMechPosition.y, Cabine[ iCabn ].CabPos1.y + 0.5, Cabine[ iCabn ].CabPos2.y + 1.8 ); - pMechPosition.z = clamp( pMechPosition.z, Cabine[ iCabn ].CabPos1.z, Cabine[ iCabn ].CabPos2.z ); - - pMechOffset.x = clamp( pMechOffset.x, Cabine[ iCabn ].CabPos1.x, Cabine[ iCabn ].CabPos2.x ); - pMechOffset.y = clamp( pMechOffset.y, Cabine[ iCabn ].CabPos1.y + 0.5, Cabine[ iCabn ].CabPos2.y + 1.8 ); - pMechOffset.z = clamp( pMechOffset.z, Cabine[ iCabn ].CabPos1.z, Cabine[ iCabn ].CabPos2.z ); - } -}; - -// returns position of the mechanic in the scene coordinates -Math3D::vector3 -TTrain::GetWorldMechPosition() { - - auto position = DynamicObject->mMatrix * pMechPosition; // położenie względem środka pojazdu w układzie scenerii - position += DynamicObject->GetPosition(); - return position; } bool TTrain::Update( double const Deltatime ) @@ -4920,17 +4796,7 @@ bool TTrain::Update( double const Deltatime ) } } - // update driver's position - { - auto Vec = Global.pCamera.Velocity * -2.0;// -7.5 * Timer::GetDeltaRenderTime(); - Vec.y = -Vec.y; - if( mvOccupied->ActiveCab < 0 ) { - Vec *= -1.0f; - Vec.y = -Vec.y; - } - Vec.RotateY( Global.pCamera.Yaw ); - vMechMovement = Vec; - } + UpdateCab(); if (DynamicObject->mdKabina) { // Ra: TODO: odczyty klawiatury/pulpitu nie powinny być uzależnione od istnienia modelu kabiny @@ -6169,7 +6035,7 @@ TTrain::update_sounds( double const Deltatime ) { if( ( false == FreeFlyModeFlag ) && ( false == Global.CabWindowOpen ) && ( DynamicObject->GetVelocity() > 0.5 ) - && ( IsHunting ) ) { + && ( DynamicObject->IsHunting ) ) { update_sounds_runningnoise( rsHuntingNoise ); // modify calculated sound volume by hunting amount @@ -6177,7 +6043,7 @@ TTrain::update_sounds( double const Deltatime ) { interpolate( 0.0, 1.0, clamp( - ( mvOccupied->Vel - HuntingShake.fadein_begin ) / ( HuntingShake.fadein_end - HuntingShake.fadein_begin ), + ( mvOccupied->Vel - DynamicObject->HuntingShake.fadein_begin ) / ( DynamicObject->HuntingShake.fadein_end - DynamicObject->HuntingShake.fadein_begin ), 0.0, 1.0 ) ); rsHuntingNoise.gain( rsHuntingNoise.gain() * huntingamount ); @@ -6489,44 +6355,6 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) rsHuntingNoise.m_amplitudefactor /= ( 1 + mvOccupied->Vmax ); rsHuntingNoise.m_frequencyfactor /= ( 1 + mvOccupied->Vmax ); } - else if (token == "mechspring:") - { - // parametry bujania kamery: - double ks, kd; - parser.getTokens(2, false); - parser - >> ks - >> kd; - MechSpring.Init(ks, kd); - parser.getTokens(6, false); - parser - >> fMechSpringX - >> fMechSpringY - >> fMechSpringZ - >> fMechMaxSpring - >> fMechRoll - >> fMechPitch; - } - else if( token == "enginespring:" ) { - parser.getTokens( 5, false ); - parser - >> EngineShake.scale - >> EngineShake.fadein_offset - >> EngineShake.fadein_factor - >> EngineShake.fadeout_offset - >> EngineShake.fadeout_factor; - // offsets values are provided as rpm for convenience - EngineShake.fadein_offset /= 60.f; - EngineShake.fadeout_offset /= 60.f; - } - else if( token == "huntingspring:" ) { - parser.getTokens( 4, false ); - parser - >> HuntingShake.scale - >> HuntingShake.frequency - >> HuntingShake.fadein_begin - >> HuntingShake.fadein_end; - } } while (token != ""); } @@ -6571,8 +6399,6 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) } // reset view angles pMechViewAngle = { 0.0, 0.0 }; - Global.pCamera.Pitch = pMechViewAngle.x; - Global.pCamera.Yaw = pMechViewAngle.y; bool parse = false; int cabindex = 0; DynamicObject->mdKabina = NULL; // likwidacja wskaźnika na dotychczasową kabinę @@ -6670,9 +6496,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; - +*/ parser.getTokens(); parser >> token; } @@ -6862,14 +6689,6 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) return (token == "none"); } -void TTrain::MechStop() -{ // likwidacja ruchu kamery w kabinie (po powrocie przez [F4]) - pMechPosition = Math3D::vector3(0, 0, 0); - pMechShake = Math3D::vector3(0, 0, 0); - vMechMovement = Math3D::vector3(0, 0, 0); - vMechVelocity = Math3D::vector3(0, 0, 0); // tu zostawały jakieś ułamki, powodujące uciekanie kamery -}; - Math3D::vector3 TTrain::MirrorPosition(bool lewe) { // zwraca współrzędne widoku kamery z lusterka switch (iCabn) @@ -6947,6 +6766,26 @@ void TTrain::DynamicSet(TDynamicObject *d) } }; +// checks whether specified point is within boundaries of the active cab +bool +TTrain::point_inside( Math3D::vector3 const Point ) const { + + return ( Point.x >= Cabine[ iCabn ].CabPos1.x ) && ( Point.x <= Cabine[ iCabn ].CabPos2.x ) + && ( Point.y >= Cabine[ iCabn ].CabPos1.y + 0.5 ) && ( Point.y <= Cabine[ iCabn ].CabPos2.y + 1.8 ) + && ( Point.z >= Cabine[ iCabn ].CabPos1.z ) && ( Point.z <= Cabine[ iCabn ].CabPos2.z ); +} + +Math3D::vector3 +TTrain::clamp_inside( Math3D::vector3 const &Point ) const { + + if( DebugModeFlag ) { return Point; } + + return { + clamp( Point.x, Cabine[ iCabn ].CabPos1.x, Cabine[ iCabn ].CabPos2.x ), + clamp( Point.y, Cabine[ iCabn ].CabPos1.y + 0.5, Cabine[ iCabn ].CabPos2.y + 1.8 ), + clamp( Point.z, Cabine[ iCabn ].CabPos1.z, Cabine[ iCabn ].CabPos2.z ) }; +} + void TTrain::radio_message( sound_source *Message, int const Channel ) { diff --git a/Train.h b/Train.h index bf82e75e..144f05ae 100644 --- a/Train.h +++ b/Train.h @@ -13,7 +13,6 @@ http://mozilla.org/MPL/2.0/. #include "DynObj.h" #include "Button.h" #include "Gauge.h" -#include "Spring.h" #include "sound.h" #include "PyInt.h" #include "command.h" @@ -105,10 +104,8 @@ class TTrain inline Math3D::vector3 GetDirection() { return DynamicObject->VectorFront(); }; inline Math3D::vector3 GetUp() { return DynamicObject->VectorUp(); }; inline std::string GetLabel( TSubModel const *Control ) const { return m_controlmapper.find( Control ); } - void UpdateMechPosition(double dt); - Math3D::vector3 GetWorldMechPosition(); + void UpdateCab(); bool Update( double const Deltatime ); - void MechStop(); void SetLights(); // McZapkie-310302: ladowanie parametrow z pliku bool LoadMMediaFile(std::string const &asFileName); @@ -565,40 +562,6 @@ public: // reszta może by?publiczna // Ra 2013-12: wirtualne "lampki" do odbijania na haslerze w PoKeys TButton btHaslerBrakes; // ciśnienie w cylindrach TButton btHaslerCurrent; // prąd na silnikach -/* - vector3 pPosition; -*/ - Math3D::vector3 pMechOffset; // driverNpos - Math3D::vector3 vMechMovement; - Math3D::vector3 pMechPosition; - Math3D::vector3 pMechShake; - Math3D::vector3 vMechVelocity; - // McZapkie: do poruszania sie po kabinie - // McZapkie: opis kabiny - obszar poruszania sie mechanika oraz zajetosc - TCab Cabine[maxcab + 1]; // przedzial maszynowy, kabina 1 (A), kabina 2 (B) - int iCabn; - TSpring MechSpring; - double fMechSpringX; // McZapkie-250303: parametry bujania - double fMechSpringY; - double fMechSpringZ; - double fMechMaxSpring; - double fMechRoll; - double fMechPitch; - struct engineshake_config { - float scale { 2.f }; - float fadein_offset { 1.5f }; // 90 rpm - float fadein_factor { 0.3f }; - float fadeout_offset { 10.f }; // 600 rpm - float fadeout_factor { 0.5f }; - } EngineShake; - struct huntingshake_config { - float scale { 1.f }; - float frequency { 1.f }; - float fadein_begin { 0.f }; // effect start speed in km/h - float fadein_end { 0.f }; // full effect speed in km/h - } HuntingShake; - float HuntingAngle { 0.f }; // crude approximation of hunting oscillation; current angle of sine wave - bool IsHunting { false }; sound_source dsbReverserKey { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-121211 sound_source dsbNastawnikJazdy { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; @@ -631,8 +594,13 @@ public: // reszta może by?publiczna 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; + // McZapkie: do poruszania sie po kabinie Math3D::vector3 pMechSittingPosition; // ABu 180404 - Math3D::vector3 MirrorPosition(bool lewe); + Math3D::vector3 MirrorPosition( bool lewe ); + Math3D::vector3 pMechOffset; // base position of the driver in the cab glm::vec2 pMechViewAngle { 0.0, 0.0 }; // camera pitch and yaw values, preserved while in external view private: @@ -687,6 +655,9 @@ private: inline TMoverParameters *Occupied() { return mvOccupied; }; inline TMoverParameters const *Occupied() const { return mvOccupied; }; void DynamicSet(TDynamicObject *d); + // checks whether specified point is within boundaries of the active cab + bool point_inside( Math3D::vector3 const Point ) const; + Math3D::vector3 clamp_inside( Math3D::vector3 const &Point ) const; }; //--------------------------------------------------------------------------- diff --git a/drivermode.cpp b/drivermode.cpp index 1bc98da2..d8996095 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -165,12 +165,9 @@ driver_mode::update() { iPause = Global.iPause; } - // fixed step part of the camera update - if( ( simulation::Train != nullptr ) - && ( Camera.Type == TCameraType::tp_Follow ) - && ( false == DebugCameraFlag ) ) { - // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery - simulation::Train->UpdateMechPosition( m_secondaryupdaterate ); + // TODO: generic shake update pass for vehicles within view range + if( Camera.m_owner != nullptr ) { + Camera.m_owner->update_shake( m_secondaryupdaterate ); } m_secondaryupdateaccumulator -= m_secondaryupdaterate; // these should be inexpensive enough we have no cap @@ -247,15 +244,15 @@ driver_mode::update() { void driver_mode::enter() { - Camera.Init(Global.FreeCameraInit[0], Global.FreeCameraInitAngle[0], ( FreeFlyModeFlag ? TCameraType::tp_Free : TCameraType::tp_Follow ) ); - Global.pCamera = Camera; - Global.pDebugCamera = DebugCamera; - TDynamicObject *nPlayerTrain { ( ( Global.asHumanCtrlVehicle != "ghostview" ) ? simulation::Vehicles.find( Global.asHumanCtrlVehicle ) : nullptr ) }; + Camera.Init(Global.FreeCameraInit[0], Global.FreeCameraInitAngle[0], nPlayerTrain ); + Global.pCamera = Camera; + Global.pDebugCamera = DebugCamera; + if (nPlayerTrain) { WriteLog( "Initializing player train, \"" + Global.asHumanCtrlVehicle + "\"" ); @@ -269,14 +266,14 @@ driver_mode::enter() { Application.set_title( Global.AppName + " (" + simulation::Train->Controlled()->Name + " @ " + Global.SceneryFile + ")" ); - FollowView(); + CabView(); } else { Error("Bad init: player train initialization failed"); FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie SafeDelete( simulation::Train ); - Camera.Type = TCameraType::tp_Free; + Camera.m_owner = nullptr; } } else @@ -286,7 +283,7 @@ driver_mode::enter() { Error("Bad scenario: failed to locate player train, \"" + Global.asHumanCtrlVehicle + "\"" ); } FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie - Camera.Type = TCameraType::tp_Free; + Camera.m_owner = nullptr; DebugCamera = Camera; } @@ -398,34 +395,42 @@ driver_mode::update_camera( double const Deltatime ) { nullptr ); if( false == Global.ControlPicking ) { + if( m_input.mouse.button( GLFW_MOUSE_BUTTON_LEFT ) == GLFW_PRESS ) { Camera.Reset(); // likwidacja obrotów - patrzy horyzontalnie na południe - if( controlled && LengthSquared3( controlled->GetPosition() - Camera.Pos ) < ( 1500 * 1500 ) ) { - // gdy bliżej niż 1.5km - Camera.LookAt = controlled->GetPosition(); + if( Camera.m_owner == nullptr ) { + if( controlled && LengthSquared3( controlled->GetPosition() - Camera.Pos ) < ( 1500 * 1500 ) ) { + // gdy bliżej niż 1.5km + Camera.LookAt = controlled->GetPosition(); + } + else { + TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global.pCamera.Pos, 300, false, false ) ); + if( !d ) + d = std::get( simulation::Region->find_vehicle( Global.pCamera.Pos, 1000, false, false ) ); // dalej szukanie, jesli bliżej nie ma + + if( d && pDynamicNearest ) { + // jeśli jakiś jest znaleziony wcześniej + if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) { + d = pDynamicNearest; // jeśli najbliższy nie jest 10 razy bliżej niż + } + } + // poprzedni najbliższy, zostaje poprzedni + if( d ) + pDynamicNearest = d; // zmiana na nowy, jeśli coś znaleziony niepusty + if( pDynamicNearest ) + Camera.LookAt = pDynamicNearest->GetPosition(); + } + Camera.RaLook(); // jednorazowe przestawienie kamery } else { - TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global.pCamera.Pos, 300, false, false ) ); - if( !d ) - d = std::get( simulation::Region->find_vehicle( Global.pCamera.Pos, 1000, false, false ) ); // dalej szukanie, jesli bliżej nie ma - - if( d && pDynamicNearest ) { - // jeśli jakiś jest znaleziony wcześniej - if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) { - d = pDynamicNearest; // jeśli najbliższy nie jest 10 razy bliżej niż - } + if( false == FreeFlyModeFlag ) { + // reset cached view angle in the cab + simulation::Train->pMechViewAngle = { Camera.Angle.x, Camera.Angle.y }; } - // poprzedni najbliższy, zostaje poprzedni - if( d ) - pDynamicNearest = d; // zmiana na nowy, jeśli coś znaleziony niepusty - if( pDynamicNearest ) - Camera.LookAt = pDynamicNearest->GetPosition(); } - if( FreeFlyModeFlag ) - Camera.RaLook(); // jednorazowe przestawienie kamery } else if( m_input.mouse.button( GLFW_MOUSE_BUTTON_RIGHT ) == GLFW_PRESS ) { - FollowView( false ); // bez wyciszania dźwięków + CabView(); } } @@ -439,28 +444,49 @@ driver_mode::update_camera( double const Deltatime ) { Global.ZoomFactor = std::max( 1.0f, Global.ZoomFactor - 15.0f * static_cast( Deltatime ) ); } - if( DebugCameraFlag ) { DebugCamera.Update(); } - else { Camera.Update(); } // uwzględnienie ruchu wywołanego klawiszami - - if( ( false == FreeFlyModeFlag ) - && ( false == Global.CabWindowOpen ) - && ( simulation::Train != nullptr ) ) { - // cache cab camera view angles in case of view type switch - simulation::Train->pMechViewAngle = { Camera.Pitch, Camera.Yaw }; - } + // uwzględnienie ruchu wywołanego klawiszami + if( false == DebugCameraFlag ) { + // regular camera + if( ( false == FreeFlyModeFlag ) + && ( false == Global.CabWindowOpen ) ) { + // if in cab potentially alter camera placement based on changes in train object + Camera.m_owneroffset = simulation::Train->pMechOffset; + Camera.Angle.x = simulation::Train->pMechViewAngle.x; + Camera.Angle.y = simulation::Train->pMechViewAngle.y; + } + Camera.Update(); + + if( false == FreeFlyModeFlag ) { + // keep the camera within cab boundaries + Camera.m_owneroffset = simulation::Train->clamp_inside( Camera.m_owneroffset ); + } + + if( ( false == FreeFlyModeFlag ) + && ( false == Global.CabWindowOpen ) ) { + // cache cab camera in case of view type switch + simulation::Train->pMechViewAngle = { Camera.Angle.x, Camera.Angle.y }; + simulation::Train->pMechOffset = Camera.m_owneroffset; + } + } + else { + // debug camera + DebugCamera.Update(); + } + // reset window state, it'll be set again if applicable in a check below Global.CabWindowOpen = false; if( ( simulation::Train != nullptr ) - && ( Camera.Type == TCameraType::tp_Follow ) + && ( Camera.m_owner != nullptr ) && ( false == DebugCameraFlag ) ) { // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery /* auto tempangle = controlled->VectorFront() * ( controlled->MoverParameters->ActiveCab == -1 ? -1 : 1 ); double modelrotate = atan2( -tempangle.x, tempangle.z ); */ - if( ( true == Global.ctrlState ) + if( ( false == FreeFlyModeFlag ) + && ( true == Global.ctrlState ) && ( ( m_input.keyboard.key( GLFW_KEY_LEFT ) != GLFW_RELEASE ) || ( m_input.keyboard.key( GLFW_KEY_RIGHT ) != GLFW_RELEASE ) ) ) { // jeśli lusterko lewe albo prawe (bez rzucania na razie) @@ -469,9 +495,11 @@ driver_mode::update_camera( double const Deltatime ) { auto const lr { m_input.keyboard.key( GLFW_KEY_LEFT ) != GLFW_RELEASE }; // Camera.Yaw powinno być wyzerowane, aby po powrocie patrzeć do przodu Camera.Pos = controlled->GetPosition() + simulation::Train->MirrorPosition( lr ); // pozycja lusterka - Camera.Yaw = 0; // odchylenie na bok od Camera.LookAt - if( simulation::Train->Occupied()->ActiveCab == 0 ) - Camera.LookAt = Camera.Pos - simulation::Train->GetDirection(); // gdy w korytarzu + Camera.Angle.y = 0; // odchylenie na bok od Camera.LookAt + if( simulation::Train->Occupied()->ActiveCab == 0 ) { + // gdy w korytarzu + Camera.LookAt = Camera.Pos - simulation::Train->GetDirection(); + } else if( Global.shiftState ) { // patrzenie w bok przez szybę Camera.LookAt = Camera.Pos - ( lr ? -1 : 1 ) * controlled->VectorLeft() * simulation::Train->Occupied()->ActiveCab; @@ -480,25 +508,47 @@ driver_mode::update_camera( double const Deltatime ) { // ale bez odbicia Camera.LookAt = Camera.Pos - simulation::Train->GetDirection() * simulation::Train->Occupied()->ActiveCab; //-1 albo 1 } - Camera.Roll = std::atan( simulation::Train->pMechShake.x * simulation::Train->fMechRoll ); // hustanie kamery na boki - Camera.Pitch = 0.5 * std::atan( simulation::Train->vMechVelocity.z * simulation::Train->fMechPitch ); // hustanie kamery przod tyl + auto const shakeangles { simulation::Train->Dynamic()->shake_angles() }; + Camera.Angle.x = 0.5 * shakeangles.second; // hustanie kamery przod tyl + Camera.Angle.z = shakeangles.first; // hustanie kamery na boki +/* + Camera.Roll = std::atan( simulation::Train->pMechShake.x * simulation::Train->BaseShake.angle_scale.x ); // hustanie kamery na boki + Camera.Pitch = 0.5 * std::atan( simulation::Train->vMechVelocity.z * simulation::Train->BaseShake.angle_scale.z ); // hustanie kamery przod tyl +*/ Camera.vUp = controlled->VectorUp(); } else { // patrzenie standardowe - // potentially restore view angle after returning from external view - // TODO: mirror view toggle as separate method - Camera.Pitch = simulation::Train->pMechViewAngle.x; - Camera.Yaw = simulation::Train->pMechViewAngle.y; + if( false == FreeFlyModeFlag ) { + // potentially restore view angle after returning from external view + // TODO: mirror view toggle as separate method + Camera.Angle.x = simulation::Train->pMechViewAngle.x; + Camera.Angle.y = simulation::Train->pMechViewAngle.y; + } + + auto const shakescale { FreeFlyModeFlag ? 5.0 : 1.0 }; + auto shakencamerapos { + Camera.m_owneroffset + + shakescale * Math3D::vector3( + 1.5 * Camera.m_owner->ShakeState.offset.x, + 2.0 * Camera.m_owner->ShakeState.offset.y, + 1.5 * Camera.m_owner->ShakeState.offset.z ) }; + + Camera.Pos = ( + Camera.m_owner->GetWorldPosition ( + FreeFlyModeFlag ? + shakencamerapos : // TODO: vehicle collision box for the external vehicle camera + simulation::Train->clamp_inside( shakencamerapos ) ) ); - Camera.Pos = simulation::Train->GetWorldMechPosition(); // Train.GetPosition1(); if( !Global.iPause ) { // podczas pauzy nie przeliczać kątów przypadkowymi wartościami - // hustanie kamery na boki - Camera.Roll = atan( simulation::Train->vMechVelocity.x * simulation::Train->fMechRoll ); - // hustanie kamery przod tyl - // Ra: tu jest uciekanie kamery w górę!!! - Camera.Pitch -= 0.5 * atan( simulation::Train->vMechVelocity.z * simulation::Train->fMechPitch ); + auto const shakeangles { Camera.m_owner->shake_angles() }; + Camera.Angle.x -= 0.5 * shakeangles.second; // hustanie kamery przod tyl + Camera.Angle.z = shakeangles.first; // hustanie kamery na boki +/* + Camera.Roll = std::atan( simulation::Train->vMechVelocity.x * simulation::Train->BaseShake.angle_scale.x ); // hustanie kamery na boki + Camera.Pitch -= 0.5 * atan( simulation::Train->vMechVelocity.z * simulation::Train->BaseShake.angle_scale.z ); // hustanie kamery przod tyl +*/ } /* // ABu011104: rzucanie pudlem @@ -513,10 +563,19 @@ driver_mode::update_camera( double const Deltatime ) { Controlled->ABuSetModelShake( temp ); // ABu: koniec rzucania */ - if( simulation::Train->Occupied()->ActiveCab == 0 ) - Camera.LookAt = simulation::Train->GetWorldMechPosition() + simulation::Train->GetDirection() * 5.0; // gdy w korytarzu - else // patrzenie w kierunku osi pojazdu, z uwzględnieniem kabiny - Camera.LookAt = simulation::Train->GetWorldMechPosition() + simulation::Train->GetDirection() * 5.0 * simulation::Train->Occupied()->ActiveCab; //-1 albo 1 + if( simulation::Train->Occupied()->ActiveCab == 0 ) { + // gdy w korytarzu + Camera.LookAt = + Camera.m_owner->GetWorldPosition( Camera.m_owneroffset ) + + simulation::Train->GetDirection() * 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 + * simulation::Train->Occupied()->ActiveCab; //-1 albo 1 + } Camera.vUp = simulation::Train->GetUp(); } } @@ -591,9 +650,7 @@ driver_mode::OnKeyDown(int cKey) { && ( Global.FreeCameraInit[ i ].z == 0.0 ) ) { // jeśli kamera jest w punkcie zerowym, zapamiętanie współrzędnych i kątów Global.FreeCameraInit[ i ] = Camera.Pos; - Global.FreeCameraInitAngle[ i ].x = Camera.Pitch; - Global.FreeCameraInitAngle[ i ].y = Camera.Yaw; - Global.FreeCameraInitAngle[ i ].z = Camera.Roll; + Global.FreeCameraInitAngle[ i ] = Camera.Angle; // logowanie, żeby można było do scenerii przepisać WriteLog( "camera " + std::to_string( Global.FreeCameraInit[ i ].x ) + " " @@ -607,7 +664,7 @@ driver_mode::OnKeyDown(int cKey) { else // również przeskakiwanie { // Ra: to z tą kamerą (Camera.Pos i Global.pCameraPosition) jest trochę bez sensu Global.pCamera.Pos = Global.FreeCameraInit[ i ]; // nowa pozycja dla generowania obiektów - Camera.Init( Global.FreeCameraInit[ i ], Global.FreeCameraInitAngle[ i ], TCameraType::tp_Free ); // przestawienie + Camera.Init( Global.FreeCameraInit[ i ], Global.FreeCameraInitAngle[ i ], nullptr ); // przestawienie } } } @@ -633,8 +690,9 @@ driver_mode::OnKeyDown(int cKey) { } case GLFW_KEY_F4: { - - InOutKey( !Global.shiftState ); // distant view with Shift, short distance step out otherwise + + if( Global.shiftState ) { ExternalView(); } // with Shift, cycle through external views + else { InOutKey(); } // without, step out of the cab or return to it break; } case GLFW_KEY_F5: { @@ -823,52 +881,150 @@ driver_mode::DistantView( bool const Near ) { + Math3D::vector3( -10.0 * left.x, 1.6, -10.0 * left.z ); } + Camera.m_owner = nullptr; Camera.LookAt = vehicle->GetPosition(); Camera.RaLook(); // jednorazowe przestawienie kamery } -// ustawienie śledzenia pojazdu void -driver_mode::FollowView(bool wycisz) { - - Camera.Reset(); // likwidacja obrotów - patrzy horyzontalnie na południe +driver_mode::ExternalView() { auto *train { simulation::Train }; + if( train == nullptr ) { return; } - if (train != nullptr ) // jest pojazd do prowadzenia? - { - if (FreeFlyModeFlag) - { // jeżeli poza kabiną, przestawiamy w jej okolicę - OK - // wyłączenie trzęsienia na siłę? - train->Dynamic()->ABuSetModelShake( {} ); + auto *vehicle { train->Dynamic() }; - DistantView(); // przestawienie kamery + // disable detailed cab in external view modes + vehicle->bDisplayCab = false; + + if( true == m_externalview ) { + // we're already in some external view mode, so select next one on the list + m_externalviewmode = clamp_circular( ++m_externalviewmode, static_cast( view::count_ ) ); + } + + FreeFlyModeFlag = true; + m_externalview = true; + + Camera.Reset(); + // configure camera placement for the selected view mode + switch( m_externalviewmode ) { + case view::consistfront: { + // bind camera with the vehicle + auto *owner { vehicle->Mechanik->Vehicle( side::front ) }; + + Camera.m_owner = owner; + + 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.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 + + break; } - else { - Camera.Pos = train->pMechPosition; - // potentially restore cached camera angles - Camera.Pitch = train->pMechViewAngle.x; - Camera.Yaw = train->pMechViewAngle.y; + case view::consistrear: { + // bind camera with the vehicle + auto *owner { vehicle->Mechanik->Vehicle( side::rear ) }; - Camera.Roll = std::atan(train->pMechShake.x * train->fMechRoll); // hustanie kamery na boki - Camera.Pitch -= 0.5 * std::atan(train->vMechVelocity.z * train->fMechPitch); // hustanie kamery przod tyl + Camera.m_owner = owner; - if( train->Occupied()->ActiveCab == 0 ) { - Camera.LookAt = - train->pMechPosition - + train->GetDirection() * 5.0; - } - else { - // patrz w strone wlasciwej kabiny - Camera.LookAt = - train->pMechPosition - + train->GetDirection() * 5.0 * train->Occupied()->ActiveCab; - } - train->pMechOffset = train->pMechSittingPosition; + 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 ) ); + + 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 + break; + } + case view::bogie: { + auto *owner { vehicle->Mechanik->Vehicle( side::front ) }; + + Camera.m_owner = owner; + + 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 ) ); + + 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 + + break; + } + case view::driveby: { + DistantView( false ); + break; + } + default: { + break; } } - else - DistantView(); +} + +// ustawienie śledzenia pojazdu +void +driver_mode::CabView() { + + // TODO: configure owner and camera placement depending on the view mode + if( true == FreeFlyModeFlag ) { return; } + + auto *train { simulation::Train }; + if( train == nullptr ) { return; } + + m_externalview = false; + + // likwidacja obrotów - patrzy horyzontalnie na południe + Camera.Reset(); + + // bind camera with the vehicle + Camera.m_owner = train->Dynamic(); + // potentially restore cached camera setup + Camera.m_owneroffset = train->pMechSittingPosition; + Camera.Angle.x = train->pMechViewAngle.x; + Camera.Angle.y = train->pMechViewAngle.y; + + auto const shakeangles { Camera.m_owner->shake_angles() }; + Camera.Angle.x -= 0.5 * shakeangles.second; // hustanie kamery przod tyl + Camera.Angle.z = shakeangles.first; // hustanie kamery na boki + + if( train->Occupied()->ActiveCab == 0 ) { + Camera.LookAt = + Camera.m_owner->GetWorldPosition( Camera.m_owneroffset ) + + Camera.m_owner->VectorFront() * 5.0; + } + else { + // patrz w strone wlasciwej kabiny + Camera.LookAt = + Camera.m_owner->GetWorldPosition( Camera.m_owneroffset ) + + Camera.m_owner->VectorFront() * 5.0 + * Camera.m_owner->MoverParameters->ActiveCab; + } + train->pMechOffset = Camera.m_owneroffset; } void @@ -925,43 +1081,41 @@ driver_mode::ChangeDynamic() { if( false == FreeFlyModeFlag ) { vehicle->bDisplayCab = true; vehicle->ABuSetModelShake( {} ); // zerowanie przesunięcia przed powrotem? - train->MechStop(); - FollowView(); // na pozycję mecha + CabView(); // na pozycję mecha } Global.changeDynObj = nullptr; } void -driver_mode::InOutKey( bool const Near ) +driver_mode::InOutKey() { // przełączenie widoku z kabiny na zewnętrzny i odwrotnie FreeFlyModeFlag = !FreeFlyModeFlag; // zmiana widoku auto *train { simulation::Train }; + if( train == nullptr ) { + FreeFlyModeFlag = true; // nadal poza kabiną + return; + } + + auto *vehicle { train->Dynamic() }; + if (FreeFlyModeFlag) { // jeżeli poza kabiną, przestawiamy w jej okolicę - OK - if (train) { - // cache current cab position so there's no need to set it all over again after each out-in switch - train->pMechSittingPosition = train->pMechOffset; + // cache current cab position so there's no need to set it all over again after each out-in switch + train->pMechSittingPosition = train->pMechOffset; + + vehicle->bDisplayCab = false; + DistantView( true ); - train->Dynamic()->bDisplayCab = false; - DistantView( Near ); - } DebugCamera = Camera; } - else - { // jazda w kabinie - if (train) - { - train->Dynamic()->bDisplayCab = true; - // zerowanie przesunięcia przed powrotem? - train->Dynamic()->ABuSetModelShake( { 0, 0, 0 } ); - train->MechStop(); - FollowView(); // na pozycję mecha - train->UpdateMechPosition( m_secondaryupdaterate ); - } - else - FreeFlyModeFlag = true; // nadal poza kabiną + else { + // jazda w kabinie + // zerowanie przesunięcia przed powrotem? + vehicle->ABuSetModelShake( { 0, 0, 0 } ); + vehicle->bDisplayCab = true; + CabView(); // na pozycję mecha } // update window title to reflect the situation Application.set_title( Global.AppName + " (" + ( train != nullptr ? train->Occupied()->Name : "" ) + " @ " + Global.SceneryFile + ")" ); diff --git a/drivermode.h b/drivermode.h index 73a788d8..19ec4493 100644 --- a/drivermode.h +++ b/drivermode.h @@ -52,6 +52,14 @@ public: private: // types + enum view { + consistfront, + consistrear, + bogie, + driveby, + count_ + }; + struct drivermode_input { gamepad_input gamepad; @@ -70,8 +78,9 @@ private: // handles vehicle change flag void OnKeyDown( int cKey ); void ChangeDynamic(); - void InOutKey( bool const Near = true ); - void FollowView( bool wycisz = true ); + void InOutKey(); + void CabView(); + void ExternalView(); void DistantView( bool const Near = false ); void set_picking( bool const Picking ); @@ -80,6 +89,8 @@ private: std::array KeyEvents { nullptr }; // eventy wyzwalane z klawiaury TCamera Camera; TCamera DebugCamera; + int m_externalviewmode { view::consistfront }; // selected external view mode + bool m_externalview { false }; 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/driveruipanels.cpp b/driveruipanels.cpp index 54070ed1..d8ee2ccb 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -852,10 +852,10 @@ debug_panel::update_section_camera( std::vector &Output ) { textline = "Azimuth: " - + to_string( 180.0 - glm::degrees( camera.Yaw ), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód + + to_string( 180.0 - glm::degrees( camera.Angle.y ), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód + " " + std::string( "S SEE NEN NWW SW" ) - .substr( 0 + 2 * floor( fmod( 8 + ( camera.Yaw + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); + .substr( 0 + 2 * floor( fmod( 8 + ( camera.Angle.y + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); Output.emplace_back( textline, Global.UITextColor ); } diff --git a/editormode.cpp b/editormode.cpp index 04bf361e..30bb12cf 100644 --- a/editormode.cpp +++ b/editormode.cpp @@ -43,7 +43,7 @@ editor_mode::editor_mode() { bool editor_mode::init() { - Camera.Init( { 0, 15, 0 }, { glm::radians( -30.0 ), glm::radians( 180.0 ), 0 }, TCameraType::tp_Free ); + Camera.Init( { 0, 15, 0 }, { glm::radians( -30.0 ), glm::radians( 180.0 ), 0 }, nullptr ); return m_input.init(); } diff --git a/version.h b/version.h index e0c28869..84844c62 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 1016 -#define VERSION_REVISION 1 +#define VERSION_MINOR 1030 +#define VERSION_REVISION 0