2015-10-30 21:11:11 +01:00
|
|
|
#include "particle_system.h"
|
2015-11-07 21:34:38 +01:00
|
|
|
#include "core/blob.h"
|
2015-11-04 22:43:54 +01:00
|
|
|
#include "core/crc32.h"
|
2015-10-30 21:18:36 +01:00
|
|
|
#include "core/math_utils.h"
|
2015-11-04 22:43:54 +01:00
|
|
|
#include "core/resource_manager.h"
|
|
|
|
#include "core/resource_manager_base.h"
|
|
|
|
#include "renderer/material.h"
|
2015-10-30 21:11:11 +01:00
|
|
|
#include "universe/universe.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace Lumix
|
|
|
|
{
|
|
|
|
|
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
static ParticleEmitter::ModuleBase* createModule(uint32 type, ParticleEmitter& emitter)
|
2015-11-07 21:34:38 +01:00
|
|
|
{
|
|
|
|
if (type == ParticleEmitter::LinearMovementModule::s_type)
|
|
|
|
{
|
|
|
|
return LUMIX_NEW(emitter.getAllocator(), ParticleEmitter::LinearMovementModule)(emitter);
|
|
|
|
}
|
|
|
|
if (type == ParticleEmitter::AlphaModule::s_type)
|
|
|
|
{
|
|
|
|
return LUMIX_NEW(emitter.getAllocator(), ParticleEmitter::AlphaModule)(emitter);
|
|
|
|
}
|
|
|
|
if (type == ParticleEmitter::RandomRotationModule::s_type)
|
|
|
|
{
|
|
|
|
return LUMIX_NEW(emitter.getAllocator(), ParticleEmitter::RandomRotationModule)(emitter);
|
|
|
|
}
|
2015-11-18 20:18:48 +01:00
|
|
|
if (type == ParticleEmitter::SizeModule::s_type)
|
|
|
|
{
|
|
|
|
return LUMIX_NEW(emitter.getAllocator(), ParticleEmitter::SizeModule)(emitter);
|
|
|
|
}
|
2015-11-07 21:34:38 +01:00
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-04 22:43:54 +01:00
|
|
|
ParticleEmitter::ModuleBase::ModuleBase(ParticleEmitter& emitter)
|
|
|
|
: m_emitter(emitter)
|
2015-10-30 21:11:11 +01:00
|
|
|
{
|
2015-11-04 22:43:54 +01:00
|
|
|
}
|
2015-10-30 21:11:11 +01:00
|
|
|
|
|
|
|
|
2015-11-04 22:43:54 +01:00
|
|
|
ParticleEmitter::LinearMovementModule::LinearMovementModule(ParticleEmitter& emitter)
|
|
|
|
: ModuleBase(emitter)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::LinearMovementModule::spawnParticle(int index)
|
|
|
|
{
|
|
|
|
m_emitter.m_velocity[index].x = m_x.getRandom();
|
|
|
|
m_emitter.m_velocity[index].y = m_y.getRandom();
|
|
|
|
m_emitter.m_velocity[index].z = m_z.getRandom();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-07 21:34:38 +01:00
|
|
|
void ParticleEmitter::LinearMovementModule::serialize(OutputBlob& blob)
|
|
|
|
{
|
|
|
|
blob.write(m_x);
|
|
|
|
blob.write(m_y);
|
|
|
|
blob.write(m_z);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::LinearMovementModule::deserialize(InputBlob& blob)
|
|
|
|
{
|
|
|
|
blob.read(m_x);
|
|
|
|
blob.read(m_y);
|
|
|
|
blob.read(m_z);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
const uint32 ParticleEmitter::LinearMovementModule::s_type = Lumix::crc32("linear_movement");
|
2015-10-30 21:11:11 +01:00
|
|
|
|
|
|
|
|
2015-11-07 13:57:11 +01:00
|
|
|
ParticleEmitter::AlphaModule::AlphaModule(ParticleEmitter& emitter)
|
|
|
|
: ModuleBase(emitter)
|
2015-11-09 22:13:17 +01:00
|
|
|
, m_values(emitter.getAllocator())
|
2015-12-07 14:50:31 +01:00
|
|
|
, m_sampled(emitter.getAllocator())
|
|
|
|
{
|
2015-12-07 21:49:27 +01:00
|
|
|
m_values.resize(9);
|
|
|
|
m_values[0].set(-0.2f, 0);
|
|
|
|
m_values[1].set(0, 0);
|
|
|
|
m_values[2].set(0.2f, 0.0f);
|
|
|
|
m_values[3].set(-0.2f, 0.0f);
|
|
|
|
m_values[4].set(0.5f, 1.0f);
|
|
|
|
m_values[5].set(0.2f, 0.0f);
|
|
|
|
m_values[6].set(-0.2f, 0.0f);
|
|
|
|
m_values[7].set(1, 0);
|
|
|
|
m_values[8].set(0.2f, 0);
|
2015-12-07 14:50:31 +01:00
|
|
|
sample();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::AlphaModule::sample()
|
2015-11-07 13:57:11 +01:00
|
|
|
{
|
2015-12-07 14:50:31 +01:00
|
|
|
m_sampled.resize(20);
|
|
|
|
auto sampleAt = [this](float t) {
|
2015-12-07 21:49:27 +01:00
|
|
|
for(int i = 1; i < m_values.size(); i += 3)
|
2015-12-07 14:50:31 +01:00
|
|
|
{
|
|
|
|
if(m_values[i].x > t)
|
|
|
|
{
|
2015-12-07 21:49:27 +01:00
|
|
|
if(i == 1) return 0.0f;
|
2015-12-07 14:50:31 +01:00
|
|
|
|
2015-12-07 21:49:27 +01:00
|
|
|
float r = (t - m_values[i - 3].x) / (m_values[i].x - m_values[i - 3].x);
|
|
|
|
return m_values[i - 3].y + r * (m_values[i].y - m_values[i - 3].y);
|
2015-12-07 14:50:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0.0f;
|
|
|
|
};
|
|
|
|
|
|
|
|
for(int i = 0; i < m_sampled.size(); ++i)
|
|
|
|
{
|
|
|
|
m_sampled[i] = sampleAt(i / (float)m_sampled.size());
|
|
|
|
}
|
2015-11-07 13:57:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-14 00:25:08 +01:00
|
|
|
void ParticleEmitter::AlphaModule::update(float)
|
2015-11-07 13:57:11 +01:00
|
|
|
{
|
2015-12-07 14:50:31 +01:00
|
|
|
if(m_emitter.m_alpha.empty()) return;
|
2015-11-07 13:57:11 +01:00
|
|
|
|
2015-12-07 14:50:31 +01:00
|
|
|
float* particle_alpha = &m_emitter.m_alpha[0];
|
2015-11-09 22:13:17 +01:00
|
|
|
float* rel_life = &m_emitter.m_rel_life[0];
|
2015-12-07 14:50:31 +01:00
|
|
|
int size = m_sampled.size() - 1;
|
2015-11-09 22:13:17 +01:00
|
|
|
float float_size = (float)size;
|
2015-12-07 14:50:31 +01:00
|
|
|
for(int i = 0, c = m_emitter.m_size.size(); i < c; ++i)
|
2015-11-07 13:57:11 +01:00
|
|
|
{
|
2015-11-09 22:13:17 +01:00
|
|
|
float float_idx = float_size * rel_life[i];
|
|
|
|
int idx = (int)float_idx;
|
|
|
|
int next_idx = Math::minValue(idx + 1, size);
|
|
|
|
float w = float_idx - idx;
|
2015-12-07 14:50:31 +01:00
|
|
|
particle_alpha[i] = m_sampled[idx] * (1 - w) + m_sampled[next_idx] * w;
|
2015-11-07 13:57:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
const uint32 ParticleEmitter::AlphaModule::s_type = Lumix::crc32("alpha");
|
2015-11-07 13:57:11 +01:00
|
|
|
|
|
|
|
|
2015-11-18 20:18:48 +01:00
|
|
|
ParticleEmitter::SizeModule::SizeModule(ParticleEmitter& emitter)
|
|
|
|
: ModuleBase(emitter)
|
|
|
|
, m_values(emitter.getAllocator())
|
2015-12-07 00:55:32 +01:00
|
|
|
, m_sampled(emitter.getAllocator())
|
|
|
|
{
|
2015-12-07 21:49:27 +01:00
|
|
|
m_values.resize(9);
|
|
|
|
m_values[0].set(-0.2f, 0);
|
|
|
|
m_values[1].set(0, 0);
|
|
|
|
m_values[2].set(0.2f, 0.0f);
|
|
|
|
m_values[3].set(-0.2f, 0.0f);
|
|
|
|
m_values[4].set(0.5f, 1.0f);
|
|
|
|
m_values[5].set(0.2f, 0.0f);
|
|
|
|
m_values[6].set(-0.2f, 0.0f);
|
|
|
|
m_values[7].set(1, 0);
|
|
|
|
m_values[8].set(0.2f, 0);
|
2015-12-07 00:55:32 +01:00
|
|
|
sample();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::SizeModule::sample()
|
2015-11-18 20:18:48 +01:00
|
|
|
{
|
2015-12-07 00:55:32 +01:00
|
|
|
m_sampled.resize(20);
|
|
|
|
auto sampleAt = [this](float t) {
|
|
|
|
for (int i = 0; i < m_values.size(); ++i)
|
|
|
|
{
|
|
|
|
if (m_values[i].x > t)
|
|
|
|
{
|
|
|
|
if (i == 0) return 0.0f;
|
|
|
|
|
|
|
|
float r = (t - m_values[i - 1].x) / (m_values[i].x - m_values[i - 1].x);
|
|
|
|
return m_values[i - 1].y + r * (m_values[i].y - m_values[i - 1].y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0.0f;
|
|
|
|
};
|
|
|
|
|
|
|
|
for (int i = 0; i < m_sampled.size(); ++i)
|
|
|
|
{
|
|
|
|
m_sampled[i] = sampleAt(i / (float)m_sampled.size());
|
|
|
|
}
|
2015-11-18 20:18:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::SizeModule::update(float)
|
|
|
|
{
|
|
|
|
if (m_emitter.m_size.empty()) return;
|
|
|
|
|
|
|
|
float* particle_size = &m_emitter.m_size[0];
|
|
|
|
float* rel_life = &m_emitter.m_rel_life[0];
|
2015-12-07 00:55:32 +01:00
|
|
|
int size = m_sampled.size() - 1;
|
2015-11-18 20:18:48 +01:00
|
|
|
float float_size = (float)size;
|
|
|
|
for (int i = 0, c = m_emitter.m_size.size(); i < c; ++i)
|
|
|
|
{
|
|
|
|
float float_idx = float_size * rel_life[i];
|
|
|
|
int idx = (int)float_idx;
|
|
|
|
int next_idx = Math::minValue(idx + 1, size);
|
|
|
|
float w = float_idx - idx;
|
2015-12-07 00:55:32 +01:00
|
|
|
particle_size[i] = m_sampled[idx] * (1 - w) + m_sampled[next_idx] * w;
|
2015-11-18 20:18:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const uint32 ParticleEmitter::SizeModule::s_type = Lumix::crc32("size");
|
|
|
|
|
|
|
|
|
2015-11-07 13:57:11 +01:00
|
|
|
ParticleEmitter::RandomRotationModule::RandomRotationModule(ParticleEmitter& emitter)
|
|
|
|
: ModuleBase(emitter)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::RandomRotationModule::spawnParticle(int index)
|
|
|
|
{
|
|
|
|
m_emitter.m_rotation[index] = Math::degreesToRadians(float(rand() % 360));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-11 21:54:25 +01:00
|
|
|
const uint32 ParticleEmitter::RandomRotationModule::s_type = Lumix::crc32("random_rotation");
|
2015-11-07 13:57:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-10-30 21:11:11 +01:00
|
|
|
Interval::Interval()
|
|
|
|
: from(0)
|
|
|
|
, to(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-30 21:18:36 +01:00
|
|
|
void Interval::check()
|
|
|
|
{
|
|
|
|
from = Math::maxValue(from, 0.0f);
|
2015-11-04 22:43:54 +01:00
|
|
|
to = Math::maxValue(from, to);
|
2015-10-30 21:18:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-30 21:11:11 +01:00
|
|
|
float Interval::getRandom() const
|
|
|
|
{
|
|
|
|
return from + rand() * ((to - from) / RAND_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ParticleEmitter::ParticleEmitter(Entity entity, Universe& universe, IAllocator& allocator)
|
|
|
|
: m_next_spawn_time(0)
|
|
|
|
, m_allocator(allocator)
|
2015-11-09 22:13:17 +01:00
|
|
|
, m_rel_life(allocator)
|
2015-10-30 21:11:11 +01:00
|
|
|
, m_life(allocator)
|
|
|
|
, m_modules(allocator)
|
|
|
|
, m_position(allocator)
|
|
|
|
, m_velocity(allocator)
|
2015-11-07 13:57:11 +01:00
|
|
|
, m_rotation(allocator)
|
|
|
|
, m_rotational_speed(allocator)
|
|
|
|
, m_alpha(allocator)
|
2015-10-30 21:11:11 +01:00
|
|
|
, m_universe(universe)
|
|
|
|
, m_entity(entity)
|
|
|
|
, m_size(allocator)
|
2015-11-04 22:43:54 +01:00
|
|
|
, m_material(nullptr)
|
2015-10-30 21:11:11 +01:00
|
|
|
{
|
|
|
|
m_spawn_period.from = 1;
|
|
|
|
m_spawn_period.to = 2;
|
|
|
|
m_initial_life.from = 1;
|
|
|
|
m_initial_life.to = 2;
|
2015-11-01 20:56:29 +01:00
|
|
|
m_initial_size.from = 1;
|
|
|
|
m_initial_size.to = 1;
|
2015-10-30 21:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ParticleEmitter::~ParticleEmitter()
|
|
|
|
{
|
2015-11-04 22:43:54 +01:00
|
|
|
setMaterial(nullptr);
|
|
|
|
|
2015-10-30 21:11:11 +01:00
|
|
|
for (auto* module : m_modules)
|
|
|
|
{
|
|
|
|
LUMIX_DELETE(m_allocator, module);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-04 22:43:54 +01:00
|
|
|
void ParticleEmitter::setMaterial(Material* material)
|
|
|
|
{
|
|
|
|
if (m_material)
|
|
|
|
{
|
|
|
|
auto* manager = m_material->getResourceManager().get(ResourceManager::MATERIAL);
|
|
|
|
manager->unload(*m_material);
|
|
|
|
}
|
|
|
|
m_material = material;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-30 21:11:11 +01:00
|
|
|
void ParticleEmitter::spawnParticle()
|
|
|
|
{
|
|
|
|
m_position.push(m_universe.getPosition(m_entity));
|
2015-11-07 13:57:11 +01:00
|
|
|
m_rotation.push(0);
|
|
|
|
m_rotational_speed.push(0);
|
2015-10-30 21:11:11 +01:00
|
|
|
m_life.push(m_initial_life.getRandom());
|
2015-11-09 22:13:17 +01:00
|
|
|
m_rel_life.push(0.0f);
|
2015-11-07 13:57:11 +01:00
|
|
|
m_alpha.push(1);
|
2015-10-30 21:11:11 +01:00
|
|
|
m_velocity.push(Vec3(0, 0, 0));
|
2015-11-01 20:56:29 +01:00
|
|
|
m_size.push(m_initial_size.getRandom());
|
2015-10-30 21:11:11 +01:00
|
|
|
for (auto* module : m_modules)
|
|
|
|
{
|
|
|
|
module->spawnParticle(m_life.size() - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-04 22:43:54 +01:00
|
|
|
void ParticleEmitter::addModule(ModuleBase* module)
|
|
|
|
{
|
|
|
|
m_modules.push(module);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-30 21:11:11 +01:00
|
|
|
void ParticleEmitter::destroyParticle(int index)
|
|
|
|
{
|
|
|
|
for (auto* module : m_modules)
|
|
|
|
{
|
|
|
|
module->destoryParticle(index);
|
|
|
|
}
|
|
|
|
m_life.eraseFast(index);
|
2015-11-09 22:13:17 +01:00
|
|
|
m_rel_life.eraseFast(index);
|
2015-10-30 21:11:11 +01:00
|
|
|
m_position.eraseFast(index);
|
|
|
|
m_velocity.eraseFast(index);
|
2015-11-07 13:57:11 +01:00
|
|
|
m_rotation.eraseFast(index);
|
|
|
|
m_rotational_speed.eraseFast(index);
|
|
|
|
m_alpha.eraseFast(index);
|
2015-10-30 21:11:11 +01:00
|
|
|
m_size.eraseFast(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::updateLives(float time_delta)
|
|
|
|
{
|
2015-11-09 22:13:17 +01:00
|
|
|
for (int i = 0, c = m_rel_life.size(); i < c; ++i)
|
2015-10-30 21:11:11 +01:00
|
|
|
{
|
2015-11-09 22:13:17 +01:00
|
|
|
float rel_life = m_rel_life[i];
|
|
|
|
rel_life += time_delta / m_life[i];
|
|
|
|
m_rel_life[i] = rel_life;
|
2015-10-30 21:11:11 +01:00
|
|
|
|
2015-11-09 22:13:17 +01:00
|
|
|
if (rel_life > 1)
|
2015-10-30 21:11:11 +01:00
|
|
|
{
|
|
|
|
destroyParticle(i);
|
|
|
|
--i;
|
|
|
|
--c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-07 21:34:38 +01:00
|
|
|
void ParticleEmitter::serialize(OutputBlob& blob)
|
|
|
|
{
|
|
|
|
blob.write(m_spawn_period);
|
|
|
|
blob.write(m_initial_life);
|
|
|
|
blob.write(m_initial_size);
|
|
|
|
blob.write(m_entity);
|
|
|
|
blob.writeString(m_material ? m_material->getPath().c_str() : "");
|
|
|
|
blob.write(m_modules.size());
|
|
|
|
for (auto* module : m_modules)
|
|
|
|
{
|
|
|
|
blob.write(module->getType());
|
|
|
|
module->serialize(blob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::deserialize(InputBlob& blob, ResourceManager& manager)
|
|
|
|
{
|
|
|
|
blob.read(m_spawn_period);
|
|
|
|
blob.read(m_initial_life);
|
|
|
|
blob.read(m_initial_size);
|
|
|
|
blob.read(m_entity);
|
|
|
|
char path[MAX_PATH_LENGTH];
|
|
|
|
blob.readString(path, lengthOf(path));
|
|
|
|
auto material_manager = manager.get(ResourceManager::MATERIAL);
|
|
|
|
auto material = static_cast<Material*>(material_manager->load(Lumix::Path(path)));
|
|
|
|
setMaterial(material);
|
|
|
|
|
|
|
|
int size;
|
|
|
|
blob.read(size);
|
|
|
|
for (auto* module : m_modules)
|
|
|
|
{
|
|
|
|
LUMIX_DELETE(m_allocator, module);
|
|
|
|
}
|
|
|
|
m_modules.clear();
|
|
|
|
for (int i = 0; i < size; ++i)
|
|
|
|
{
|
2015-11-11 21:54:25 +01:00
|
|
|
uint32 type;
|
2015-11-07 21:34:38 +01:00
|
|
|
blob.read(type);
|
|
|
|
auto* module = createModule(type, *this);
|
|
|
|
m_modules.push(module);
|
|
|
|
module->deserialize(blob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-30 21:11:11 +01:00
|
|
|
void ParticleEmitter::updatePositions(float time_delta)
|
|
|
|
{
|
|
|
|
for (int i = 0, c = m_position.size(); i < c; ++i)
|
|
|
|
{
|
2015-11-04 22:43:54 +01:00
|
|
|
m_position[i] += m_velocity[i] * time_delta;
|
2015-10-30 21:11:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-07 13:57:11 +01:00
|
|
|
void ParticleEmitter::updateRotations(float time_delta)
|
|
|
|
{
|
|
|
|
for (int i = 0, c = m_rotation.size(); i < c; ++i)
|
|
|
|
{
|
|
|
|
m_rotation[i] += m_rotational_speed[i] * time_delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-30 21:11:11 +01:00
|
|
|
void ParticleEmitter::update(float time_delta)
|
|
|
|
{
|
|
|
|
spawnParticles(time_delta);
|
|
|
|
updateLives(time_delta);
|
|
|
|
updatePositions(time_delta);
|
2015-11-07 13:57:11 +01:00
|
|
|
updateRotations(time_delta);
|
2015-10-30 21:11:11 +01:00
|
|
|
for (auto* module : m_modules)
|
|
|
|
{
|
|
|
|
module->update(time_delta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParticleEmitter::spawnParticles(float time_delta)
|
|
|
|
{
|
|
|
|
m_next_spawn_time -= time_delta;
|
|
|
|
|
|
|
|
while (m_next_spawn_time < 0)
|
|
|
|
{
|
|
|
|
m_next_spawn_time += m_spawn_period.getRandom();
|
|
|
|
|
|
|
|
spawnParticle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct InitialVelocityModule : public ParticleEmitter::ModuleBase
|
|
|
|
{
|
|
|
|
Interval m_x;
|
|
|
|
Interval m_y;
|
|
|
|
Interval m_z;
|
|
|
|
|
|
|
|
InitialVelocityModule(ParticleEmitter& emitter)
|
|
|
|
: ModuleBase(emitter)
|
|
|
|
{
|
|
|
|
m_z.from = -1;
|
|
|
|
m_z.to = 1;
|
|
|
|
m_x.from = m_x.to = m_x.to = m_y.from = m_y.to = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void spawnParticle(int index) override
|
|
|
|
{
|
|
|
|
Vec3 v;
|
|
|
|
v.x = m_x.getRandom();
|
|
|
|
v.y = m_y.getRandom();
|
|
|
|
v.z = m_z.getRandom();
|
|
|
|
m_emitter.m_velocity[index] = v;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace Lumix
|