mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-11 22:29:03 +01:00
Merge pull request #257 from MaSzyna-EU07/fix/vehicle-removal-smoke
fix(gfx): a vehicle taken off the tracks stops smoking (#90)
This commit is contained in:
@@ -464,6 +464,7 @@ set(CORE_SOURCES
|
|||||||
"scripting/ladderlogic.cpp"
|
"scripting/ladderlogic.cpp"
|
||||||
"vehicle/handover.cpp"
|
"vehicle/handover.cpp"
|
||||||
"vehicle/horn.cpp"
|
"vehicle/horn.cpp"
|
||||||
|
"vehicle/smoke.cpp"
|
||||||
)
|
)
|
||||||
add_library(eu07_core STATIC ${CORE_SOURCES})
|
add_library(eu07_core STATIC ${CORE_SOURCES})
|
||||||
target_link_libraries(eu07_core PUBLIC eu07_settings)
|
target_link_libraries(eu07_core PUBLIC eu07_settings)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "model/AnimModel.h"
|
#include "model/AnimModel.h"
|
||||||
#include "simulation/simulationenvironment.h"
|
#include "simulation/simulationenvironment.h"
|
||||||
#include "utilities/Logs.h"
|
#include "utilities/Logs.h"
|
||||||
|
#include "vehicle/smoke.h"
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -162,53 +163,32 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) {
|
|||||||
glm::dvec3{ std::numeric_limits<double>::max() },
|
glm::dvec3{ std::numeric_limits<double>::max() },
|
||||||
glm::dvec3{ std::numeric_limits<double>::lowest() } };
|
glm::dvec3{ std::numeric_limits<double>::lowest() } };
|
||||||
|
|
||||||
m_spawncount = (
|
auto const spawning { ( true == Global.Smoke ) && ( false == Onlydespawn ) };
|
||||||
( ( false == Global.Smoke ) || ( true == Onlydespawn ) ) ?
|
|
||||||
0.f :
|
|
||||||
std::min<float>(
|
|
||||||
static_cast<float>(m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity )),
|
|
||||||
static_cast<float>(m_max_particles) ) );
|
|
||||||
// consider special spawn rate cases
|
|
||||||
if( m_ownertype == owner_type::vehicle ) {
|
if( m_ownertype == owner_type::vehicle ) {
|
||||||
|
|
||||||
if (Global.AirTemperature <= 5.f || m_owner.vehicle->MoverParameters->dizel_heat.Ts < 45.f)
|
auto const &mover { *m_owner.vehicle->MoverParameters };
|
||||||
{
|
if( Global.AirTemperature <= 5.f || mover.dizel_heat.Ts < 45.f ) {
|
||||||
m_emitter.color = glm::vec3{128, 128, 128};
|
m_emitter.color = glm::vec3{ 128, 128, 128 };
|
||||||
}
|
}
|
||||||
|
// a vehicle taken off the tracks (gone through a portal, or waiting in the pool of the road traffic) puffs no more,
|
||||||
if (m_owner.vehicle->MoverParameters->dizel_spinup == true)
|
// the particles it already let out fade away as usual
|
||||||
{
|
m_spawncount = (
|
||||||
m_spawncount =
|
( true == spawning ) ?
|
||||||
((false == Global.Smoke) || (true == Onlydespawn)) ?
|
smoke::vehicle_spawn_count(
|
||||||
0.f :
|
mover, m_owner.vehicle->bEnabled,
|
||||||
std::min<float>(
|
{ m_spawncount, m_spawnrate, Global.SmokeFidelity, static_cast<float>( m_max_particles ) },
|
||||||
// m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
|
Timedelta ) :
|
||||||
static_cast<float>(m_spawncount +(m_spawnrate * Timedelta * Global.SmokeFidelity * (((m_owner.vehicle->MoverParameters->enrot) / 4 ) * 0.01))), static_cast<float>(m_max_particles));
|
0.f );
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
m_spawncount = (
|
||||||
if (m_owner.vehicle->MoverParameters->DirAbsolute == 0 ||
|
( true == spawning ) ?
|
||||||
m_owner.vehicle->MoverParameters->Im == 0)
|
std::min<float>(
|
||||||
{
|
static_cast<float>(m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity )),
|
||||||
m_spawncount =
|
static_cast<float>(m_max_particles) ) :
|
||||||
((false == Global.Smoke) || (true == Onlydespawn)) ?
|
0.f );
|
||||||
0.f :
|
}
|
||||||
std::min<float>(
|
|
||||||
// m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
|
|
||||||
static_cast<float>(m_spawncount + (m_spawnrate * Timedelta * Global.SmokeFidelity * ((((m_owner.vehicle->MoverParameters->DElist[m_owner.vehicle->MoverParameters->MainCtrlPosNo].RPM -m_owner.vehicle->MoverParameters->enrot) /60) *0.02) *(m_owner.vehicle->MoverParameters->EnginePower * 0.005)))), static_cast<float>(m_max_particles));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
m_spawncount =
|
|
||||||
((false == Global.Smoke) || (true == Onlydespawn)) ?
|
|
||||||
0.f :
|
|
||||||
std::min<float>(
|
|
||||||
// m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
|
|
||||||
static_cast<float>(m_spawncount + (m_spawnrate * Timedelta * Global.SmokeFidelity * ((((m_owner.vehicle->MoverParameters->DElist[m_owner.vehicle->MoverParameters->MainCtrlPosNo].RPM -m_owner.vehicle->MoverParameters->enrot) /60) * (sqrt(m_owner.vehicle->MoverParameters->Im) * 0.01) * 0.02) *(m_owner.vehicle->MoverParameters->EnginePower * 0.005)))), static_cast<float>(m_max_particles));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update spawned particles
|
// update spawned particles
|
||||||
for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) {
|
for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) {
|
||||||
|
|||||||
85
tests/unit/test_smoke.cpp
Normal file
85
tests/unit/test_smoke.cpp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "doctest.h"
|
||||||
|
|
||||||
|
#include "vehicle.h"
|
||||||
|
|
||||||
|
#include "vehicle/smoke.h"
|
||||||
|
|
||||||
|
// the smoke of a vehicle's engine: how many particles the smoke source of the vehicle's model spawns in an update
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// a source of 20 particles per second, with room for 100
|
||||||
|
smoke::spawn_rate const source{0.f, 20.f, 1.f, 100.f};
|
||||||
|
double const update{0.1}; // s
|
||||||
|
|
||||||
|
// a diesel at work, its engine in the state of one of the cases below
|
||||||
|
test::vehicle diesel() {
|
||||||
|
test::vehicle vehicle{test::definition::load("wagon")};
|
||||||
|
vehicle->MainCtrlPosNo = 1;
|
||||||
|
vehicle->DElist[1].RPM = 1500.0;
|
||||||
|
vehicle->enrot = 300.0;
|
||||||
|
vehicle->EnginePower = 400.0;
|
||||||
|
return vehicle;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("smoke: a vehicle taken off the tracks puffs no more (#90)") {
|
||||||
|
auto vehicle{diesel()};
|
||||||
|
SUBCASE("engine starting") { vehicle->dizel_spinup = true; }
|
||||||
|
SUBCASE("engine idling") { vehicle->DirAbsolute = 0; }
|
||||||
|
SUBCASE("engine under load") {
|
||||||
|
vehicle->DirAbsolute = 1;
|
||||||
|
vehicle->Im = 400.0;
|
||||||
|
}
|
||||||
|
// on the tracks the engine puffs
|
||||||
|
REQUIRE(smoke::vehicle_spawn_count(vehicle.mover(), true, source, update) >= 1.f);
|
||||||
|
|
||||||
|
// gone through a portal, the pending particles are dropped too
|
||||||
|
auto pending{source};
|
||||||
|
pending.pending = 5.f;
|
||||||
|
CHECK(smoke::vehicle_spawn_count(vehicle.mover(), false, pending, update) == 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("smoke: a road vehicle back on the road from the pool puffs again") {
|
||||||
|
auto vehicle{diesel()};
|
||||||
|
// waiting in the pool, the count stays at zero update after update
|
||||||
|
auto rate{source};
|
||||||
|
for (auto i{0}; i < 10; ++i) {
|
||||||
|
rate.pending = smoke::vehicle_spawn_count(vehicle.mover(), false, rate, update);
|
||||||
|
}
|
||||||
|
REQUIRE(rate.pending == 0.f);
|
||||||
|
|
||||||
|
CHECK(smoke::vehicle_spawn_count(vehicle.mover(), true, rate, update) >= 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("smoke: the engine adds to the steady rate of the source") {
|
||||||
|
auto vehicle{diesel()};
|
||||||
|
vehicle->DirAbsolute = 0;
|
||||||
|
auto const steady{source.rate * update}; // the rate of a source of the scenery
|
||||||
|
|
||||||
|
vehicle->EnginePower = 0.0;
|
||||||
|
CHECK(smoke::vehicle_spawn_count(vehicle.mover(), true, source, update) == doctest::Approx(steady));
|
||||||
|
|
||||||
|
vehicle->EnginePower = 400.0;
|
||||||
|
auto const working{smoke::vehicle_spawn_count(vehicle.mover(), true, source, update)};
|
||||||
|
CHECK(working > steady);
|
||||||
|
|
||||||
|
// the faster the starting engine turns, the more it puffs
|
||||||
|
vehicle->dizel_spinup = true;
|
||||||
|
vehicle->enrot = 100.0;
|
||||||
|
auto const slow{smoke::vehicle_spawn_count(vehicle.mover(), true, source, update)};
|
||||||
|
vehicle->enrot = 400.0;
|
||||||
|
CHECK(smoke::vehicle_spawn_count(vehicle.mover(), true, source, update) > slow);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("smoke: the pending particles never outgrow the room of the source") {
|
||||||
|
auto vehicle{diesel()};
|
||||||
|
vehicle->dizel_spinup = true;
|
||||||
|
vehicle->enrot = 100000.0;
|
||||||
|
auto rate{source};
|
||||||
|
rate.pending = 99.f;
|
||||||
|
|
||||||
|
CHECK(smoke::vehicle_spawn_count(vehicle.mover(), true, rate, 10.0) == rate.limit);
|
||||||
|
}
|
||||||
@@ -4852,7 +4852,7 @@ void TDynamicObject::RenderSounds() {
|
|||||||
#endif
|
#endif
|
||||||
&& ( // compound test whether the vehicle belongs to user-driven consist (as these don't emit outer noise in cab view)
|
&& ( // compound test whether the vehicle belongs to user-driven consist (as these don't emit outer noise in cab view)
|
||||||
FreeFlyModeFlag ? true : // in external view all vehicles emit outer noise
|
FreeFlyModeFlag ? true : // in external view all vehicles emit outer noise
|
||||||
// Global.pWorld->train() == nullptr ? true : // (can skip this check, with no player train the external view is a given)
|
simulation::Train == nullptr ? true : // no player train, e.g. it went through a portal and the view isn't switched yet
|
||||||
ctOwner == nullptr ? true : // standalone vehicle, can't be part of user-driven train
|
ctOwner == nullptr ? true : // standalone vehicle, can't be part of user-driven train
|
||||||
ctOwner != simulation::Train->Dynamic()->ctOwner ? true : // confirmed isn't a part of the user-driven train
|
ctOwner != simulation::Train->Dynamic()->ctOwner ? true : // confirmed isn't a part of the user-driven train
|
||||||
Global.CabWindowOpen ? true : // sticking head out we get to hear outer noise
|
Global.CabWindowOpen ? true : // sticking head out we get to hear outer noise
|
||||||
|
|||||||
46
vehicle/smoke.cpp
Normal file
46
vehicle/smoke.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "vehicle/smoke.h"
|
||||||
|
|
||||||
|
#include "McZapkie/MOVER.h"
|
||||||
|
|
||||||
|
namespace smoke {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// how hard the engine works, scaling the spawn rate of the source
|
||||||
|
double engine_load(TMoverParameters const &Mover) {
|
||||||
|
if (Mover.dizel_spinup) {
|
||||||
|
// starting up, it puffs harder the faster it turns
|
||||||
|
return ((Mover.enrot / 4) * 0.01);
|
||||||
|
}
|
||||||
|
auto const rpmrise{(Mover.DElist[Mover.MainCtrlPosNo].RPM - Mover.enrot) / 60};
|
||||||
|
if (Mover.DirAbsolute == 0 || Mover.Im == 0) {
|
||||||
|
// idling: speeding up to the revolutions of the master controller
|
||||||
|
return ((rpmrise * 0.02) * (Mover.EnginePower * 0.005));
|
||||||
|
}
|
||||||
|
// under load: the more current the traction motors draw, the more smoke
|
||||||
|
return ((rpmrise * (std::sqrt(Mover.Im) * 0.01) * 0.02) * (Mover.EnginePower * 0.005));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
float vehicle_spawn_count(TMoverParameters const &Mover, bool const Ontracks, spawn_rate const &Rate, double const Timedelta) {
|
||||||
|
if (false == Ontracks) {
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
// the steady rate of the source, the way a source of the scenery spawns...
|
||||||
|
auto const steady{std::min<float>(static_cast<float>(Rate.pending + (Rate.rate * Timedelta * Rate.fidelity)), Rate.limit)};
|
||||||
|
// ...and on top of it the share of the engine
|
||||||
|
return std::min<float>(static_cast<float>(steady + (Rate.rate * Timedelta * Rate.fidelity * engine_load(Mover))), Rate.limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace smoke
|
||||||
30
vehicle/smoke.h
Normal file
30
vehicle/smoke.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class TMoverParameters;
|
||||||
|
|
||||||
|
// the smoke of a vehicle's engine, let out by the smoke sources of its model (rendering/particles.h)
|
||||||
|
namespace smoke {
|
||||||
|
|
||||||
|
// what a smoke source spawns, before the engine of its vehicle is taken into account
|
||||||
|
struct spawn_rate {
|
||||||
|
float pending{0.f}; // particles left to spawn from the previous updates
|
||||||
|
float rate{0.f}; // particles per second
|
||||||
|
float fidelity{1.f}; // multiplier of the smoke settings
|
||||||
|
float limit{0.f}; // most particles the source can have
|
||||||
|
};
|
||||||
|
|
||||||
|
// number of particles the smoke source of a vehicle has to spawn after Timedelta seconds: the steady rate of the
|
||||||
|
// source, plus a share which grows with the work of its engine. a vehicle taken off the tracks (Ontracks false: gone through a portal, or waiting in the pool of the road
|
||||||
|
// traffic) puffs no more, the particles it already let out fade away as usual
|
||||||
|
float vehicle_spawn_count(TMoverParameters const &Mover, bool Ontracks, spawn_rate const &Rate, double Timedelta);
|
||||||
|
|
||||||
|
} // namespace smoke
|
||||||
Reference in New Issue
Block a user