16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-23 06:19:18 +02:00

audio subsystem: completed base functionality of the renderer, external and engine sounds moved from cab to vehicle, minor fixes to various sound-related methods

This commit is contained in:
tmj-fstate
2017-11-29 01:50:57 +01:00
parent bc43c21174
commit 3afff7c3ab
27 changed files with 2488 additions and 1929 deletions

View File

@@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/.
#include "Model3d.h" #include "Model3d.h"
#include "Console.h" #include "Console.h"
#include "logs.h" #include "logs.h"
#include "renderer.h"
void TButton::Clear(int i) void TButton::Clear(int i)
{ {
@@ -24,8 +25,8 @@ void TButton::Clear(int i)
Update(); // kasowanie bitu Feedback, o ile jakiś ustawiony Update(); // kasowanie bitu Feedback, o ile jakiś ustawiony
}; };
void TButton::Init(std::string const &asName, TModel3d *pModel, bool bNewOn) void TButton::Init( std::string const &asName, TModel3d *pModel, bool bNewOn ) {
{
if( pModel == nullptr ) { return; } if( pModel == nullptr ) { return; }
pModelOn = pModel->GetFromName( asName + "_on" ); pModelOn = pModel->GetFromName( asName + "_on" );
@@ -34,7 +35,7 @@ void TButton::Init(std::string const &asName, TModel3d *pModel, bool bNewOn)
Update(); Update();
}; };
void TButton::Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2) { void TButton::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pModel1, TModel3d *pModel2 ) {
std::string submodelname; std::string submodelname;
@@ -52,25 +53,32 @@ void TButton::Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2) {
} }
} }
// bind defined sounds with the button owner
m_soundfxincrease.owner( Owner );
m_soundfxdecrease.owner( Owner );
if( pModel1 ) { if( pModel1 ) {
// poszukiwanie submodeli w modelu // poszukiwanie submodeli w modelu
Init( submodelname, pModel1, false ); Init( submodelname, pModel1, false );
if( ( pModelOn != nullptr ) || ( pModelOff != nullptr ) ) {
// we got our models, bail out
return;
}
} }
if( pModel2 ) { if( ( pModelOn == nullptr )
&& ( pModelOff == nullptr )
&& ( pModel2 != nullptr ) ) {
// poszukiwanie submodeli w modelu // poszukiwanie submodeli w modelu
Init( submodelname, pModel2, false ); Init( submodelname, pModel2, false );
if( ( pModelOn != nullptr ) || ( pModelOff != nullptr ) ) {
// we got our models, bail out
return;
}
} }
// if we failed to locate even one state submodel, cry
ErrorLog( "Failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + pModel1->NameGet() + "\"" ); if( ( pModelOn == nullptr )
}; && ( pModelOff == nullptr ) ) {
// if we failed to locate even one state submodel, cry
ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + pModel1->NameGet() + "\"" );
}
// pass submodel location to defined sounds
auto const offset { model_offset() };
m_soundfxincrease.offset( offset );
m_soundfxdecrease.offset( offset );
}
bool bool
TButton::Load_mapping( cParser &Input ) { TButton::Load_mapping( cParser &Input ) {
@@ -85,6 +93,22 @@ TButton::Load_mapping( cParser &Input ) {
return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized
} }
// returns offset of submodel associated with the button from the model centre
glm::vec3
TButton::model_offset() const {
auto const
submodel { (
pModelOn ? pModelOn :
pModelOff ? pModelOff :
nullptr ) };
return (
submodel != nullptr ?
submodel->offset( 1.f ) :
glm::vec3() );
}
void void
TButton::Turn( bool const State ) { TButton::Turn( bool const State ) {

View File

@@ -12,18 +12,28 @@ http://mozilla.org/MPL/2.0/.
#include "Classes.h" #include "Classes.h"
#include "sound.h" #include "sound.h"
class TButton // animacja dwustanowa, włącza jeden z dwóch submodeli (jednego z nich może nie być)
{ // animacja dwustanowa, włącza jeden z dwóch submodeli (jednego class TButton {
// z nich może nie być)
private: public:
TSubModel // methods
*pModelOn { nullptr }, TButton() = default;
*pModelOff { nullptr }; // submodel dla stanu załączonego i wyłączonego void Clear(int const i = -1);
bool m_state { false }; inline void FeedbackBitSet( int const i ) {
bool const *bData { nullptr }; iFeedbackBit = 1 << i; };
int iFeedbackBit { 0 }; // Ra: bit informacji zwrotnej, do wyprowadzenia na pulpit void Turn( bool const State );
sound_source m_soundfxincrease; // sound associated with increasing control's value inline
sound_source m_soundfxdecrease; // sound associated with decreasing control's value bool Active() {
return ( ( pModelOn != nullptr )
|| ( pModelOff != nullptr ) ); }
void Update();
void Init( std::string const &asName, TModel3d *pModel, bool bNewOn = false );
void Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pModel1, TModel3d *pModel2 = nullptr );
void AssignBool(bool const *bValue);
// returns offset of submodel associated with the button from the model centre
glm::vec3 model_offset() const;
private:
// methods // methods
// imports member data pair from the config file // imports member data pair from the config file
bool bool
@@ -32,20 +42,15 @@ class TButton
void void
play(); play();
public: // members
TButton() = default; TSubModel
void Clear(int const i = -1); *pModelOn { nullptr },
inline void FeedbackBitSet(int const i) { *pModelOff { nullptr }; // submodel dla stanu załączonego i wyłączonego
iFeedbackBit = 1 << i; }; bool m_state { false };
void Turn( bool const State ); bool const *bData { nullptr };
inline int iFeedbackBit { 0 }; // Ra: bit informacji zwrotnej, do wyprowadzenia na pulpit
bool Active() { sound_source m_soundfxincrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with increasing control's value
return ( ( pModelOn != nullptr ) sound_source m_soundfxdecrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with decreasing control's value
|| ( pModelOff != nullptr ) ); }
void Update();
void Init(std::string const &asName, TModel3d *pModel, bool bNewOn = false);
void Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2 = NULL);
void AssignBool(bool const *bValue);
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@@ -998,8 +998,11 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
if (go == cm_Unknown) // jeśli nie było komendy wcześniej if (go == cm_Unknown) // jeśli nie było komendy wcześniej
go = cm_Ready; // gotów do odjazdu z W4 (semafor może go = cm_Ready; // gotów do odjazdu z W4 (semafor może
// zatrzymać) // zatrzymać)
if (false == tsGuardSignal->empty()) // jeśli mamy głos kierownika, to odegrać if( ( tsGuardSignal != nullptr )
&& ( false == tsGuardSignal->empty() ) ); {
// jeśli mamy głos kierownika, to odegrać
iDrivigFlags |= moveGuardSignal; iDrivigFlags |= moveGuardSignal;
}
continue; // nie analizować prędkości continue; // nie analizować prędkości
} // koniec startu z zatrzymania } // koniec startu z zatrzymania
} // koniec obsługi początkowych stacji } // koniec obsługi początkowych stacji
@@ -2577,6 +2580,10 @@ bool TController::IncSpeed()
if (tsGuardSignal->GetStatus() & DSBSTATUS_PLAYING) // jeśli gada, to nie jedziemy if (tsGuardSignal->GetStatus() & DSBSTATUS_PLAYING) // jeśli gada, to nie jedziemy
return false; return false;
#else #else
if( ( tsGuardSignal != nullptr )
&& ( true == tsGuardSignal->is_playing() ) ) {
return false;
}
#endif #endif
bool OK = true; bool OK = true;
if( ( iDrivigFlags & moveDoorOpened ) if( ( iDrivigFlags & moveDoorOpened )
@@ -2590,7 +2597,7 @@ bool TController::IncSpeed()
} }
if( true == mvOccupied->DepartureSignal ) { if( true == mvOccupied->DepartureSignal ) {
// shut off departure warning // shut off departure warning
mvOccupied->DepartureSignal = false; mvOccupied->signal_departure( false );
} }
if (mvControlling->SlippingWheels) if (mvControlling->SlippingWheels)
return false; // jak poślizg, to nie przyspieszamy return false; // jak poślizg, to nie przyspieszamy
@@ -2984,7 +2991,7 @@ void TController::Doors(bool what)
if( ( true == mvOccupied->DoorClosureWarning ) if( ( true == mvOccupied->DoorClosureWarning )
&& ( false == mvOccupied->DepartureSignal ) && ( false == mvOccupied->DepartureSignal )
&& ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) {
mvOccupied->DepartureSignal = true; // załącenie bzyczka mvOccupied->signal_departure( true ); // załącenie bzyczka
fActionTime = -3.0 - 0.1 * Random( 10 ); // 3-4 second wait fActionTime = -3.0 - 0.1 * Random( 10 ); // 3-4 second wait
} }
if( fActionTime > -0.5 ) { if( fActionTime > -0.5 ) {
@@ -3094,6 +3101,11 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
pVehicle->GetPosition().x, pVehicle->GetPosition().y, pVehicle->GetPosition().z, pVehicle->GetPosition().x, pVehicle->GetPosition().y, pVehicle->GetPosition().z,
false); false);
#else #else
tsGuardSignal = new sound_source( sound_placement::external );
tsGuardSignal->deserialize( NewCommand, sound_type::single );
tsGuardSignal->owner( pVehicle );
// place virtual conductor some distance away
tsGuardSignal->offset( { pVehicle->MoverParameters->Dim.W * -0.75f, 1.7f, fLength * -0.25f } );
#endif #endif
iGuardRadio = 0; // nie przez radio iGuardRadio = 0; // nie przez radio
} }
@@ -3109,6 +3121,12 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
pVehicle->GetPosition().x, pVehicle->GetPosition().y, pVehicle->GetPosition().z, pVehicle->GetPosition().x, pVehicle->GetPosition().y, pVehicle->GetPosition().z,
false); false);
#else #else
tsGuardSignal = new sound_source( sound_placement::external );
tsGuardSignal->deserialize( NewCommand, sound_type::single );
tsGuardSignal->owner( pVehicle );
// command will be transmitted by radio
// TODO: put the exact location in the proper cab
tsGuardSignal->offset( { 0.f, 1.f, pVehicle->MoverParameters->Dim.L * 0.75f } );
#endif #endif
iGuardRadio = iRadioChannel; iGuardRadio = iRadioChannel;
} }
@@ -4475,43 +4493,48 @@ TController::UpdateSituation(double dt) {
// jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w // jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w
// składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić // składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić
if (iDrivigFlags & moveGuardSignal) if (iDrivigFlags & moveGuardSignal)
{ // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas { // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas stania
// stania
iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać
if( iDrivigFlags & moveDoorOpened ) // jeśli drzwi otwarte if( ( iDrivigFlags & moveDoorOpened )
if( !mvOccupied && ( false == mvOccupied->DoorOpenCtrl ) ) {
->DoorOpenCtrl ) // jeśli drzwi niesterowane przez maszynistę // jeśli drzwi otwarte, niesterowane przez maszynistę
Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika
#ifdef EU07_USE_OLD_SOUNDCODE
tsGuardSignal->Stop();
#else
#endif
// w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy
// bezpośrednim
// albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w
// pobliżu, a drugi radiowy, słyszalny w innych lokomotywach
// na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić
// obsługę kanałów radiowych itd.
if( !iGuardRadio ) {
// jeśli nie przez radio
#ifdef EU07_USE_OLD_SOUNDCODE
tsGuardSignal->Play( 1.0, 0, !FreeFlyModeFlag, pVehicle->GetPosition() ); // dla true jest głośniej
#else
#endif
} }
else { if( tsGuardSignal != nullptr ) {
// if (iGuardRadio==iRadioChannel) //zgodność kanału
// if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu
// (albo może mieć radio przenośne) - kierownik mógłby powtarzać
// przy braku reakcji
if( SquareMagnitude( pVehicle->GetPosition() - Global::pCameraPosition ) < 2000 * 2000 ) {
// w odległości mniejszej niż 2km
#ifdef EU07_USE_OLD_SOUNDCODE #ifdef EU07_USE_OLD_SOUNDCODE
tsGuardSignal->Play( 1.0, 0, true, pVehicle->GetPosition() ); // dźwięk niby przez radio tsGuardSignal->Stop();
#else #else
tsGuardSignal->stop();
#endif #endif
// w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy
// bezpośrednim
// albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w
// pobliżu, a drugi radiowy, słyszalny w innych lokomotywach
// na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić
// obsługę kanałów radiowych itd.
if( !iGuardRadio ) {
// jeśli nie przez radio
#ifdef EU07_USE_OLD_SOUNDCODE
tsGuardSignal->Play( 1.0, 0, !FreeFlyModeFlag, pVehicle->GetPosition() ); // dla true jest głośniej
#else
tsGuardSignal->play( sound_flags::exclusive );
#endif
}
else {
// if (iGuardRadio==iRadioChannel) //zgodność kanału
// if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu
// (albo może mieć radio przenośne) - kierownik mógłby powtarzać
// przy braku reakcji
if( SquareMagnitude( pVehicle->GetPosition() - Global::pCameraPosition ) < 2000 * 2000 ) {
// w odległości mniejszej niż 2km
#ifdef EU07_USE_OLD_SOUNDCODE
tsGuardSignal->Play( 1.0, 0, true, pVehicle->GetPosition() ); // dźwięk niby przez radio
#else
// TODO: proper system for sending/receiving radio messages
tsGuardSignal->play( sound_flags::exclusive );
#endif
}
} }
} }
} }

