16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-23 00:29:19 +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 "Console.h"
#include "logs.h"
#include "renderer.h"
void TButton::Clear(int i)
{
@@ -24,8 +25,8 @@ void TButton::Clear(int i)
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; }
pModelOn = pModel->GetFromName( asName + "_on" );
@@ -34,7 +35,7 @@ void TButton::Init(std::string const &asName, TModel3d *pModel, bool bNewOn)
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;
@@ -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 ) {
// poszukiwanie submodeli w modelu
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
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
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
}
// 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
TButton::Turn( bool const State ) {

View File

@@ -12,18 +12,28 @@ http://mozilla.org/MPL/2.0/.
#include "Classes.h"
#include "sound.h"
class TButton
{ // animacja dwustanowa, włącza jeden z dwóch submodeli (jednego
// z nich może nie być)
private:
TSubModel
*pModelOn { nullptr },
*pModelOff { nullptr }; // submodel dla stanu załączonego i wyłączonego
bool m_state { false };
bool const *bData { nullptr };
int iFeedbackBit { 0 }; // Ra: bit informacji zwrotnej, do wyprowadzenia na pulpit
sound_source m_soundfxincrease; // sound associated with increasing control's value
sound_source m_soundfxdecrease; // sound associated with decreasing control's value
// animacja dwustanowa, włącza jeden z dwóch submodeli (jednego z nich może nie być)
class TButton {
public:
// methods
TButton() = default;
void Clear(int const i = -1);
inline void FeedbackBitSet( int const i ) {
iFeedbackBit = 1 << i; };
void Turn( bool const State );
inline
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
// imports member data pair from the config file
bool
@@ -32,20 +42,15 @@ class TButton
void
play();
public:
TButton() = default;
void Clear(int const i = -1);
inline void FeedbackBitSet(int const i) {
iFeedbackBit = 1 << i; };
void Turn( bool const State );
inline
bool Active() {
return ( ( pModelOn != nullptr )
|| ( 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);
// members
TSubModel
*pModelOn { nullptr },
*pModelOff { nullptr }; // submodel dla stanu załączonego i wyłączonego
bool m_state { false };
bool const *bData { nullptr };
int iFeedbackBit { 0 }; // Ra: bit informacji zwrotnej, do wyprowadzenia na pulpit
sound_source m_soundfxincrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with increasing control's value
sound_source m_soundfxdecrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with decreasing control's value
};
//---------------------------------------------------------------------------

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
go = cm_Ready; // gotów do odjazdu z W4 (semafor może
// 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;
}
continue; // nie analizować prędkości
} // koniec startu z zatrzymania
} // koniec obsługi początkowych stacji
@@ -2577,6 +2580,10 @@ bool TController::IncSpeed()
if (tsGuardSignal->GetStatus() & DSBSTATUS_PLAYING) // jeśli gada, to nie jedziemy
return false;
#else
if( ( tsGuardSignal != nullptr )
&& ( true == tsGuardSignal->is_playing() ) ) {
return false;
}
#endif
bool OK = true;
if( ( iDrivigFlags & moveDoorOpened )
@@ -2590,7 +2597,7 @@ bool TController::IncSpeed()
}
if( true == mvOccupied->DepartureSignal ) {
// shut off departure warning
mvOccupied->DepartureSignal = false;
mvOccupied->signal_departure( false );
}
if (mvControlling->SlippingWheels)
return false; // jak poślizg, to nie przyspieszamy
@@ -2984,7 +2991,7 @@ void TController::Doors(bool what)
if( ( true == mvOccupied->DoorClosureWarning )
&& ( false == mvOccupied->DepartureSignal )
&& ( 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
}
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,
false);
#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
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,
false);
#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
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
// składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić
if (iDrivigFlags & moveGuardSignal)
{ // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas
// stania
{ // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas stania
iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać
if( iDrivigFlags & moveDoorOpened ) // jeśli drzwi otwarte
if( !mvOccupied
->DoorOpenCtrl ) // jeśli drzwi niesterowane przez maszynistę
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
if( ( iDrivigFlags & moveDoorOpened )
&& ( false == mvOccupied->DoorOpenCtrl ) ) {
// jeśli drzwi otwarte, niesterowane przez maszynistę
Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika
}
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
if( tsGuardSignal != nullptr ) {
#ifdef EU07_USE_OLD_SOUNDCODE
tsGuardSignal->Play( 1.0, 0, true, pVehicle->GetPosition() ); // dźwięk niby przez radio
tsGuardSignal->Stop();
#else
tsGuardSignal->stop();
#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
}
}
}
}

