Fix particles and some fun in the background LOL

This commit is contained in:
Eduardo Bart 2013-01-19 18:24:42 -02:00
parent 62bb91b5a6
commit 9907e9e5c7
26 changed files with 510 additions and 310 deletions

BIN
data/particles/particle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,35 @@
Particle
name: default_particle
duration: 10
min-position-radius: 0
max-position-radius: 256
min-position-angle: 0
max-position-angle: 360
velocity: 10
min-velocity-angle: 0
max-velocity-angle: 360
colors: #ffffff00 #ffffffff #00000000
colors-stops: 0 0.1 1
size: 24 24
texture: /data/particles/particle
composition-mode: normal
Effect
name: background-effect
description: Effect for the game background
System
position: 0 0
Emitter
position: 0 0
delay: 0
duration: 0
burst-rate: 50
burst-count: 1
particle-type: default_particle
AttractionAffector
position: 0 0
acceleration: 1000

View File

@ -12,10 +12,6 @@ ScrollableFlatPanel < ScrollablePanel
image-source: /images/ui/panel_flat
image-border: 1
ParticlesFlatPanel < Panel
image-source: /images/ui/panel_flat
image-border: 1
LightFlatPanel < Panel
image-source: /images/ui/panel_lightflat
image-border: 1

View File

@ -9,6 +9,68 @@ Panel
anchors.bottom: parent.bottom
margin-top: 1
focusable: false
@onSetup: |
scheduleEvent(function()
local count = 0
cycleEvent(function()
if count > 360 then return end
self:setRotation(count)
count = count + 5
end, 10)
end, 10)
UIParticles
anchors.fill: parent
effect: background-effect
reference-pos: 0.5 0.25
Label
text: :O Just For Fun LOL ^.^
font: sans-bold-16px
color: black
background: #ffffff60
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-left: 10
margin-top: 40
height: 24
rotation: -15
@onSetup: |
local count = 0
cycleEvent(function()
local text = ':O Just For Fun LOL ^.^'
self:setText(string.sub(text, 0, count))
if count > #text + 10 then count = 0 end
count = count + 1
end, 100)
Label
text: PLEASE REMOVE THAT SHIT!
font: sans-bold-16px
color: black
background: #ffffff60
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
margin-left: 10
margin-bottom: 40
height: 24
rotation: 15
visible: false
@onSetup: scheduleEvent(function() self:show() end, 4000)
Label
text: WTF IS WRONG WITH THIS BACKGROUND?
font: sans-bold-16px
color: pink
background: #ffffff99
anchors.top: parent.top
anchors.right: parent.right
margin-left: 10
margin-top: 80
height: 24
rotation: 10
visible: false
@onSetup: scheduleEvent(function() self:show() end, 8000)
UILabel
id: clientVersionLabel

View File

@ -13,7 +13,12 @@ function init()
end
end
-- TODO load particles
local particles = g_resources.listDirectoryFiles('/particles')
for _i,particle in pairs(particles) do
if string.ends(particle, '.otps') then
g_particles.importParticle('/particles/' .. particle)
end
end
end
function terminate()

View File

@ -320,11 +320,13 @@ if(FRAMEWORK_GRAPHICS)
${CMAKE_CURRENT_LIST_DIR}/graphics/particleaffector.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/particleaffector.h
${CMAKE_CURRENT_LIST_DIR}/graphics/particle.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/particle.h
${CMAKE_CURRENT_LIST_DIR}/graphics/particletype.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/particletype.h
${CMAKE_CURRENT_LIST_DIR}/graphics/particleemitter.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/particleemitter.h
${CMAKE_CURRENT_LIST_DIR}/graphics/particleeffect.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/particleeffect.h
${CMAKE_CURRENT_LIST_DIR}/graphics/particle.h
${CMAKE_CURRENT_LIST_DIR}/graphics/particlemanager.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/particlemanager.h
${CMAKE_CURRENT_LIST_DIR}/graphics/particlesystem.cpp

View File

@ -205,7 +205,7 @@ void GraphicalApplication::poll()
// poll window input events
g_window.poll();
g_particles.update();
g_particles.poll();
g_textures.poll();
Application::poll();

View File

@ -38,6 +38,7 @@ class Shader;
class ShaderProgram;
class PainterShaderProgram;
class Particle;
class ParticleType;
class ParticleEmitter;
class ParticleAffector;
class ParticleSystem;
@ -54,6 +55,7 @@ typedef stdext::shared_object_ptr<Shader> ShaderPtr;
typedef stdext::shared_object_ptr<ShaderProgram> ShaderProgramPtr;
typedef stdext::shared_object_ptr<PainterShaderProgram> PainterShaderProgramPtr;
typedef stdext::shared_object_ptr<Particle> ParticlePtr;
typedef stdext::shared_object_ptr<ParticleType> ParticleTypePtr;
typedef stdext::shared_object_ptr<ParticleEmitter> ParticleEmitterPtr;
typedef stdext::shared_object_ptr<ParticleAffector> ParticleAffectorPtr;
typedef stdext::shared_object_ptr<ParticleSystem> ParticleSystemPtr;

View File

