From 16bb12011a3da070de73382c41aca23c457ec74b Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Thu, 15 Dec 2011 17:59:24 -0200 Subject: [PATCH] rework particles math --- TODO | 4 +- modules/particle.otpa | 20 +-- src/framework/core/clock.h | 4 + src/framework/graphics/particleemitter.cpp | 159 +++++++++------------ src/framework/graphics/particleemitter.h | 20 ++- src/framework/graphics/particlemanager.cpp | 3 - 6 files changed, 95 insertions(+), 115 deletions(-) diff --git a/TODO b/TODO index 8f85bed6..2b806db3 100644 --- a/TODO +++ b/TODO @@ -38,4 +38,6 @@ create image class use CoordsBuffer in font cache into framebuffers implement glbuffer for CoordsBuffer -use indices in CoordsBuffer \ No newline at end of file +use indices in CoordsBuffer + +create a Timer class \ No newline at end of file diff --git a/modules/particle.otpa b/modules/particle.otpa index 9882eee6..f738b850 100644 --- a/modules/particle.otpa +++ b/modules/particle.otpa @@ -4,27 +4,27 @@ ParticleSystem duration: 9 burstRate: 0.03 burstCount: 1 - - + + particle-size: 8 8 - + particle-position-radius: 0 - particle-velocity-angle: 360 + particle-velocity-angle: 0 particle-acceleration: 0 - + particle-color: #22940684 particle-texture: circle2.png - + Emitter position: 100 100 duration: 9 burstRate: 1 burstCount: 30 - + particle-size: 5 5 - + particle-position-radius: 0 - particle-velocity-angle: 360 - particle-acceleration-angle: 315 + particle-velocity-angle: 0 + particle-acceleration-angle: 90 particle-color: #ff0000ff particle-texture: circle2.png diff --git a/src/framework/core/clock.h b/src/framework/core/clock.h index 6a749d46..39c2a747 100644 --- a/src/framework/core/clock.h +++ b/src/framework/core/clock.h @@ -37,6 +37,10 @@ public: ticks_t ticksElapsed(long prevTicks) { return ticks() - prevTicks; } ticks_t ticksFor(int delay) { return ticks() + delay; } + double time() { return m_currentTicks/1000.0; } + double timeElapsed(double prevTime) { return time() - prevTime; } + double timeFor(double delay) { return time() + delay; } + private: ticks_t m_currentTicks; std::chrono::system_clock::time_point m_startupTime; diff --git a/src/framework/graphics/particleemitter.cpp b/src/framework/graphics/particleemitter.cpp index 5e8b2082..7c637ed5 100644 --- a/src/framework/graphics/particleemitter.cpp +++ b/src/framework/graphics/particleemitter.cpp @@ -26,24 +26,23 @@ #include #include -Particle::Particle(const Rect& rect, float vx, float vy, float ax, float ay, float duration, const Color& color, TexturePtr texture) +#define DEG_TO_RAD (acos(-1)/180.0) + +Particle::Particle(const Point& pos, const Size& size, const PointF& velocity, const PointF& acceleration, float duration, const Color& color, TexturePtr texture) { - m_rect = rect; - m_ix = rect.x(); m_iy = rect.y(); - m_vx = vx; m_vy = vy; - m_ax = ax; m_ay = ay; + m_rect = Rect(pos, size); + m_pos = PointF(pos.x, pos.y); + m_size = size; + m_velocity = velocity; + m_acceleration = acceleration; m_color = color; m_texture = texture; m_duration = duration; - m_startTicks = g_clock.ticks(); + m_startTime = g_clock.time(); + m_lastUpdateTime = g_clock.time(); m_finished = false; } -Particle::~Particle() -{ - //dump << "deleted"; -} - void Particle::render() { g_painter.setColor(m_color); @@ -59,17 +58,24 @@ void Particle::render() void Particle::update() { - ticks_t t = g_clock.ticks() - m_startTicks; + float elapsedTime = g_clock.timeElapsed(m_lastUpdateTime); + m_lastUpdateTime = g_clock.time(); // check if finished - if(m_duration >= 0 && t > m_duration * 1000) { + if(m_duration > 0 && g_clock.timeElapsed(m_startTime) >= m_duration) { m_finished = true; return; } - //update position - m_rect.moveTo(m_ix + (m_vx * t / 1000.0) + (m_ax * t*t / (2.0 * 1000 * 1000)), - m_iy + (m_vy * t / 1000.0) + (m_ay * t*t / (2.0 * 1000 * 1000))); + // update position + PointF delta = m_velocity * elapsedTime; + delta.y *= -1; // painter orientate Y axis in the inverse direction + m_pos += delta; + + // update acceleration + m_velocity += m_acceleration * elapsedTime; + + m_rect.moveTo((int)m_pos.x, (int)m_pos.y); } ParticleEmitter::ParticleEmitter() @@ -78,44 +84,41 @@ ParticleEmitter::ParticleEmitter() m_duration = -1; m_burstRate = 1; m_burstCount = 32; m_currentBurst = 0; - m_startTicks = g_clock.ticks(); + m_startTime = g_clock.time(); m_finished = false; // particles default configuration. (make them reasonable for user detect missing properties on scripts) - m_pMinPositionRadius = 0; m_pMaxPositionRadius = 3; - m_pMinPositionAngle = -Fw::pi; m_pMaxPositionAngle = Fw::pi; - m_pMinSize = Size(32, 32); m_pMaxSize = Size(32, 32); - m_pMinDuration = 0; m_pMaxDuration = 10; - - m_pMinVelocity = 32; m_pMaxVelocity = 64; - m_pMinVelocityAngle = -Fw::pi; m_pMaxVelocityAngle = Fw::pi; - - m_pMinAcceleration = 32; m_pMaxAcceleration = 64; - m_pMinAccelerationAngle = -Fw::pi; m_pMaxAccelerationAngle = Fw::pi; - - m_pColor = Color(255, 0, 0, 128); - m_pTexture = nullptr; + m_pMinPositionRadius = 0; + m_pMaxPositionRadius = 3; + m_pMinPositionAngle = 0; + m_pMaxPositionAngle = 360; + m_pMinSize = Size(32, 32); + m_pMaxSize = Size(32, 32); + m_pMinDuration = 0; + m_pMaxDuration = 10; + m_pMinVelocity = 32; + m_pMaxVelocity = 64; + m_pMinVelocityAngle = 0; + m_pMaxVelocityAngle = 360; + m_pMinAcceleration = 32; + m_pMaxAcceleration = 64; + m_pMinAccelerationAngle = 0; + m_pMaxAccelerationAngle = 360; + m_pColor = Color(255, 255, 255, 128); } bool ParticleEmitter::load(const OTMLNodePtr& node) { for(const OTMLNodePtr& childNode : node->children()) { - // self related - if(childNode->tag() == "position") { - std::string value = childNode->value(); - std::vector split; - boost::split(split, value, boost::is_any_of(std::string(" "))); - if(split.size() == 2) - m_position = Point(Fw::safeCast(split[0]), Fw::safeCast(split[1])); - } + if(childNode->tag() == "position") + m_position = childNode->value(); else if(childNode->tag() == "duration") m_duration = childNode->value(); else if(childNode->tag() == "burstRate") m_burstRate = childNode->value(); else if(childNode->tag() == "burstCount") m_burstCount = childNode->value(); - // particles generation related else if(childNode->tag() == "particle-position-radius") { m_pMinPositionRadius = childNode->value(); @@ -125,15 +128,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node) m_pMinPositionRadius = childNode->value(); else if(childNode->tag() == "particle-max-position-radius") m_pMaxPositionRadius = childNode->value(); - else if(childNode->tag() == "particle-position-angle") { - m_pMinPositionAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; - m_pMaxPositionAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; + m_pMinPositionAngle = childNode->value() * DEG_TO_RAD; + m_pMaxPositionAngle = childNode->value() * DEG_TO_RAD; } else if(childNode->tag() == "particle-min-position-angle") - m_pMinPositionAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; + m_pMinPositionAngle = childNode->value() * DEG_TO_RAD; else if(childNode->tag() == "particle-max-position-angle") - m_pMaxPositionAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; + m_pMaxPositionAngle = childNode->value() * DEG_TO_RAD; else if(childNode->tag() == "particle-velocity") { m_pMinVelocity = childNode->value(); @@ -143,16 +145,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node) m_pMinVelocity = childNode->value(); else if(childNode->tag() == "particle-max-velocity") m_pMaxVelocity = childNode->value(); - else if(childNode->tag() == "particle-velocity-angle") { - m_pMinVelocityAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; - m_pMaxVelocityAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; + m_pMinVelocityAngle = childNode->value() * DEG_TO_RAD; + m_pMaxVelocityAngle = childNode->value() * DEG_TO_RAD; } else if(childNode->tag() == "particle-min-velocity-angle") - m_pMinVelocityAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; + m_pMinVelocityAngle = childNode->value() * DEG_TO_RAD; else if(childNode->tag() == "particle-max-velocity-angle") - m_pMaxVelocityAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; - + m_pMaxVelocityAngle = childNode->value() * DEG_TO_RAD; else if(childNode->tag() == "particle-acceleration") { m_pMinAcceleration = childNode->value(); m_pMaxAcceleration = childNode->value(); @@ -161,16 +161,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node) m_pMinAcceleration = childNode->value(); else if(childNode->tag() == "particle-max-acceleration") m_pMaxAcceleration = childNode->value(); - else if(childNode->tag() == "particle-acceleration-angle") { - m_pMinAccelerationAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; - m_pMaxAccelerationAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; + m_pMinAccelerationAngle = childNode->value() * DEG_TO_RAD; + m_pMaxAccelerationAngle = childNode->value() * DEG_TO_RAD; } else if(childNode->tag() == "particle-min-acceleration-angle") - m_pMinAccelerationAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; + m_pMinAccelerationAngle = childNode->value() * DEG_TO_RAD; else if(childNode->tag() == "particle-max-acceleration-angle") - m_pMaxAccelerationAngle = (childNode->value() * Fw::pi / 180.0) - Fw::pi; - + m_pMaxAccelerationAngle = childNode->value() * DEG_TO_RAD; else if(childNode->tag() == "particle-duration") { m_pMinDuration = childNode->value(); m_pMaxDuration = childNode->value(); @@ -179,32 +177,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node) m_pMinDuration = childNode->value(); else if(childNode->tag() == "particle-max-duration") m_pMaxDuration = childNode->value(); - else if(childNode->tag() == "particle-size") { - std::string value = childNode->value(); - std::vector split; - boost::split(split, value, boost::is_any_of(std::string(" "))); - if(split.size() == 2) { - m_pMinSize = Size(Fw::safeCast(split[0]), Fw::safeCast(split[1])); - m_pMaxSize = Size(Fw::safeCast(split[0]), Fw::safeCast(split[1])); - } - } - else if(childNode->tag() == "particle-min-size") { - std::string value = childNode->value(); - std::vector split; - boost::split(split, value, boost::is_any_of(std::string(" "))); - if(split.size() == 2) { - m_pMinSize = Size(Fw::safeCast(split[0]), Fw::safeCast(split[1])); - } - } - else if(childNode->tag() == "particle-max-size") { - std::string value = childNode->value(); - std::vector split; - boost::split(split, value, boost::is_any_of(std::string(" "))); - if(split.size() == 2) { - m_pMaxSize = Size(Fw::safeCast(split[0]), Fw::safeCast(split[1])); - } + m_pMinSize = childNode->value(); + m_pMaxSize = childNode->value(); } + else if(childNode->tag() == "particle-min-size") + m_pMinSize = childNode->value(); + else if(childNode->tag() == "particle-max-size") + m_pMaxSize = childNode->value(); else if(childNode->tag() == "particle-color") m_pColor = childNode->value(); else if(childNode->tag() == "particle-texture") @@ -221,7 +201,7 @@ void ParticleEmitter::render() void ParticleEmitter::update() { - ticks_t elapsedTicks = g_clock.ticks() - m_startTicks; + float elapsedTime = g_clock.timeElapsed(m_startTime); // update particles for(auto it = m_particles.begin(), end = m_particles.end(); it != end;) { @@ -235,20 +215,20 @@ void ParticleEmitter::update() } // check if finished - if(m_duration >= 0 && elapsedTicks > m_duration * 1000) { + if(m_duration > 0 && elapsedTime > m_duration) { // stop emitting but only self remove when there are no particles left if(m_particles.size() == 0) m_finished = true; return; } - int currentBurst = elapsedTicks / 1000.0 / m_burstRate + 1; + int currentBurst = (elapsedTime / m_burstRate) + 1; for(int b = m_currentBurst; b < currentBurst; ++b) { // every burst created at same position. float pRadius = Fw::randomRange(m_pMinPositionRadius, m_pMaxPositionRadius); float pAngle = Fw::randomRange(m_pMinPositionAngle, m_pMaxPositionAngle); - Point pPosition = Point(-pRadius * cos(pAngle), pRadius * sin(pAngle)); + Point pPosition = m_position + Point(pRadius * cos(pAngle), pRadius * sin(pAngle)); for(int p = 0; p < m_burstCount; ++p) { @@ -256,17 +236,16 @@ void ParticleEmitter::update() float pDuration = Fw::randomRange(m_pMinDuration, m_pMaxDuration); // particles initial velocity - float pVelocity = Fw::randomRange(m_pMinVelocity, m_pMaxVelocity); + float pVelocityAbs = Fw::randomRange(m_pMinVelocity, m_pMaxVelocity); float pVelocityAngle = Fw::randomRange(m_pMinVelocityAngle, m_pMaxVelocityAngle); + PointF pVelocity(pVelocityAbs * cos(pVelocityAngle), pVelocityAbs * sin(pVelocityAngle)); // particles initial acceleration - float pAcceleration = Fw::randomRange(m_pMinAcceleration, m_pMaxAcceleration); + float pAccelerationAbs = Fw::randomRange(m_pMinAcceleration, m_pMaxAcceleration); float pAccelerationAngle = Fw::randomRange(m_pMinAccelerationAngle, m_pMaxAccelerationAngle); + PointF pAcceleration(pAccelerationAbs * cos(pAccelerationAngle), pAccelerationAbs * sin(pAccelerationAngle)); - m_particles.push_back(ParticlePtr(new Particle(Rect(m_position + pPosition, pSize), - -pVelocity * cos(pVelocityAngle), pVelocity * sin(pVelocityAngle), - -pAcceleration * cos(pAccelerationAngle), pAcceleration * sin(pAccelerationAngle), - pDuration, m_pColor, m_pTexture))); + m_particles.push_back(ParticlePtr(new Particle(pPosition, pSize, pVelocity, pAcceleration, pDuration, m_pColor, m_pTexture))); } } diff --git a/src/framework/graphics/particleemitter.h b/src/framework/graphics/particleemitter.h index 256abaa1..6eb4f084 100644 --- a/src/framework/graphics/particleemitter.h +++ b/src/framework/graphics/particleemitter.h @@ -29,9 +29,7 @@ class Particle { public: - - Particle(const Rect& rect, float vx, float vy, float ax, float ay, float duration, const Color& color = Color(255, 255, 255), TexturePtr texture = nullptr); - ~Particle(); + Particle(const Point& pos, const Size& size, const PointF& velocity, const PointF& acceleration, float duration, const Color& color = Fw::white, TexturePtr texture = nullptr); void render(); void update(); @@ -39,16 +37,16 @@ public: bool hasFinished() { return m_finished; } private: - Rect m_rect; Color m_color; TexturePtr m_texture; - - int m_ix, m_iy; - float m_vx, m_vy; - float m_ax, m_ay; - + PointF m_pos; + PointF m_velocity; + PointF m_acceleration; + Size m_size; + Rect m_rect; float m_duration; - ticks_t m_startTicks; + double m_startTime; + double m_lastUpdateTime; bool m_finished; }; typedef std::shared_ptr ParticlePtr; @@ -69,7 +67,7 @@ private: // self related Point m_position; int m_duration; - ticks_t m_startTicks; + double m_startTime; bool m_finished; float m_burstRate; int m_currentBurst, m_burstCount; diff --git a/src/framework/graphics/particlemanager.cpp b/src/framework/graphics/particlemanager.cpp index 6d1667f7..b46786cc 100644 --- a/src/framework/graphics/particlemanager.cpp +++ b/src/framework/graphics/particlemanager.cpp @@ -28,9 +28,6 @@ ParticleManager g_particleManager; bool ParticleManager::load(const std::string& filename) { - if(!g_resources.fileExists(filename)) - return false; - try { OTMLDocumentPtr doc = OTMLDocument::parse(filename); const OTMLNodePtr& node = doc->at("ParticleSystem");