17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-22 14:23:20 +02:00

Let the smoke leave the exhaust as a widening jet that fades in

This commit is contained in:
2026-10-20 18:00:51 +02:00
parent abbdca4773
commit cf36b24e8d
8 changed files with 42 additions and 6 deletions

View File

@@ -274,6 +274,10 @@ are no longer loaded.
of a particle goes over to `v` (straight up or down, besides the drift with the wind), the faster the larger `drag: d`
(per second, default 1: about 63% of the way in a second). It takes the place of the old rules of the rise (slower in
cold and humid air); a template without it behaves as before. The particles don't stop at the ground.
Two more keys make the smoke come out of the exhaust as a jet instead of puffs swelling above it: `spread: s` — the
particles grow by `s` metres of size for each metre they go through the air (a jet widens by about 0.3–0.5 per metre),
on top of `size_change`, so they leave the exhaust as small as `min_size`/`max_size` (the size of the pipe) and widen
as they rise; `fade_in: t` — they take `t` seconds to show up after they're born, instead of popping up at full opacity.
- **Rain and snow** are particles in three boxes around the camera (10, 24 and 60 metres), more of them the heavier the
rain or snow (the overcast above 1). The drops and flakes fall, drift with the wind, the flakes flutter, and each is
drawn as a streak of its motion against the camera: they slant when the train moves. They are lit like the smoke, so the

View File