@ -50,10 +50,8 @@ void Particle::render()
if(!m_texture)
g_painter->drawFilledRect(m_rect);
else {
g_painter->saveState();
g_painter->setCompositionMode(m_compositionMode);
g_painter->drawTexturedRect(m_rect, m_texture);
g_painter->restoreSavedState();
}
}
@ -94,31 +92,22 @@ void Particle::updatePosition(float elapsedTime)
void Particle::updateSize()
{
Size size = m_startSize + (m_finalSize - m_startSize) / m_duration * m_elapsedTime;
if(m_size != size) {
m_size = size;
}
m_size = m_startSize + (m_finalSize - m_startSize) / m_duration * m_elapsedTime;
m_rect.resize(m_size);
}
void Particle::updateColor()
{
if(m_elapsedTime < m_colorsStops[1]) {
Color color = Color(m_colors[0].r() + (m_colors[1].r() - m_colors[0].r()) / (m_colorsStops[1] - m_colorsStops[0]) * (m_elapsedTime - m_colorsStops[0]),
m_colors[0].g() + (m_colors[1].g() - m_colors[0].g()) / (m_colorsStops[1] - m_colorsStops[0]) * (m_elapsedTime - m_colorsStops[0]),
m_colors[0].b() + (m_colors[1].b() - m_colors[0].b()) / (m_colorsStops[1] - m_colorsStops[0]) * (m_elapsedTime - m_colorsStops[0]),
m_colors[0].a() + (m_colors[1].a() - m_colors[0].a()) / (m_colorsStops[1] - m_colorsStops[0]) * (m_elapsedTime - m_colorsStops[0]));
if(m_color != color) {
m_color = color;
}
}
else {
float currentLife = m_elapsedTime / m_duration;
if(currentLife < m_colorsStops[1]) {
float range = m_colorsStops[1] - m_colorsStops[0];
float factor = (currentLife - m_colorsStops[0])/range;
m_color = m_colors[0] * (1.0f - factor) + m_colors[1] * factor;
} else {
if(m_colors.size() > 1) {
m_colors.erase(m_colors.begin());
m_colorsStops.erase(m_colorsStops.begin());
}
else {
} else {
if(m_color != m_colors[0]) {
m_color = m_colors[0];
}

View File

@ -25,7 +25,6 @@
#include "declarations.h"
#include "painter.h"
#include <framework/global.h>
class Particle : public stdext::shared_object
{

View File

@ -46,7 +46,7 @@ void ParticleAffector::update(float elapsedTime)
m_elapsedTime += elapsedTime;
}
bool ParticleAffector::load(const OTMLNodePtr& node)
void ParticleAffector::load(const OTMLNodePtr& node)
{
float minDelay = 0, maxDelay = 0;
float minDuration = -1, maxDuration = -1;
@ -55,17 +55,14 @@ bool ParticleAffector::load(const OTMLNodePtr& node)
if(childNode->tag() == "delay") {
minDelay = childNode->value<float>();
maxDelay = childNode->value<float>();
}
else if(childNode->tag() == "min-delay")
} else if(childNode->tag() == "min-delay")
minDelay = childNode->value<float>();
else if(childNode->tag() == "max-delay")
maxDelay = childNode->value<float>();
if(childNode->tag() == "duration") {
else if(childNode->tag() == "duration") {
minDuration = childNode->value<float>();
maxDuration = childNode->value<float>();
}
else if(childNode->tag() == "min-duration")
} else if(childNode->tag() == "min-duration")
minDuration = childNode->value<float>();
else if(childNode->tag() == "max-duration")
maxDuration = childNode->value<float>();
@ -73,14 +70,11 @@ bool ParticleAffector::load(const OTMLNodePtr& node)
m_delay = stdext::random_range(minDelay, maxDelay);
m_duration = stdext::random_range(minDuration, maxDuration);
return true;
}
bool GravityAffector::load(const OTMLNodePtr& node)
void GravityAffector::load(const OTMLNodePtr& node)
{
if(!ParticleAffector::load(node))
return false;
ParticleAffector::load(node);
m_angle = 270 * DEG_TO_RAD;
m_gravity = 9.8;
@ -91,7 +85,6 @@ bool GravityAffector::load(const OTMLNodePtr& node)
else if(childNode->tag() == "gravity")
m_gravity = childNode->value<float>();
}
return true;
}
void GravityAffector::updateParticle(const ParticlePtr& particle, float elapsedTime)
@ -100,14 +93,13 @@ void GravityAffector::updateParticle(const ParticlePtr& particle, float elapsedT
return;
PointF velocity = particle->getVelocity();
velocity += PointF(m_gravity * elapsedTime * cos(m_angle), m_gravity * elapsedTime * sin(m_angle));
velocity += PointF(m_gravity * elapsedTime * std::cos(m_angle), m_gravity * elapsedTime * std::sin(m_angle));
particle->setVelocity(velocity);
}
bool AttractionAffector::load(const OTMLNodePtr& node)
void AttractionAffector::load(const OTMLNodePtr& node)
{
if(!ParticleAffector::load(node))
return false;
ParticleAffector::load(node);
m_acceleration = 32;
m_reduction = 0;
@ -123,7 +115,6 @@ bool AttractionAffector::load(const OTMLNodePtr& node)
else if(childNode->tag() == "repelish")
m_repelish = childNode->value<bool>();
}
return true;
}
void AttractionAffector::updateParticle(const ParticlePtr& particle, float elapsedTime)

View File

@ -32,7 +32,7 @@ public:
ParticleAffector();
void update(float elapsedTime);
virtual bool load(const OTMLNodePtr& node);
virtual void load(const OTMLNodePtr& node);
virtual void updateParticle(const ParticlePtr&, float) {}
bool hasFinished() { return m_finished; }
@ -45,7 +45,7 @@ protected:
class GravityAffector : public ParticleAffector {
public:
bool load(const OTMLNodePtr& node);
void load(const OTMLNodePtr& node);
void updateParticle(const ParticlePtr& particle, float elapsedTime);
private:
@ -54,7 +54,7 @@ private:
class AttractionAffector : public ParticleAffector {
public:
bool load(const OTMLNodePtr& node);
void load(const OTMLNodePtr& node);
void updateParticle(const ParticlePtr& particle, float elapsedTime);
private:

View File

@ -27,35 +27,30 @@ ParticleEffectType::ParticleEffectType()
}
bool ParticleEffectType::load(const OTMLNodePtr& node)
void ParticleEffectType::load(const OTMLNodePtr& node)
{
m_node = node;
m_node = node->clone();
for(const OTMLNodePtr& childNode : node->children()) {
if(childNode->tag() == "name") {
setName(childNode->value());
if(childNode->tag() == "name")
m_name = childNode->value();
else if(childNode->tag() == "description")
m_description = childNode->value();
}
else if(childNode->tag() == "description") {
setDescription(childNode->value());
}
}
return !m_name.empty();
}
bool ParticleEffect::load(const ParticleEffectTypePtr& effectType)
void ParticleEffect::load(const ParticleEffectTypePtr& effectType)
{
if(!effectType)
return false;
stdext::throw_exception("effect type not found");
for(const OTMLNodePtr& childNode : effectType->getNode()->children()) {
if(childNode->tag() == "System") {
ParticleSystemPtr system = ParticleSystemPtr(new ParticleSystem);
if(system->load(childNode)) {
system->load(childNode);
m_systems.push_back(system);
}
}
}
return true;
}
void ParticleEffect::render()
{
@ -65,15 +60,14 @@ void ParticleEffect::render()
void ParticleEffect::update()
{
for(auto it = m_systems.begin(), end = m_systems.end(); it != end;) {
for(auto it = m_systems.begin(); it != m_systems.end();) {
const ParticleSystemPtr& system = *it;
if(system->hasFinished()) {
it = m_systems.erase(it);
continue;
}
} else {
system->update();
++it;
}
}
}

View File

@ -33,20 +33,14 @@ class ParticleEffectType : public LuaObject
public:
ParticleEffectType();
bool load(const OTMLNodePtr& node);
void setName(const std::string& name) { m_name = name; }
void setFile(const std::string& file) { m_file = file; }
void setDescription(const std::string& description) { m_description = description; }
void load(const OTMLNodePtr& node);
std::string getName() { return m_name; }
std::string getFile() { return m_file; }
std::string getDescription() { return m_description; }
OTMLNodePtr getNode() { return m_node; }
private:
std::string m_name;
std::string m_file;
std::string m_description;
OTMLNodePtr m_node;
};
@ -56,7 +50,7 @@ class ParticleEffect : public LuaObject
public:
ParticleEffect() {}
bool load(const ParticleEffectTypePtr& effectType);
void load(const ParticleEffectTypePtr& effectType);
bool hasFinished() { return m_systems.size() == 0; }
void render();
void update();

View File

@ -25,39 +25,22 @@
#include "particlesystem.h"
#include <framework/core/clock.h>
#include <framework/graphics/texturemanager.h>
#include "particlemanager.h"
ParticleEmitter::ParticleEmitter()
{
m_position = Point(0, 0);
m_duration = -1;
m_delay = 0;
m_burstRate = 1; m_burstCount = 32;
m_burstRate = 1;
m_burstCount = 32;
m_currentBurst = 0;
m_elapsedTime = 0;
m_finished = false;
m_active = false;
// particles default configuration. (make them reasonable for user detect missing properties on scripts)
m_pMinPositionRadius = 0;
m_pMaxPositionRadius = 3;
m_pMinPositionAngle = 0;
m_pMaxPositionAngle = 360;
m_pStartSize = Size(32, 32);
m_pFinalSize = Size(32, 32);
m_pMinDuration = 0;
m_pMaxDuration = 10;
m_pIgnorePhysicsAfter = -1;
m_pMinVelocity = 32;
m_pMaxVelocity = 64;
m_pMinVelocityAngle = 0;
m_pMaxVelocityAngle = 360;
m_pMinAcceleration = 32;
m_pMaxAcceleration = 64;
m_pMinAccelerationAngle = 0;
m_pMaxAccelerationAngle = 360;
}
bool ParticleEmitter::load(const OTMLNodePtr& node)
void ParticleEmitter::load(const OTMLNodePtr& node)
{
for(const OTMLNodePtr& childNode : node->children()) {
// self related
@ -67,120 +50,24 @@ bool ParticleEmitter::load(const OTMLNodePtr& node)
m_duration = childNode->value<float>();
else if(childNode->tag() == "delay")
m_delay = childNode->value<float>();
else if(childNode->tag() == "burstRate")
else if(childNode->tag() == "burst-rate")
m_burstRate = childNode->value<float>();
else if(childNode->tag() == "burstCount")
else if(childNode->tag() == "burst-count")
m_burstCount = childNode->value<int>();
// particles generation related
else if(childNode->tag() == "particle-position-radius") {
m_pMinPositionRadius = childNode->value<float>();
m_pMaxPositionRadius = childNode->value<float>();
}
else if(childNode->tag() == "particle-min-position-radius")
m_pMinPositionRadius = childNode->value<float>();
else if(childNode->tag() == "particle-max-position-radius")
m_pMaxPositionRadius = childNode->value<float>();
else if(childNode->tag() == "particle-position-angle") {
m_pMinPositionAngle = childNode->value<float>() * DEG_TO_RAD;
m_pMaxPositionAngle = childNode->value<float>() * DEG_TO_RAD;
}
else if(childNode->tag() == "particle-min-position-angle")
m_pMinPositionAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-max-position-angle")
m_pMaxPositionAngle = childNode->value<float>() * DEG_TO_RAD;
// velocity
else if(childNode->tag() == "particle-velocity") {
m_pMinVelocity = childNode->value<float>();
m_pMaxVelocity = childNode->value<float>();
}
else if(childNode->tag() == "particle-min-velocity")
m_pMinVelocity = childNode->value<float>();
else if(childNode->tag() == "particle-max-velocity")
m_pMaxVelocity = childNode->value<float>();
else if(childNode->tag() == "particle-velocity-angle") {
m_pMinVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
m_pMaxVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
}
else if(childNode->tag() == "particle-min-velocity-angle")
m_pMinVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-max-velocity-angle")
m_pMaxVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-acceleration") {
m_pMinAcceleration = childNode->value<float>();
m_pMaxAcceleration = childNode->value<float>();
else if(childNode->tag() == "particle-type")
m_particleType = g_particles.getParticleType(childNode->value());
}
// acceleration
else if(childNode->tag() == "particle-min-acceleration")
m_pMinAcceleration = childNode->value<float>();
else if(childNode->tag() == "particle-max-acceleration")
m_pMaxAcceleration = childNode->value<float>();
else if(childNode->tag() == "particle-acceleration-angle") {
m_pMinAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
m_pMaxAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
}
else if(childNode->tag() == "particle-min-acceleration-angle")
m_pMinAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "particle-max-acceleration-angle")
m_pMaxAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
// duration
else if(childNode->tag() == "particle-duration") {
m_pMinDuration = childNode->value<float>();
m_pMaxDuration = childNode->value<float>();
}
else if(childNode->tag() == "particle-min-duration")
m_pMinDuration = childNode->value<float>();
else if(childNode->tag() == "particle-max-duration")
m_pMaxDuration = childNode->value<float>();
else if(childNode->tag() == "particle-ignore-physics-after")
m_pIgnorePhysicsAfter = childNode->value<float>();
// visual
else if(childNode->tag() == "particle-size") {
m_pStartSize = childNode->value<Size>();
m_pFinalSize = childNode->value<Size>();
}
else if(childNode->tag() == "particle-start-size")
m_pStartSize = childNode->value<Size>();
else if(childNode->tag() == "particle-final-size")
m_pFinalSize = childNode->value<Size>();
else if(childNode->tag() == "particle-colors")
m_pColors = stdext::split<Color>(childNode->value());
else if(childNode->tag() == "particle-colors-stops")
m_pColorsStops = stdext::split<float>(childNode->value());
else if(childNode->tag() == "particle-texture")
m_pTexture = g_textures.getTexture(childNode->value());
else if(childNode->tag() == "particle-composition-mode") {
if(childNode->value() == "normal")
m_pCompositionMode = Painter::CompositionMode_Normal;
else if(childNode->value() == "multiply")
m_pCompositionMode = Painter::CompositionMode_Multiply;
else if(childNode->value() == "addition")
m_pCompositionMode = Painter::CompositionMode_Add;
}
}
if(m_pColors.empty())
m_pColors.push_back(Color(255, 255, 255, 128));
if(m_pColorsStops.empty())
m_pColorsStops.push_back(0);
if(m_pColors.size() != m_pColorsStops.size()) {
g_logger.error("particle colors must be equal to colorstops-1");
return false;
}
return true;
if(!m_particleType)
stdext::throw_exception("emitter didn't provide a valid particle type");
}
void ParticleEmitter::update(float elapsedTime, const ParticleSystemPtr& system)
{
m_elapsedTime += elapsedTime;
// check if finished
if(m_duration >= 0 && m_elapsedTime >= m_duration + m_delay) {
if(m_duration > 0 && m_elapsedTime >= m_duration + m_delay) {
m_finished = true;
return;
}
@ -188,36 +75,39 @@ void ParticleEmitter::update(float elapsedTime, const ParticleSystemPtr& system)
if(!m_active && m_elapsedTime > m_delay)
m_active = true;
if(m_active) {
int currentBurst = std::floor((m_elapsedTime - m_delay) / m_burstRate) + 1;
for(int b = m_currentBurst; b < currentBurst; ++b) {
if(!m_active)
return;
int nextBurst = std::floor((m_elapsedTime - m_delay) * m_burstRate) + 1;
const ParticleType *type = m_particleType.get();
for(int b = m_currentBurst; b < nextBurst; ++b) {
// every burst created at same position.
float pRadius = stdext::random_range(m_pMinPositionRadius, m_pMaxPositionRadius);
float pAngle = stdext::random_range(m_pMinPositionAngle, m_pMaxPositionAngle);
float pRadius = stdext::random_range(type->pMinPositionRadius, type->pMaxPositionRadius);
float pAngle = stdext::random_range(type->pMinPositionAngle, type->pMaxPositionAngle);
Point pPosition = m_position + Point(pRadius * cos(pAngle), pRadius * sin(pAngle));
Point pPosition = m_position + Point(pRadius * std::cos(pAngle), pRadius * std::sin(pAngle));
for(int p = 0; p < m_burstCount; ++p) {
float pDuration = stdext::random_range(m_pMinDuration, m_pMaxDuration);
float pDuration = stdext::random_range(type->pMinDuration, type->pMaxDuration);
// particles initial velocity
float pVelocityAbs = stdext::random_range(m_pMinVelocity, m_pMaxVelocity);
float pVelocityAngle = stdext::random_range(m_pMinVelocityAngle, m_pMaxVelocityAngle);
PointF pVelocity(pVelocityAbs * cos(pVelocityAngle), pVelocityAbs * sin(pVelocityAngle));
float pVelocityAbs = stdext::random_range(type->pMinVelocity, type->pMaxVelocity);
float pVelocityAngle = stdext::random_range(type->pMinVelocityAngle, type->pMaxVelocityAngle);
PointF pVelocity(pVelocityAbs * std::cos(pVelocityAngle), pVelocityAbs * std::sin(pVelocityAngle));
// particles initial acceleration
float pAccelerationAbs = stdext::random_range(m_pMinAcceleration, m_pMaxAcceleration);
float pAccelerationAngle = stdext::random_range(m_pMinAccelerationAngle, m_pMaxAccelerationAngle);
PointF pAcceleration(pAccelerationAbs * cos(pAccelerationAngle), pAccelerationAbs * sin(pAccelerationAngle));
float pAccelerationAbs = stdext::random_range(type->pMinAcceleration, type->pMaxAcceleration);
float pAccelerationAngle = stdext::random_range(type->pMinAccelerationAngle, type->pMaxAccelerationAngle);
PointF pAcceleration(pAccelerationAbs * std::cos(pAccelerationAngle), pAccelerationAbs * std::sin(pAccelerationAngle));
system->addParticle(ParticlePtr(new Particle(pPosition, m_pStartSize, m_pFinalSize, pVelocity, pAcceleration, pDuration, m_pIgnorePhysicsAfter, m_pColors, m_pColorsStops, m_pCompositionMode, m_pTexture)));
ParticlePtr particle(new Particle(pPosition, type->pStartSize, type->pFinalSize,
pVelocity, pAcceleration,
pDuration, type->pIgnorePhysicsAfter,
type->pColors, type->pColorsStops,
type->pCompositionMode, type->pTexture));
system->addParticle(particle);
}
}
m_currentBurst = currentBurst;
}
m_elapsedTime += elapsedTime;
m_currentBurst = nextBurst;
}

View File

@ -34,7 +34,7 @@ class ParticleEmitter : public stdext::shared_object
public:
ParticleEmitter();
bool load(const OTMLNodePtr& node);
void load(const OTMLNodePtr& node);
void update(float elapsedTime, const ParticleSystemPtr& system);
@ -48,30 +48,7 @@ private:
bool m_finished, m_active;
float m_burstRate;
int m_currentBurst, m_burstCount;
// particles size
Size m_pStartSize, m_pFinalSize;
// particles initial position related to emitter position
float m_pMinPositionRadius, m_pMaxPositionRadius;
float m_pMinPositionAngle, m_pMaxPositionAngle;
// particles initial velocity
float m_pMinVelocity, m_pMaxVelocity;
float m_pMinVelocityAngle, m_pMaxVelocityAngle;
// particles initial acceleration
float m_pMinAcceleration, m_pMaxAcceleration;
float m_pMinAccelerationAngle, m_pMaxAccelerationAngle;
// particles duration
float m_pMinDuration, m_pMaxDuration, m_pIgnorePhysicsAfter;
// visual ralated
std::vector<Color> m_pColors;
std::vector<float> m_pColorsStops;
TexturePtr m_pTexture;
Painter::CompositionMode m_pCompositionMode;
ParticleTypePtr m_particleType;
};
#endif

View File

@ -35,13 +35,14 @@ bool ParticleManager::importParticle(std::string file)
for(const OTMLNodePtr& node : doc->children()) {
if(node->tag() == "Effect") {
ParticleEffectTypePtr particleEffectType = ParticleEffectTypePtr(new ParticleEffectType);
if(particleEffectType->load(node)) {
particleEffectType->setFile(g_resources.resolvePath(file));
particleEffectType->load(node);
m_effectsTypes[particleEffectType->getName()] = particleEffectType;
}
}
else if(node->tag() == "Particle") {
// nothing yet
ParticleTypePtr particleType = ParticleTypePtr(new ParticleType);
particleType->load(node);
m_particleTypes[particleType->getName()] = particleType;
dump << particleType->getName();
}
}
return true;
@ -53,29 +54,34 @@ bool ParticleManager::importParticle(std::string file)
ParticleEffectPtr ParticleManager::createEffect(const std::string& name)
{
try {
ParticleEffectPtr particleEffect = ParticleEffectPtr(new ParticleEffect);
if(particleEffect->load(m_effectsTypes[name]))
particleEffect->load(m_effectsTypes[name]);
m_effects.push_back(particleEffect);
return particleEffect;
} catch(stdext::exception& e) {
g_logger.error(stdext::format("failed to create effect '%s': %s", name, e.what()));
return nullptr;
}
}
void ParticleManager::terminate()
{
m_effects.clear();
m_effectsTypes.clear();
m_particleTypes.clear();
}
void ParticleManager::update()
void ParticleManager::poll()
{
for(auto it = m_effects.begin(), end = m_effects.end(); it != end;) {
for(auto it = m_effects.begin(); it != m_effects.end();) {
const ParticleEffectPtr& particleEffect = *it;
if(particleEffect->hasFinished()) {
it = m_effects.erase(it);
continue;
}
} else {
particleEffect->update();
++it;
}
}
}

View File

@ -25,6 +25,7 @@
#include "declarations.h"
#include "particleeffect.h"
#include "particletype.h"
class ParticleManager
{
@ -33,13 +34,18 @@ public:
ParticleEffectPtr createEffect(const std::string& name);
void terminate();
void update();
void poll();
std::map<std::string, ParticleEffectTypePtr> getEffectsTypes() { return m_effectsTypes; }
ParticleTypePtr getParticleType(std::string name) { return m_particleTypes[name]; }
ParticleEffectTypePtr getParticleEffectType(std::string name) { return m_effectsTypes[name]; }
const std::map<std::string, ParticleTypePtr>& getParticleTypes() { return m_particleTypes; }
const std::map<std::string, ParticleEffectTypePtr>& getEffectsTypes() { return m_effectsTypes; }
private:
std::list<ParticleEffectPtr> m_effects;
std::map<std::string, ParticleEffectTypePtr> m_effectsTypes;
std::map<std::string, ParticleTypePtr> m_particleTypes;
};
extern ParticleManager g_particles;

View File

@ -30,13 +30,12 @@ ParticleSystem::ParticleSystem()
m_lastUpdateTime = g_clock.seconds();
}
bool ParticleSystem::load(const OTMLNodePtr& node)
void ParticleSystem::load(const OTMLNodePtr& node)
{
for(const OTMLNodePtr& childNode : node->children()) {
if(childNode->tag() == "Emitter") {
ParticleEmitterPtr emitter = ParticleEmitterPtr(new ParticleEmitter());
if(!emitter->load(childNode))
return false;
emitter->load(childNode);
m_emitters.push_back(emitter);
}
else if(childNode->tag().find("Affector") != std::string::npos) {
@ -48,13 +47,11 @@ bool ParticleSystem::load(const OTMLNodePtr& node)
affector = ParticleAffectorPtr(new AttractionAffector);
if(affector) {
if(!affector->load(childNode))
return false;
affector->load(childNode);
m_affectors.push_back(affector);
}
}
}
return true;
}
void ParticleSystem::addParticle(const ParticlePtr& particle)
@ -66,57 +63,57 @@ void ParticleSystem::render()
{
for(auto it = m_particles.begin(), end = m_particles.end(); it != end; ++it)
(*it)->render();
g_painter->resetCompositionMode();
}
void ParticleSystem::update()
{
static const float delay = 0.0166; // 60 updates/s
// check time
float elapsedTime = g_clock.seconds() - m_lastUpdateTime;
if(elapsedTime < delay)
return;
// check if finished
if(m_particles.empty() && m_emitters.empty()) {
m_finished = true;
return;
}
// check time
float elapsedTime = g_clock.seconds() - m_lastUpdateTime;
if(elapsedTime < delay)
return;
m_lastUpdateTime = g_clock.seconds() - std::fmod(elapsedTime, delay);
auto self = static_self_cast<ParticleSystem>();
for(int i = 0; i < elapsedTime / delay; ++i) {
for(int i = 0; i < std::floor(elapsedTime / delay); ++i) {
// update emitters
for(auto it = m_emitters.begin(), end = m_emitters.end(); it != end;) {
for(auto it = m_emitters.begin(); it != m_emitters.end();) {
const ParticleEmitterPtr& emitter = *it;
if(emitter->hasFinished()) {
it = m_emitters.erase(it);
continue;
}
} else {
emitter->update(delay, self);
++it;
}
}
// update affectors
for(auto it = m_affectors.begin(), end = m_affectors.end(); it != end;) {
for(auto it = m_affectors.begin(); it != m_affectors.end();) {
const ParticleAffectorPtr& affector = *it;
if(affector->hasFinished()) {
it = m_affectors.erase(it);
continue;
}
} else {
affector->update(delay);
++it;
}
}
// update particles
for(auto it = m_particles.begin(), end = m_particles.end(); it != end;) {
for(auto it = m_particles.begin(); it != m_particles.end();) {
const ParticlePtr& particle = *it;
if(particle->hasFinished()) {
it = m_particles.erase(it);
continue;
}
} else {
// pass particles through affectors
for(const ParticleAffectorPtr& particleAffector : m_affectors)
particleAffector->updateParticle(particle, delay);
@ -126,3 +123,4 @@ void ParticleSystem::update()
}
}
}
}

View File

@ -32,7 +32,7 @@ class ParticleSystem : public stdext::shared_object {
public:
ParticleSystem();
bool load(const OTMLNodePtr& node);
void load(const OTMLNodePtr& node);
void addParticle(const ParticlePtr& particle);

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "particletype.h"
#include <framework/graphics/texturemanager.h>
ParticleType::ParticleType()
{
// particles default configuration. (make them reasonable for user detect missing properties on scripts)
pMinPositionRadius = 0;
pMaxPositionRadius = 3;
pMinPositionAngle = 0;
pMaxPositionAngle = 360;
pStartSize = Size(32, 32);
pFinalSize = Size(32, 32);
pMinDuration = 0;
pMaxDuration = 10;
pIgnorePhysicsAfter = -1;
pMinVelocity = 32;
pMaxVelocity = 64;
pMinVelocityAngle = 0;
pMaxVelocityAngle = 360;
pMinAcceleration = 32;
pMaxAcceleration = 64;
pMinAccelerationAngle = 0;
pMaxAccelerationAngle = 360;
}
void ParticleType::load(const OTMLNodePtr& node)
{
for(const OTMLNodePtr& childNode : node->children()) {
if(childNode->tag() == "name")
pName = childNode->value();
else if(childNode->tag() == "position-radius") {
pMinPositionRadius = childNode->value<float>();
pMaxPositionRadius = childNode->value<float>();
}
else if(childNode->tag() == "min-position-radius")
pMinPositionRadius = childNode->value<float>();
else if(childNode->tag() == "max-position-radius")
pMaxPositionRadius = childNode->value<float>();
else if(childNode->tag() == "position-angle") {
pMinPositionAngle = childNode->value<float>() * DEG_TO_RAD;
pMaxPositionAngle = childNode->value<float>() * DEG_TO_RAD;
}
else if(childNode->tag() == "min-position-angle")
pMinPositionAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "max-position-angle")
pMaxPositionAngle = childNode->value<float>() * DEG_TO_RAD;
// velocity
else if(childNode->tag() == "velocity") {
pMinVelocity = childNode->value<float>();
pMaxVelocity = childNode->value<float>();
}
else if(childNode->tag() == "min-velocity")
pMinVelocity = childNode->value<float>();
else if(childNode->tag() == "max-velocity")
pMaxVelocity = childNode->value<float>();
else if(childNode->tag() == "velocity-angle") {
pMinVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
pMaxVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
}
else if(childNode->tag() == "min-velocity-angle")
pMinVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "max-velocity-angle")
pMaxVelocityAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "acceleration") {
pMinAcceleration = childNode->value<float>();
pMaxAcceleration = childNode->value<float>();
}
// acceleration
else if(childNode->tag() == "min-acceleration")
pMinAcceleration = childNode->value<float>();
else if(childNode->tag() == "max-acceleration")
pMaxAcceleration = childNode->value<float>();
else if(childNode->tag() == "acceleration-angle") {
pMinAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
pMaxAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
}
else if(childNode->tag() == "min-acceleration-angle")
pMinAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
else if(childNode->tag() == "max-acceleration-angle")
pMaxAccelerationAngle = childNode->value<float>() * DEG_TO_RAD;
// duration
else if(childNode->tag() == "duration") {
pMinDuration = childNode->value<float>();
pMaxDuration = childNode->value<float>();
}
else if(childNode->tag() == "min-duration")
pMinDuration = childNode->value<float>();
else if(childNode->tag() == "max-duration")
pMaxDuration = childNode->value<float>();
else if(childNode->tag() == "ignore-physics-after")
pIgnorePhysicsAfter = childNode->value<float>();
// visual
else if(childNode->tag() == "size") {
pStartSize = childNode->value<Size>();
pFinalSize = childNode->value<Size>();
}
else if(childNode->tag() == "start-size")
pStartSize = childNode->value<Size>();
else if(childNode->tag() == "final-size")
pFinalSize = childNode->value<Size>();
else if(childNode->tag() == "colors")
pColors = stdext::split<Color>(childNode->value());
else if(childNode->tag() == "colors-stops")
pColorsStops = stdext::split<float>(childNode->value());
else if(childNode->tag() == "texture")
pTexture = g_textures.getTexture(childNode->value());
else if(childNode->tag() == "composition-mode") {
if(childNode->value() == "normal")
pCompositionMode = Painter::CompositionMode_Normal;
else if(childNode->value() == "multiply")
pCompositionMode = Painter::CompositionMode_Multiply;
else if(childNode->value() == "addition")
pCompositionMode = Painter::CompositionMode_Add;
}
}
if(pColors.empty())
pColors.push_back(Color(255, 255, 255, 128));
if(pColorsStops.empty())
pColorsStops.push_back(0);
if(pColors.size() != pColorsStops.size())
stdext::throw_exception("particle colors must be equal to colorstops-1");
pTexture->setSmooth(true);
pTexture->buildHardwareMipmaps();
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef PARTICLETYPE_H
#define PARTICLETYPE_H
#include "declarations.h"
#include <framework/graphics/painter.h>
#include <framework/luaengine/luaobject.h>
#include <framework/otml/otml.h>
class ParticleType : public LuaObject
{
public:
ParticleType();
void load(const OTMLNodePtr& node);
std::string getName() { return pName; }
protected:
// name
std::string pName;
// size
Size pStartSize, pFinalSize;
// initial position related to emitter position
float pMinPositionRadius, pMaxPositionRadius;
float pMinPositionAngle, pMaxPositionAngle;
// initial velocity
float pMinVelocity, pMaxVelocity;
float pMinVelocityAngle, pMaxVelocityAngle;
// initial acceleration
float pMinAcceleration, pMaxAcceleration;
float pMinAccelerationAngle, pMaxAccelerationAngle;
// duration
float pMinDuration, pMaxDuration, pIgnorePhysicsAfter;
// visual ralated
std::vector<Color> pColors;
std::vector<float> pColorsStops;
TexturePtr pTexture;
ParticleTypePtr particleType;
Painter::CompositionMode pCompositionMode;
friend class ParticleEmitter;
};
#endif

