rework particles math

This commit is contained in:
Eduardo Bart 2011-12-15 17:59:24 -02:00
parent 1339e18202
commit 16bb12011a
6 changed files with 95 additions and 115 deletions

4
TODO
View File

@ -38,4 +38,6 @@ create image class
use CoordsBuffer in font use CoordsBuffer in font
cache into framebuffers cache into framebuffers
implement glbuffer for CoordsBuffer implement glbuffer for CoordsBuffer
use indices in CoordsBuffer use indices in CoordsBuffer
create a Timer class

View File

@ -4,27 +4,27 @@ ParticleSystem
duration: 9 duration: 9
burstRate: 0.03 burstRate: 0.03
burstCount: 1 burstCount: 1
particle-size: 8 8 particle-size: 8 8
particle-position-radius: 0 particle-position-radius: 0
particle-velocity-angle: 360 particle-velocity-angle: 0
particle-acceleration: 0 particle-acceleration: 0
particle-color: #22940684 particle-color: #22940684
particle-texture: circle2.png particle-texture: circle2.png
Emitter Emitter
position: 100 100 position: 100 100
duration: 9 duration: 9
burstRate: 1 burstRate: 1
burstCount: 30 burstCount: 30
particle-size: 5 5 particle-size: 5 5
particle-position-radius: 0 particle-position-radius: 0
particle-velocity-angle: 360 particle-velocity-angle: 0
particle-acceleration-angle: 315 particle-acceleration-angle: 90
particle-color: #ff0000ff particle-color: #ff0000ff
particle-texture: circle2.png particle-texture: circle2.png

View File

@ -37,6 +37,10 @@ public:
ticks_t ticksElapsed(long prevTicks) { return ticks() - prevTicks; } ticks_t ticksElapsed(long prevTicks) { return ticks() - prevTicks; }
ticks_t ticksFor(int delay) { return ticks() + delay; } 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: private:
ticks_t m_currentTicks; ticks_t m_currentTicks;
std::chrono::system_clock::time_point m_startupTime; std::chrono::system_clock::time_point m_startupTime;

View File

