17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-08 06:15:12 +01:00

test(gfx): unit tests of the smoke of a vehicle taken off the tracks (#90)

The spawn count of the smoke source of a vehicle moves out of
smoke_source::update() into smoke::vehicle_spawn_count() (vehicle/smoke.h),
a free function on the mover state, which joins the core library so the
unit tests can call it the way the source does. The rates are the same as
before, the steady rate of the source still counted in before the share of
the engine.

Tests: tests/unit/test_smoke.cpp. A vehicle taken off the tracks spawns no
smoke and drops the pending particles, with the engine starting, idling or
under load; a road vehicle back from the pool puffs again; the engine adds
to the steady rate; the pending count stays within the room of the source.
Two of the four fail without the fix.

Checked again on td.scn with the SM42 driven into a portal: the smoke
source keeps puffing on the run and drops to 0 particles once the vehicle
is taken off the tracks.
This commit is contained in:
jerrrrycho
2026-11-07 11:38:49 +01:00
parent f94b308062
commit 4489dd7469
5 changed files with 186 additions and 51 deletions

View File

@@ -463,6 +463,7 @@ set(CORE_SOURCES
"scripting/ladderlogic.cpp"
"vehicle/handover.cpp"
"vehicle/horn.cpp"
"vehicle/smoke.cpp"
)
add_library(eu07_core STATIC ${CORE_SOURCES})
target_link_libraries(eu07_core PUBLIC eu07_settings)

View File

@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
#include "model/AnimModel.h"
#include "simulation/simulationenvironment.h"
#include "utilities/Logs.h"
#include "vehicle/smoke.h"
void
@@ -162,60 +163,32 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) {
glm::dvec3{ std::numeric_limits<double>::max() },
glm::dvec3{ std::numeric_limits<double>::lowest() } };
// a vehicle taken off the tracks (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
auto const nospawn {
false == Global.Smoke
|| true == Onlydespawn
|| ( m_ownertype == owner_type::vehicle && false == m_owner.vehicle->bEnabled ) };
auto const spawning { ( true == Global.Smoke ) && ( false == Onlydespawn ) };
m_spawncount = (
( true == nospawn ) ?
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 (Global.AirTemperature <= 5.f || m_owner.vehicle->MoverParameters->dizel_heat.Ts < 45.f)
{
m_emitter.color = glm::vec3{128, 128, 128};
}
if (m_owner.vehicle->MoverParameters->dizel_spinup == true)
{
m_spawncount =
(true == nospawn) ?
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->enrot) / 4 ) * 0.01))), static_cast<float>(m_max_particles));
}
else
{
if (m_owner.vehicle->MoverParameters->DirAbsolute == 0 ||
m_owner.vehicle->MoverParameters->Im == 0)
{
m_spawncount =
(true == nospawn) ?
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 =
(true == nospawn) ?
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));
}
}
}
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 };
}
// a vehicle taken off the tracks (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
m_spawncount = (
( true == spawning ) ?
smoke::vehicle_spawn_count(
mover, m_owner.vehicle->bEnabled,
{ m_spawncount, m_spawnrate, Global.SmokeFidelity, static_cast<float>( m_max_particles ) },
Timedelta ) :
0.f );
}
else {
m_spawncount = (
( true == spawning ) ?
std::min<float>(
static_cast<float>(m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity )),
static_cast<float>(m_max_particles) ) :
0.f );
}
// update spawned particles
for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) {

85
tests/unit/test_smoke.cpp Normal file
View 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);
}

46
vehicle/smoke.cpp Normal file
View 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
View 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