1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
#include "particle/particle.h"
#include "particle/ParticleEffect.h"
#include "particle/ParticleSource.h"
#include "particle/effects/VolumeEffect.h"
#include "bmpman/bmpman.h"
#include "weapon/beam.h"
/**
* @defgroup particleEffects Particle Effects
*
* @ingroup particleSystems
*/
namespace particle {
namespace effects {
VolumeEffect::VolumeEffect(const SCP_string& name)
: ParticleEffect(name) {}
bool VolumeEffect::processSource(ParticleSource* source) {
if (!m_timing.continueProcessing(source)) {
return false;
}
// This uses the internal features of the timing class for determining if and how many effects should be
// triggered this frame
util::EffectTiming::TimingState time_state;
for (int time_since_creation = m_timing.shouldCreateEffect(source, time_state); time_since_creation >= 0; time_since_creation = m_timing.shouldCreateEffect(source, time_state)) {
float interp = static_cast<float>(time_since_creation)/(f2fl(Frametime) * 1000.0f);
auto num = m_particleNum.next();
if (source->getOrigin()->getType() == SourceOriginType::BEAM) {
// beam particle numbers are per km
object* b_obj = source->getOrigin()->getObjectHost();
float dist = vm_vec_dist(&Beams[b_obj->instance].last_start, &Beams[b_obj->instance].last_shot) / 1000.0f;
float km;
float remainder = modff(dist, &km);
uint old_num = num;
num = (uint)(old_num * km); // multiply by the number of kilometers
num += (uint)(old_num * remainder); // try to add any remainders if we have more than 1 per kilometer
// if we still have nothing let's give it one last shot
if (num < 1 && frand() < remainder * old_num)
num += 1;
}
vec3d stretch_dir = source->getOrientation()->getDirectionVector(source->getOrigin(), m_particleProperties.m_parent_local);
matrix stretch_matrix = vm_stretch_matrix(&stretch_dir, m_stretch);
for (uint i = 0; i < num; ++i) {
if (m_particleChance < 1.0f) {
auto roll = m_particleRoll.next();
if (roll <= 0.0f)
continue;
}
vec3d pos;
// get an unbiased random point in the sphere
vm_vec_random_in_sphere(&pos, &vmd_zero_vector, 1.0f, false);
// maybe bias it towards the center or edge
if (m_bias != 1.0f) {
float mag = vm_vec_mag(&pos);
vm_vec_normalize(&pos);
mag = powf(mag, m_bias);
pos *= mag;
}
// maybe stretch it
vec3d copy_pos = pos;
if (m_stretch != 1.0f)
vm_vec_rotate(&pos, ©_pos, &stretch_matrix);
particle_info info;
source->getOrigin()->applyToParticleInfo(info, m_particleProperties.m_parent_local, interp, m_particleProperties.m_manual_offset);
// make their velocity radial, and based on position, allows for some very cool effects
vec3d velocity = pos;
pos *= m_radius;
info.pos += pos;
vm_vec_scale(&velocity, m_velocity.next());
info.vel *= m_vel_inherit.next();
info.vel += velocity;
m_particleProperties.createParticle(info);
}
}
// Continue processing this source
return true;
}
void VolumeEffect::parseValues(bool nocreate) {
m_particleProperties.parse(nocreate);
if (internal::required_string_if_new("+Velocity:", nocreate)) {
m_velocity = ::util::ParsedRandomFloatRange::parseRandomRange();
}
if (internal::required_string_if_new("+Number:", nocreate)) {
m_particleNum = ::util::ParsedRandomUintRange::parseRandomRange();
}
if (!nocreate) {
m_particleChance = 1.0f;
}
if (optional_string("+Chance:")) {
float chance;
stuff_float(&chance);
if (chance <= 0.0f) {
error_display(0,
"Particle %s tried to set +Chance: %f\nChances below 0 would result in no particles.",
m_name.c_str(),
chance);
} else if (chance >= 1.0f) {
error_display(0,
"Particle %s tried to set +Chance: %f\nChances above 1 are ignored, please use +Number: "
"(min,max) to spawn multiple particles.",
m_name.c_str(),
chance);
chance = 1.0f;
}
m_particleChance = chance;
}
m_particleRoll = ::util::UniformFloatRange(m_particleChance - 1.0f, m_particleChance);
if (internal::required_string_if_new("+Volume radius:", nocreate)) {
float radius;
stuff_float(&radius);
if (radius < 0.001f) {
error_display(0, "A volume radius of %f is not valid. Must be greater than 0. Defaulting to 10.", radius);
radius = 10.0f;
}
m_radius = radius;
}
if (optional_string("+Bias:")) {
float bias;
stuff_float(&bias);
if (bias < 0.001f) {
error_display(0, "A volume bias value of %f is not valid. Must be greater than 0.", bias);
bias = 1.0f;
}
m_bias = bias;
}
if (optional_string("+Stretch:")) {
float stretch;
stuff_float(&stretch);
if (stretch < 0.001f) {
error_display(0, "A volume stretch value of %f is not valid. Must be greater than 0.", stretch);
stretch = 1.0f;
}
m_stretch = stretch;
}
if (optional_string("+Parent Velocity Factor:")) {
m_vel_inherit = ::util::ParsedRandomFloatRange::parseRandomRange();
}
m_timing = util::EffectTiming::parseTiming();
}
void VolumeEffect::pageIn() {
m_particleProperties.pageIn();
}
void VolumeEffect::initializeSource(ParticleSource& source) {
m_timing.applyToSource(&source);
}
}
}
|