1964
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)
// Ra: koneic animacji do ogarnięcia
private:
void ABuLittleUpdate(double ObjSqrDist);
bool btnOn; // ABu: czy byly uzywane buttony, jesli tak, to po renderingu wylacz
// bo ten sam model moze byc jeszcze wykorzystany przez inny obiekt!
double ComputeRadius( Math3D::vector3 p1, Math3D::vector3 p2, Math3D::vector3 p3, Math3D::vector3 p4);
private:
// types
struct coupler_sounds {
sound_source dsbCouplerAttach { sound_placement::external }; // moved from cab
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 btCoupler2;
TAirCoupler btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki
@@ -303,44 +324,55 @@ private:
double dRailLength;
double dRailPosition[MaxAxles]; // licznik pozycji osi w/m szyny
double dWheelsPosition[MaxAxles]; // pozycja osi w/m srodka pojazdu
sound_source rsStukot[MaxAxles]; // dzwieki poszczegolnych osi //McZapkie-270202
sound_source rsSilnik; // McZapkie-010302 - silnik
sound_source rsWentylator; // McZapkie-030302
sound_source rsPisk; // McZapkie-260302
sound_source rsDerailment; // McZapkie-051202
sound_source rsPrzekladnia;
sound_source sHorn1;
sound_source sHorn2;
sound_source sCompressor; // NBMX wrzesien 2003
sound_source sConverter;
sound_source sSmallCompressor;
sound_source sDepartureSignal;
sound_source sTurbo;
sound_source sSand;
sound_source sReleaser;
// Winger 010304
sound_source sPantUp;
sound_source sPantDown;
sound_source rsDoorOpen; // Ra: przeniesione z kabiny
sound_source rsDoorClose;
std::vector<sound_source> rsStukot; // dzwieki poszczegolnych osi //McZapkie-270202
// engine sounds
sound_source dsbDieselIgnition { sound_placement::engine }; // moved from cab
sound_source rsSilnik { sound_placement::engine };
sound_source dsbRelay { sound_placement::engine };
sound_source dsbWejscie_na_bezoporow { sound_placement::engine }; // moved from cab
sound_source dsbWejscie_na_drugi_uklad { sound_placement::engine }; // moved from cab
sound_source rsPrzekladnia { sound_placement::engine };
sound_source rsEngageSlippery { sound_placement::engine }; // moved from cab
sound_source rsDieselInc { sound_placement::engine }; // youBy
sound_source sTurbo { sound_placement::engine };
sound_source rsWentylator { sound_placement::engine }; // McZapkie-030302
sound_source sConverter { sound_placement::engine };
sound_source sCompressor { sound_placement::engine }; // NBMX wrzesien 2003
sound_source sSmallCompressor { sound_placement::engine };
// braking sounds
sound_source dsbPneumaticRelay { sound_placement::external };
sound_source rsUnbrake { sound_placement::external }; // yB - odglos luzowania
sound_source sReleaser { sound_placement::external };
sound_source rsSlippery { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab
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_frq_act;
double eng_dfrq;
double eng_turbo;
void ABuBogies();
void ABuModelRoll();
Math3D::vector3 modelShake;
bool renderme; // yB - czy renderowac
// TRealSound sBrakeAcc; //dźwięk przyspieszacza
sound_source sBrakeAcc;
bool bBrakeAcc;
sound_source rsUnbrake; // yB - odglos luzowania
float ModCamRot;
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:
int iHornWarning; // numer syreny do użycia po otrzymaniu sygnału do jazdy
@@ -376,10 +408,10 @@ private:
void SetPneumatic(bool front, bool red);
std::string asName;
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
// youBy - dym
// TSmoke Smog;
@@ -430,13 +462,17 @@ private:
inline Math3D::vector3 RearPosition() {
return vCoulpler[iDirection]; };
inline Math3D::vector3 AxlePositionGet() {
return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; };
return iAxleFirst ?
Axle1.pPosition :
Axle0.pPosition; };
inline Math3D::vector3 VectorFront() const {
return vFront; };
inline Math3D::vector3 VectorUp() {
inline Math3D::vector3 VectorUp() const {
return vUp; };
inline Math3D::vector3 VectorLeft() const {
return vLeft; };
inline double const * Matrix() const {
return mMatrix.readArray(); };
inline double * Matrix() {
return mMatrix.getArray(); };
inline double GetVelocity() {
@@ -446,7 +482,9 @@ private:
inline double GetWidth() const {
return MoverParameters->Dim.W; };
inline TTrack * GetTrack() {
return (iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack()); };
return (iAxleFirst ?
Axle1.GetTrack() :
Axle0.GetTrack()); };
// McZapkie-260202
void LoadMMediaFile(std::string BaseDir, std::string TypeName, std::string ReplacableSkin);
@@ -455,13 +493,22 @@ private:
return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection()); };
// zwraca kierunek pojazdu na torze z aktywną osą
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ą
inline double RaTranslationGet() {
return iAxleFirst ? Axle1.GetTranslation() : Axle0.GetTranslation(); };
return iAxleFirst ?
Axle1.GetTranslation() :
Axle0.GetTranslation(); };
// zwraca tor z aktywną osią
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 RadioStop();
void Damage(char flag);

View File

