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

build 210303. vehicle control hint system, virtual trainman toggle, configurable diesel engine rpm deceleration rate, vehicle repair enhancement, debug panel enhancements, customizable vehicle displays refresh rate, idling compressor sound, traction ac motor sound, braking sound enhancement, sky state enhancement, minor bug fixes

This commit is contained in:
tmj-fstate
2021-03-04 03:41:53 +01:00
parent 4b38395cb6
commit 517c9c89f8
33 changed files with 5836 additions and 3783 deletions

View File

@@ -410,12 +410,12 @@ int TDynamicObject::GetPneumatic(bool front, bool red)
y = btPneumatic2r.GetStatus();
}
z = 0; // brak węży?
if ((x == 1) && (y == 1))
z = 3; // dwa proste
if ((x == 2) && (y == 0))
z = 1; // lewy skośny, brak prawego
if ((x == 0) && (y == 2))
z = 2; // brak lewego, prawy skośny
if ((x > 0) && (y > 0))
z = 3; // dwa
if ((x > 0) && (y == 0))
z = 1; // lewy, brak prawego
if ((x == 0) && (y > 0))
z = 2; // brak lewego, prawy
return z;
}
@@ -975,64 +975,46 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist)
// else btCPass2.TurnOff();
if (MoverParameters->Power24vIsAvailable || MoverParameters->Power110vIsAvailable)
{ // sygnaly konca pociagu
if (m_endsignals1.Active())
{
if (TestFlag(MoverParameters->iLights[0], 2) || TestFlag(MoverParameters->iLights[0], 32))
{
if (m_endsignals1.Active()) {
if (TestFlag(MoverParameters->iLights[end::front], ( light::redmarker_left | light::redmarker_right ) ) ) {
m_endsignals1.Turn( true );
btnOn = true;
}
// else btEndSignals1.TurnOff();
}
else
{
if (TestFlag(MoverParameters->iLights[0], 2))
{
else {
if (TestFlag(MoverParameters->iLights[end::front], light::redmarker_left)) {
m_endsignal13.Turn( true );
btnOn = true;
}
// else btEndSignals11.TurnOff();
if (TestFlag(MoverParameters->iLights[0], 32))
{
if (TestFlag(MoverParameters->iLights[end::front], light::redmarker_right)) {
m_endsignal12.Turn( true );
btnOn = true;
}
// else btEndSignals13.TurnOff();
}
if (m_endsignals2.Active())
{
if (TestFlag(MoverParameters->iLights[1], 2) || TestFlag(MoverParameters->iLights[1], 32))
{
if (m_endsignals2.Active()) {
if (TestFlag(MoverParameters->iLights[end::rear], ( light::redmarker_left | light::redmarker_right ) ) ) {
m_endsignals2.Turn( true );
btnOn = true;
}
// else btEndSignals2.TurnOff();
}
else
{
if (TestFlag(MoverParameters->iLights[1], 2))
{
else {
if (TestFlag(MoverParameters->iLights[end::rear], light::redmarker_left)) {
m_endsignal23.Turn( true );
btnOn = true;
}
// else btEndSignals21.TurnOff();
if (TestFlag(MoverParameters->iLights[1], 32))
{
if (TestFlag(MoverParameters->iLights[end::rear], light::redmarker_right)) {
m_endsignal22.Turn( true );
btnOn = true;
}
// else btEndSignals23.TurnOff();
}
}
// tablice blaszane:
if (TestFlag(MoverParameters->iLights[end::front], light::rearendsignals))
{
if (TestFlag(MoverParameters->iLights[end::front], light::rearendsignals)) {
m_endtab1.Turn( true );
btnOn = true;
}
// else btEndSignalsTab1.TurnOff();
if (TestFlag(MoverParameters->iLights[end::rear], light::rearendsignals))
{
if (TestFlag(MoverParameters->iLights[end::rear], light::rearendsignals)) {
m_endtab2.Turn( true );
btnOn = true;
}
@@ -1851,7 +1833,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
if (!MoverParameters->LoadFIZ(asBaseDir))
{ // jak wczytanie CHK się nie uda, to błąd
if (ConversionError == 666)
ErrorLog( "Bad vehicle: failed do locate definition file \"" + BaseDir + "/" + Type_Name + ".fiz" + "\"" );
ErrorLog( "Bad vehicle: failed to locate definition file \"" + BaseDir + "/" + Type_Name + ".fiz" + "\"" );
else {
ErrorLog( "Bad vehicle: failed to load definition from file \"" + BaseDir + "/" + Type_Name + ".fiz\" (error " + to_string( ConversionError ) + ")" );
}
@@ -2980,64 +2962,41 @@ bool TDynamicObject::Update(double dt, double dt1)
else {
MoverParameters->InsideConsist = false;
}
// HACK: we're using sound event to detect whether vehicle was connected to another
if( TestFlag( MoverParameters->AIFlag, sound::attachcoupler ) ) {
auto *driver{ ctOwner ? ctOwner : Mechanik };
if( driver != nullptr ) {
driver->CheckVehicles( Connect );
}
SetFlag( MoverParameters->AIFlag, -sound::attachcoupler );
ClearFlag( MoverParameters->AIFlag, sound::attachcoupler );
}
// napiecie sieci trakcyjnej
// Ra 15-01: przeliczenie poboru prądu powinno być robione wcześniej, żeby na
// tym etapie były
// znane napięcia
// TTractionParam tmpTraction;
// tmpTraction.TractionVoltage=0;
if (MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector)
{ // dla EZT tylko silnikowy
// if (Global.bLiveTraction)
{ // Ra 2013-12: to niżej jest chyba trochę bez sensu
tmpTraction.TractionVoltage = std::max( std::abs( MoverParameters->PantRearVolt ), std::abs( MoverParameters->PantFrontVolt ) );
/*
if (v == 0.0) {
v = MoverParameters->PantFrontVolt;
if( v == 0.0 ) {
// if( MoverParameters->TrainType & ( dt_EZT | dt_ET40 | dt_ET41 | dt_ET42 ) ) {
// dwuczłony mogą mieć sprzęg WN
// NOTE: condition disabled, other vehicles types can have power cables as well
v = MoverParameters->GetTrainsetVoltage(); // ostatnia szansa
// }
}
}
*/
if ( tmpTraction.TractionVoltage > 0.0)
{ // jeśli jest zasilanie
NoVoltTime = 0;
if (MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) { // dla EZT tylko silnikowy
tmpTraction.TractionVoltage = std::max( std::abs( MoverParameters->PantRearVolt ), std::abs( MoverParameters->PantFrontVolt ) );
// jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod izolatorem daje 0.15s)
// Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało WS pod izolatorem
if( tmpTraction.TractionVoltage > 0.0) {
NoVoltTime = 0;
}
else {
NoVoltTime += dt1;
if( NoVoltTime <= 0.2 ) {
tmpTraction.TractionVoltage = MoverParameters->PantographVoltage;
}
else {
NoVoltTime += dt1;
if( NoVoltTime > 0.2 ) {
// jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod izolatorem daje 0.15s)
// Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało WS pod izolatorem
if( MoverParameters->Vel > 0.5 ) {
// jeśli jedzie
// Ra 2014-07: doraźna blokada logowania zimnych lokomotyw - zrobić to trzeba inaczej
if( MoverParameters->Pantographs[end::front].is_active
|| MoverParameters->Pantographs[end::rear].is_active ) {
if( ( MoverParameters->Mains )
&& ( MoverParameters->GetTrainsetHighVoltage() < 0.1f ) ) {
// Ra 15-01: logować tylko, jeśli WS załączony
// yB 16-03: i nie jest to asynchron zasilany z daleka
// Ra 15-01: bezwzględne współrzędne pantografu nie są dostępne,
// więc lepiej się tego nie zaloguje
ErrorLog(
"Bad traction: " + MoverParameters->Name
+ " lost power for " + to_string( NoVoltTime, 2 ) + " sec. at "
+ to_string( glm::dvec3{ vPosition } ) );
}
// jeśli jedzie
if( MoverParameters->Vel > 0.5 ) {
// Ra 2014-07: doraźna blokada logowania zimnych lokomotyw - zrobić to trzeba inaczej
if( MoverParameters->Pantographs[end::front].is_active
|| MoverParameters->Pantographs[end::rear].is_active ) {
if( ( MoverParameters->Mains ) // Ra 15-01: logować tylko, jeśli WS załączony
&& ( MoverParameters->GetTrainsetHighVoltage() < 0.1f ) ) { // yB 16-03: i nie jest to asynchron zasilany z daleka
// Ra 15-01: bezwzględne współrzędne pantografu nie są dostępne więc lepiej się tego nie zaloguje
ErrorLog(
"Bad traction: " + MoverParameters->Name
+ " lost power for " + to_string( NoVoltTime, 2 ) + " sec. at "
+ to_string( glm::dvec3{ vPosition } ) );
}
}
}
@@ -3053,13 +3012,14 @@ bool TDynamicObject::Update(double dt, double dt1)
MoverParameters->PantographVoltage = tmpTraction.TractionVoltage;
// McZapkie: predkosc w torze przekazac do TrackParam
// McZapkie: Vel ma wymiar [km/h] (absolutny), V ma wymiar [m/s], taka
// przyjalem notacje
// McZapkie: Vel ma wymiar [km/h] (absolutny), V ma wymiar [m/s], taka przyjalem notacje
tp.Velmax = MyTrack->VelocityGet();
if (Mechanik)
{ // Ra 2F3F: do Driver.cpp to przenieść?
MoverParameters->EqvtPipePress = GetEPP(); // srednie cisnienie w PG
if( Mechanik->primary() ) {
MoverParameters->EqvtPipePress = GetEPP(); // srednie cisnienie w PG
}
if ((Mechanik->primary()) &&
((MoverParameters->EngineType == TEngineType::DieselEngine) ||
(MoverParameters->EngineType == TEngineType::DieselElectric))
@@ -3395,7 +3355,7 @@ bool TDynamicObject::Update(double dt, double dt1)
MED_oldFED = FzadED;
}
Mechanik->UpdateSituation(dt1); // przebłyski świadomości AI
Mechanik->Update(dt1); // przebłyski świadomości AI
}
// fragment "z EXE Kursa"
@@ -4100,19 +4060,36 @@ void TDynamicObject::RenderSounds() {
if( MoverParameters->CompressorSpeed > 0.0 ) {
// McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka
if( MoverParameters->CompressorFlag ) {
// for compressor coupled with the diesel engine sound pitch is driven by engine revolutions
if( MoverParameters->CompressorPower == 3 ) {
// presume the compressor sound is recorded for idle revolutions
// increase the pitch according to increase of engine revolutions
auto const enginefactor { ( MoverParameters->EngineMaxRPM() / MoverParameters->EngineIdleRPM() ) * MoverParameters->EngineRPMRatio() };
sCompressor.pitch(
auto const enginefactor {
clamp( // try to keep the sound pitch in semi-reasonable range
enginefactor,
0.5, 2.5 ) );
MoverParameters->EngineMaxRPM() / MoverParameters->EngineIdleRPM() * MoverParameters->EngineRPMRatio(),
0.5, 2.5 ) };
sCompressor.pitch( enginefactor );
sCompressorIdle.pitch( enginefactor );
}
if( sCompressorIdle.empty() ) {
// legacy sound path, if there's no dedicated idle sound
sCompressor.play( sound_flags::exclusive | sound_flags::looping );
}
else {
// enhanced sound path, with dedicated sound for idling compressor
if( MoverParameters->CompressorGovernorLock ) {
sCompressor.stop();
sCompressorIdle.play( sound_flags::exclusive | sound_flags::looping );
}
else {
sCompressor.play( sound_flags::exclusive | sound_flags::looping );
sCompressorIdle.stop();
}
}
sCompressor.play( sound_flags::exclusive | sound_flags::looping );
}
else {
sCompressor.stop();
sCompressorIdle.stop();
}
}
@@ -4254,7 +4231,7 @@ void TDynamicObject::RenderSounds() {
auto brakeforceratio{ 0.0 };
if( //( false == mvOccupied->SlippingWheels ) &&
( MoverParameters->UnitBrakeForce > 10.0 )
&& ( MoverParameters->Vel > 0.05 ) ) {
&& ( MoverParameters->Vel > 0.05 ) ) {
brakeforceratio =
clamp(
@@ -5499,15 +5476,19 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
m_powertrainsounds.water_heater.owner( this );
}
else if( ( token == "tractionmotor:" )
else if( ( ( token == "tractionmotor:" ) || ( token == "tractionacmotor:" ) )
&& ( MoverParameters->Power > 0 ) ) {
// dc motors are (legacy) default
m_powertrainsounds.dcmotors = ( token == "tractionmotor:" );
// plik z dzwiekiem silnika, mnozniki i ofsety amp. i czest.
sound_source motortemplate { sound_placement::external };
motortemplate.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency );
motortemplate.owner( this );
auto const amplitudedivisor = static_cast<float>( MoverParameters->nmax * 60 + MoverParameters->Power * 3 );
motortemplate.m_amplitudefactor /= amplitudedivisor;
if( m_powertrainsounds.dcmotors ) {
auto const amplitudedivisor = static_cast<float>( MoverParameters->nmax * 60 + MoverParameters->Power * 3 );
motortemplate.m_amplitudefactor /= amplitudedivisor;
}
if( true == m_powertrainsounds.motors.empty() ) {
// fallback for cases without specified motor locations, convert sound template to a single sound source
@@ -5584,13 +5565,22 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
else if( token == "transmission:" ) {
// plik z dzwiekiem, mnozniki i ofsety amp. i czest.
// NOTE, fixed default parameters, legacy system leftover
m_powertrainsounds.transmission.m_amplitudefactor = 0.029;
m_powertrainsounds.transmission.m_amplitudeoffset = 0.1;
m_powertrainsounds.transmission.m_frequencyfactor = 0.005;
m_powertrainsounds.transmission.m_frequencyoffset = 1.0;
auto &sound { m_powertrainsounds.transmission };
sound.m_amplitudefactor = 0.029;
sound.m_amplitudeoffset = 0.1;
sound.m_frequencyfactor = 0.005;
sound.m_frequencyoffset = 1.0;
m_powertrainsounds.transmission.deserialize( parser, sound_type::single, sound_parameters::range );
m_powertrainsounds.transmission.owner( this );
sound.deserialize( parser, sound_type::single, sound_parameters::range );
sound.owner( this );
}
else if( token == "brakesound:" ) {
// hamowanie zwykle:
rsBrake.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency );
rsBrake.owner( this );
// NOTE: can't pre-calculate amplitude normalization based on max brake force, as this varies depending on vehicle speed
rsBrake.m_frequencyfactor /= ( 1 + MoverParameters->Vmax );
}
else if( token == "brake:" ) {
@@ -5696,6 +5686,12 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
sCompressor.owner( this );
}
else if( token == "compressoridle:" ) {
// pliki ze sprezarka
sCompressorIdle.deserialize( parser, sound_type::multipart, sound_parameters::range );
sCompressorIdle.owner( this );
}
else if( token == "converter:" ) {
// pliki z przetwornica
sConverter.deserialize( parser, sound_type::multipart, sound_parameters::range );
@@ -6066,14 +6062,6 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
dsbPneumaticRelay.deserialize( parser, sound_type::single );
dsbPneumaticRelay.owner( this );
}
// braking sounds
else if( token == "brakesound:" ) {
// hamowanie zwykle:
rsBrake.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency );
rsBrake.owner( this );
// NOTE: can't pre-calculate amplitude normalization based on max brake force, as this varies depending on vehicle speed
rsBrake.m_frequencyfactor /= ( 1 + MoverParameters->Vmax );
}
else if( token == "slipperysound:" ) {
// sanie:
rsSlippery.deserialize( parser, sound_type::single, sound_parameters::amplitude );
@@ -6415,7 +6403,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
// other engine compartment sounds
auto const nullvector { glm::vec3() };
std::vector<sound_source *> enginesounds = {
&sConverter, &sCompressor, &sSmallCompressor, &sHeater
&sConverter, &sCompressor, &sCompressorIdle, &sSmallCompressor, &sHeater
};
for( auto sound : enginesounds ) {
if( sound->offset() == nullvector ) {
@@ -6641,8 +6629,7 @@ void TDynamicObject::RaLightsSet(int head, int rear)
// EN57 może nie mieć końcówek od środka członu
if (MoverParameters->Power > 1.0) // jeśli ma moc napędową
if (!MoverParameters->DirActive) // jeśli nie ma ustawionego kierunku
{ // jeśli ma zarówno światła jak i końcówki, ustalić, czy jest w stanie
// aktywnym
{ // jeśli ma zarówno światła jak i końcówki, ustalić, czy jest w stanie aktywnym
// np. lokomotywa na zimno będzie mieć końcówki a nie światła
rear = light::rearendsignals; // tablice blaszane
// trzeba to uzależnić od "załączenia baterii" w pojeździe
@@ -6659,22 +6646,66 @@ void TDynamicObject::RaLightsSet(int head, int rear)
rear = light::rearendsignals; // tablice blaszane
}
}
if (iDirection) // w zależności od kierunku pojazdu w składzie
{ // jesli pojazd stoi sprzęgiem 0 w stronę czoła
if (head >= 0)
MoverParameters->iLights[0] = head;
if (rear >= 0)
MoverParameters->iLights[1] = rear;
// w zależności od kierunku pojazdu w składzie
if( head >= 0 ) {
auto const vehicleend { iDirection > 0 ? end::front : end::rear };
MoverParameters->iLights[ vehicleend ] = ( head & iInventory[ vehicleend ] );
}
else
{ // jak jest odwrócony w składzie (-1), to zapalamy odwrotnie
if (head >= 0)
MoverParameters->iLights[1] = head;
if (rear >= 0)
MoverParameters->iLights[0] = rear;
if( rear >= 0 ) {
auto const vehicleend{ iDirection > 0 ? end::rear : end::front };
MoverParameters->iLights[ vehicleend ] = ( rear & iInventory[ vehicleend ] );
}
};
bool TDynamicObject::has_signal_pc1_on() const {
auto const vehicleend { iDirection > 0 ? end::front : end::rear };
auto const equippedlights { iInventory[ vehicleend ] };
auto const pattern { equippedlights & ( light::headlight_left | light::headlight_right | light::headlight_upper ) };
auto const patternfallback { equippedlights & ( light::auxiliary_left | light::auxiliary_right | light::headlight_upper ) };
auto const hasauxiliarylights { ( equippedlights & ( light::auxiliary_left | light::auxiliary_right ) ) != 0 };
auto const activelights { MoverParameters->iLights[ vehicleend ] };
return ( ( ( pattern != 0 ) && ( activelights == pattern ) )
|| ( ( hasauxiliarylights ) && ( activelights == patternfallback ) )
|| ( ( pattern == 0 ) && ( patternfallback == 0 ) && ( activelights == light::rearendsignals ) ) ); // pc4
}
bool TDynamicObject::has_signal_pc2_on() const {
auto const vehicleend { iDirection > 0 ? end::front : end::rear };
auto const equippedlights { iInventory[ vehicleend ] };
auto const pattern { equippedlights & ( light::redmarker_left | light::headlight_right | light::headlight_upper ) };
auto const patternfallback { equippedlights & ( light::redmarker_left | light::auxiliary_right | light::headlight_upper ) };
auto const hasauxiliarylights { ( equippedlights & ( light::auxiliary_left | light::auxiliary_right ) ) != 0 };
auto const activelights { MoverParameters->iLights[ vehicleend ] };
return ( ( activelights == pattern )
|| ( hasauxiliarylights && ( activelights == patternfallback ) ) );
}
bool TDynamicObject::has_signal_pc5_on() const {
auto const vehicleend { iDirection > 0 ? end::rear : end::front };
auto const equippedlights { iInventory[ vehicleend ] };
auto const pattern { equippedlights & ( light::redmarker_left | light::redmarker_right ) };
auto const patternfallback { equippedlights & ( light::rearendsignals ) };
auto const activelights { MoverParameters->iLights[ vehicleend ] };
return ( ( ( pattern != 0 ) && ( activelights == pattern ) )
|| ( ( patternfallback != 0 ) && ( activelights == patternfallback ) ) );
}
bool TDynamicObject::has_signal_on( int const Side, int const Pattern ) const {
/*
auto const vehicleend { iDirection > 0 ? Side : 1 - Side };
auto const pattern { iInventory[ vehicleend ] & Pattern };
return ( MoverParameters->iLights[ vehicleend ] == pattern );
*/
return ( MoverParameters->iLights[ Side ] == ( Pattern & iInventory[ Side ] ) );
}
int TDynamicObject::DirectionSet(int d)
{ // ustawienie kierunku w składzie (wykonuje AI)
auto const lastdirection { iDirection };
@@ -6723,7 +6754,7 @@ TDynamicObject * TDynamicObject::NextC(int C) const {
nullptr ); // hide neighbour lacking specified connection type
}
// checks whether there's unbroken connection of specified type to specified vehicle
// checks whether there's unbroken connection of specified type to specified vehicle
bool
TDynamicObject::is_connected( TDynamicObject const *Vehicle, coupling const Coupling ) const {
@@ -6749,7 +6780,7 @@ TDynamicObject::is_connected( TDynamicObject const *Vehicle, coupling const Coup
return true;
}
}
// no lack in either direction, give up
// no luck in either direction, give up
return false;
}
@@ -7062,22 +7093,30 @@ material_handle TDynamicObject::DestinationFind( std::string Destination ) {
if( Destination.empty() ) { return null_handle; }
Destination = Bezogonkow( Destination ); // do szukania pliku obcinamy ogonki
// destination textures are kept in the vehicle's directory so we point the current texture path there
auto const currenttexturepath { Global.asCurrentTexturePath };
Global.asCurrentTexturePath = asBaseDir;
// now see if we can find any version of the texture
std::vector<std::string> const destinations {
Destination + '@' + MoverParameters->TypeName,
Destination };
auto destinationhandle { null_handle };
for( auto const &destination : destinations ) {
auto material = TextureTest( ToLower( destination ) );
if( false == material.empty() ) {
destinationhandle = GfxRenderer->Fetch_Material( material );
break;
if( starts_with( Destination, "make:" ) ) {
// autogenerated texture
destinationhandle = GfxRenderer->Fetch_Material( Destination );
}
else {
// regular texture
Destination = Bezogonkow( Destination ); // do szukania pliku obcinamy ogonki
// now see if we can find any version of the texture
std::vector<std::string> const destinations {
Destination + '@' + MoverParameters->TypeName,
Destination };
for( auto const &destination : destinations ) {
auto material = TextureTest( ToLower( destination ) );
if( false == material.empty() ) {
destinationhandle = GfxRenderer->Fetch_Material( material );
break;
}
}
}
// whether we got anything, restore previous texture path
@@ -7549,91 +7588,113 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub
// motor sounds
volume = 0.0;
if( false == motors.empty() ) {
// ac and dc motors have significantly different sounds
if( dcmotors ) {
if( std::abs( Vehicle.nrot ) > 0.01 ) {
if( std::abs( Vehicle.nrot ) > 0.01 ) {
auto const &motor { motors.front() };
// frequency calculation
auto normalizer { 1.f };
if( true == motor.is_combined() ) {
// for combined motor sound we calculate sound point in rpm, to make .mmd files setup easier
// NOTE: we supply 1/100th of actual value, as the sound module converts does the opposite, converting received (typically) 0-1 values to 0-100 range
normalizer = 60.f * 0.01f;
}
auto const motorrevolutions { std::abs( Vehicle.nrot ) * Vehicle.Transmision.Ratio };
frequency =
motor.m_frequencyoffset
+ motor.m_frequencyfactor * motorrevolutions * normalizer;
// base volume calculation
switch( Vehicle.EngineType ) {
case TEngineType::ElectricInductionMotor: {
volume =
motor.m_amplitudeoffset
+ motor.m_amplitudefactor * ( Vehicle.EnginePower + motorrevolutions * 2 );
break;
auto const &motor { motors.front() };
// frequency calculation
auto normalizer { 1.f };
if( true == motor.is_combined() ) {
// for combined motor sound we calculate sound point in rpm, to make .mmd files setup easier
// NOTE: we supply 1/100th of actual value, as the sound module converts does the opposite, converting received (typically) 0-1 values to 0-100 range
normalizer = 60.f * 0.01f;
}
case TEngineType::ElectricSeriesMotor: {
volume =
motor.m_amplitudeoffset
+ motor.m_amplitudefactor * ( Vehicle.EnginePower + motorrevolutions * 60.0 );
break;
}
default: {
volume =
motor.m_amplitudeoffset
+ motor.m_amplitudefactor * motorrevolutions * 60.0;
break;
}
}
auto const motorrevolutions { std::abs( Vehicle.nrot ) * Vehicle.Transmision.Ratio };
frequency =
motor.m_frequencyoffset
+ motor.m_frequencyfactor * motorrevolutions * normalizer;
if( Vehicle.EngineType == TEngineType::ElectricSeriesMotor ) {
// volume variation
if( ( volume < 1.0 )
&& ( Vehicle.EnginePower < 100 ) ) {
auto const volumevariation { LocalRandom( 100 ) * Vehicle.enrot / ( 1 + Vehicle.nmax ) };
if( volumevariation < 2 ) {
volume += volumevariation / 200;
// base volume calculation
switch( Vehicle.EngineType ) {
case TEngineType::ElectricInductionMotor: {
volume =
motor.m_amplitudeoffset
+ motor.m_amplitudefactor * ( Vehicle.EnginePower + motorrevolutions * 2 );
break;
}
case TEngineType::ElectricSeriesMotor: {
volume =
motor.m_amplitudeoffset
+ motor.m_amplitudefactor * ( Vehicle.EnginePower + motorrevolutions * 60.0 );
break;
}
default: {
volume =
motor.m_amplitudeoffset
+ motor.m_amplitudefactor * motorrevolutions * 60.0;
break;
}
}
if( ( Vehicle.DynamicBrakeFlag )
&& ( Vehicle.EnginePower > 0.1 ) ) {
// Szociu - 29012012 - jeżeli uruchomiony jest hamulec elektrodynamiczny, odtwarzany jest dźwięk silnika
volume += 0.8;
if( Vehicle.EngineType == TEngineType::ElectricSeriesMotor ) {
// volume variation
if( ( volume < 1.0 )
&& ( Vehicle.EnginePower < 100 ) ) {
auto const volumevariation { LocalRandom( 100 ) * Vehicle.enrot / ( 1 + Vehicle.nmax ) };
if( volumevariation < 2 ) {
volume += volumevariation / 200;
}
}
if( ( Vehicle.DynamicBrakeFlag )
&& ( Vehicle.EnginePower > 0.1 ) ) {
// Szociu - 29012012 - jeżeli uruchomiony jest hamulec elektrodynamiczny, odtwarzany jest dźwięk silnika
volume += 0.8;
}
}
}
// scale motor volume based on whether they're active
motor_momentum =
clamp(
motor_momentum
- 1.0 * Deltatime // smooth out decay
+ std::abs( Vehicle.Mm ) / 60.0 * Deltatime,
0.0, 1.25 );
volume *= std::max( 0.25f, motor_momentum );
motor_volume = interpolate( motor_volume, volume, 0.25 );
if( motor_volume >= 0.05 ) {
// apply calculated parameters to all motor instances
for( auto &motor : motors ) {
motor
.pitch( frequency )
.gain( motor_volume )
.play( sound_flags::exclusive | sound_flags::looping );
// scale motor volume based on whether they're active
motor_momentum =
clamp(
motor_momentum
- 1.0 * Deltatime // smooth out decay
+ std::abs( Vehicle.Mm ) / 60.0 * Deltatime,
0.0, 1.25 );
volume *= std::max( 0.25f, motor_momentum );
motor_volume = interpolate( motor_volume, volume, 0.25 );
if( motor_volume >= 0.05 ) {
// apply calculated parameters to all motor instances
for( auto &motor : motors ) {
motor
.pitch( frequency )
.gain( motor_volume )
.play( sound_flags::exclusive | sound_flags::looping );
}
}
else {
// stop all motor instances
for( auto &motor : motors ) {
motor.stop();
}
}
}
else {
// stop all motor instances
motor_volume = 0.0;
for( auto &motor : motors ) {
motor.stop();
}
}
}
else {
// stop all motor instances
motor_volume = 0.0;
for( auto &motor : motors ) {
motor.stop();
// ac motors
if( Vehicle.EngineType == TEngineType::ElectricInductionMotor ) {
if( Vehicle.InverterFrequency > 0.001 ) {
for( auto &motor : motors ) {
motor
.pitch( motor.m_frequencyoffset + motor.m_frequencyfactor * Vehicle.InverterFrequency )
.gain( motor.m_amplitudeoffset + motor.m_amplitudefactor * std::sqrt( std::abs( Vehicle.eimv_pr ) ) )
.play( sound_flags::exclusive | sound_flags::looping );
}
}
else {
for( auto &motor : motors ) {
motor.stop();
}
}
}
}
}