diff --git a/data/shaders/pointemitter.vert b/data/shaders/pointemitter.vert index 68fb15c8b..503acd2d1 100644 --- a/data/shaders/pointemitter.vert +++ b/data/shaders/pointemitter.vert @@ -1,4 +1,5 @@ uniform int dt; +uniform mat4 previous_frame_sourcematrix; uniform mat4 sourcematrix; uniform int level; uniform float size_increase_factor; @@ -32,31 +33,48 @@ out float new_size; void main(void) { - float updated_lifetime = lifetime + (float(dt)/lifetime_initial); - if (updated_lifetime > 1.) - { - if (gl_VertexID < level) + float updated_lifetime = lifetime + (float(dt)/lifetime_initial); + if (updated_lifetime > 1.) { - float dt_from_last_frame = fract(updated_lifetime) * lifetime_initial; - vec4 updated_initialposition = sourcematrix * vec4(particle_position_initial, 1.0); - vec4 updated_initial_velocity = sourcematrix * vec4(particle_position_initial + particle_velocity_initial, 1.0) - updated_initialposition; - new_particle_position = updated_initialposition.xyz + updated_initial_velocity.xyz * float(dt_from_last_frame); - new_particle_velocity = updated_initial_velocity.xyz; - new_lifetime = fract(updated_lifetime); - new_size = mix(size_initial, size_initial * size_increase_factor, fract(updated_lifetime)); + if (gl_VertexID < level) + { + float dt_from_last_frame = fract(updated_lifetime) * lifetime_initial; + float coeff = dt_from_last_frame / dt; + + vec4 previous_frame_position = previous_frame_sourcematrix * vec4(particle_position_initial, 1.0); + vec4 current_frame_position = sourcematrix * vec4(particle_position_initial, 1.0); + + vec4 updated_initialposition = mix(current_frame_position, + previous_frame_position, + coeff); + + vec4 updated_initial_velocity = mix(sourcematrix * vec4(particle_velocity_initial, 0.0), + previous_frame_sourcematrix * vec4(particle_velocity_initial, 0.0), + coeff); + //+ (current_frame_position - previous_frame_position) / dt; + //To be accurate, emitter speed should be added. + //But the simple formula ( (current_frame_position - previous_frame_position) / dt ) with a constant speed + //between 2 frames creates visual artifacts when the framerate is low, and a more accurate formula would need + //more complex computations. + + new_particle_position = updated_initialposition.xyz + dt_from_last_frame * updated_initial_velocity.xyz; + new_particle_velocity = updated_initial_velocity.xyz; + + new_lifetime = fract(updated_lifetime); + new_size = mix(size_initial, size_initial * size_increase_factor, fract(updated_lifetime)); + } + else + { + new_lifetime = fract(updated_lifetime); + new_size = 0; + } } else { - new_lifetime = fract(updated_lifetime); - new_size = 0; + new_particle_position = particle_position + particle_velocity.xyz * float(dt); + new_particle_velocity = particle_velocity; + new_lifetime = updated_lifetime; + new_size = (size == 0) ? 0. : mix(size_initial, size_initial * size_increase_factor, updated_lifetime); } - } - else - { - new_particle_position = particle_position + particle_velocity.xyz * float(dt); - new_particle_velocity = particle_velocity; - new_lifetime = updated_lifetime; - new_size = (size == 0) ? 0. : mix(size_initial, size_initial * size_increase_factor, updated_lifetime); - } - gl_Position = vec4(0.); + gl_Position = vec4(0.); } diff --git a/src/graphics/gpu_particles.cpp b/src/graphics/gpu_particles.cpp index e63363de5..9eb8525d0 100644 --- a/src/graphics/gpu_particles.cpp +++ b/src/graphics/gpu_particles.cpp @@ -37,7 +37,7 @@ /** Transform feedback shader that simulates the particles on GPU. */ class PointEmitterShader : public Shader - < PointEmitterShader, core::matrix4, int, int, float > + < PointEmitterShader, core::matrix4, core::matrix4, int, int, float > { public: PointEmitterShader() @@ -45,7 +45,8 @@ public: const char *varyings[] = { "new_particle_position", "new_lifetime", "new_particle_velocity", "new_size" }; loadTFBProgram("pointemitter.vert", varyings, 4); - assignUniforms("sourcematrix", "dt", "level", "size_increase_factor"); + assignUniforms("previous_frame_sourcematrix", "sourcematrix", + "dt", "level", "size_increase_factor"); } // PointEmitterShader }; // PointEmitterShader @@ -262,7 +263,7 @@ void ParticleSystemProxy::generateParticlesFromPointEmitter(scene::IParticlePoin ParticleParams[i].PositionZ = 0; // Initial lifetime is >1 InitialValues[i].Lifetime = 2.; - + memcpy(&(InitialValues[i].PositionX), &(ParticleParams[i].PositionX), 3 * sizeof(float)); generateLifetimeSizeDirection(emitter, ParticleParams[i].Lifetime, ParticleParams[i].Size, @@ -466,11 +467,11 @@ void ParticleSystemProxy::CommonSimulationVAO(GLuint position_vbo, GLuint initia } void ParticleSystemProxy::simulate() -{ +{ int timediff = int(GUIEngine::getLatestDt() * 1000.f); int active_count = getEmitter()->getMaxLifeTime() * getEmitter()->getMaxParticlesPerSecond() / 1000; core::matrix4 matrix = getAbsoluteTransformation(); - + glEnable(GL_RASTERIZER_DISCARD); if (has_height_map) { @@ -482,9 +483,10 @@ void ParticleSystemProxy::simulate() else { PointEmitterShader::getInstance()->use(); - PointEmitterShader::getInstance()->setUniforms(matrix, timediff, active_count, size_increase_factor); + PointEmitterShader::getInstance()->setUniforms(m_previous_frame_matrix, matrix, timediff, active_count, size_increase_factor); } - + m_previous_frame_matrix = matrix; + glBindVertexArray(current_simulation_vao); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfb_buffers[1]); @@ -617,8 +619,14 @@ void ParticleSystemProxy::render() { return; } if (m_first_execution) + { generateVAOs(); - m_first_execution = false; - simulate(); - draw(); + m_previous_frame_matrix = getAbsoluteTransformation(); + m_first_execution = false; + } + else + { + simulate(); + draw(); + } } diff --git a/src/graphics/gpu_particles.hpp b/src/graphics/gpu_particles.hpp index f04464661..d23d3952f 100644 --- a/src/graphics/gpu_particles.hpp +++ b/src/graphics/gpu_particles.hpp @@ -40,9 +40,12 @@ protected: float m_color_to[3]; bool m_first_execution; bool m_randomize_initial_y; - + GLuint texture; - + + /** Previous frame particles emitter source matrix */ + core::matrix4 m_previous_frame_matrix; + /** Current count of particles. */ unsigned m_count; /** Previous count - for error handling only. */ diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 973430bad..71c3ed9ea 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2582,9 +2582,8 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, // the normal maximum speed of the kart. if(nitro_frac>1.0f) nitro_frac = 1.0f; } - // speed * dt is the new size of the box in which particles start - m_kart_gfx->updateNitroGraphics(nitro_frac, getSpeed()*dt); - + m_kart_gfx->updateNitroGraphics(nitro_frac); + // Handle leaning of karts // ----------------------- // Note that we compare with maximum speed of the kart, not diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index ae62b1073..35785a31c 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -350,11 +350,10 @@ void KartGFX::update(float dt) } // update // ---------------------------------------------------------------------------- -/** Updates nitro dependent particle effects (and box sizes). +/** Updates nitro dependent particle effects. * \param nitro_frac Nitro fraction/ - * \param new_size New size of the box in which new particles are emitted. */ -void KartGFX::updateNitroGraphics(float nitro_frac, float new_size) +void KartGFX::updateNitroGraphics(float nitro_frac) { // Upate particle effects (creation rate, and emitter size // depending on speed) @@ -375,11 +374,6 @@ void KartGFX::updateNitroGraphics(float nitro_frac, float new_size) setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0); m_nitro_light->setVisible(false); } - resizeBox(KartGFX::KGFX_NITRO1, new_size); - resizeBox(KartGFX::KGFX_NITRO2, new_size); - resizeBox(KartGFX::KGFX_NITROSMOKE1, new_size); - resizeBox(KartGFX::KGFX_NITROSMOKE2, new_size); - resizeBox(KartGFX::KGFX_ZIPPER, new_size); } // updateGraphics diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index 5ac31df26..8f3c26490 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -99,7 +99,7 @@ public: void setCreationRateRelative(const KartGFXType type, float f); void updateTerrain(const ParticleKind *pk); void update(float dt); - void updateNitroGraphics(float f, float new_size); + void updateNitroGraphics(float f); void updateSkidLight(unsigned int level); }; // KartWGFX