@ -26,24 +26,23 @@
#include <framework/graphics/texturemanager.h> #include <framework/graphics/texturemanager.h>
#include <framework/util/tools.h> #include <framework/util/tools.h>
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_rect = Rect(pos, size);
m_ix = rect.x(); m_iy = rect.y(); m_pos = PointF(pos.x, pos.y);
m_vx = vx; m_vy = vy; m_size = size;
m_ax = ax; m_ay = ay; m_velocity = velocity;
m_acceleration = acceleration;
m_color = color; m_color = color;
m_texture = texture; m_texture = texture;
m_duration = duration; m_duration = duration;
m_startTicks = g_clock.ticks(); m_startTime = g_clock.time();
m_lastUpdateTime = g_clock.time();
m_finished = false; m_finished = false;
} }
Particle::~Particle()
{
//dump << "deleted";
}
void Particle::render() void Particle::render()
{ {
g_painter.setColor(m_color); g_painter.setColor(m_color);
@ -59,17 +58,24 @@ void Particle::render()
void Particle::update() 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 // 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; m_finished = true;
return; return;
} }
//update position // update position
m_rect.moveTo(m_ix + (m_vx * t / 1000.0) + (m_ax * t*t / (2.0 * 1000 * 1000)), PointF delta = m_velocity * elapsedTime;
m_iy + (m_vy * t / 1000.0) + (m_ay * t*t / (2.0 * 1000 * 1000))); 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() ParticleEmitter::ParticleEmitter()
@ -78,44 +84,41 @@ ParticleEmitter::ParticleEmitter()
m_duration = -1; m_duration = -1;
m_burstRate = 1; m_burstCount = 32; m_burstRate = 1; m_burstCount = 32;
m_currentBurst = 0; m_currentBurst = 0;
m_startTicks = g_clock.ticks(); m_startTime = g_clock.time();
m_finished = false; m_finished = false;
// particles default configuration. (make them reasonable for user detect missing properties on scripts) // particles default configuration. (make them reasonable for user detect missing properties on scripts)
m_pMinPositionRadius = 0; m_pMaxPositionRadius = 3; m_pMinPositionRadius = 0;
m_pMinPositionAngle = -Fw::pi; m_pMaxPositionAngle = Fw::pi; m_pMaxPositionRadius = 3;
m_pMinSize = Size(32, 32); m_pMaxSize = Size(32, 32); m_pMinPositionAngle = 0;
m_pMinDuration = 0; m_pMaxDuration = 10; m_pMaxPositionAngle = 360;
m_pMinSize = Size(32, 32);
m_pMinVelocity = 32; m_pMaxVelocity = 64; m_pMaxSize = Size(32, 32);
m_pMinVelocityAngle = -Fw::pi; m_pMaxVelocityAngle = Fw::pi; m_pMinDuration = 0;
m_pMaxDuration = 10;
m_pMinAcceleration = 32; m_pMaxAcceleration = 64; m_pMinVelocity = 32;
m_pMinAccelerationAngle = -Fw::pi; m_pMaxAccelerationAngle = Fw::pi; m_pMaxVelocity = 64;
m_pMinVelocityAngle = 0;
m_pColor = Color(255, 0, 0, 128); m_pMaxVelocityAngle = 360;
m_pTexture = nullptr; 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) bool ParticleEmitter::load(const OTMLNodePtr& node)
{ {
for(const OTMLNodePtr& childNode : node->children()) { for(const OTMLNodePtr& childNode : node->children()) {
// self related // self related
if(childNode->tag() == "position") { if(childNode->tag() == "position")
std::string value = childNode->value(); m_position = childNode->value<Point>();
std::vector<std::string> split;
boost::split(split, value, boost::is_any_of(std::string(" ")));
if(split.size() == 2)
m_position = Point(Fw::safeCast<int>(split[0]), Fw::safeCast<int>(split[1]));
}
else if(childNode->tag() == "duration") else if(childNode->tag() == "duration")
m_duration = childNode->value<float>(); m_duration = childNode->value<float>();
else if(childNode->tag() == "burstRate") else if(childNode->tag() == "burstRate")
m_burstRate = childNode->value<float>(); m_burstRate = childNode->value<float>();
else if(childNode->tag() == "burstCount") else if(childNode->tag() == "burstCount")
m_burstCount = childNode->value<int>(); m_burstCount = childNode->value<int>();
// particles generation related // particles generation related
else if(childNode->tag() == "particle-position-radius") { else if(childNode->tag() == "particle-position-radius") {
m_pMinPositionRadius = childNode->value<float>(); m_pMinPositionRadius = childNode->value<float>();
@ -125,15 +128,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node)
m_pMinPositionRadius = childNode->value<float>(); m_pMinPositionRadius = childNode->value<float>();
else if(childNode->tag() == "particle-max-position-radius") else if(childNode->tag() == "particle-max-position-radius")
m_pMaxPositionRadius = childNode->value<float>(); m_pMaxPositionRadius = childNode->value<float>();
else if(childNode->tag() == "particle-position-angle") { else if(childNode->tag() == "particle-position-angle") {
m_pMinPositionAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMinPositionAngle = childNode->value<float>() * DEG_TO_RAD;
m_pMaxPositionAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMaxPositionAngle = childNode->value<float>() * DEG_TO_RAD;
} }
else if(childNode->tag() == "particle-min-position-angle") else if(childNode->tag() == "particle-min-position-angle")
m_pMinPositionAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMinPositionAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-max-position-angle") else if(childNode->tag() == "particle-max-position-angle")
m_pMaxPositionAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMaxPositionAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-velocity") { else if(childNode->tag() == "particle-velocity") {
m_pMinVelocity = childNode->value<float>(); m_pMinVelocity = childNode->value<float>();
@ -143,16 +145,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node)
m_pMinVelocity = childNode->value<float>(); m_pMinVelocity = childNode->value<float>();
else if(childNode->tag() == "particle-max-velocity") else if(childNode->tag() == "particle-max-velocity")
m_pMaxVelocity = childNode->value<float>(); m_pMaxVelocity = childNode->value<float>();
else if(childNode->tag() == "particle-velocity-angle") { else if(childNode->tag() == "particle-velocity-angle") {
m_pMinVelocityAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMinVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
m_pMaxVelocityAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMaxVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
} }
else if(childNode->tag() == "particle-min-velocity-angle") else if(childNode->tag() == "particle-min-velocity-angle")
m_pMinVelocityAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMinVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-max-velocity-angle") else if(childNode->tag() == "particle-max-velocity-angle")
m_pMaxVelocityAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMaxVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-acceleration") { else if(childNode->tag() == "particle-acceleration") {
m_pMinAcceleration = childNode->value<float>(); m_pMinAcceleration = childNode->value<float>();
m_pMaxAcceleration = childNode->value<float>(); m_pMaxAcceleration = childNode->value<float>();
@ -161,16 +161,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node)
m_pMinAcceleration = childNode->value<float>(); m_pMinAcceleration = childNode->value<float>();
else if(childNode->tag() == "particle-max-acceleration") else if(childNode->tag() == "particle-max-acceleration")
m_pMaxAcceleration = childNode->value<float>(); m_pMaxAcceleration = childNode->value<float>();
else if(childNode->tag() == "particle-acceleration-angle") { else if(childNode->tag() == "particle-acceleration-angle") {
m_pMinAccelerationAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMinAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
m_pMaxAccelerationAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMaxAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
} }
else if(childNode->tag() == "particle-min-acceleration-angle") else if(childNode->tag() == "particle-min-acceleration-angle")
m_pMinAccelerationAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMinAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-max-acceleration-angle") else if(childNode->tag() == "particle-max-acceleration-angle")
m_pMaxAccelerationAngle = (childNode->value<float>() * Fw::pi / 180.0) - Fw::pi; m_pMaxAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-duration") { else if(childNode->tag() == "particle-duration") {
m_pMinDuration = childNode->value<float>(); m_pMinDuration = childNode->value<float>();
m_pMaxDuration = childNode->value<float>(); m_pMaxDuration = childNode->value<float>();
@ -179,32 +177,14 @@ bool ParticleEmitter::load(const OTMLNodePtr& node)
m_pMinDuration = childNode->value<float>(); m_pMinDuration = childNode->value<float>();
else if(childNode->tag() == "particle-max-duration") else if(childNode->tag() == "particle-max-duration")
m_pMaxDuration = childNode->value<float>(); m_pMaxDuration = childNode->value<float>();
else if(childNode->tag() == "particle-size") { else if(childNode->tag() == "particle-size") {
std::string value = childNode->value(); m_pMinSize = childNode->value<Size>();
std::vector<std::string> split; m_pMaxSize = childNode->value<Size>();
boost::split(split, value, boost::is_any_of(std::string(" ")));
if(split.size() == 2) {
m_pMinSize = Size(Fw::safeCast<int>(split[0]), Fw::safeCast<int>(split[1]));
m_pMaxSize = Size(Fw::safeCast<int>(split[0]), Fw::safeCast<int>(split[1]));
}
}
else if(childNode->tag() == "particle-min-size") {
std::string value = childNode->value();
std::vector<std::string> split;
boost::split(split, value, boost::is_any_of(std::string(" ")));
if(split.size() == 2) {
m_pMinSize = Size(Fw::safeCast<int>(split[0]), Fw::safeCast<int>(split[1]));
}
}
else if(childNode->tag() == "particle-max-size") {
std::string value = childNode->value();
std::vector<std::string> split;
boost::split(split, value, boost::is_any_of(std::string(" ")));
if(split.size() == 2) {
m_pMaxSize = Size(Fw::safeCast<int>(split[0]), Fw::safeCast<int>(split[1]));
}
} }
else if(childNode->tag() == "particle-min-size")
m_pMinSize = childNode->value<Size>();
else if(childNode->tag() == "particle-max-size")
m_pMaxSize = childNode->value<Size>();
else if(childNode->tag() == "particle-color") else if(childNode->tag() == "particle-color")
m_pColor = childNode->value<Color>(); m_pColor = childNode->value<Color>();
else if(childNode->tag() == "particle-texture") else if(childNode->tag() == "particle-texture")
@ -221,7 +201,7 @@ void ParticleEmitter::render()
void ParticleEmitter::update() void ParticleEmitter::update()
{ {
ticks_t elapsedTicks = g_clock.ticks() - m_startTicks; float elapsedTime = g_clock.timeElapsed(m_startTime);
// update particles // update particles
for(auto it = m_particles.begin(), end = m_particles.end(); it != end;) { for(auto it = m_particles.begin(), end = m_particles.end(); it != end;) {
@ -235,20 +215,20 @@ void ParticleEmitter::update()
} }
// check if finished // 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 // stop emitting but only self remove when there are no particles left
if(m_particles.size() == 0) if(m_particles.size() == 0)
m_finished = true; m_finished = true;
return; return;
} }
int currentBurst = elapsedTicks / 1000.0 / m_burstRate + 1; int currentBurst = (elapsedTime / m_burstRate) + 1;
for(int b = m_currentBurst; b < currentBurst; ++b) { for(int b = m_currentBurst; b < currentBurst; ++b) {
// every burst created at same position. // every burst created at same position.
float pRadius = Fw::randomRange(m_pMinPositionRadius, m_pMaxPositionRadius); float pRadius = Fw::randomRange(m_pMinPositionRadius, m_pMaxPositionRadius);
float pAngle = Fw::randomRange(m_pMinPositionAngle, m_pMaxPositionAngle); 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) { for(int p = 0; p < m_burstCount; ++p) {
@ -256,17 +236,16 @@ void ParticleEmitter::update()
float pDuration = Fw::randomRange(m_pMinDuration, m_pMaxDuration); float pDuration = Fw::randomRange(m_pMinDuration, m_pMaxDuration);
// particles initial velocity // 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); float pVelocityAngle = Fw::randomRange(m_pMinVelocityAngle, m_pMaxVelocityAngle);
PointF pVelocity(pVelocityAbs * cos(pVelocityAngle), pVelocityAbs * sin(pVelocityAngle));
// particles initial acceleration // 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); 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), m_particles.push_back(ParticlePtr(new Particle(pPosition, pSize, pVelocity, pAcceleration, pDuration, m_pColor, m_pTexture)));
-pVelocity * cos(pVelocityAngle), pVelocity * sin(pVelocityAngle),
-pAcceleration * cos(pAccelerationAngle), pAcceleration * sin(pAccelerationAngle),
pDuration, m_pColor, m_pTexture)));
} }
} }