1966
DynObj.cpp

File diff suppressed because it is too large Load Diff

131
DynObj.h
View File

@@ -258,12 +258,33 @@ private:
TSubModel *smWiper; // wycieraczka (poniekąd też wajcha) TSubModel *smWiper; // wycieraczka (poniekąd też wajcha)
// Ra: koneic animacji do ogarnięcia // Ra: koneic animacji do ogarnięcia
private: private:
void ABuLittleUpdate(double ObjSqrDist); // types
bool btnOn; // ABu: czy byly uzywane buttony, jesli tak, to po renderingu wylacz struct coupler_sounds {
// bo ten sam model moze byc jeszcze wykorzystany przez inny obiekt! sound_source dsbCouplerAttach { sound_placement::external }; // moved from cab
double ComputeRadius( Math3D::vector3 p1, Math3D::vector3 p2, Math3D::vector3 p3, Math3D::vector3 p4); sound_source dsbCouplerDetach { sound_placement::external }; // moved from cab
sound_source dsbCouplerStretch { sound_placement::external }; // moved from cab
sound_source dsbBufferClamp { sound_placement::external }; // moved from cab
};
struct pantograph_sounds {
sound_source sPantUp { sound_placement::external };
sound_source sPantDown { sound_placement::external };
};
struct door_sounds {
sound_source rsDoorOpen{ sound_placement::general }; // Ra: przeniesione z kabiny
sound_source rsDoorClose{ sound_placement::general };
};
// methods
void ABuLittleUpdate(double ObjSqrDist);
double ComputeRadius( Math3D::vector3 p1, Math3D::vector3 p2, Math3D::vector3 p3, Math3D::vector3 p4);
void ABuBogies();
void ABuModelRoll();
void TurnOff();
// members
TButton btCoupler1; // sprzegi TButton btCoupler1; // sprzegi
TButton btCoupler2; TButton btCoupler2;
TAirCoupler btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki TAirCoupler btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki
@@ -303,44 +324,55 @@ private:
double dRailLength; double dRailLength;
double dRailPosition[MaxAxles]; // licznik pozycji osi w/m szyny double dRailPosition[MaxAxles]; // licznik pozycji osi w/m szyny
double dWheelsPosition[MaxAxles]; // pozycja osi w/m srodka pojazdu double dWheelsPosition[MaxAxles]; // pozycja osi w/m srodka pojazdu
sound_source rsStukot[MaxAxles]; // dzwieki poszczegolnych osi //McZapkie-270202 std::vector<sound_source> rsStukot; // dzwieki poszczegolnych osi //McZapkie-270202
sound_source rsSilnik; // McZapkie-010302 - silnik // engine sounds
sound_source rsWentylator; // McZapkie-030302 sound_source dsbDieselIgnition { sound_placement::engine }; // moved from cab
sound_source rsPisk; // McZapkie-260302 sound_source rsSilnik { sound_placement::engine };
sound_source rsDerailment; // McZapkie-051202 sound_source dsbRelay { sound_placement::engine };
sound_source rsPrzekladnia; sound_source dsbWejscie_na_bezoporow { sound_placement::engine }; // moved from cab
sound_source sHorn1; sound_source dsbWejscie_na_drugi_uklad { sound_placement::engine }; // moved from cab
sound_source sHorn2; sound_source rsPrzekladnia { sound_placement::engine };
sound_source sCompressor; // NBMX wrzesien 2003 sound_source rsEngageSlippery { sound_placement::engine }; // moved from cab
sound_source sConverter; sound_source rsDieselInc { sound_placement::engine }; // youBy
sound_source sSmallCompressor; sound_source sTurbo { sound_placement::engine };
sound_source sDepartureSignal; sound_source rsWentylator { sound_placement::engine }; // McZapkie-030302
sound_source sTurbo; sound_source sConverter { sound_placement::engine };
sound_source sSand; sound_source sCompressor { sound_placement::engine }; // NBMX wrzesien 2003
sound_source sReleaser; sound_source sSmallCompressor { sound_placement::engine };
// braking sounds
// Winger 010304 sound_source dsbPneumaticRelay { sound_placement::external };
sound_source sPantUp; sound_source rsUnbrake { sound_placement::external }; // yB - odglos luzowania
sound_source sPantDown; sound_source sReleaser { sound_placement::external };
sound_source rsDoorOpen; // Ra: przeniesione z kabiny sound_source rsSlippery { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab
sound_source rsDoorClose; sound_source sSand { sound_placement::external };
sound_source rsBrake { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab
sound_source sBrakeAcc { sound_placement::external };
bool bBrakeAcc;
sound_source rsPisk { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // McZapkie-260302
sound_source rsDerailment { sound_placement::external, 250.f }; // McZapkie-051202
// moving part and other external sounds
std::array<coupler_sounds, 2> m_couplersounds; // always front and rear
std::vector<pantograph_sounds> m_pantographsounds; // typically 2 but can be less (or more?)
std::vector<door_sounds> m_doorsounds; // can expect symmetrical arrangement, but don't count on it
sound_source sDepartureSignal { sound_placement::general };
sound_source sHorn1 { sound_placement::external };
sound_source sHorn2 { sound_placement::external };
sound_source rsRunningNoise { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
sound_source rscurve { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; // youBy
double eng_vol_act; double eng_vol_act;
double eng_frq_act; double eng_frq_act;
double eng_dfrq; double eng_dfrq;
double eng_turbo; double eng_turbo;
void ABuBogies();
void ABuModelRoll();
Math3D::vector3 modelShake; Math3D::vector3 modelShake;
bool renderme; // yB - czy renderowac bool renderme; // yB - czy renderowac
// TRealSound sBrakeAcc; //dźwięk przyspieszacza
sound_source sBrakeAcc;
bool bBrakeAcc;
sound_source rsUnbrake; // yB - odglos luzowania
float ModCamRot; float ModCamRot;
int iInventory[ 2 ] { 0, 0 }; // flagi bitowe posiadanych submodeli (np. świateł) int iInventory[ 2 ] { 0, 0 }; // flagi bitowe posiadanych submodeli (np. świateł)
void TurnOff(); bool btnOn; // ABu: czy byly uzywane buttony, jesli tak, to po renderingu wylacz
// bo ten sam model moze byc jeszcze wykorzystany przez inny obiekt!
float m_lastbrakepressure { -1.f }; // helper, cached level of pressure in brake cylinder
float m_brakepressurechange { 0.f }; // recent change of pressure in brake cylinder
public: public:
int iHornWarning; // numer syreny do użycia po otrzymaniu sygnału do jazdy int iHornWarning; // numer syreny do użycia po otrzymaniu sygnału do jazdy
@@ -376,10 +408,10 @@ private:
void SetPneumatic(bool front, bool red); void SetPneumatic(bool front, bool red);
std::string asName; std::string asName;
std::string name() const { std::string name() const {
return this ? asName : std::string(); }; return this ?
asName :
std::string(); };
sound_source rsDiesielInc; // youBy
sound_source rscurve; // youBy
// std::ofstream PneuLogFile; //zapis parametrow pneumatycznych // std::ofstream PneuLogFile; //zapis parametrow pneumatycznych
// youBy - dym // youBy - dym
// TSmoke Smog; // TSmoke Smog;
@@ -430,13 +462,17 @@ private:
inline Math3D::vector3 RearPosition() { inline Math3D::vector3 RearPosition() {
return vCoulpler[iDirection]; }; return vCoulpler[iDirection]; };
inline Math3D::vector3 AxlePositionGet() { inline Math3D::vector3 AxlePositionGet() {
return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; }; return iAxleFirst ?
Axle1.pPosition :
Axle0.pPosition; };
inline Math3D::vector3 VectorFront() const { inline Math3D::vector3 VectorFront() const {
return vFront; }; return vFront; };
inline Math3D::vector3 VectorUp() { inline Math3D::vector3 VectorUp() const {
return vUp; }; return vUp; };
inline Math3D::vector3 VectorLeft() const { inline Math3D::vector3 VectorLeft() const {
return vLeft; }; return vLeft; };
inline double const * Matrix() const {
return mMatrix.readArray(); };
inline double * Matrix() { inline double * Matrix() {
return mMatrix.getArray(); }; return mMatrix.getArray(); };
inline double GetVelocity() { inline double GetVelocity() {
@@ -446,7 +482,9 @@ private:
inline double GetWidth() const { inline double GetWidth() const {
return MoverParameters->Dim.W; }; return MoverParameters->Dim.W; };
inline TTrack * GetTrack() { inline TTrack * GetTrack() {
return (iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack()); }; return (iAxleFirst ?
Axle1.GetTrack() :
Axle0.GetTrack()); };
// McZapkie-260202 // McZapkie-260202
void LoadMMediaFile(std::string BaseDir, std::string TypeName, std::string ReplacableSkin); void LoadMMediaFile(std::string BaseDir, std::string TypeName, std::string ReplacableSkin);
@@ -455,13 +493,22 @@ private:
return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection()); }; return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection()); };
// zwraca kierunek pojazdu na torze z aktywną osą // zwraca kierunek pojazdu na torze z aktywną osą
inline double RaDirectionGet() { inline double RaDirectionGet() {
return iAxleFirst ? Axle1.GetDirection() : Axle0.GetDirection(); }; return iAxleFirst ?
Axle1.GetDirection() :
Axle0.GetDirection(); };
// zwraca przesunięcie wózka względem Point1 toru z aktywną osią // zwraca przesunięcie wózka względem Point1 toru z aktywną osią
inline double RaTranslationGet() { inline double RaTranslationGet() {
return iAxleFirst ? Axle1.GetTranslation() : Axle0.GetTranslation(); }; return iAxleFirst ?
Axle1.GetTranslation() :
Axle0.GetTranslation(); };
// zwraca tor z aktywną osią // zwraca tor z aktywną osią
inline TTrack * RaTrackGet() { inline TTrack * RaTrackGet() {
return iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack(); }; return iAxleFirst ?
Axle1.GetTrack() :
Axle0.GetTrack(); };
void couple( int const Side );
int uncouple( int const Side );
void CouplersDettach(double MinDist, int MyScanDir); void CouplersDettach(double MinDist, int MyScanDir);
void RadioStop(); void RadioStop();
void Damage(char flag); void Damage(char flag);

View File

@@ -1022,34 +1022,27 @@ event_manager::CheckQuery() {
return false; return false;
} }
case tp_Sound: { case tp_Sound: {
#ifdef EU07_USE_OLD_SOUNDCODE if( m_workevent->Params[ 9 ].tsTextSound == nullptr ) {
break;
}
switch( m_workevent->Params[ 0 ].asInt ) { switch( m_workevent->Params[ 0 ].asInt ) {
// trzy możliwe przypadki: // trzy możliwe przypadki:
case 0: { case 0: {
m_workevent->Params[ 9 ].tsTextSound->Stop(); m_workevent->Params[ 9 ].tsTextSound->stop();
break; break;
} }
case 1: { case 1: {
m_workevent->Params[ 9 ].tsTextSound->Play( m_workevent->Params[ 9 ].tsTextSound->play( sound_flags::exclusive );
1,
0,
true,
m_workevent->Params[ 9 ].tsTextSound->vSoundPosition );
break; break;
} }
case -1: { case -1: {
m_workevent->Params[ 9 ].tsTextSound->Play( m_workevent->Params[ 9 ].tsTextSound->play( sound_flags::exclusive | sound_flags::looping );
1,
DSBPLAY_LOOPING,
true,
m_workevent->Params[ 9 ].tsTextSound->vSoundPosition );
break; break;
} }
default: { default: {
break; break;
} }
} }
#endif
break; break;
} }
case tp_Disable: case tp_Disable:
@@ -1574,14 +1567,19 @@ event_manager::InitLaunchers() {
} }
} }
launcher->Event1 = ( if( launcher->asEvent1Name != "none" ) {
launcher->asEvent1Name != "none" ? launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name );
simulation::Events.FindEvent( launcher->asEvent1Name ) : if( launcher->Event1 == nullptr ) {
nullptr ); ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" cannot find event \"" + launcher->asEvent1Name + "\"" );
launcher->Event2 = ( }
launcher->asEvent2Name != "none" ? }
simulation::Events.FindEvent( launcher->asEvent2Name ) :
nullptr ); if( launcher->asEvent2Name != "none" ) {
launcher->Event2 = simulation::Events.FindEvent( launcher->asEvent2Name );
if( launcher->Event2 == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" cannot find event \"" + launcher->asEvent2Name + "\"" );
}
}
} }
} }

View File