View File

@ -682,7 +682,6 @@ void Application::registerLuaFunctions()
g_lua.registerClass<ParticleEffectType>();
g_lua.bindClassStaticFunction<ParticleEffectType>("create", []{ return ParticleEffectTypePtr(new ParticleEffectType); });
g_lua.bindClassMemberFunction<ParticleEffectType>("getName", &ParticleEffectType::getName);
g_lua.bindClassMemberFunction<ParticleEffectType>("getFile", &ParticleEffectType::getFile);
g_lua.bindClassMemberFunction<ParticleEffectType>("getDescription", &ParticleEffectType::getDescription);
// UIParticles

View File

@ -23,27 +23,49 @@
#include "uiparticles.h"
#include <framework/graphics/particlemanager.h>
UIParticles::UIParticles()
{
m_referencePos = PointF(-1,-1);
}
void UIParticles::drawSelf(Fw::DrawPane drawPane)
{
if((drawPane & Fw::ForegroundPane) == 0)
return;
if(drawPane & Fw::ForegroundPane) {
if(drawPane != Fw::BothPanes) {
glDisable(GL_BLEND);
g_painter->setColor(Color::alpha);
g_painter->drawFilledRect(m_rect);
glEnable(GL_BLEND);
}
}
if(drawPane & Fw::BackgroundPane) {
UIWidget::drawSelf(Fw::ForegroundPane);
g_painter->saveAndResetState();
g_painter->setColor(Color::white);
g_painter->setClipRect(getPaddingRect());
if(m_referencePos.x < 0 && m_referencePos.y < 0)
g_painter->translate(m_rect.center());
else
g_painter->translate(m_rect.x() + m_referencePos.x * m_rect.width(), m_rect.y() + m_referencePos.y * m_rect.height());
for(auto it = m_effects.begin(), end = m_effects.end(); it != end; ++it)
(*it)->render();
g_painter->restoreSavedState();
}
}
void UIParticles::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleName, styleNode);
/*for(const OTMLNodePtr& node : styleNode->children()) {
if(node->tag() == "reference")
setItemId(node->value<int>());
else if(node->tag() == "item-count")
setItemCount(node->value<int>());
else if(node->tag() == "virtual")
setVirtual(node->value<bool>());
}*/
for(const OTMLNodePtr& node : styleNode->children()) {
if(node->tag() == "effect")
addEffect(node->value());
else if(node->tag() == "reference-pos")
setReferencePos(node->value<PointF>());
}
}
void UIParticles::addEffect(const std::string& name)

View File

@ -29,14 +29,20 @@
class UIParticles : public UIWidget
{
public:
UIParticles();
void drawSelf(Fw::DrawPane drawPane);
void addEffect(const std::string& name);
void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode);
void setReferencePos(const PointF& point) { m_referencePos = point; }
PointF getReferencePos() { return m_referencePos; }
private:
std::vector<ParticleEffectPtr> m_effects;
PointF m_referencePos;
};
#endif // UIPARTICLES_H
#endif

View File

@ -167,7 +167,8 @@ void UIWidget::drawImage(const Rect& screenCoords)
}
}
m_imageTexture->setSmooth(m_imageSmooth);
// smooth is now enabled by default for all textures
//m_imageTexture->setSmooth(m_imageSmooth);
g_painter->setColor(m_imageColor);
g_painter->drawTextureCoords(m_imageCoordsBuffer, m_imageTexture);