@@ -246,10 +246,10 @@ void opengl46_gpu_particles::simulate_smoke(particle_manager &Particles, glm::dv
record.previous = glm::vec4{glm::vec3{state.previous - m_origin}, 0.f};
record.launch = glm::vec4{state.inclination[0], state.inclination[1], state.velocity[0], state.velocity[1]};
record.initial = glm::vec4{state.size[0], state.size[1], state.opacity[0], state.opacity[1]};
record.sizechange = glm::vec4{state.sizechange[0], state.sizechange[1], state.sizechange[2], 0.f};
record.sizechange = glm::vec4{state.sizechange[0], state.sizechange[1], state.sizechange[2], state.spread};
record.opacitychange = glm::vec4{state.opacitychange[0], state.opacitychange[1], state.opacitychange[2], 0.f};
record.motion = glm::vec4{state.rise, state.damping, state.risemin, 0.f};
record.settling = glm::vec4{state.settling, state.drag, state.settles ? 1.f : 0.f, 0.f};
record.settling = glm::vec4{state.settling, state.drag, state.settles ? 1.f : 0.f, state.fadein};
record.color = glm::vec4{glm::clamp(state.color, glm::vec3{0.f}, glm::vec3{1.f}), 0.f};
record.oldcolor = glm::vec4{glm::clamp(state.color * oldlight, glm::vec3{0.f}, glm::vec3{1.f}), 0.f};
if (pool.size > 0)

View File

@@ -67,7 +67,7 @@ opengl46_particles::update( opengl_camera const &Camera ) {
vertex.color[ 0 ] = particlecolor.r;
vertex.color[ 1 ] = particlecolor.g;
vertex.color[ 2 ] = particlecolor.b;
vertex.color.a = std::clamp(particle.opacity, 0.0f, 1.0f);
vertex.color.a = std::clamp(particle.opacity, 0.0f, 1.0f) * sourcedata.fade_in(particle.age);
auto const offset { glm::vec3{ particle.position - Camera.position() } };
auto const rotation { glm::angleAxis( particle.rotation, glm::vec3{ 0.f, 0.f, 1.f } ) };

View File

@@ -122,6 +122,12 @@ smoke_source::deserialize_mapping( cParser &Input ) {
else if( key == "drag:" ) {
m_drag = std::max( 0.f, Input.getToken<float>( true, "\n\r\t ,;[]" ) );
}
else if( key == "spread:" ) {
m_spread = std::max( 0.f, Input.getToken<float>( true, "\n\r\t ,;[]" ) );
}
else if( key == "fade_in:" ) {
m_fadein = std::max( 0.f, Input.getToken<float>( true, "\n\r\t ,;[]" ) );
}
return true; // return value marks a [ key: value ] pair was extracted, nothing about whether it's recognized
}
@@ -385,6 +391,10 @@ smoke_source::update( smoke_particle &Particle, bounding_box &Boundingbox, doubl
if( Particle.opacity <= 0.f ) { return false; }
// ... otherwise proceed with full update
m_sizemodifier.update( Particle.size, Timedelta );
if( m_spread > 0.f ) {
// the jet widens with the way it goes
Particle.size = std::min( Particle.size + m_spread * glm::length( Particle.velocity ) * static_cast<float>( Timedelta ), m_sizemodifier.limit( value_limit::max ) );
}
if( m_settles ) {
// the drag of the air takes the particle towards the velocity it settles at
@@ -463,6 +473,8 @@ smoke_source::update_gpu( double const Timedelta, bool const Onlydespawn ) {
m_gpu.damping = static_cast<float>( 0.050 * Global.Overcast );
m_gpu.risemin = static_cast<float>( 0.25 * ( 2.f - Global.Overcast ) );
}
m_gpu.spread = m_spread;
m_gpu.fadein = m_fadein;
m_gpu.settles = m_settles;
m_gpu.settling = m_settling;
m_gpu.drag = m_drag;

View File

@@ -105,6 +105,8 @@ public:
bool settles { false };
float settling { 0.f };
float drag { 1.f };
float spread { 0.f }; // see smoke_source::m_spread
float fadein { 0.f }; // see smoke_source::m_fadein
glm::vec3 color { 0.f };
};
// methods
@@ -128,6 +130,12 @@ public:
std::size_t
max_particles() const {
return m_max_particles; }
// share of the opacity of a particle the age has let through so far, 0-1 (fade_in:)
float
fade_in( float const Age ) const {
if( m_fadein <= 0.f ) { return 1.f; }
auto const share { std::clamp( Age / m_fadein, 0.f, 1.f ) };
return share * share * ( 3.f - 2.f * share ); }
glm::vec3 const &
color() const {
return m_emitter.color; }
@@ -199,6 +207,11 @@ private:
bool m_settles { false };
float m_settling { 0.f }; // terminal_velocity:
float m_drag { 1.f }; // drag:
// optional growth of the particles with the way they go through the air, metres of size per metre: the jet out of the
// exhaust widens into a cone as it takes in the air around, instead of the puffs swelling in one place
float m_spread { 0.f }; // spread:
// optional time the particles take to show up after they're born, seconds, so they don't pop up at the exhaust
float m_fadein { 0.f }; // fade_in:
// texture_handle m_texture { -1 }; // texture assigned to particle billboards
// current state
float m_spawncount { 0.f }; // number of particles to spawn during next update

View File

@@ -19,10 +19,10 @@ struct smoke_source_s
vec4 previous; // xyz: emitter at the update before, from the origin
vec4 launch; // xy: inclination, radians, zw: speed, m/s
vec4 initial; // xy: size, zw: opacity
vec4 sizechange; // x: per second, y: min, z: max
vec4 sizechange; // x: per second, y: min, z: max, w: per metre the particle goes through the air (spread:)
vec4 opacitychange; // x: per second, y: min, z: max
vec4 motion; // x: rise, y: damping (per second, of the vertical velocity), z: least vertical velocity
vec4 settling; // x: velocity the particles settle at, m/s upwards, y: drag, per second, z: 1 when they settle (in place of motion)
vec4 settling; // x: velocity the particles settle at, m/s upwards, y: drag, per second, z: 1 when they settle (in place of motion), w: seconds they take to show up (fade_in:)
vec4 color; // rgb: colour of the smoke, lit by the lights of the pass
vec4 oldcolor; // rgb: colour lit as the cpu does it, without the lights (gfx.smoke.lighting off)
};

View File

@@ -48,5 +48,9 @@ void main()
vec3 color = source.oldcolor.rgb;
if (options.x > 0.5)
color = source.color.rgb * particle_light((view * vec4(world, 1.0)).xyz, world, options.y);
particle_emit(world, size, vec3(particle.position.w, 0.0, 0.0), color, clamp(particle.state.x, 0.0, 1.0), uint(options.z));
float opacity = clamp(particle.state.x, 0.0, 1.0);
// showing up after the birth (smoke_source::fade_in())
if (source.settling.w > 0.0)
opacity *= smoothstep(0.0, source.settling.w, particle.state.y);
particle_emit(world, size, vec3(particle.position.w, 0.0, 0.0), color, opacity, uint(options.z));
}

View File

@@ -37,6 +37,9 @@ bool advance(inout smoke_particle_s particle, smoke_source_s source, float secon
if (particle.state.x <= 0.0)
return false;
particle.velocity.w = clamp(particle.velocity.w + source.sizechange.x * seconds, source.sizechange.y, source.sizechange.z);
// the jet widens with the way it goes
if (source.sizechange.w > 0.0)
particle.velocity.w = min(particle.velocity.w + source.sizechange.w * length(particle.velocity.xyz) * seconds, source.sizechange.z);
if (source.settling.z > 0.5)
{
// the drag of the air takes the particle towards the velocity it settles at