@@ -19,24 +19,23 @@ http://mozilla.org/MPL/2.0/.
#include "Model3d.h" #include "Model3d.h"
#include "Timer.h" #include "Timer.h"
#include "logs.h" #include "logs.h"
#include "renderer.h"
void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale, double fNewOffset, double fNewFriction, double fNewValue) void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale, double fNewOffset, double fNewFriction, double fNewValue)
{ // ustawienie parametrów animacji submodelu { // ustawienie parametrów animacji submodelu
if (NewSubModel) if (NewSubModel) {
{ // warunek na wszelki wypadek, gdyby się submodel nie // warunek na wszelki wypadek, gdyby się submodel nie podłączył
// podłączył
fFriction = fNewFriction; fFriction = fNewFriction;
fValue = fNewValue; fValue = fNewValue;
fOffset = fNewOffset; fOffset = fNewOffset;
fScale = fNewScale; fScale = fNewScale;
SubModel = NewSubModel; SubModel = NewSubModel;
eType = eNewType; eType = eNewType;
if (eType == gt_Digital) if (eType == gt_Digital) {
{
TSubModel *sm = SubModel->ChildGet(); TSubModel *sm = SubModel->ChildGet();
do do {
{ // pętla po submodelach potomnych i obracanie ich o kąt zależy od // pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue)
// cyfry w (fValue)
if (sm->pName.size()) if (sm->pName.size())
{ // musi mieć niepustą nazwę { // musi mieć niepustą nazwę
if (sm->pName[0] >= '0') if (sm->pName[0] >= '0')
@@ -48,10 +47,17 @@ void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale,
} }
else // a banan może być z optymalizacją? else // a banan może być z optymalizacją?
NewSubModel->WillBeAnimated(); // wyłączenie ignowania jedynkowego transformu NewSubModel->WillBeAnimated(); // wyłączenie ignowania jedynkowego transformu
// pass submodel location to defined sounds
auto const offset { model_offset() };
m_soundfxincrease.offset( offset );
m_soundfxdecrease.offset( offset );
for( auto &soundfxrecord : m_soundfxvalues ) {
soundfxrecord.second.offset( offset );
}
} }
}; };
bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) { bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2, double mul ) {
std::string submodelname, gaugetypename; std::string submodelname, gaugetypename;
double scale, offset, friction; double scale, offset, friction;
@@ -83,10 +89,17 @@ bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) {
} }
} }
// bind defined sounds with the button owner
m_soundfxincrease.owner( Owner );
m_soundfxdecrease.owner( Owner );
for( auto &soundfxrecord : m_soundfxvalues ) {
soundfxrecord.second.owner( Owner );
}
scale *= mul; scale *= mul;
TSubModel *submodel = md1->GetFromName( submodelname ); TSubModel *submodel = md1->GetFromName( submodelname );
if( scale == 0.0 ) { if( scale == 0.0 ) {
ErrorLog( "Scale of 0.0 defined for sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\". Forcing scale of 1.0 to prevent division by 0" ); ErrorLog( "Bad model: scale of 0.0 defined for sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\". Forcing scale of 1.0 to prevent division by 0" );
scale = 1.0; scale = 1.0;
} }
if (submodel) // jeśli nie znaleziony if (submodel) // jeśli nie znaleziony
@@ -94,7 +107,7 @@ bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) {
else if (md2) // a jest podany drugi model (np. zewnętrzny) else if (md2) // a jest podany drugi model (np. zewnętrzny)
submodel = md2->GetFromName(submodelname); // to może tam będzie, co za różnica gdzie submodel = md2->GetFromName(submodelname); // to może tam będzie, co za różnica gdzie
if( submodel == nullptr ) { if( submodel == nullptr ) {
ErrorLog( "Failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\"" ); ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\"" );
} }
std::map<std::string, TGaugeType> gaugetypes { std::map<std::string, TGaugeType> gaugetypes {
@@ -132,7 +145,7 @@ TGauge::Load_mapping( cParser &Input ) {
if( indexstart != std::string::npos ) { if( indexstart != std::string::npos ) {
m_soundfxvalues.emplace( m_soundfxvalues.emplace(
std::stoi( key.substr( indexstart, indexend - indexstart ) ), std::stoi( key.substr( indexstart, indexend - indexstart ) ),
sound_source().deserialize( Input, sound_type::single ) ); sound_source( sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE ).deserialize( Input, sound_type::single ) );
} }
} }
return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized
@@ -284,8 +297,6 @@ void TGauge::Update() {
} }
}; };
void TGauge::Render(){};
void TGauge::AssignFloat(float *fValue) void TGauge::AssignFloat(float *fValue)
{ {
cDataType = 'f'; cDataType = 'f';
@@ -317,4 +328,14 @@ void TGauge::UpdateValue()
} }
}; };
// returns offset of submodel associated with the button from the model centre
glm::vec3
TGauge::model_offset() const {
return (
SubModel != nullptr ?
SubModel->offset( 1.f ) :
glm::vec3() );
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

50
Gauge.h
View File

@@ -24,6 +24,30 @@ enum TGaugeType {
// animowany wskaźnik, mogący przyjmować wiele stanów pośrednich // animowany wskaźnik, mogący przyjmować wiele stanów pośrednich
class TGauge { class TGauge {
public:
// methods
TGauge() = default;
inline
void Clear() { *this = TGauge(); }
void Init(TSubModel *NewSubModel, TGaugeType eNewTyp, double fNewScale = 1, double fNewOffset = 0, double fNewFriction = 0, double fNewValue = 0);
bool Load(cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2 = nullptr, double mul = 1.0);
void PermIncValue(double fNewDesired);
void IncValue(double fNewDesired);
void DecValue(double fNewDesired);
void UpdateValue( double fNewDesired );
void UpdateValue( double fNewDesired, sound_source &Fallbacksound );
void PutValue(double fNewDesired);
double GetValue() const;
void Update();
void AssignFloat(float *fValue);
void AssignDouble(double *dValue);
void AssignInt(int *iValue);
void UpdateValue();
// returns offset of submodel associated with the button from the model centre
glm::vec3 model_offset() const;
// members
TSubModel *SubModel; // McZapkie-310302: zeby mozna bylo sprawdzac czy zainicjowany poprawnie
private: private:
// methods // methods
// imports member data pair from the config file // imports member data pair from the config file
@@ -44,32 +68,10 @@ private:
double *dData { nullptr }; double *dData { nullptr };
int *iData; int *iData;
}; };
sound_source m_soundfxincrease; // sound associated with increasing control's value sound_source m_soundfxincrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with increasing control's value
sound_source m_soundfxdecrease; // sound associated with decreasing control's value sound_source m_soundfxdecrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with decreasing control's value
std::map<int, sound_source> m_soundfxvalues; // sounds associated with specific values std::map<int, sound_source> m_soundfxvalues; // sounds associated with specific values
public:
// methods
TGauge() = default;
inline
void Clear() { *this = TGauge(); }
void Init(TSubModel *NewSubModel, TGaugeType eNewTyp, double fNewScale = 1, double fNewOffset = 0, double fNewFriction = 0, double fNewValue = 0);
bool Load(cParser &Parser, TModel3d *md1, TModel3d *md2 = nullptr, double mul = 1.0);
void PermIncValue(double fNewDesired);
void IncValue(double fNewDesired);
void DecValue(double fNewDesired);
void UpdateValue( double fNewDesired );
void UpdateValue( double fNewDesired, sound_source &Fallbacksound );
void PutValue(double fNewDesired);
double GetValue() const;
void Update();
void Render();
void AssignFloat(float *fValue);
void AssignDouble(double *dValue);
void AssignInt(int *iValue);
void UpdateValue();
// members
TSubModel *SubModel; // McZapkie-310302: zeby mozna bylo sprawdzac czy zainicjowany poprawnie
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@@ -132,6 +132,8 @@ bool Global::FullPhysics { true }; // full calculations performed for each simul
// parametry testowe (do testowania scenerii i obiektów) // parametry testowe (do testowania scenerii i obiektów)
bool Global::bWireFrame = false; bool Global::bWireFrame = false;
bool Global::bSoundEnabled = true; bool Global::bSoundEnabled = true;
float Global::AudioVolume = 1.0f;
int Global::iWriteLogEnabled = 3; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów int Global::iWriteLogEnabled = 3; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów
bool Global::MultipleLogs{ false }; bool Global::MultipleLogs{ false };

View File

@@ -166,6 +166,7 @@ public:
static bool bFreeFly; static bool bFreeFly;
static bool bWireFrame; static bool bWireFrame;
static bool bSoundEnabled; static bool bSoundEnabled;
static float AudioVolume;
// McZapkie-131202 // McZapkie-131202
static bool bAdjustScreenFreq; static bool bAdjustScreenFreq;
static bool bEnableTraction; static bool bEnableTraction;

View File

@@ -197,14 +197,15 @@ static int const s_SHPebrake = 64; //hamuje
static int const s_CAtest = 128; static int const s_CAtest = 128;
/*dzwieki*/ /*dzwieki*/
static int const sound_none = 0; enum sound {
static int const sound_loud = 1; none,
static int const sound_couplerstretch = 2; loud = 0x1,
static int const sound_bufferclamp = 4; couplerstretch = 0x2,
static int const sound_bufferbump = 8; bufferclash = 0x4,
static int const sound_relay = 16; relay = 0x10,
static int const sound_manyrelay = 32; parallel = 0x20,
static int const sound_brakeacc = 64; pneumatic = 0x40
};
//szczególne typy pojazdów (inna obsługa) dla zmiennej TrainType //szczególne typy pojazdów (inna obsługa) dla zmiennej TrainType
//zamienione na flagi bitowe, aby szybko wybierać grupę (np. EZT+SZT) //zamienione na flagi bitowe, aby szybko wybierać grupę (np. EZT+SZT)
@@ -602,6 +603,8 @@ struct TCoupling {
power_coupling power_high; power_coupling power_high;
power_coupling power_low; // TODO: implement this power_coupling power_low; // TODO: implement this
int sounds { 0 }; // sounds emitted by the coupling devices
}; };
class TMoverParameters class TMoverParameters
@@ -1167,6 +1170,7 @@ public:
bool DoorLeft(bool State); //obsluga drzwi lewych bool DoorLeft(bool State); //obsluga drzwi lewych
bool DoorRight(bool State); //obsluga drzwi prawych bool DoorRight(bool State); //obsluga drzwi prawych
bool DoorBlockedFlag(void); //sprawdzenie blokady drzwi bool DoorBlockedFlag(void); //sprawdzenie blokady drzwi
bool signal_departure( bool const State, int const Notify = range::consist ); // toggles departure warning
/* funkcje dla samochodow*/ /* funkcje dla samochodow*/
bool ChangeOffsetH(double DeltaOffset); bool ChangeOffsetH(double DeltaOffset);

View File

@@ -1369,11 +1369,11 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap
if (EngineType == ElectricSeriesMotor) if (EngineType == ElectricSeriesMotor)
if (AutoRelayCheck()) if (AutoRelayCheck())
SetFlag(SoundFlag, sound_relay); SetFlag(SoundFlag, sound::relay);
if (EngineType == DieselEngine) if (EngineType == DieselEngine)
if (dizel_Update(dt)) if (dizel_Update(dt))
SetFlag(SoundFlag, sound_relay); SetFlag(SoundFlag, sound::relay);
// uklady hamulcowe: // uklady hamulcowe:
if (VeselVolume > 0) if (VeselVolume > 0)
Compressor = CompressedVolume / VeselVolume; Compressor = CompressedVolume / VeselVolume;
@@ -1530,7 +1530,7 @@ double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape
if (EngineType == DieselEngine) if (EngineType == DieselEngine)
if (dizel_Update(dt)) if (dizel_Update(dt))
SetFlag(SoundFlag, sound_relay); SetFlag(SoundFlag, sound::relay);
// uklady hamulcowe: // uklady hamulcowe:
if (VeselVolume > 0) if (VeselVolume > 0)
Compressor = CompressedVolume / VeselVolume; Compressor = CompressedVolume / VeselVolume;
@@ -1703,7 +1703,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed)
if( RList[ MainCtrlPos ].Bn > 1 ) { if( RList[ MainCtrlPos ].Bn > 1 ) {
if( true == MaxCurrentSwitch( false )) { if( true == MaxCurrentSwitch( false )) {
// wylaczanie wysokiego rozruchu // wylaczanie wysokiego rozruchu
SetFlag( SoundFlag, sound_relay ); SetFlag( SoundFlag, sound::relay );
} // Q TODO: } // Q TODO:
// if (EngineType=ElectricSeriesMotor) and (MainCtrlPos=1) // if (EngineType=ElectricSeriesMotor) and (MainCtrlPos=1)
// then // then
@@ -2988,6 +2988,10 @@ bool TMoverParameters::BrakeDelaySwitch(int BDS)
} }
else else
rBDS = false; rBDS = false;
if( true == rBDS ) {
// if setting was changed emit the sound of pneumatic relay
SetFlag( SoundFlag, sound::pneumatic );
}
return rBDS; return rBDS;
} }
@@ -3086,8 +3090,8 @@ void TMoverParameters::CompressorCheck(double dt)
else else
{ {
CompressedVolume = CompressedVolume * 0.8; CompressedVolume = CompressedVolume * 0.8;
SetFlag(SoundFlag, sound_relay | sound_loud); SetFlag(SoundFlag, sound::relay | sound::loud);
// SetFlag(SoundFlag, sound_loud); // SetFlag(SoundFlag, sound::loud);
} }
} }
} }
@@ -4078,6 +4082,31 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt)
// tempdist:=tempdist+CoupleDist; //ABu: proby szybkiego naprawienia bledu // tempdist:=tempdist+CoupleDist; //ABu: proby szybkiego naprawienia bledu
} }
dV = V - (double)DirPatch( CouplerN, CNext ) * Couplers[ CouplerN ].Connected->V;
absdV = abs( dV );
// potentially generate sounds on clash or stretch
if( ( newdist < 0.0 )
&& ( Couplers[ CouplerN ].Dist > newdist )
&& ( dV < -0.5 ) ) {
// 090503: dzwieki pracy zderzakow
SetFlag(
Couplers[ CouplerN ].sounds,
( absdV > 5.0 ?
( sound::bufferclash | sound::loud ) :
sound::bufferclash ) );
}
else if( ( Couplers[ CouplerN ].CouplingFlag != coupling::faux )
&& ( newdist > 0.001 )
&& ( Couplers[ CouplerN ].Dist <= 0.001 )
&& ( absdV > 0.005 ) ) {
// 090503: dzwieki pracy sprzegu
SetFlag(
Couplers[ CouplerN ].sounds,
( absdV > 0.1 ?
( sound::couplerstretch | sound::loud ) :
sound::couplerstretch ) );
}
// blablabla // blablabla
// ABu: proby znalezienia problemu ze zle odbijajacymi sie skladami // ABu: proby znalezienia problemu ze zle odbijajacymi sie skladami
//if (Couplers[CouplerN].CouplingFlag=ctrain_virtual) and (newdist>0) then //if (Couplers[CouplerN].CouplingFlag=ctrain_virtual) and (newdist>0) then
@@ -4099,23 +4128,6 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt)
Couplers[CouplerN].Connected->Couplers[CNext].FmaxB) * Couplers[CouplerN].Connected->Couplers[CNext].FmaxB) *
CouplerTune / 2.0; CouplerTune / 2.0;
} }
dV = V - (double)DirPatch(CouplerN, CNext) * Couplers[CouplerN].Connected->V;
absdV = abs(dV);
if ((newdist < -0.001) && (Couplers[CouplerN].Dist >= -0.001) &&
(absdV > 0.010)) // 090503: dzwieki pracy zderzakow
{
if (SetFlag(SoundFlag, sound_bufferclamp))
if (absdV > 0.5)
SetFlag(SoundFlag, sound_loud);
}
else if ((newdist > 0.002) && (Couplers[CouplerN].Dist <= 0.002) &&
(absdV > 0.005)) // 090503: dzwieki pracy sprzegu
{
if (Couplers[CouplerN].CouplingFlag > 0)
if (SetFlag(SoundFlag, sound_couplerstretch))
if (absdV > 0.1)
SetFlag(SoundFlag, sound_loud);
}
distDelta = distDelta =
abs(newdist) - abs(Couplers[CouplerN].Dist); // McZapkie-191103: poprawka na histereze abs(newdist) - abs(Couplers[CouplerN].Dist); // McZapkie-191103: poprawka na histereze
Couplers[CouplerN].Dist = newdist; Couplers[CouplerN].Dist = newdist;
@@ -4919,7 +4931,7 @@ bool TMoverParameters::FuseOn(void)
{ {
FuseFlag = false; // wlaczenie ponowne obwodu FuseFlag = false; // wlaczenie ponowne obwodu
FO = true; FO = true;
SetFlag(SoundFlag, sound_relay | sound_loud); SetFlag(SoundFlag, sound::relay | sound::loud);
} }
} }
return FO; return FO;
@@ -4935,7 +4947,7 @@ void TMoverParameters::FuseOff(void)
{ {
FuseFlag = true; FuseFlag = true;
EventFlag = true; EventFlag = true;
SetFlag(SoundFlag, sound_relay | sound_loud); SetFlag(SoundFlag, sound::relay | sound::loud);
} }
} }
@@ -5206,7 +5218,7 @@ bool TMoverParameters::AutoRelayCheck(void)
// MainCtrlActualPos:=MainCtrlPos; //hunter-111012: // MainCtrlActualPos:=MainCtrlPos; //hunter-111012:
// szybkie wchodzenie na bezoporowa (303E) // szybkie wchodzenie na bezoporowa (303E)
OK = true; OK = true;
SetFlag(SoundFlag, sound_manyrelay | sound_loud); SetFlag(SoundFlag, sound::parallel | sound::loud);
} }
else if ((LastRelayTime > CtrlDelay) && (ARFASI)) else if ((LastRelayTime > CtrlDelay) && (ARFASI))
{ {
@@ -5240,13 +5252,13 @@ bool TMoverParameters::AutoRelayCheck(void)
if ((RList[MainCtrlActualPos].R == 0) && if ((RList[MainCtrlActualPos].R == 0) &&
(!(MainCtrlActualPos == MainCtrlPosNo))) // wejscie na bezoporowa (!(MainCtrlActualPos == MainCtrlPosNo))) // wejscie na bezoporowa
{ {
SetFlag(SoundFlag, sound_manyrelay | sound_loud); SetFlag(SoundFlag, sound::parallel | sound::loud);
} }
else if ((RList[MainCtrlActualPos].R > 0) && else if ((RList[MainCtrlActualPos].R > 0) &&
(RList[MainCtrlActualPos - 1].R == (RList[MainCtrlActualPos - 1].R ==
0)) // wejscie na drugi uklad 0)) // wejscie na drugi uklad
{ {
SetFlag(SoundFlag, sound_manyrelay); SetFlag(SoundFlag, sound::parallel);
} }
} }
} }
@@ -5259,7 +5271,7 @@ bool TMoverParameters::AutoRelayCheck(void)
// MainCtrlActualPos:=MainCtrlPos; //hunter-111012: // MainCtrlActualPos:=MainCtrlPos; //hunter-111012:
// szybkie wchodzenie na bezoporowa (303E) // szybkie wchodzenie na bezoporowa (303E)
OK = true; OK = true;
SetFlag(SoundFlag, sound_manyrelay); SetFlag(SoundFlag, sound::parallel);
} }
else if (LastRelayTime > CtrlDownDelay) else if (LastRelayTime > CtrlDownDelay)
{ {
@@ -5272,7 +5284,7 @@ bool TMoverParameters::AutoRelayCheck(void)
if (RList[MainCtrlActualPos].R == if (RList[MainCtrlActualPos].R ==
0) // dzwieki schodzenia z bezoporowej} 0) // dzwieki schodzenia z bezoporowej}
{ {
SetFlag(SoundFlag, sound_manyrelay); SetFlag(SoundFlag, sound::parallel);
} }
} }
} }
@@ -5304,7 +5316,7 @@ bool TMoverParameters::AutoRelayCheck(void)
StLinFlag = true; // ybARC - zalaczenie stycznikow liniowych StLinFlag = true; // ybARC - zalaczenie stycznikow liniowych
MainCtrlActualPos = 1; MainCtrlActualPos = 1;
DelayCtrlFlag = false; DelayCtrlFlag = false;
SetFlag(SoundFlag, sound_relay | sound_loud); SetFlag(SoundFlag, sound::relay | sound::loud);
OK = true; OK = true;
} }
} }
@@ -5858,6 +5870,32 @@ bool TMoverParameters::DoorRight(bool State)
return DR; return DR;
} }
// toggles departure warning
bool
TMoverParameters::signal_departure( bool const State, int const Notify ) {
if( DepartureSignal == State ) {
// TBD: should the command be passed to other vehicles regardless of whether it affected the primary target?
return false;
}
DepartureSignal = State;
if( Notify != range::local ) {
// wysłanie wyłączenia do pozostałych?
SendCtrlToNext(
"DepartureSignal",
( State == true ?
1 :
0 ),
CabNo,
( Notify == range::unit ?
ctrain_controll | ctrain_depot :
ctrain_controll ) );
}
return true;
}
// ************************************************************************************************* // *************************************************************************************************
// Q: 20160713 // Q: 20160713
// Przesuwa pojazd o podaną wartość w bok względem toru (dla samochodów) // Przesuwa pojazd o podaną wartość w bok względem toru (dla samochodów)
@@ -8227,6 +8265,13 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
} }
OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
} }
else if( Command == "DepartureSignal" ) {
DepartureSignal = (
CValue1 == 1 ?
true :
false );
OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
}
else if (Command == "PantFront") /*Winger 160204*/ else if (Command == "PantFront") /*Winger 160204*/
{ // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów
// Czemu EZT ma być traktowane inaczej? Ukrotnienie ma, a człon może być odwrócony // Czemu EZT ma być traktowane inaczej? Ukrotnienie ma, a człon może być odwrócony
@@ -8346,6 +8391,8 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
if( true == Hamulec->SetBDF( brakesetting ) ) { if( true == Hamulec->SetBDF( brakesetting ) ) {
BrakeDelayFlag = brakesetting; BrakeDelayFlag = brakesetting;
OK = true; OK = true;
// if setting was changed emit the sound of pneumatic relay
SetFlag( SoundFlag, sound::pneumatic );
} }
else { else {
OK = false; OK = false;

View File

@@ -578,47 +578,58 @@ void TESt::CheckReleaser( double const dt )
} }
} }
void TESt::CheckState( double const BCP, double &dV1 ) void TESt::CheckState( double const BCP, double &dV1 ) {
{
double VVP;
double BVP;
double CVP;
BVP = BrakeRes->P(); double const VVP { ValveRes->P() };
VVP = ValveRes->P(); double const BVP { BrakeRes->P() };
// if (BVP<VVP) then double const CVP { CntrlRes->P() };
// VVP:=(BVP+VVP)/2;
CVP = CntrlRes->P() - 0.0;
// sprawdzanie stanu // sprawdzanie stanu
if (((BrakeStatus & b_hld) == b_hld) && (BCP > 0.25)) if( BCP > 0.25 ) {
if ((VVP + 0.003 + BCP / BVM < CVP))
BrakeStatus |= b_on; // hamowanie stopniowe if( ( BrakeStatus & b_hld ) == b_hld ) {
else if ((VVP - 0.003 + (BCP - 0.1) / BVM > CVP))
BrakeStatus &= ~( b_on | b_hld ); // luzowanie if( ( VVP + 0.003 + BCP / BVM ) < CVP ) {
else if ((VVP + BCP / BVM > CVP)) // hamowanie stopniowe
BrakeStatus &= ~b_on; // zatrzymanie napelaniania BrakeStatus |= b_on;
else }
; else {
else if ((VVP + 0.10 < CVP) && (BCP < 0.25)) // poczatek hamowania if( ( VVP + BCP / BVM ) > CVP ) {
{ // zatrzymanie napelaniania
if ((BrakeStatus & b_hld) == b_off) BrakeStatus &= ~b_on;
{ }
ValveRes->CreatePress(0.02 * VVP); if( ( VVP - 0.003 + ( BCP - 0.1 ) / BVM ) > CVP ) {
SoundFlag |= sf_Acc; // luzowanie
ValveRes->Act(); BrakeStatus &= ~( b_on | b_hld );
}
}
} }
BrakeStatus |= (b_on | b_hld); else {
// ValveRes.CreatePress(0); if( ( VVP + BCP / BVM < CVP )
// dV1:=1; && ( ( CVP - VVP ) * BVM > 0.25 ) ) {
// zatrzymanie luzowanie
BrakeStatus |= b_hld;
}
}
} }
else if ((VVP + (BCP - 0.1) / BVM < CVP) && ((CVP - VVP) * BVM > 0.25) && else {
(BCP > 0.25)) // zatrzymanie luzowanie
BrakeStatus |= b_hld;
if ((BrakeStatus & b_hld) == b_off) if( VVP + 0.1 < CVP ) {
// poczatek hamowania
if( ( BrakeStatus & b_hld ) == 0 ) {
// przyspieszacz
ValveRes->CreatePress( 0.02 * VVP );
SoundFlag |= sf_Acc;
ValveRes->Act();
}
BrakeStatus |= ( b_on | b_hld );
}
}
if( ( BrakeStatus & b_hld ) == 0 ) {
SoundFlag |= sf_CylU; SoundFlag |= sf_CylU;
}
} }
double TESt::CVs( double const BP ) double TESt::CVs( double const BP )
@@ -828,6 +839,10 @@ double TEStEP2::GetPF( double const PP, double const dt, double const Vel )
else if ((VVP + BCP / BVM < CVP - 0.12) && (BCP > 0.25)) // zatrzymanie luzowanie else if ((VVP + BCP / BVM < CVP - 0.12) && (BCP > 0.25)) // zatrzymanie luzowanie
BrakeStatus |= b_hld; BrakeStatus |= b_hld;
if( ( BrakeStatus & b_hld ) == 0 ) {
SoundFlag |= sf_CylU;
}
// przeplyw ZS <-> PG // przeplyw ZS <-> PG
if ((BVP < CVP - 0.2) || (BrakeStatus != b_off) || (BCP > 0.25)) if ((BVP < CVP - 0.2) || (BrakeStatus != b_off) || (BCP > 0.25))
temp = 0; temp = 0;
@@ -1211,6 +1226,40 @@ double TLSt::GetPF( double const PP, double const dt, double const Vel )
double dV1{ 0.0 }; double dV1{ 0.0 };
// sprawdzanie stanu // sprawdzanie stanu
// NOTE: partial copypaste from checkstate() of base class
// TODO: clean inheritance for checkstate() and checkreleaser() and reuse these instead of manual copypaste
if( ( ( BrakeStatus & b_hld ) == b_hld ) && ( BCP > 0.25 ) ) {
if( ( VVP + 0.003 + BCP / BVM < CVP ) ) {
// hamowanie stopniowe
BrakeStatus |= b_on;
}
else if( ( VVP - 0.003 + ( BCP - 0.1 ) / BVM > CVP ) ) {
// luzowanie
BrakeStatus &= ~( b_on | b_hld );
}
else if( ( VVP + BCP / BVM > CVP ) ) {
// zatrzymanie napelaniania
BrakeStatus &= ~b_on;
}
}
else if ((VVP + 0.10 < CVP) && (BCP < 0.25)) {
// poczatek hamowania
if ((BrakeStatus & b_hld) == b_off)
{
SoundFlag |= sf_Acc;
}
BrakeStatus |= (b_on | b_hld);
}
else if( ( VVP + ( BCP - 0.1 ) / BVM < CVP )
&& ( ( CVP - VVP ) * BVM > 0.25 )
&& ( BCP > 0.25 ) ) {
// zatrzymanie luzowanie
BrakeStatus |= b_hld;
}
if( ( BrakeStatus & b_hld ) == 0 ) {
SoundFlag |= sf_CylU;
}
// equivalent of checkreleaser() in the base class?
if( ( BrakeStatus & b_rls ) == b_rls ) { if( ( BrakeStatus & b_rls ) == b_rls ) {
if( CVP < 0.0 ) { if( CVP < 0.0 ) {
BrakeStatus &= ~b_rls; BrakeStatus &= ~b_rls;
@@ -1219,18 +1268,11 @@ double TLSt::GetPF( double const PP, double const dt, double const Vel )
{ // 008 { // 008
dV = PF1( CVP, BCP, 0.024 ) * dt; dV = PF1( CVP, BCP, 0.024 ) * dt;
CntrlRes->Flow( dV ); CntrlRes->Flow( dV );
/*
// NOTE: attempted fix, disabled because it breaks when releaser is used while releasing breakes
dV = PF1(CVP, VVP, 0.024) * dt;
CntrlRes->Flow( dV );
dV1 = dV; //minus potem jest
ImplsRes->Flow( -dV1 );
*/
} }
} }
double temp;
// przeplyw ZS <-> PG // przeplyw ZS <-> PG
double temp;
if (((CVP - BCP) * BVM > 0.5)) if (((CVP - BCP) * BVM > 0.5))
temp = 0.0; temp = 0.0;
else if ((VVP > CVP + 0.4)) else if ((VVP > CVP + 0.4))
@@ -1888,22 +1930,51 @@ void TKE::CheckState( double const BCP, double &dV1 )
CVP = CntrlRes->P(); CVP = CntrlRes->P();
// sprawdzanie stanu // sprawdzanie stanu
if ((BrakeStatus & b_hld) == b_hld) if( BCP > 0.1 ) {
if ((VVP + 0.003 + BCP / BVM < CVP))
BrakeStatus |= b_on; // hamowanie stopniowe; if( ( BrakeStatus & b_hld ) == b_hld ) {
else if ((VVP - 0.003 + BCP / BVM > CVP))
BrakeStatus &= ~( b_on | b_hld ); // luzowanie; if( ( VVP + 0.003 + BCP / BVM ) < CVP ) {
else if ((VVP + BCP / BVM > CVP)) // hamowanie stopniowe;
BrakeStatus &= ~b_on; // zatrzymanie napelaniania; BrakeStatus |= b_on;
else }
; else {
else if ((VVP + 0.10 < CVP) && (BCP < 0.1)) // poczatek hamowania if( ( VVP + BCP / BVM ) > CVP ) {
{ // zatrzymanie napelaniania;
BrakeStatus |= (b_on | b_hld); BrakeStatus &= ~b_on;
ValveRes->CreatePress(0.8 * VVP); // przyspieszacz }
if( ( VVP - 0.003 + BCP / BVM ) > CVP ) {
// luzowanie;
BrakeStatus &= ~( b_on | b_hld );
}
}
}
else {
if( ( VVP + BCP / BVM < CVP )
&& ( ( CVP - VVP ) * BVM > 0.25 ) ) {
// zatrzymanie luzowanie
BrakeStatus |= b_hld;
}
}
}
else {
if( VVP + 0.1 < CVP ) {
// poczatek hamowania
if( ( BrakeStatus & b_hld ) == 0 ) {
// przyspieszacz
ValveRes->CreatePress( 0.8 * VVP );
SoundFlag |= sf_Acc;
ValveRes->Act();
}
BrakeStatus |= ( b_on | b_hld );
}
}
if( ( BrakeStatus & b_hld ) == 0 ) {
SoundFlag |= sf_CylU;
} }
else if ((VVP + BCP / BVM < CVP) && ((CVP - VVP) * BVM > 0.25)) // zatrzymanie luzowanie
BrakeStatus |= b_hld;
} }
double TKE::CVs( double const BP ) double TKE::CVs( double const BP )

