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

Simulate and light the smoke on the GPU and make rain and snow of particles

This commit is contained in:
2026-10-20 16:44:27 +02:00
parent 6a109e984c
commit 867497b658
22 changed files with 1637 additions and 13 deletions

View File

@@ -152,15 +152,9 @@ smoke_source::bind( TAnimModel const *Node ) {
owner_type::none );
}
// updates state of owned particles
// adds the particles to spawn in an update of Timedelta to m_spawncount
void
smoke_source::update( double const Timedelta, bool const Onlydespawn ) {
// prepare bounding box for new pass
// TODO: include bounding box in the bounding_area class
bounding_box boundingbox {
glm::dvec3{ std::numeric_limits<double>::max() },
glm::dvec3{ std::numeric_limits<double>::lowest() } };
smoke_source::accumulate_spawns( double const Timedelta, bool const Onlydespawn ) {
m_spawncount = (
( ( false == Global.Smoke ) || ( true == Onlydespawn ) ) ?
@@ -209,6 +203,19 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) {
}
}
}
}
// updates state of owned particles
void
smoke_source::update( double const Timedelta, bool const Onlydespawn ) {
// prepare bounding box for new pass
// TODO: include bounding box in the bounding_area class
bounding_box boundingbox {
glm::dvec3{ std::numeric_limits<double>::max() },
glm::dvec3{ std::numeric_limits<double>::lowest() } };
accumulate_spawns( Timedelta, Onlydespawn );
// update spawned particles
for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) {
@@ -329,23 +336,37 @@ smoke_source::initialize( smoke_particle &Particle ) {
Particle.position = location();
if( m_ownertype == owner_type::vehicle ) {
Particle.opacity *= static_cast<float>(m_owner.vehicle->MoverParameters->dizel_fill);
float velocityscale, opacityscale;
launch_scales( velocityscale, opacityscale );
Particle.opacity *= opacityscale;
Particle.velocity *= velocityscale;
}
}
// shares of the launch velocity and of the opacity of a particle born now, which the owner gives
void
smoke_source::launch_scales( float &Velocity, float &Opacity ) const {
Velocity = 1.f;
Opacity = 1.f;
if( m_ownertype != owner_type::vehicle ) { return; }
Opacity = static_cast<float>(m_owner.vehicle->MoverParameters->dizel_fill);
if (m_owner.vehicle->MoverParameters->EngineType == TEngineType::DieselElectric)
{
if (m_owner.vehicle->MoverParameters->dizel_spinup == true)
{
Particle.velocity *= 0.38*(((m_owner.vehicle->MoverParameters->enrot)/2)*0.5); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
Velocity = static_cast<float>(0.38*(((m_owner.vehicle->MoverParameters->enrot)/2)*0.5)); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
}
else
{
Particle.velocity *= ((((m_owner.vehicle->MoverParameters->enrot)/60)*0.6) * (m_owner.vehicle->MoverParameters->EnginePower *0.03)); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
Velocity = static_cast<float>((((m_owner.vehicle->MoverParameters->enrot)/60)*0.6) * (m_owner.vehicle->MoverParameters->EnginePower *0.03)); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
//Particle.velocity *= m_owner.vehicle->GetVelocity(); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
}
}
}
}
// updates state of provided particle and bounding box. returns: true if particle is still alive afterwards, false otherwise
@@ -385,6 +406,58 @@ smoke_source::update( smoke_particle &Particle, bounding_box &Boundingbox, doubl
return true;
}
// the same as update() for the particles simulated on the gpu: the births of the update and what the particles start from and
// change by, worked out as the cpu does it in update(), initialize() and update( Particle ). the particles of the cpu are dropped
void
smoke_source::update_gpu( double const Timedelta, bool const Onlydespawn ) {
m_particles.clear();
accumulate_spawns( Timedelta, Onlydespawn );
// the cpu fills the free places, then replaces the oldest; a full source can't take more than its size in an update and
// drops the rest of whole particles, as the cpu does
auto const spawns { std::min( std::floor( m_spawncount ), static_cast<float>( m_max_particles ) ) };
m_spawncount -= spawns;
m_spawncount -= std::floor( m_spawncount );
m_gpu.spawns = static_cast<std::uint32_t>( std::max( spawns, 0.f ) );
auto const here { location() };
m_gpu.previous = ( m_gpu.started ? m_gpu.location : here );
m_gpu.location = here;
m_gpu.started = true;
float velocityscale, opacityscale;
launch_scales( velocityscale, opacityscale );
for( int limit = value_limit::min; limit <= value_limit::max; ++limit ) {
m_gpu.inclination[ limit ] = glm::radians( m_emitter.inclination[ limit ] );
m_gpu.velocity[ limit ] = m_emitter.velocity[ limit ] * velocityscale;
m_gpu.size[ limit ] = m_emitter.size[ limit ];
m_gpu.opacity[ limit ] = m_emitter.opacity[ limit ] / Global.SmokeFidelity * opacityscale;
}
m_gpu.sizechange[ 0 ] = m_sizemodifier.value_change();
m_gpu.sizechange[ 1 ] = m_sizemodifier.limit( value_limit::min );
m_gpu.sizechange[ 2 ] = m_sizemodifier.limit( value_limit::max );
m_gpu.opacitychange[ 0 ] = m_opacitymodifier.value_change();
m_gpu.opacitychange[ 1 ] = m_opacitymodifier.limit( value_limit::min );
m_gpu.opacitychange[ 2 ] = m_opacitymodifier.limit( value_limit::max );
// crude smoke dispersion simulation, as in update( Particle )
if( m_ownertype == owner_type::vehicle ) {
m_gpu.rise = static_cast<float>( 0.025 * std::min( 0.f, Global.AirTemperature - 90 ) );
m_gpu.damping = static_cast<float>( 0.05 * pow( m_owner.vehicle->GetVelocity() * 1, 0.4 ) * Global.Overcast );
m_gpu.risemin = std::numeric_limits<float>::lowest();
}
else {
m_gpu.rise = static_cast<float>( 0.005 * std::min( 0.f, Global.AirTemperature - 10 ) );
m_gpu.damping = static_cast<float>( 0.050 * Global.Overcast );
m_gpu.risemin = static_cast<float>( 0.25 * ( 2.f - Global.Overcast ) );
}
m_gpu.color = m_emitter.color;
// the renderer culls the particles one by one; the area is where they come from, for the distance of the source
m_area.center = here;
m_area.radius = 0;
}
// adds a new particle source of specified type, placing it in specified world location
@@ -437,13 +510,33 @@ particle_manager::update() {
if( timedelta == 0.0 ) { return; }
++m_updates;
m_timedelta = timedelta;
auto const distancethreshold { 2 * Global.BaseDrawRange * Global.fDistanceFactor }; // to reduce workload distant enough sources won't spawn new particles
for( auto &source : m_sources ) {
auto const viewerdistance { glm::length( source.area().center - glm::dvec3{ Global.pCamera.Pos } ) - source.area().radius };
source.update( timedelta, viewerdistance > distancethreshold );
if( m_gpu ) {
source.update_gpu( timedelta, viewerdistance > distancethreshold );
}
else {
source.update( timedelta, viewerdistance > distancethreshold );
}
}
}
void
particle_manager::gpu( bool const Gpu ) {
if( Gpu == m_gpu ) { return; }
m_gpu = Gpu;
for( auto &source : m_sources ) {
source.m_particles.clear();
source.m_gpu.started = false;
source.m_gpu.spawns = 0;
}
}