diff --git a/particles.cpp b/particles.cpp index 9f9dbc94..f8011155 100644 --- a/particles.cpp +++ b/particles.cpp @@ -117,11 +117,6 @@ smoke_source::initialize() { // this gives us estimate of longest potential lifespan of single particle, and how many particles total can there be at any given time // TBD, TODO: explicit lifespan variable as part of the source configuration? static_cast( m_spawnrate / std::abs( m_opacitymodifier.value_change() ) ) ) ); -/* - m_particlehead = - m_particletail = - std::begin( m_particles ); -*/ } void @@ -134,6 +129,16 @@ smoke_source::bind( TDynamicObject const *Vehicle ) { owner_type::none ); } +void +smoke_source::bind( TAnimModel const *Node ) { + + m_owner.node = Node; + m_ownertype = ( + m_owner.node != nullptr ? + owner_type::node : + owner_type::none ); +} + // updates state of owned particles void smoke_source::update( double const Timedelta, bool const Onlydespawn ) { @@ -151,32 +156,6 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) { m_spawncount + ( m_spawnrate * Timedelta ), m_particles.capacity() ) ); // update spawned particles -/* - while( m_particlehead != m_particletail ) { - - auto &particle { *m_particlehead }; - bool particleisalive; - - while( ( false == ( particleisalive = update( particle, Timedelta ) ) ) - && ( m_spawncount >= 1.f ) ) { - // replace dead particle with a new one - m_spawncount -= 1.f; - initialize( particle ); - } - if( false == particleisalive ) { - // we have a dead particle and no pending spawn requests, (try to) move the last particle here - do { - --m_particletail; - if( m_particlehead == m_particletail ) { break; } - particle = *m_particletail; - } while( false == ( particleisalive = update( particle, Timedelta ) ) ); - } - if( true == particleisalive ) { - // ensure at the end of the pass the head iterator is placed after the last alive particle - ++m_particlehead; - } - } -*/ for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) { auto &particle { *particleiterator }; @@ -205,18 +184,6 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) { } } // spawn pending particles in remaining container slots -/* - while( ( m_spawncount >= 1.f ) - && ( m_particlehead != std::end( m_particles ) ) ) { - - m_spawncount -= 1.f; - auto &particle { *m_particlehead }; - initialize( particle ); - if( true == update( particle, Timedelta ) ) { - ++m_particlehead; - } - } -*/ while( ( m_spawncount >= 1.f ) && ( m_particles.size() < m_particles.capacity() ) ) { @@ -258,12 +225,6 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) { // discard pending spawn requests our container couldn't fit m_spawncount -= std::floor( m_spawncount ); } -/* - // after the pass the head iterator is left last alive particle - m_particletail = m_particlehead; - m_particlehead = std::begin( m_particles ); -*/ - // determine bounding area from calculated bounding box if( false == m_particles.empty() ) { m_area.center = interpolate( boundingbox[ value_limit::min ], boundingbox[ value_limit::max ], 0.5 ); @@ -291,7 +252,8 @@ smoke_source::location() const { } case owner_type::node: { // TODO: take into account node rotation - location = m_offset; + auto const rotation { glm::angleAxis( glm::radians( m_owner.node->Angles().y ), glm::vec3{ 0.f, 1.f, 0.f } ) }; + location = rotation * glm::vec3{ m_offset }; location += m_owner.node->location(); break; } @@ -342,10 +304,12 @@ smoke_source::update( smoke_particle &Particle, bounding_box &Boundingbox, doubl // crude smoke dispersion simulation // http://www.auburn.edu/academic/forestry_wildlife/fire/smoke_guide/smoke_dispersion.htm - Particle.velocity.y = std::max( 0.25 * ( 1.f - Global.Overcast ), Particle.velocity.y - 0.25 * Global.Overcast * Timedelta ); + Particle.velocity.y += ( 0.005 * Particle.velocity.y ) * std::min( 0.f, Global.AirTemperature - 10 ) * Timedelta; // decelerate faster in cold weather + Particle.velocity.y -= ( 0.010 * Particle.velocity.y ) * Global.Overcast * Timedelta; // decelerate faster with high air humidity and/or precipitation + Particle.velocity.y = std::max( 0.25 * ( 2.f - Global.Overcast ), Particle.velocity.y ); // put a cap on deceleration Particle.position += Particle.velocity * static_cast( Timedelta ); - Particle.position += 0.35f * simulation::Environment.wind() * static_cast( Timedelta ); + Particle.position += 0.1f * Particle.age * simulation::Environment.wind() * static_cast( Timedelta ); // m_velocitymodifier.update( Particle.velocity, Timedelta ); Particle.age += Timedelta; @@ -389,6 +353,18 @@ particle_manager::insert( std::string const &Sourcetemplate, TDynamicObject cons return true; } +bool +particle_manager::insert( std::string const &Sourcetemplate, TAnimModel const *Node, glm::dvec3 const Location ) { + + if( false == insert( Sourcetemplate, Location ) ) { return false; } + + // attach the source to specified node + auto &source { m_sources.back() }; + source.bind( Node ); + + return true; +} + // updates state of all owned emitters void particle_manager::update() { diff --git a/particles.h b/particles.h index 4e03bf63..f7de0630 100644 --- a/particles.h +++ b/particles.h @@ -81,6 +81,8 @@ public: initialize(); void bind( TDynamicObject const *Vehicle ); + void + bind( TAnimModel const *Node ); // updates state of owned particles void update( double const Timedelta, bool const Onlydespawn ); @@ -171,6 +173,8 @@ public: insert( std::string const &Sourcetemplate, glm::dvec3 const Location ); bool insert( std::string const &Sourcetemplate, TDynamicObject const *Vehicle, glm::dvec3 const Location ); + bool + insert( std::string const &Sourcetemplate, TAnimModel const *Node, glm::dvec3 const Location ); // updates state of all owned emitters void update(); diff --git a/simulationenvironment.cpp b/simulationenvironment.cpp index 48e45ba4..6b5833c8 100644 --- a/simulationenvironment.cpp +++ b/simulationenvironment.cpp @@ -195,8 +195,12 @@ world_environment::update_wind() { m_wind.change_time -= timedelta; if( m_wind.change_time < 0 ) { m_wind.change_time = Random( 5, 30 ); - m_wind.azimuth_change = Random( -5, 5 ); m_wind.velocity_change = Random( -1, 1 ); + if( Random() < 0.2 ) { + // changes in wind direction should be less frequent than changes in wind speed + // TBD, TODO: configuration-driven direction change frequency + m_wind.azimuth_change = Random( -5, 5 ); + } } // TBD, TODO: wind configuration m_wind.azimuth = clamp_circular( m_wind.azimuth + m_wind.azimuth_change * timedelta ); diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index ee4424ed..a58e16d1 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -458,6 +458,15 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch // model import can potentially fail if( instance == nullptr ) { return; } + if( instance->Model() != nullptr ) { + for( auto const &smokesource : instance->Model()->smoke_sources() ) { + Particles.insert( + smokesource.first, + instance, + smokesource.second ); + } + } + if( false == simulation::Instances.insert( instance ) ) { ErrorLog( "Bad scenario: duplicate 3d model instance name \"" + instance->name() + "\" defined in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); }