View File

@@ -1050,14 +1050,17 @@ void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, g
*/ */
}; };
void TSubModel::ParentMatrix(float4x4 *m) void TSubModel::ParentMatrix( float4x4 *m ) const { // pobranie transformacji względem wstawienia modelu
{ // pobranie transformacji względem wstawienia modelu // jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D),
// jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D), to // to dodatkowy obrót obrót T3D jest wymagany np. do policzenia wysokości pantografów
// dodatkowy obrót if( fMatrix != nullptr ) {
// obrót T3D jest wymagany np. do policzenia wysokości pantografów // skopiowanie, bo będziemy mnożyć
*m = float4x4(*fMatrix); // skopiowanie, bo będziemy mnożyć *m = float4x4( *fMatrix );
// m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak) }
TSubModel *sm = this; else {
m->Identity();
}
auto *sm = this;
while (sm->Parent) while (sm->Parent)
{ // przenieść tę funkcję do modelu { // przenieść tę funkcję do modelu
if (sm->Parent->GetMatrix()) if (sm->Parent->GetMatrix())
@@ -1160,6 +1163,37 @@ TSubModel *TModel3d::GetFromName(std::string const &Name)
} }
}; };
// returns offset vector from root
glm::vec3
TSubModel::offset( float const Geometrytestoffsetthreshold ) const {
float4x4 parentmatrix;
ParentMatrix( &parentmatrix );
auto offset { glm::vec3 { glm::make_mat4( parentmatrix.readArray() ) * glm::vec4 { 0, 0, 0, 1 } } };
if( glm::length2( offset ) < Geometrytestoffsetthreshold ) {
// offset of zero generally means the submodel has optimized identity matrix
// for such cases we resort to an estimate from submodel geometry
// TODO: do proper bounding area calculation for submodel when loading mesh and grab the centre point from it here
if( m_geometry != null_handle ) {
auto const &vertices{ GfxRenderer.Vertices( m_geometry ) };
if( false == vertices.empty() ) {
// transformation matrix for the submodel can still contain rotation and/or scaling,
// so we pass the vertex positions through it rather than just grab them directly
offset = glm::vec3();
auto const vertexfactor { 1.f / vertices.size() };
auto const transformationmatrix { glm::make_mat4( parentmatrix.readArray() ) };
for( auto const &vertex : vertices ) {
offset += glm::vec3 { transformationmatrix * glm::vec4 { vertex.position, 1 } } * vertexfactor;
}
}
}
}
return offset;
}
bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic) bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic)
{ {
// wczytanie modelu z pliku // wczytanie modelu z pliku

View File

@@ -124,7 +124,9 @@ private:
TSubModel *Next { nullptr }; TSubModel *Next { nullptr };
TSubModel *Child { nullptr }; TSubModel *Child { nullptr };
public: // temporary access, clean this up during refactoring
gfx::geometry_handle m_geometry { 0, 0 }; // geometry of the submodel gfx::geometry_handle m_geometry { 0, 0 }; // geometry of the submodel
private:
material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak
bool bWire { false }; // nie używane, ale wczytywane bool bWire { false }; // nie używane, ale wczytywane
float Opacity { 1.0f }; float Opacity { 1.0f };
@@ -138,9 +140,7 @@ public: // chwilowo
float m_boundingradius { 0 }; float m_boundingradius { 0 };
size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
TAnimType b_aAnim{ at_None }; // kody animacji oddzielnie, bo zerowane TAnimType b_aAnim{ at_None }; // kody animacji oddzielnie, bo zerowane
public:
float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer) float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer)
public:
TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D)
TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy
int iVisible{ 1 }; // roboczy stan widoczności int iVisible{ 1 }; // roboczy stan widoczności
@@ -172,7 +172,10 @@ public:
void SetRotateIK1(float3 vNewAngles); void SetRotateIK1(float3 vNewAngles);
TSubModel * GetFromName( std::string const &search, bool i = true ); TSubModel * GetFromName( std::string const &search, bool i = true );
inline float4x4 * GetMatrix() { return fMatrix; }; inline float4x4 * GetMatrix() { return fMatrix; };
inline void Hide() { iVisible = 0; }; inline float4x4 const * GetMatrix() const { return fMatrix; };
// returns offset vector from root
glm::vec3 offset( float const Geometrytestoffsetthreshold = 0.f ) const;
inline void Hide() { iVisible = 0; };
void create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle const &Bank ); void create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle const &Bank );
int FlagsCheck(); int FlagsCheck();
@@ -200,7 +203,7 @@ public:
return *(fMatrix->TranslationGet()) + Child->Translation1Get(); } return *(fMatrix->TranslationGet()) + Child->Translation1Get(); }
material_handle GetMaterial() const { material_handle GetMaterial() const {
return m_material; } return m_material; }
void ParentMatrix(float4x4 *m); void ParentMatrix(float4x4 *m) const;
float MaxY( float4x4 const &m ); float MaxY( float4x4 const &m );
void deserialize(std::istream&); void deserialize(std::istream&);