@@ -1022,34 +1022,27 @@ event_manager::CheckQuery() {
return false;
}
case tp_Sound: {
#ifdef EU07_USE_OLD_SOUNDCODE
if( m_workevent->Params[ 9 ].tsTextSound == nullptr ) {
break;
}
switch( m_workevent->Params[ 0 ].asInt ) {
// trzy możliwe przypadki:
case 0: {
m_workevent->Params[ 9 ].tsTextSound->Stop();
m_workevent->Params[ 9 ].tsTextSound->stop();
break;
}
case 1: {
m_workevent->Params[ 9 ].tsTextSound->Play(
1,
0,
true,
m_workevent->Params[ 9 ].tsTextSound->vSoundPosition );
m_workevent->Params[ 9 ].tsTextSound->play( sound_flags::exclusive );
break;
}
case -1: {
m_workevent->Params[ 9 ].tsTextSound->Play(
1,
DSBPLAY_LOOPING,
true,
m_workevent->Params[ 9 ].tsTextSound->vSoundPosition );
m_workevent->Params[ 9 ].tsTextSound->play( sound_flags::exclusive | sound_flags::looping );
break;
}
default: {
break;
}
}
#endif
break;
}
case tp_Disable:
@@ -1574,14 +1567,19 @@ event_manager::InitLaunchers() {
}
}
launcher->Event1 = (
launcher->asEvent1Name != "none" ?
simulation::Events.FindEvent( launcher->asEvent1Name ) :
nullptr );
launcher->Event2 = (
launcher->asEvent2Name != "none" ?
simulation::Events.FindEvent( launcher->asEvent2Name ) :
nullptr );
if( launcher->asEvent1Name != "none" ) {
launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name );
if( launcher->Event1 == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" cannot find event \"" + launcher->asEvent1Name + "\"" );
}
}
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 "Timer.h"
#include "logs.h"
#include "renderer.h"
void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale, double fNewOffset, double fNewFriction, double fNewValue)
{ // ustawienie parametrów animacji submodelu
if (NewSubModel)
{ // warunek na wszelki wypadek, gdyby się submodel nie
// podłączył
if (NewSubModel) {
// warunek na wszelki wypadek, gdyby się submodel nie podłączył
fFriction = fNewFriction;
fValue = fNewValue;
fOffset = fNewOffset;
fScale = fNewScale;
SubModel = NewSubModel;
eType = eNewType;
if (eType == gt_Digital)
{
if (eType == gt_Digital) {
TSubModel *sm = SubModel->ChildGet();
do
{ // pętla po submodelach potomnych i obracanie ich o kąt zależy od
// cyfry w (fValue)
do {
// pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue)
if (sm->pName.size())
{ // musi mieć niepustą nazwę
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ą?
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;
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;
TSubModel *submodel = md1->GetFromName( submodelname );
TSubModel *submodel = md1->GetFromName( submodelname );
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;
}
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)
submodel = md2->GetFromName(submodelname); // to może tam będzie, co za różnica gdzie
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 {
@@ -132,7 +145,7 @@ TGauge::Load_mapping( cParser &Input ) {
if( indexstart != std::string::npos ) {
m_soundfxvalues.emplace(
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
@@ -284,8 +297,6 @@ void TGauge::Update() {
}
};
void TGauge::Render(){};
void TGauge::AssignFloat(float *fValue)
{
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
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:
// methods
// imports member data pair from the config file
@@ -44,32 +68,10 @@ private:
double *dData { nullptr };
int *iData;
};
sound_source m_soundfxincrease; // sound associated with increasing control's value
sound_source m_soundfxdecrease; // sound associated with decreasing 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_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with decreasing control's value
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)
bool Global::bWireFrame = false;
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
bool Global::MultipleLogs{ false };

View File

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

View File

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

View File

@@ -1369,11 +1369,11 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap
if (EngineType == ElectricSeriesMotor)
if (AutoRelayCheck())
SetFlag(SoundFlag, sound_relay);
SetFlag(SoundFlag, sound::relay);
if (EngineType == DieselEngine)
if (dizel_Update(dt))
SetFlag(SoundFlag, sound_relay);
SetFlag(SoundFlag, sound::relay);
// uklady hamulcowe:
if (VeselVolume > 0)
Compressor = CompressedVolume / VeselVolume;
@@ -1530,7 +1530,7 @@ double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape
if (EngineType == DieselEngine)
if (dizel_Update(dt))
SetFlag(SoundFlag, sound_relay);
SetFlag(SoundFlag, sound::relay);
// uklady hamulcowe:
if (VeselVolume > 0)
Compressor = CompressedVolume / VeselVolume;
@@ -1703,7 +1703,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed)
if( RList[ MainCtrlPos ].Bn > 1 ) {
if( true == MaxCurrentSwitch( false )) {
// wylaczanie wysokiego rozruchu
SetFlag( SoundFlag, sound_relay );
SetFlag( SoundFlag, sound::relay );
} // Q TODO:
// if (EngineType=ElectricSeriesMotor) and (MainCtrlPos=1)
// then
@@ -2988,6 +2988,10 @@ bool TMoverParameters::BrakeDelaySwitch(int BDS)
}
else
rBDS = false;
if( true == rBDS ) {
// if setting was changed emit the sound of pneumatic relay
SetFlag( SoundFlag, sound::pneumatic );
}
return rBDS;
}
@@ -3086,8 +3090,8 @@ void TMoverParameters::CompressorCheck(double dt)
else
{
CompressedVolume = CompressedVolume * 0.8;
SetFlag(SoundFlag, sound_relay | sound_loud);
// SetFlag(SoundFlag, sound_loud);
SetFlag(SoundFlag, sound::relay | 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
}
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
// ABu: proby znalezienia problemu ze zle odbijajacymi sie skladami
//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) *
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 =
abs(newdist) - abs(Couplers[CouplerN].Dist); // McZapkie-191103: poprawka na histereze
Couplers[CouplerN].Dist = newdist;
@@ -4919,7 +4931,7 @@ bool TMoverParameters::FuseOn(void)
{
FuseFlag = false; // wlaczenie ponowne obwodu
FO = true;
SetFlag(SoundFlag, sound_relay | sound_loud);
SetFlag(SoundFlag, sound::relay | sound::loud);
}
}
return FO;
@@ -4935,7 +4947,7 @@ void TMoverParameters::FuseOff(void)
{
FuseFlag = 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:
// szybkie wchodzenie na bezoporowa (303E)
OK = true;
SetFlag(SoundFlag, sound_manyrelay | sound_loud);
SetFlag(SoundFlag, sound::parallel | sound::loud);
}
else if ((LastRelayTime > CtrlDelay) && (ARFASI))
{
@@ -5240,13 +5252,13 @@ bool TMoverParameters::AutoRelayCheck(void)
if ((RList[MainCtrlActualPos].R == 0) &&
(!(MainCtrlActualPos == MainCtrlPosNo))) // wejscie na bezoporowa
{
SetFlag(SoundFlag, sound_manyrelay | sound_loud);
SetFlag(SoundFlag, sound::parallel | sound::loud);
}
else if ((RList[MainCtrlActualPos].R > 0) &&
(RList[MainCtrlActualPos - 1].R ==
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:
// szybkie wchodzenie na bezoporowa (303E)
OK = true;
SetFlag(SoundFlag, sound_manyrelay);
SetFlag(SoundFlag, sound::parallel);
}
else if (LastRelayTime > CtrlDownDelay)
{
@@ -5272,7 +5284,7 @@ bool TMoverParameters::AutoRelayCheck(void)
if (RList[MainCtrlActualPos].R ==
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
MainCtrlActualPos = 1;
DelayCtrlFlag = false;
SetFlag(SoundFlag, sound_relay | sound_loud);
SetFlag(SoundFlag, sound::relay | sound::loud);
OK = true;
}
}
@@ -5858,6 +5870,32 @@ bool TMoverParameters::DoorRight(bool State)
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
// 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 );
}
else if( Command == "DepartureSignal" ) {
DepartureSignal = (
CValue1 == 1 ?
true :
false );
OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
}
else if (Command == "PantFront") /*Winger 160204*/
{ // 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
@@ -8346,6 +8391,8 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
if( true == Hamulec->SetBDF( brakesetting ) ) {
BrakeDelayFlag = brakesetting;
OK = true;
// if setting was changed emit the sound of pneumatic relay
SetFlag( SoundFlag, sound::pneumatic );
}
else {
OK = false;

View File

@@ -578,47 +578,58 @@ void TESt::CheckReleaser( double const dt )
}
}
void TESt::CheckState( double const BCP, double &dV1 )
{
double VVP;
double BVP;
double CVP;
void TESt::CheckState( double const BCP, double &dV1 ) {
BVP = BrakeRes->P();
VVP = ValveRes->P();
// if (BVP<VVP) then
// VVP:=(BVP+VVP)/2;
CVP = CntrlRes->P() - 0.0;
double const VVP { ValveRes->P() };
double const BVP { BrakeRes->P() };
double const CVP { CntrlRes->P() };
// sprawdzanie stanu
if (((BrakeStatus & b_hld) == b_hld) && (BCP > 0.25))
if ((VVP + 0.003 + BCP / BVM < CVP))
BrakeStatus |= b_on; // hamowanie stopniowe
else if ((VVP - 0.003 + (BCP - 0.1) / BVM > CVP))
BrakeStatus &= ~( b_on | b_hld ); // luzowanie
else if ((VVP + BCP / BVM > CVP))
BrakeStatus &= ~b_on; // zatrzymanie napelaniania
else
;
else if ((VVP + 0.10 < CVP) && (BCP < 0.25)) // poczatek hamowania
{
if ((BrakeStatus & b_hld) == b_off)
{
ValveRes->CreatePress(0.02 * VVP);
SoundFlag |= sf_Acc;
ValveRes->Act();
if( BCP > 0.25 ) {
if( ( BrakeStatus & b_hld ) == b_hld ) {
if( ( VVP + 0.003 + BCP / BVM ) < CVP ) {
// hamowanie stopniowe
BrakeStatus |= b_on;
}
else {
if( ( VVP + BCP / BVM ) > CVP ) {
// zatrzymanie napelaniania
BrakeStatus &= ~b_on;
}
if( ( VVP - 0.003 + ( BCP - 0.1 ) / BVM ) > CVP ) {
// luzowanie
BrakeStatus &= ~( b_on | b_hld );
}
}
}
BrakeStatus |= (b_on | b_hld);
else {
// ValveRes.CreatePress(0);
// dV1:=1;
if( ( VVP + BCP / BVM < CVP )
&& ( ( CVP - VVP ) * BVM > 0.25 ) ) {
// zatrzymanie luzowanie
BrakeStatus |= b_hld;
}
}
}
else if ((VVP + (BCP - 0.1) / BVM < CVP) && ((CVP - VVP) * BVM > 0.25) &&
(BCP > 0.25)) // zatrzymanie luzowanie
BrakeStatus |= b_hld;
else {
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;
}
}
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
BrakeStatus |= b_hld;
if( ( BrakeStatus & b_hld ) == 0 ) {
SoundFlag |= sf_CylU;
}
// przeplyw ZS <-> PG
if ((BVP < CVP - 0.2) || (BrakeStatus != b_off) || (BCP > 0.25))
temp = 0;
@@ -1211,6 +1226,40 @@ double TLSt::GetPF( double const PP, double const dt, double const Vel )
double dV1{ 0.0 };
// 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( CVP < 0.0 ) {
BrakeStatus &= ~b_rls;
@@ -1219,18 +1268,11 @@ double TLSt::GetPF( double const PP, double const dt, double const Vel )
{ // 008
dV = PF1( CVP, BCP, 0.024 ) * dt;
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
double temp;
if (((CVP - BCP) * BVM > 0.5))
temp = 0.0;
else if ((VVP > CVP + 0.4))
@@ -1888,22 +1930,51 @@ void TKE::CheckState( double const BCP, double &dV1 )
CVP = CntrlRes->P();
// sprawdzanie stanu
if ((BrakeStatus & b_hld) == b_hld)
if ((VVP + 0.003 + BCP / BVM < CVP))
BrakeStatus |= b_on; // hamowanie stopniowe;
else if ((VVP - 0.003 + BCP / BVM > CVP))
BrakeStatus &= ~( b_on | b_hld ); // luzowanie;
else if ((VVP + BCP / BVM > CVP))
BrakeStatus &= ~b_on; // zatrzymanie napelaniania;
else
;
else if ((VVP + 0.10 < CVP) && (BCP < 0.1)) // poczatek hamowania
{
BrakeStatus |= (b_on | b_hld);
ValveRes->CreatePress(0.8 * VVP); // przyspieszacz
if( BCP > 0.1 ) {
if( ( BrakeStatus & b_hld ) == b_hld ) {
if( ( VVP + 0.003 + BCP / BVM ) < CVP ) {
// hamowanie stopniowe;
BrakeStatus |= b_on;
}
else {
if( ( VVP + BCP / BVM ) > CVP ) {
// zatrzymanie napelaniania;
BrakeStatus &= ~b_on;
}
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 )

View File

@@ -1050,14 +1050,17 @@ void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, g
*/
};
void TSubModel::ParentMatrix(float4x4 *m)
{ // pobranie transformacji względem wstawienia modelu
// jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D), to
// dodatkowy obrót
// obrót T3D jest wymagany np. do policzenia wysokości pantografów
*m = float4x4(*fMatrix); // skopiowanie, bo będziemy mnożyć
// m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak)
TSubModel *sm = this;
void TSubModel::ParentMatrix( float4x4 *m ) const { // pobranie transformacji względem wstawienia modelu
// jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D),
// to dodatkowy obrót obrót T3D jest wymagany np. do policzenia wysokości pantografów
if( fMatrix != nullptr ) {
// skopiowanie, bo będziemy mnożyć
*m = float4x4( *fMatrix );
}
else {
m->Identity();
}
auto *sm = this;
while (sm->Parent)
{ // przenieść tę funkcję do modelu
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)
{
// wczytanie modelu z pliku

View File

@@ -124,7 +124,9 @@ private:
TSubModel *Next { nullptr };
TSubModel *Child { nullptr };
public: // temporary access, clean this up during refactoring
gfx::geometry_handle m_geometry { 0, 0 }; // geometry of the submodel
private:
material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak
bool bWire { false }; // nie używane, ale wczytywane
float Opacity { 1.0f };
@@ -138,9 +140,7 @@ public: // chwilowo
float m_boundingradius { 0 };
size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
TAnimType b_aAnim{ at_None }; // kody animacji oddzielnie, bo zerowane
public:
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 *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy
int iVisible{ 1 }; // roboczy stan widoczności
@@ -172,7 +172,10 @@ public:
void SetRotateIK1(float3 vNewAngles);
TSubModel * GetFromName( std::string const &search, bool i = true );
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 );
int FlagsCheck();
@@ -200,7 +203,7 @@ public:
return *(fMatrix->TranslationGet()) + Child->Translation1Get(); }
material_handle GetMaterial() const {
return m_material; }
void ParentMatrix(float4x4 *m);
void ParentMatrix(float4x4 *m) const;
float MaxY( float4x4 const &m );
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();
bool Update( double const Deltatime );
void update_sounds( double const Deltatime );
bool m_updated = false;
void MechStop();
void SetLights();
// McZapkie-310302: ladowanie parametrow z pliku
@@ -102,6 +101,7 @@ class TTrain
// types
typedef void( *command_handler )( TTrain *Train, command_data const &Command );
typedef std::unordered_map<user_command, command_handler> commandhandler_map;
// methods
// clears state of all cabin controls
void clear_cab_controls();
// sets cabin controls based on current state of the vehicle
@@ -226,7 +226,7 @@ public: // reszta może by?publiczna
TGauge ggMainCtrl;
TGauge ggMainCtrlAct;
TGauge ggScndCtrl;
TGauge ggScndCtrlButton;
TGauge ggScndCtrlButton; // NOTE: not used?
TGauge ggDirKey;
TGauge ggBrakeCtrl;
TGauge ggLocalBrake;
@@ -398,42 +398,23 @@ public: // reszta może by?publiczna
double fMechRoll;
double fMechPitch;
sound_source dsbNastawnikJazdy;
sound_source dsbNastawnikBocz; // hunter-081211
sound_source dsbRelay;
sound_source dsbPneumaticRelay;
sound_source dsbSwitch;
sound_source dsbPneumaticSwitch;
sound_source dsbReverserKey; // hunter-121211
sound_source dsbReverserKey { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-121211
sound_source dsbNastawnikJazdy { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbNastawnikBocz { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-081211
sound_source dsbSwitch { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbPneumaticSwitch { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbCouplerAttach; // Ra: w kabinie????
sound_source dsbCouplerDetach; // Ra: w kabinie???
sound_source rsHiss { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // upuszczanie
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???
// Winger 010304
sound_source dsbWejscie_na_bezoporow;
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
sound_source rsFadeSound { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbHasler { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbBuzzer { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbSlipAlarm { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // Bombardier 011010: alarm przy poslizgu dla 181/182
int iCabLightFlag; // McZapkie:120503: oswietlenie kabiny (0: wyl, 1: przyciemnione, 2: pelne)
bool bCabLight; // hunter-091012: czy swiatlo jest zapalone?
@@ -443,9 +424,6 @@ public: // reszta może by?publiczna
vector3 MirrorPosition(bool lewe);
private:
sound_source dsbCouplerStretch;
sound_source dsbEN57_CouplerStretch;
sound_source dsbBufferClamp;
double fBlinkTimer;
float fHaslerTimer;
float fConverterTimer; // hunter-261211: dla przekaznika
@@ -494,7 +472,9 @@ private:
float fEIMParams[9][10]; // parametry dla silnikow asynchronicznych
int RadioChannel() { return iRadioChannel; };
inline TDynamicObject *Dynamic() { return DynamicObject; };
inline TDynamicObject const *Dynamic() const { return DynamicObject; };
inline TMoverParameters *Controlled() { return mvControlled; };
inline TMoverParameters const *Controlled() const { return mvControlled; };
void DynamicSet(TDynamicObject *d);
void Silence();

View File

@@ -732,7 +732,9 @@ void TWorld::OnKeyDown(int cKey)
if (vehicle->MoverParameters->IncLocalBrakeLevelFAST())
if (Train)
{ // dźwięk oczywiście jest w kabinie
#ifdef EU07_USE_OLD_SOUNDCODE
Train->dsbPneumaticRelay.play();
#endif
}
}
}
@@ -751,7 +753,9 @@ void TWorld::OnKeyDown(int cKey)
if (vehicle->MoverParameters->DecLocalBrakeLevelFAST())
if (Train)
{ // dźwięk oczywiście jest w kabinie
#ifdef EU07_USE_OLD_SOUNDCODE
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ę
delete Train;
Train = new TTrain();
if( dynamic->iCabs & 1 ) {
if( dynamic->iCabs & 0x1 ) {
dynamic->MoverParameters->ActiveCab = 1;
Train->Init( dynamic, true );
}
if( dynamic->iCabs & 4 ) {
if( dynamic->iCabs & 0x4 ) {
dynamic->MoverParameters->ActiveCab = -1;
Train->Init( dynamic, true );
}
if( dynamic->iCabs & 2 ) {
if( dynamic->iCabs & 0x2 ) {
dynamic->MoverParameters->ActiveCab = 0;
Train->Init( dynamic, true );
}

View File

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

View File

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

View File

@@ -11,12 +11,15 @@ http://mozilla.org/MPL/2.0/.
#include "audiorenderer.h"
#include "sound.h"
#include "globals.h"
#include "logs.h"
namespace audio {
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
void
openal_source::play() {
@@ -29,17 +32,16 @@ openal_source::play() {
void
openal_source::stop() {
::alSourcei( id, AL_LOOPING, AL_FALSE );
loop( false );
::alSourceStop( id );
// NOTE: we don't update the is_playing flag
// 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
is_playing = false;
}
// updates state of the source
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
::alGetSourcei( id, AL_BUFFERS_PROCESSED, &buffer_index );
@@ -58,10 +60,79 @@ openal_source::update( int const Deltatime ) {
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
void
openal_source::loop( bool const State ) {
if( is_looping == State ) { return; }
is_looping = State;
::alSourcei(
id,
AL_LOOPING,
@@ -78,9 +149,7 @@ openal_source::clear() {
controller = nullptr;
// unqueue bound buffers:
// ensure no buffer is in use...
::alSourcei( id, AL_LOOPING, AL_FALSE );
::alSourceStop( id );
is_playing = false;
stop();
// ...prepare space for returned ids of unqueued buffers (not that we need that info)...
std::vector<ALuint> bufferids;
bufferids.resize( buffers.size() );
@@ -88,6 +157,8 @@ openal_source::clear() {
::alSourceUnqueueBuffers( id, bufferids.size(), bufferids.data() );
buffers.clear();
buffer_index = 0;
// reset properties
properties = sound_properties();
}
@@ -125,6 +196,9 @@ openal_renderer::init() {
// basic initialization failed
return false;
}
//
// ::alDistanceModel( AL_LINEAR_DISTANCE );
::alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED );
// all done
m_ready = true;
return true;
@@ -141,10 +215,45 @@ openal_renderer::insert( sound_source *Controller, audio::buffer_handle const So
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
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 ) };
while( source != std::end( m_sources ) ) {
// update each source

View File

@@ -14,6 +14,15 @@ http://mozilla.org/MPL/2.0/.
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 {
// implementation part of the sound emitter
@@ -29,6 +38,8 @@ struct openal_source {
buffer_sequence buffers; // sequence of samples the source will emit
int buffer_index; // currently queued sample from the buffer sequence
bool is_playing { false };
bool is_looping { false };
sound_properties properties;
// methods
template <class Iterator_>
@@ -47,25 +58,42 @@ struct openal_source {
// starts playback of queued buffers
void
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
void
stop();
// updates state of the source
void
update( int const Deltatime );
// toggles looping of the sound emitted by the source
void
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
// NOTE: doesn't release allocated implementation-side source
void
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 {
friend class opengl_renderer;
public:
// destructor
~openal_renderer();
@@ -89,9 +117,12 @@ public:
// schedules playback of specified sample, under control of the specified sound emitter
void
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
void
update( int const Deltatime );
update( double const Deltatime );
private:
// types
@@ -107,6 +138,7 @@ private:
ALCdevice * m_device { nullptr };
ALCcontext * m_context { nullptr };
bool m_ready { false }; // renderer is initialized and functional
glm::dvec3 m_listenerposition;
buffer_manager m_buffers;
// 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;
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
//---------------------------------------------------------------------------

View File

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

View File

@@ -441,6 +441,25 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
#endif
switch_units( true, true, true );
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
setup_drawing( true );
Render_Alpha( simulation::Region );
@@ -2990,11 +3009,12 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
if( glarelevel > 0.0f ) {
// 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 );
::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel );
::glDisable( GL_LIGHTING );
::glDepthMask( GL_FALSE );
::glBlendFunc( GL_SRC_ALPHA, GL_ONE );
::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 );
sound->name( Nodedata.name );
#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 );
#endif

288
sound.cpp
View File

@@ -12,6 +12,20 @@ http://mozilla.org/MPL/2.0/.
#include "sound.h"
#include "parser.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
sound_source &
@@ -27,14 +41,14 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons
switch( Legacytype ) {
case sound_type::single: {
// 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;
}
case sound_type::multipart: {
// three samples: start, middle, stop
m_soundbegin = 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_soundend = 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.buffer = 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;
}
default: {
@@ -44,12 +58,19 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons
if( Legacyparameters & sound_parameters::range ) {
Input.getTokens( 1, false );
Input >> m_range;
}
if( Legacyparameters & sound_parameters::amplitude ) {
Input.getTokens( 2, false );
Input
>> m_amplitudefactor
>> m_amplitudeoffset;
}
if( Legacyparameters & sound_parameters::frequency ) {
Input.getTokens( 2, false );
Input
>> m_frequencyfactor
>> m_frequencyoffset;
}
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
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;
switch( m_stage ) {
case stage::none:
case stage::restart: {
if( m_soundbegin != null_handle ) {
std::vector<audio::buffer_handle> bufferlist{ m_soundbegin, m_soundmain };
audio::renderer.insert( this, std::begin( bufferlist ), std::end( bufferlist ) );
}
else {
audio::renderer.insert( this, m_soundmain );
}
break;
if( false == is_playing() ) {
// dispatch appropriate sound
// TODO: support for parameter-driven sound table
if( m_soundbegin.buffer != null_handle ) {
std::vector<audio::buffer_handle> bufferlist { m_soundbegin.buffer, m_soundmain.buffer };
audio::renderer.insert( this, std::begin( bufferlist ), std::end( bufferlist ) );
}
case stage::main: {
// TODO: schedule another main sample playback, or a suitable sample from the table for combined sources
break;
else {
audio::renderer.insert( this, m_soundmain.buffer );
}
case stage::end: {
// schedule stop of current sequence end sample...
m_stage = stage::restart;
// ... and queue startup or main sound again
return play( Flags );
break;
}
default: {
break;
}
else {
if( ( m_soundbegin.buffer == null_handle )
&& ( ( m_flags & ( sound_flags::exclusive | sound_flags::looping ) ) == 0 ) ) {
// for single part non-looping samples we allow spawning multiple instances, if not prevented by set flags
audio::renderer.insert( this, m_soundmain.buffer );
}
}
}
@@ -100,30 +128,14 @@ sound_source::play( int const Flags ) {
void
sound_source::stop() {
if( ( m_stage == stage::none )
|| ( m_stage == stage::end ) ) {
return;
}
if( false == is_playing() ) { return; }
switch( m_stage ) {
case stage::begin:
case stage::main: {
// set the source to kill any currently active sounds
m_stage = stage::cease;
if( m_soundend != null_handle ) {
// 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;
}
m_stop = true;
if( ( m_soundend.buffer != null_handle )
&& ( m_soundend.buffer != m_soundmain.buffer ) ) { // end == main can happen in malformed legacy cases
// spawn potentially defined sound end sample, if the emitter is currently active
audio::renderer.insert( this, m_soundend.buffer );
}
}
@@ -134,19 +146,28 @@ sound_source::update( audio::openal_source &Source ) {
if( true == Source.is_playing ) {
// kill the sound if requested
if( m_stage == stage::cease ) {
if( true == m_stop ) {
Source.stop();
update_counter( Source.buffers[ Source.buffer_index ], -1 );
if( false == is_playing() ) {
m_stop = false;
}
return;
}
// TODO: positional update
if( m_soundbegin != null_handle ) {
// multipart sound
// check and update if needed current sound properties
update_location();
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
if( ( m_stage == stage::begin )
&& ( Source.buffers[ Source.buffer_index ] == m_soundmain ) ) {
if( ( false == Source.is_looping )
&& ( Source.buffers[ Source.buffer_index ] == m_soundmain.buffer ) ) {
// when it happens update active sample flags, and activate the looping
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
if( Source.buffer_index != Source.buffers.size() ) {
auto const buffer { Source.buffers[ Source.buffer_index ] };
if( buffer == m_soundbegin ) { m_stage = stage::begin; }
// TODO: take ito accound sample table for combined sounds
else if( buffer == m_soundmain ) { m_stage = stage::main; }
else if( buffer == m_soundend ) { m_stage = stage::end; }
// TODO: emitter initialization
if( ( buffer == m_soundmain )
update_counter( buffer, 1 );
// emitter initialization
Source.range( m_range );
Source.pitch( m_pitchvariation );
update_location();
update_placement_gain();
Source.sync_with( m_properties );
if( ( buffer == m_soundmain.buffer )
&& ( true == TestFlag( m_flags, sound_flags::looping ) ) ) {
// main sample can be optionally set to loop
Source.loop( true );
@@ -169,30 +192,151 @@ sound_source::update( audio::openal_source &Source ) {
Source.play();
}
else {
//
auto const buffer { Source.buffers[ Source.buffer_index - 1 ] };
if( ( buffer == m_soundend )
|| ( ( m_soundend == null_handle )
&& ( buffer == m_soundmain ) ) ) {
m_stage = stage::none;
}
update_counter( buffer, -1 );
}
}
}
// 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
sound_source::empty() const {
// 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
return ( m_soundmain == null_handle );
return ( m_soundmain.buffer == null_handle );
}
// returns true if the source is emitting any sound
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 "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,
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
};
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,
// updates parameters of created audio emitters for the playback duration
// TODO: move to simulation namespace after clean up of owner classes
class sound_source {
public:
// constructors
sound_source( sound_placement const Placement, float const Range = 50.f );
// destructor
~sound_source();
// methods
// restores state of the class from provided data stream
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 &
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
void
play( int const Flags = NULL );
play( int const Flags = 0 );
// stops currently active play commands controlled by this emitter
void
stop();
// adjusts parameters of provided implementation-side sound source
void
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
void
name( std::string Name );
@@ -58,38 +94,62 @@ public:
// returns true if there isn't any sound buffer associated with the object, false otherwise
bool
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
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:
// types
enum stage {
none,
begin,
main,
end,
cease,
restart
struct sound_data {
audio::buffer_handle buffer { null_handle };
int playing { 0 }; // number of currently active sample instances
};
// 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
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
sound_placement m_placement;
float m_range { 50.f }; // audible range of the emitted sounds
std::string m_name;
stage m_stage{ stage::none };
int m_flags{ NULL };
audio::buffer_handle m_soundmain { null_handle }; // main sound emitted by the source
audio::buffer_handle m_soundbegin { null_handle }; // optional, sound emitted before the main sound
audio::buffer_handle m_soundend { null_handle }; // optional, sound emitted after the main sound
int m_flags { 0 }; // requested playback parameters
sound_properties m_properties; // current properties of the emitted sounds
float m_pitchvariation { 0.f }; // emitter-specific shift in base pitch
bool m_stop { false }; // indicates active sample instances should be terminated
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
};
// 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
inline void sound_source::name( std::string Name ) { m_name = 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> {
};