View File

@ -29,9 +29,7 @@
class Particle { class Particle {
public: public:
Particle(const Point& pos, const Size& size, const PointF& velocity, const PointF& acceleration, float duration, const Color& color = Fw::white, TexturePtr texture = nullptr);
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();
void render(); void render();
void update(); void update();
@ -39,16 +37,16 @@ public:
bool hasFinished() { return m_finished; } bool hasFinished() { return m_finished; }
private: private:
Rect m_rect;
Color m_color; Color m_color;
TexturePtr m_texture; TexturePtr m_texture;
PointF m_pos;
int m_ix, m_iy; PointF m_velocity;
float m_vx, m_vy; PointF m_acceleration;
float m_ax, m_ay; Size m_size;
Rect m_rect;
float m_duration; float m_duration;
ticks_t m_startTicks; double m_startTime;
double m_lastUpdateTime;
bool m_finished; bool m_finished;
}; };
typedef std::shared_ptr<Particle> ParticlePtr; typedef std::shared_ptr<Particle> ParticlePtr;
@ -69,7 +67,7 @@ private:
// self related // self related
Point m_position; Point m_position;
int m_duration; int m_duration;
ticks_t m_startTicks; double m_startTime;
bool m_finished; bool m_finished;
float m_burstRate; float m_burstRate;
int m_currentBurst, m_burstCount; int m_currentBurst, m_burstCount;

View File

@ -28,9 +28,6 @@ ParticleManager g_particleManager;
bool ParticleManager::load(const std::string& filename) bool ParticleManager::load(const std::string& filename)
{ {
if(!g_resources.fileExists(filename))
return false;
try { try {
OTMLDocumentPtr doc = OTMLDocument::parse(filename); OTMLDocumentPtr doc = OTMLDocument::parse(filename);
const OTMLNodePtr& node = doc->at("ParticleSystem"); const OTMLNodePtr& node = doc->at("ParticleSystem");