930
Train.cpp

File diff suppressed because it is too large Load Diff

58
Train.h
View File

@@ -91,7 +91,6 @@ class TTrain
vector3 GetWorldMechPosition(); vector3 GetWorldMechPosition();
bool Update( double const Deltatime ); bool Update( double const Deltatime );
void update_sounds( double const Deltatime ); void update_sounds( double const Deltatime );
bool m_updated = false;
void MechStop(); void MechStop();
void SetLights(); void SetLights();
// McZapkie-310302: ladowanie parametrow z pliku // McZapkie-310302: ladowanie parametrow z pliku
@@ -102,6 +101,7 @@ class TTrain
// types // types
typedef void( *command_handler )( TTrain *Train, command_data const &Command ); typedef void( *command_handler )( TTrain *Train, command_data const &Command );
typedef std::unordered_map<user_command, command_handler> commandhandler_map; typedef std::unordered_map<user_command, command_handler> commandhandler_map;
// methods
// clears state of all cabin controls // clears state of all cabin controls
void clear_cab_controls(); void clear_cab_controls();
// sets cabin controls based on current state of the vehicle // sets cabin controls based on current state of the vehicle
@@ -226,7 +226,7 @@ public: // reszta może by?publiczna
TGauge ggMainCtrl; TGauge ggMainCtrl;
TGauge ggMainCtrlAct; TGauge ggMainCtrlAct;
TGauge ggScndCtrl; TGauge ggScndCtrl;
TGauge ggScndCtrlButton; TGauge ggScndCtrlButton; // NOTE: not used?
TGauge ggDirKey; TGauge ggDirKey;
TGauge ggBrakeCtrl; TGauge ggBrakeCtrl;
TGauge ggLocalBrake; TGauge ggLocalBrake;
@@ -398,42 +398,23 @@ public: // reszta może by?publiczna
double fMechRoll; double fMechRoll;
double fMechPitch; double fMechPitch;
sound_source dsbNastawnikJazdy; sound_source dsbReverserKey { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-121211
sound_source dsbNastawnikBocz; // hunter-081211 sound_source dsbNastawnikJazdy { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbRelay; sound_source dsbNastawnikBocz { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-081211
sound_source dsbPneumaticRelay; sound_source dsbSwitch { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbSwitch; sound_source dsbPneumaticSwitch { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbPneumaticSwitch;
sound_source dsbReverserKey; // hunter-121211
sound_source dsbCouplerAttach; // Ra: w kabinie???? sound_source rsHiss { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // upuszczanie
sound_source dsbCouplerDetach; // Ra: w kabinie??? sound_source rsHissU { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // napelnianie
sound_source rsHissE { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // nagle
sound_source rsHissX { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // fala
sound_source rsHissT { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // czasowy
sound_source rsSBHiss { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // local
sound_source dsbDieselIgnition; // Ra: w kabinie??? sound_source rsFadeSound { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbHasler { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
// Winger 010304 sound_source dsbBuzzer { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbWejscie_na_bezoporow; sound_source dsbSlipAlarm { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // Bombardier 011010: alarm przy poslizgu dla 181/182
sound_source dsbWejscie_na_drugi_uklad; // hunter-081211: poprawka literowki
// PSound dsbHiss1;
// PSound dsbHiss2;
// McZapkie-280302
sound_source rsBrake;
sound_source rsSlippery;
sound_source rsHiss; // upuszczanie
sound_source rsHissU; // napelnianie
sound_source rsHissE; // nagle
sound_source rsHissX; // fala
sound_source rsHissT; // czasowy
sound_source rsSBHiss;
sound_source rsRunningNoise;
sound_source rsEngageSlippery;
sound_source rsFadeSound;
sound_source dsbHasler;
sound_source dsbBuzzer;
sound_source dsbSlipAlarm; // Bombardier 011010: alarm przy poslizgu dla 181/182
int iCabLightFlag; // McZapkie:120503: oswietlenie kabiny (0: wyl, 1: przyciemnione, 2: pelne) int iCabLightFlag; // McZapkie:120503: oswietlenie kabiny (0: wyl, 1: przyciemnione, 2: pelne)
bool bCabLight; // hunter-091012: czy swiatlo jest zapalone? bool bCabLight; // hunter-091012: czy swiatlo jest zapalone?
@@ -443,9 +424,6 @@ public: // reszta może by?publiczna
vector3 MirrorPosition(bool lewe); vector3 MirrorPosition(bool lewe);
private: private:
sound_source dsbCouplerStretch;
sound_source dsbEN57_CouplerStretch;
sound_source dsbBufferClamp;
double fBlinkTimer; double fBlinkTimer;
float fHaslerTimer; float fHaslerTimer;
float fConverterTimer; // hunter-261211: dla przekaznika float fConverterTimer; // hunter-261211: dla przekaznika
@@ -494,7 +472,9 @@ private:
float fEIMParams[9][10]; // parametry dla silnikow asynchronicznych float fEIMParams[9][10]; // parametry dla silnikow asynchronicznych
int RadioChannel() { return iRadioChannel; }; int RadioChannel() { return iRadioChannel; };
inline TDynamicObject *Dynamic() { return DynamicObject; }; inline TDynamicObject *Dynamic() { return DynamicObject; };
inline TDynamicObject const *Dynamic() const { return DynamicObject; };
inline TMoverParameters *Controlled() { return mvControlled; }; inline TMoverParameters *Controlled() { return mvControlled; };
inline TMoverParameters const *Controlled() const { return mvControlled; };
void DynamicSet(TDynamicObject *d); void DynamicSet(TDynamicObject *d);
void Silence(); void Silence();

View File

@@ -732,7 +732,9 @@ void TWorld::OnKeyDown(int cKey)
if (vehicle->MoverParameters->IncLocalBrakeLevelFAST()) if (vehicle->MoverParameters->IncLocalBrakeLevelFAST())
if (Train) if (Train)
{ // dźwięk oczywiście jest w kabinie { // dźwięk oczywiście jest w kabinie
#ifdef EU07_USE_OLD_SOUNDCODE
Train->dsbPneumaticRelay.play(); Train->dsbPneumaticRelay.play();
#endif
} }
} }
} }
@@ -751,7 +753,9 @@ void TWorld::OnKeyDown(int cKey)
if (vehicle->MoverParameters->DecLocalBrakeLevelFAST()) if (vehicle->MoverParameters->DecLocalBrakeLevelFAST())
if (Train) if (Train)
{ // dźwięk oczywiście jest w kabinie { // dźwięk oczywiście jest w kabinie
#ifdef EU07_USE_OLD_SOUNDCODE
Train->dsbPneumaticRelay.play(); Train->dsbPneumaticRelay.play();
#endif
} }
} }
} }
@@ -2031,15 +2035,15 @@ void TWorld::CreateE3D(std::string const &Path, bool Dynamic)
if( dynamic->iCabs ) { // jeśli ma jakąkolwiek kabinę if( dynamic->iCabs ) { // jeśli ma jakąkolwiek kabinę
delete Train; delete Train;
Train = new TTrain(); Train = new TTrain();
if( dynamic->iCabs & 1 ) { if( dynamic->iCabs & 0x1 ) {
dynamic->MoverParameters->ActiveCab = 1; dynamic->MoverParameters->ActiveCab = 1;
Train->Init( dynamic, true ); Train->Init( dynamic, true );
} }
if( dynamic->iCabs & 4 ) { if( dynamic->iCabs & 0x4 ) {
dynamic->MoverParameters->ActiveCab = -1; dynamic->MoverParameters->ActiveCab = -1;
Train->Init( dynamic, true ); Train->Init( dynamic, true );
} }
if( dynamic->iCabs & 2 ) { if( dynamic->iCabs & 0x2 ) {
dynamic->MoverParameters->ActiveCab = 0; dynamic->MoverParameters->ActiveCab = 0;
Train->Init( dynamic, true ); Train->Init( dynamic, true );
} }

View File

@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
#include "audio.h" #include "audio.h"
#include "globals.h" #include "globals.h"
#include "mczapkie/mctools.h" #include "mczapkie/mctools.h"
#include "logs.h"
#define DR_WAV_IMPLEMENTATION #define DR_WAV_IMPLEMENTATION
#include "dr_wav.h" #include "dr_wav.h"
@@ -137,6 +138,7 @@ buffer_manager::create( std::string const &Filename ) {
return emplace( filelookup ); return emplace( filelookup );
} }
// if we still didn't find anything, give up // if we still didn't find anything, give up
ErrorLog( "Bad file: failed do locate audio file \"" + Filename + "\"" );
return null_handle; return null_handle;
} }

View File

@@ -14,7 +14,7 @@ http://mozilla.org/MPL/2.0/.
namespace audio { namespace audio {
ALuint const null_resource { ~ALuint{ 0 } }; ALuint const null_resource{ ~( ALuint { 0 } ) };
// wrapper for audio sample // wrapper for audio sample
struct openal_buffer { struct openal_buffer {

View File

@@ -11,12 +11,15 @@ http://mozilla.org/MPL/2.0/.
#include "audiorenderer.h" #include "audiorenderer.h"
#include "sound.h" #include "sound.h"
#include "globals.h"
#include "logs.h" #include "logs.h"
namespace audio { namespace audio {
openal_renderer renderer; openal_renderer renderer;
float const EU07_SOUND_CUTOFFRANGE { 3000.f }; // 2750 m = max expected emitter spawn range, plus safety margin
// starts playback of queued buffers // starts playback of queued buffers
void void
openal_source::play() { openal_source::play() {
@@ -29,17 +32,16 @@ openal_source::play() {
void void
openal_source::stop() { openal_source::stop() {
::alSourcei( id, AL_LOOPING, AL_FALSE ); loop( false );
::alSourceStop( id ); ::alSourceStop( id );
// NOTE: we don't update the is_playing flag is_playing = false;
// this way the state will change only on next update loop,
// giving the controller a chance to properly change state from cease to none
// even with multiple active sounds under control
} }
// updates state of the source // updates state of the source
void void
openal_source::update( int const Deltatime ) { openal_source::update( double const Deltatime ) {
update_deltatime = Deltatime; // cached for time-based processing of data from the controller
// TODO: test whether the emitter was within range during the last tick, potentially update the counter and flag it for timeout // TODO: test whether the emitter was within range during the last tick, potentially update the counter and flag it for timeout
::alGetSourcei( id, AL_BUFFERS_PROCESSED, &buffer_index ); ::alGetSourcei( id, AL_BUFFERS_PROCESSED, &buffer_index );
@@ -58,10 +60,79 @@ openal_source::update( int const Deltatime ) {
controller->update( *this ); controller->update( *this );
} }
// configures state of the source to match the provided set of properties
void
openal_source::sync_with( sound_properties const &State ) {
/*
// velocity
// not used yet
glm::vec3 const velocity { ( State.location - properties.location ) / update_deltatime };
*/
// location
properties.location = State.location;
auto sourceoffset { glm::vec3 { properties.location - glm::dvec3 { Global::pCameraPosition } } };
if( glm::length2( sourceoffset ) > std::max( ( sound_range * sound_range ), ( EU07_SOUND_CUTOFFRANGE * EU07_SOUND_CUTOFFRANGE ) ) ) {
// range cutoff check
stop();
return;
}
if( sound_range >= 0 ) {
::alSourcefv( id, AL_POSITION, glm::value_ptr( sourceoffset ) );
}
else {
// sounds with 'unlimited' range are positioned on top of the listener
::alSourcefv( id, AL_POSITION, glm::value_ptr( glm::vec3() ) );
}
// gain
if( ( State.placement_stamp != properties.placement_stamp )
|| ( State.base_gain != properties.base_gain ) ) {
// gain value has changed
properties.base_gain = State.base_gain;
properties.placement_gain = State.placement_gain;
properties.placement_stamp = State.placement_stamp;
::alSourcef( id, AL_GAIN, properties.base_gain * properties.placement_gain * Global::AudioVolume );
}
// pitch
if( State.base_pitch != properties.base_pitch ) {
// pitch value has changed
properties.base_pitch = State.base_pitch;
::alSourcef( id, AL_PITCH, properties.base_pitch * pitch_variation );
}
}
// sets max audible distance for sounds emitted by the source
void
openal_source::range( float const Range ) {
auto const range(
Range >= 0 ?
Range :
5 ); // range of -1 means sound of unlimited range, positioned at the listener
::alSourcef( id, AL_REFERENCE_DISTANCE, range * ( 1.f / 16.f ) );
::alSourcef( id, AL_ROLLOFF_FACTOR, 1.5f );
// NOTE: we cache actual specified range, as we'll be giving 'unlimited' range special treatment
sound_range = Range;
}
// sets modifier applied to the pitch of sounds emitted by the source
void
openal_source::pitch( float const Pitch ) {
pitch_variation = Pitch;
// invalidate current pitch value to enforce change of next syns
properties.base_pitch = -1.f;
}
// toggles looping of the sound emitted by the source // toggles looping of the sound emitted by the source
void void
openal_source::loop( bool const State ) { openal_source::loop( bool const State ) {
if( is_looping == State ) { return; }
is_looping = State;
::alSourcei( ::alSourcei(
id, id,
AL_LOOPING, AL_LOOPING,
@@ -78,9 +149,7 @@ openal_source::clear() {
controller = nullptr; controller = nullptr;
// unqueue bound buffers: // unqueue bound buffers:
// ensure no buffer is in use... // ensure no buffer is in use...
::alSourcei( id, AL_LOOPING, AL_FALSE ); stop();
::alSourceStop( id );
is_playing = false;
// ...prepare space for returned ids of unqueued buffers (not that we need that info)... // ...prepare space for returned ids of unqueued buffers (not that we need that info)...
std::vector<ALuint> bufferids; std::vector<ALuint> bufferids;
bufferids.resize( buffers.size() ); bufferids.resize( buffers.size() );
@@ -88,6 +157,8 @@ openal_source::clear() {
::alSourceUnqueueBuffers( id, bufferids.size(), bufferids.data() ); ::alSourceUnqueueBuffers( id, bufferids.size(), bufferids.data() );
buffers.clear(); buffers.clear();
buffer_index = 0; buffer_index = 0;
// reset properties
properties = sound_properties();
} }
@@ -125,6 +196,9 @@ openal_renderer::init() {
// basic initialization failed // basic initialization failed
return false; return false;
} }
//
// ::alDistanceModel( AL_LINEAR_DISTANCE );
::alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED );
// all done // all done
m_ready = true; m_ready = true;
return true; return true;
@@ -141,10 +215,45 @@ openal_renderer::insert( sound_source *Controller, audio::buffer_handle const So
std::begin( buffers ), std::end( buffers ) ); std::begin( buffers ), std::end( buffers ) );
} }
// removes from the queue all sounds controlled by the specified sound emitter
void
openal_renderer::erase( sound_source const *Controller ) {
auto source { std::begin( m_sources ) };
while( source != std::end( m_sources ) ) {
if( source->controller == Controller ) {
// if the controller is the one specified, kill it
source->clear();
m_sourcespares.push( *source );
source = m_sources.erase( source );
}
else {
// otherwise proceed through the list normally
++source;
}
}
}
// updates state of all active emitters // updates state of all active emitters
void void
openal_renderer::update( int const Deltatime ) { openal_renderer::update( double const Deltatime ) {
// update listener
glm::dmat4 cameramatrix;
Global::pCamera->SetMatrix( cameramatrix );
auto rotationmatrix { glm::mat3{ cameramatrix } };
glm::vec3 const orientation[] = {
glm::vec3{ 0, 0,-1 } * rotationmatrix ,
glm::vec3{ 0, 1, 0 } * rotationmatrix };
::alListenerfv( AL_ORIENTATION, reinterpret_cast<ALfloat const *>( orientation ) );
/*
glm::dvec3 const listenerposition { Global::pCameraPosition };
// not used yet
glm::vec3 const velocity { ( listenerposition - m_listenerposition ) / Deltatime };
m_listenerposition = listenerposition;
*/
// update active emitters
auto source { std::begin( m_sources ) }; auto source { std::begin( m_sources ) };
while( source != std::end( m_sources ) ) { while( source != std::end( m_sources ) ) {
// update each source // update each source

View File

@@ -14,6 +14,15 @@ http://mozilla.org/MPL/2.0/.
class sound_source; class sound_source;
// sound emitter state sync item
struct sound_properties {
glm::dvec3 location;
float base_gain { 1.f };
float placement_gain { 1.f };
std::uintptr_t placement_stamp { ~( std::uintptr_t{ 0 } ) };
float base_pitch { 1.f };
};
namespace audio { namespace audio {
// implementation part of the sound emitter // implementation part of the sound emitter
@@ -29,6 +38,8 @@ struct openal_source {
buffer_sequence buffers; // sequence of samples the source will emit buffer_sequence buffers; // sequence of samples the source will emit
int buffer_index; // currently queued sample from the buffer sequence int buffer_index; // currently queued sample from the buffer sequence
bool is_playing { false }; bool is_playing { false };
bool is_looping { false };
sound_properties properties;
// methods // methods
template <class Iterator_> template <class Iterator_>
@@ -47,25 +58,42 @@ struct openal_source {
// starts playback of queued buffers // starts playback of queued buffers
void void
play(); play();
// updates state of the source
void
update( double const Deltatime );
// configures state of the source to match the provided set of properties
void
sync_with( sound_properties const &State );
// stops the playback // stops the playback
void void
stop(); stop();
// updates state of the source
void
update( int const Deltatime );
// toggles looping of the sound emitted by the source // toggles looping of the sound emitted by the source
void void
loop( bool const State ); loop( bool const State );
// sets max audible distance for sounds emitted by the source
void
range( float const Range );
// sets modifier applied to the pitch of sounds emitted by the source
void
pitch( float const Pitch );
// releases bound buffers and resets state of the class variables // releases bound buffers and resets state of the class variables
// NOTE: doesn't release allocated implementation-side source // NOTE: doesn't release allocated implementation-side source
void void
clear(); clear();
private:
// members
double update_deltatime; // time delta of most current update
float pitch_variation { 1.f }; // emitter-specific variation of the base pitch
float sound_range { 50.f }; // cached audible range of the emitted samples
}; };
class openal_renderer { class openal_renderer {
friend class opengl_renderer;
public: public:
// destructor // destructor
~openal_renderer(); ~openal_renderer();
@@ -89,9 +117,12 @@ public:
// schedules playback of specified sample, under control of the specified sound emitter // schedules playback of specified sample, under control of the specified sound emitter
void void
insert( sound_source *Controller, audio::buffer_handle const Sound ); insert( sound_source *Controller, audio::buffer_handle const Sound );
// removes from the queue all sounds controlled by the specified sound emitter
void
erase( sound_source const *Controller );
// updates state of all active emitters // updates state of all active emitters
void void
update( int const Deltatime ); update( double const Deltatime );
private: private:
// types // types
@@ -107,6 +138,7 @@ private:
ALCdevice * m_device { nullptr }; ALCdevice * m_device { nullptr };
ALCcontext * m_context { nullptr }; ALCcontext * m_context { nullptr };
bool m_ready { false }; // renderer is initialized and functional bool m_ready { false }; // renderer is initialized and functional
glm::dvec3 m_listenerposition;
buffer_manager m_buffers; buffer_manager m_buffers;
// TBD: list of sources as vector, sorted by distance, for openal implementations with limited number of active sources? // TBD: list of sources as vector, sorted by distance, for openal implementations with limited number of active sources?
@@ -116,6 +148,20 @@ private:
extern openal_renderer renderer; extern openal_renderer renderer;
inline
float
amplitude_to_db( float const Amplitude ) {
return 20.f * std::log10( Amplitude );
}
inline
float
db_to_amplitude( float const Decibels ) {
return std::pow( 10.f, Decibels / 20.f );
}
} // audio } // audio
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@@ -162,7 +162,7 @@ class matrix4x4
} }
// Low-level access to the array. // Low-level access to the array.
const scalar_t *readArray(void) const scalar_t *readArray(void) const
{ {
return e; return e;
} }

View File

@@ -441,6 +441,25 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
#endif #endif
switch_units( true, true, true ); switch_units( true, true, true );
Render( simulation::Region ); Render( simulation::Region );
/*
// debug: audio nodes
for( auto const &audiosource : audio::renderer.m_sources ) {
::glPushMatrix();
auto const position = audiosource.properties.location - m_renderpass.camera.position();
::glTranslated( position.x, position.y, position.z );
::glPushAttrib( GL_ENABLE_BIT );
::glDisable( GL_TEXTURE_2D );
::glColor3f( 0.36f, 0.75f, 0.35f );
::gluSphere( m_quadric, 0.125, 4, 2 );
::glPopAttrib();
::glPopMatrix();
}
*/
// ...translucent parts // ...translucent parts
setup_drawing( true ); setup_drawing( true );
Render_Alpha( simulation::Region ); Render_Alpha( simulation::Region );
@@ -2990,11 +3009,12 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
if( glarelevel > 0.0f ) { if( glarelevel > 0.0f ) {
// setup // setup
::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT ); ::glPushAttrib( GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT );
Bind_Texture( m_glaretexture ); Bind_Texture( m_glaretexture );
::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel ); ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel );
::glDisable( GL_LIGHTING ); ::glDisable( GL_LIGHTING );
::glDepthMask( GL_FALSE );
::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE );
::glPushMatrix(); ::glPushMatrix();

View File

@@ -851,7 +851,9 @@ state_manager::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpa
auto *sound = new TTextSound( soundname, Nodedata.range_max, location.x, location.y, location.z, false, false, Nodedata.range_min ); auto *sound = new TTextSound( soundname, Nodedata.range_max, location.x, location.y, location.z, false, false, Nodedata.range_min );
sound->name( Nodedata.name ); sound->name( Nodedata.name );
#else #else
auto *sound = new sound_source(); auto *sound = new sound_source( sound_placement::external, Nodedata.range_max );
sound->offset( location );
sound->name( Nodedata.name );
sound->deserialize( Input.getToken<std::string>(), sound_type::single ); sound->deserialize( Input.getToken<std::string>(), sound_type::single );
#endif #endif

288
sound.cpp
View File

@@ -12,6 +12,20 @@ http://mozilla.org/MPL/2.0/.
#include "sound.h" #include "sound.h"
#include "parser.h" #include "parser.h"
#include "globals.h" #include "globals.h"
#include "world.h"
#include "train.h"
// constructors
sound_source::sound_source( sound_placement const Placement, float const Range ) :
m_placement( Placement ),
m_range( Range )
{}
// destructor
sound_source::~sound_source() {
audio::renderer.erase( this );
}
// restores state of the class from provided data stream // restores state of the class from provided data stream
sound_source & sound_source &
@@ -27,14 +41,14 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons
switch( Legacytype ) { switch( Legacytype ) {
case sound_type::single: { case sound_type::single: {
// single sample only // single sample only
m_soundmain = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) ); m_soundmain.buffer = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
break; break;
} }
case sound_type::multipart: { case sound_type::multipart: {
// three samples: start, middle, stop // three samples: start, middle, stop
m_soundbegin = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) ); m_soundbegin.buffer = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
m_soundmain = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) ); m_soundmain.buffer = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
m_soundend = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) ); m_soundend.buffer = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
break; break;
} }
default: { default: {
@@ -44,12 +58,19 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons
if( Legacyparameters & sound_parameters::range ) { if( Legacyparameters & sound_parameters::range ) {
Input.getTokens( 1, false ); Input.getTokens( 1, false );
Input >> m_range;
} }
if( Legacyparameters & sound_parameters::amplitude ) { if( Legacyparameters & sound_parameters::amplitude ) {
Input.getTokens( 2, false ); Input.getTokens( 2, false );
Input
>> m_amplitudefactor
>> m_amplitudeoffset;
} }
if( Legacyparameters & sound_parameters::frequency ) { if( Legacyparameters & sound_parameters::frequency ) {
Input.getTokens( 2, false ); Input.getTokens( 2, false );
Input
>> m_frequencyfactor
>> m_frequencyoffset;
} }
return *this; return *this;
@@ -64,34 +85,41 @@ sound_source::play( int const Flags ) {
// if the sound is disabled altogether or nothing can be emitted from this source, no point wasting time // if the sound is disabled altogether or nothing can be emitted from this source, no point wasting time
return; return;
} }
if( m_range > 0 ) {
auto const cutoffrange{ (
( m_soundbegin.buffer != null_handle ) && ( Flags & sound_flags::looping ) ?
m_range * 10 : // larger margin to let the startup sample finish playing at safe distance
m_range * 5 ) };
if( glm::length2( location() - glm::dvec3{ Global::pCameraPosition } ) > std::min( 2750.f * 2750.f, cutoffrange * cutoffrange ) ) {
// drop sounds from beyond sensible and/or audible range
return;
}
}
// initialize emitter-specific pitch variation if it wasn't yet set
if( m_pitchvariation == 0.f ) {
m_pitchvariation = 0.01f * static_cast<float>( Random( 95, 105 ) );
}
m_flags = Flags; m_flags = Flags;
switch( m_stage ) { if( false == is_playing() ) {
case stage::none: // dispatch appropriate sound
case stage::restart: { // TODO: support for parameter-driven sound table
if( m_soundbegin != null_handle ) { if( m_soundbegin.buffer != null_handle ) {
std::vector<audio::buffer_handle> bufferlist{ m_soundbegin, m_soundmain }; std::vector<audio::buffer_handle> bufferlist { m_soundbegin.buffer, m_soundmain.buffer };
audio::renderer.insert( this, std::begin( bufferlist ), std::end( bufferlist ) ); audio::renderer.insert( this, std::begin( bufferlist ), std::end( bufferlist ) );
}
else {
audio::renderer.insert( this, m_soundmain );
}
break;
} }
case stage::main: { else {
// TODO: schedule another main sample playback, or a suitable sample from the table for combined sources audio::renderer.insert( this, m_soundmain.buffer );
break;
} }
case stage::end: { }
// schedule stop of current sequence end sample... else {
m_stage = stage::restart;
// ... and queue startup or main sound again if( ( m_soundbegin.buffer == null_handle )
return play( Flags ); && ( ( m_flags & ( sound_flags::exclusive | sound_flags::looping ) ) == 0 ) ) {
break; // for single part non-looping samples we allow spawning multiple instances, if not prevented by set flags
} audio::renderer.insert( this, m_soundmain.buffer );
default: {
break;
} }
} }
} }
@@ -100,30 +128,14 @@ sound_source::play( int const Flags ) {
void void
sound_source::stop() { sound_source::stop() {
if( ( m_stage == stage::none ) if( false == is_playing() ) { return; }
|| ( m_stage == stage::end ) ) {
return;
}
switch( m_stage ) { m_stop = true;
case stage::begin:
case stage::main: { if( ( m_soundend.buffer != null_handle )
// set the source to kill any currently active sounds && ( m_soundend.buffer != m_soundmain.buffer ) ) { // end == main can happen in malformed legacy cases
m_stage = stage::cease; // spawn potentially defined sound end sample, if the emitter is currently active
if( m_soundend != null_handle ) { audio::renderer.insert( this, m_soundend.buffer );
// and if there's defined sample for the sound end, play it instead
audio::renderer.insert( this, m_soundend );
}
break;
}
case stage::end: {
// set the source to kill any currently active sounds
m_stage = stage::cease;
break;
}
default: {
break;
}
} }
} }
@@ -134,19 +146,28 @@ sound_source::update( audio::openal_source &Source ) {
if( true == Source.is_playing ) { if( true == Source.is_playing ) {
// kill the sound if requested // kill the sound if requested
if( m_stage == stage::cease ) { if( true == m_stop ) {
Source.stop(); Source.stop();
update_counter( Source.buffers[ Source.buffer_index ], -1 );
if( false == is_playing() ) {
m_stop = false;
}
return; return;
} }
// TODO: positional update // check and update if needed current sound properties
if( m_soundbegin != null_handle ) { update_location();
// multipart sound update_placement_gain();
Source.sync_with( m_properties );
if( m_soundbegin.buffer != null_handle ) {
// potentially a multipart sound
// detect the moment when the sound moves from startup sample to the main // detect the moment when the sound moves from startup sample to the main
if( ( m_stage == stage::begin ) if( ( false == Source.is_looping )
&& ( Source.buffers[ Source.buffer_index ] == m_soundmain ) ) { && ( Source.buffers[ Source.buffer_index ] == m_soundmain.buffer ) ) {
// when it happens update active sample flags, and activate the looping // when it happens update active sample flags, and activate the looping
Source.loop( true ); Source.loop( true );
m_stage = stage::main; --( m_soundbegin.playing );
++( m_soundmain.playing );
} }
} }
} }
@@ -155,12 +176,14 @@ sound_source::update( audio::openal_source &Source ) {
// we can determine this from number of processed buffers // we can determine this from number of processed buffers
if( Source.buffer_index != Source.buffers.size() ) { if( Source.buffer_index != Source.buffers.size() ) {
auto const buffer { Source.buffers[ Source.buffer_index ] }; auto const buffer { Source.buffers[ Source.buffer_index ] };
if( buffer == m_soundbegin ) { m_stage = stage::begin; } update_counter( buffer, 1 );
// TODO: take ito accound sample table for combined sounds // emitter initialization
else if( buffer == m_soundmain ) { m_stage = stage::main; } Source.range( m_range );
else if( buffer == m_soundend ) { m_stage = stage::end; } Source.pitch( m_pitchvariation );
// TODO: emitter initialization update_location();
if( ( buffer == m_soundmain ) update_placement_gain();
Source.sync_with( m_properties );
if( ( buffer == m_soundmain.buffer )
&& ( true == TestFlag( m_flags, sound_flags::looping ) ) ) { && ( true == TestFlag( m_flags, sound_flags::looping ) ) ) {
// main sample can be optionally set to loop // main sample can be optionally set to loop
Source.loop( true ); Source.loop( true );
@@ -169,30 +192,151 @@ sound_source::update( audio::openal_source &Source ) {
Source.play(); Source.play();
} }
else { else {
//
auto const buffer { Source.buffers[ Source.buffer_index - 1 ] }; auto const buffer { Source.buffers[ Source.buffer_index - 1 ] };
if( ( buffer == m_soundend ) update_counter( buffer, -1 );
|| ( ( m_soundend == null_handle )
&& ( buffer == m_soundmain ) ) ) {
m_stage = stage::none;
}
} }
} }
} }
// sets base volume of the emiter to specified value
sound_source &
sound_source::gain( float const Gain ) {
m_properties.base_gain = clamp( Gain, 0.f, 2.f );
return *this;
}
// returns current base volume of the emitter
float
sound_source::gain() const {
return m_properties.base_gain;
}
// sets base pitch of the emitter to specified value
sound_source &
sound_source::pitch( float const Pitch ) {
m_properties.base_pitch = clamp( Pitch, 0.1f, 10.f );
return *this;
}
bool bool
sound_source::empty() const { sound_source::empty() const {
// NOTE: we test only the main sound, won't bother playing potential bookends if this is missing // NOTE: we test only the main sound, won't bother playing potential bookends if this is missing
// TODO: take into account presence of sample table, for combined sounds // TODO: take into account presence of sample table, for combined sounds
return ( m_soundmain == null_handle ); return ( m_soundmain.buffer == null_handle );
} }
// returns true if the source is emitting any sound // returns true if the source is emitting any sound
bool bool
sound_source::is_playing() const { sound_source::is_playing( bool const Includesoundends ) const {
return ( m_stage != stage::none ); return ( ( m_soundbegin.playing + m_soundmain.playing ) > 0 );
}
// returns location of the sound source in simulation region space
glm::dvec3 const
sound_source::location() const {
if( m_owner == nullptr ) {
// if emitter isn't attached to any vehicle the offset variable defines location in region space
return { m_offset };
}
// otherwise combine offset with the location of the carrier
return {
m_owner->GetPosition()
+ m_owner->VectorLeft() * m_offset.x
+ m_owner->VectorUp() * m_offset.y
+ m_owner->VectorFront() * m_offset.z };
}
void
sound_source::update_counter( audio::buffer_handle const Buffer, int const Value ) {
if( Buffer == m_soundbegin.buffer ) { m_soundbegin.playing += Value; }
// TODO: take ito accound sample table for combined sounds
else if( Buffer == m_soundmain.buffer ) { m_soundmain.playing += Value; }
else if( Buffer == m_soundend.buffer ) { m_soundend.playing += Value; }
}
float const EU07_SOUNDGAIN_LOW { 0.2f };
float const EU07_SOUNDGAIN_MEDIUM { 0.65f };
float const EU07_SOUNDGAIN_FULL { 1.f };
void
sound_source::update_location() {
m_properties.location = location();
}
bool
sound_source::update_placement_gain() {
// NOTE, HACK: current cab id can vary from -1 to +1
// we use this as modifier to force re-calculations when moving between compartments
int const activecab = (
FreeFlyModeFlag ?
0 :
( Global::pWorld->train() ?
Global::pWorld->train()->Dynamic()->MoverParameters->ActiveCab :
0 ) );
// location-based gain factor:
std::uintptr_t placementstamp = reinterpret_cast<std::uintptr_t>( (
FreeFlyModeFlag ?
nullptr :
( Global::pWorld->train() ?
Global::pWorld->train()->Dynamic() :
nullptr ) ) )
+ activecab;
if( placementstamp == m_properties.placement_stamp ) { return false; }
// listener location has changed, calculate new location-based gain factor
switch( m_placement ) {
case sound_placement::general: {
m_properties.placement_gain = EU07_SOUNDGAIN_FULL;
break;
}
case sound_placement::external: {
m_properties.placement_gain = (
placementstamp == 0 ?
EU07_SOUNDGAIN_FULL : // listener outside
EU07_SOUNDGAIN_LOW ); // listener in a vehicle
break;
}
case sound_placement::internal: {
m_properties.placement_gain = (
placementstamp == 0 ?
EU07_SOUNDGAIN_LOW : // listener outside
( Global::pWorld->train()->Dynamic() != m_owner ?
EU07_SOUNDGAIN_LOW : // in another vehicle
( activecab == 0 ?
EU07_SOUNDGAIN_LOW : // listener in the engine compartment
EU07_SOUNDGAIN_FULL ) ) ); // listener in the cab of the same vehicle
break;
}
case sound_placement::engine: {
m_properties.placement_gain = (
placementstamp == 0 ?
EU07_SOUNDGAIN_MEDIUM : // listener outside
( Global::pWorld->train()->Dynamic() != m_owner ?
EU07_SOUNDGAIN_LOW : // in another vehicle
( activecab == 0 ?
EU07_SOUNDGAIN_FULL : // listener in the engine compartment
EU07_SOUNDGAIN_LOW ) ) ); // listener in another compartment of the same vehicle
break;
}
default: {
// shouldn't ever land here, but, eh
m_properties.placement_gain = EU07_SOUNDGAIN_FULL;
break;
}
}
m_properties.placement_stamp = placementstamp;
return true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

100
sound.h
View File

@@ -13,7 +13,11 @@ http://mozilla.org/MPL/2.0/.
#include "classes.h" #include "classes.h"
#include "names.h" #include "names.h"
enum sound_type { float const EU07_SOUND_CABCONTROLSCUTOFFRANGE { 7.5f };
float const EU07_SOUND_BRAKINGCUTOFFRANGE { 100.f };
float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 100.f };
enum class sound_type {
single, single,
multipart multipart
}; };
@@ -29,27 +33,59 @@ enum sound_flags {
exclusive = 0x2 // the source won't dispatch more than one active instance of the sound; implied for multi-sounds exclusive = 0x2 // the source won't dispatch more than one active instance of the sound; implied for multi-sounds
}; };
enum class sound_placement {
general, // source is equally audible in potential carrier and outside of it
internal, // source is located inside of the carrier, and less audible when the listener is outside
engine, // source is located in the engine compartment, less audible when the listener is outside and even less in the cabs
external // source is located on the outside of the carrier, and less audible when the listener is inside
};
// mini controller and audio dispatcher; issues play commands for the audio renderer, // mini controller and audio dispatcher; issues play commands for the audio renderer,
// updates parameters of created audio emitters for the playback duration // updates parameters of created audio emitters for the playback duration
// TODO: move to simulation namespace after clean up of owner classes // TODO: move to simulation namespace after clean up of owner classes
class sound_source { class sound_source {
public: public:
// constructors
sound_source( sound_placement const Placement, float const Range = 50.f );
// destructor
~sound_source();
// methods // methods
// restores state of the class from provided data stream // restores state of the class from provided data stream
sound_source & sound_source &
deserialize( cParser &Input, sound_type const Legacytype, int const Legacyparameters = NULL ); deserialize( cParser &Input, sound_type const Legacytype, int const Legacyparameters = 0 );
sound_source & sound_source &
deserialize( std::string const &Input, sound_type const Legacytype, int const Legacyparameters = NULL ); deserialize( std::string const &Input, sound_type const Legacytype, int const Legacyparameters = 0 );
// issues contextual play commands for the audio renderer // issues contextual play commands for the audio renderer
void void
play( int const Flags = NULL ); play( int const Flags = 0 );
// stops currently active play commands controlled by this emitter // stops currently active play commands controlled by this emitter
void void
stop(); stop();
// adjusts parameters of provided implementation-side sound source // adjusts parameters of provided implementation-side sound source
void void
update( audio::openal_source &Source ); update( audio::openal_source &Source );
// sets base volume of the emiter to specified value
sound_source &
gain( float const Gain );
// returns current base volume of the emitter
float
gain() const;
// sets base pitch of the emitter to specified value
sound_source &
pitch( float const Pitch );
// owner setter/getter
void
owner( TDynamicObject const *Owner );
TDynamicObject const *
owner() const;
// sound source offset setter/getter
void
offset( glm::vec3 const Offset );
glm::vec3 const &
offset() const;
// sound source name setter/getter // sound source name setter/getter
void void
name( std::string Name ); name( std::string Name );
@@ -58,38 +94,62 @@ public:
// returns true if there isn't any sound buffer associated with the object, false otherwise // returns true if there isn't any sound buffer associated with the object, false otherwise
bool bool
empty() const; empty() const;
// returns true if the source is emitting any sound // returns true if the source is emitting any sound; by default doesn't take into account optional ending soudnds
bool bool
is_playing() const; is_playing( bool const Includesoundends = false ) const;
// returns location of the sound source in simulation region space
glm::dvec3 const
location() const;
// members
float m_amplitudefactor { 1.f }; // helper, value potentially used by gain calculation
float m_amplitudeoffset { 0.f }; // helper, value potentially used by gain calculation
float m_frequencyfactor { 1.f }; // helper, value potentially used by pitch calculation
float m_frequencyoffset { 0.f }; // helper, value potentially used by pitch calculation
private: private:
// types // types
enum stage { struct sound_data {
none, audio::buffer_handle buffer { null_handle };
begin, int playing { 0 }; // number of currently active sample instances
main,
end,
cease,
restart
}; };
// methods
void
update_counter( audio::buffer_handle const Buffer, int const Value );
void
update_location();
// potentially updates area-based gain factor of the source. returns: true if location has changed
bool
update_placement_gain();
// members // members
TDynamicObject * m_owner { nullptr }; // optional, the vehicle carrying this sound source TDynamicObject const * m_owner { nullptr }; // optional, the vehicle carrying this sound source
glm::vec3 m_offset; // relative position of the source, either from the owner or the region centre glm::vec3 m_offset; // relative position of the source, either from the owner or the region centre
sound_placement m_placement;
float m_range { 50.f }; // audible range of the emitted sounds
std::string m_name; std::string m_name;
stage m_stage{ stage::none }; int m_flags { 0 }; // requested playback parameters
int m_flags{ NULL }; sound_properties m_properties; // current properties of the emitted sounds
audio::buffer_handle m_soundmain { null_handle }; // main sound emitted by the source float m_pitchvariation { 0.f }; // emitter-specific shift in base pitch
audio::buffer_handle m_soundbegin { null_handle }; // optional, sound emitted before the main sound bool m_stop { false }; // indicates active sample instances should be terminated
audio::buffer_handle m_soundend { null_handle }; // optional, sound emitted after the main sound sound_data m_soundmain; // main sound emitted by the source
sound_data m_soundbegin; // optional, sound emitted before the main sound
sound_data m_soundend; // optional, sound emitted after the main sound
// TODO: table of samples with associated values, activated when controlling variable matches the value // TODO: table of samples with associated values, activated when controlling variable matches the value
}; };
// owner setter/getter
inline void sound_source::owner( TDynamicObject const *Owner ) { m_owner = Owner; }
inline TDynamicObject const * sound_source::owner() const { return m_owner; }
// sound source offset setter/getter
inline void sound_source::offset( glm::vec3 const Offset ) { m_offset = Offset; }
inline glm::vec3 const & sound_source::offset() const { return m_offset; }
// sound source name setter/getter // sound source name setter/getter
inline void sound_source::name( std::string Name ) { m_name = Name; } inline void sound_source::name( std::string Name ) { m_name = Name; }
inline std::string const & sound_source::name() const { return m_name; } inline std::string const & sound_source::name() const { return m_name; }
// collection of generators for power grid present in the scene // collection of sound sources present in the scene
class sound_table : public basic_table<sound_source> { class sound_table : public basic_table<sound_source> {
}; };