mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
Give Particles Access to Their Spawner Effect (#7034)
* Make particles aware of the effect that spawned them * Allow modular curves to call global functions from submember inputs * Add more in and outputs for particle lifetime curves * Allow post-curve velocity as a curve input * fix warning and incorrect assig * Disable CheckTriviallyCopyableMove * Clang Tidy
This commit is contained in:
1 parent
b0166dd6a6
commit
1e7431a044
10 files changed
+165
-209
No files matched your search
@@ -48,5 +48,7 @@ CheckOptions:
|
||||
value: 'std::vector;std::deque;std::list;SCP_vector;SCP_deque;SCP_list'
|
||||
- key: 'readability-braces-around-statements.ShortStatementLines'
|
||||
value: '4' # Avoid flagging simple if (...) return false; statements
|
||||
- key: 'performance-move-const-arg.CheckTriviallyCopyableMove' # This isn't actually performance relevant, but using move on trivially copyable types can well indicate that the variable should not be used anymore, such as when passing a builder type to the finialization function
|
||||
value: 'false'
|
||||
...
|
||||
|
||||
@@ -48,8 +48,6 @@ ParticleEffect::ParticleEffect(SCP_string name)
|
||||
m_manual_offset (std::nullopt),
|
||||
m_manual_velocity_offset(std::nullopt),
|
||||
m_particleTrail(ParticleEffectHandle::invalid()),
|
||||
m_size_lifetime_curve(-1),
|
||||
m_vel_lifetime_curve (-1),
|
||||
m_particleChance(1.f),
|
||||
m_distanceCulled(-1.f)
|
||||
{}
|
||||
@@ -119,8 +117,6 @@ ParticleEffect::ParticleEffect(SCP_string name,
|
||||
m_manual_offset(offsetLocal),
|
||||
m_manual_velocity_offset(velocityOffsetLocal),
|
||||
m_particleTrail(particleTrail),
|
||||
m_size_lifetime_curve(-1),
|
||||
m_vel_lifetime_curve (-1),
|
||||
m_particleChance(particleChance),
|
||||
m_distanceCulled(distanceCulled) {}
|
||||
|
||||
@@ -268,11 +264,11 @@ auto ParticleEffect::processSourceInternal(float interp, const ParticleSource& s
|
||||
for (uint i = 0; i < num_spawn; ++i) {
|
||||
float particleFraction = static_cast<float>(i) / static_cast<float>(num_spawn);
|
||||
|
||||
particle_info info;
|
||||
particle info;
|
||||
|
||||
info.reverse = m_reverseAnimation;
|
||||
info.pos = pos;
|
||||
info.vel = velParent;
|
||||
info.velocity = velParent;
|
||||
|
||||
if (m_parent_local) {
|
||||
info.attached_objnum = parent;
|
||||
@@ -284,9 +280,9 @@ auto ParticleEffect::processSourceInternal(float interp, const ParticleSource& s
|
||||
}
|
||||
|
||||
if (m_vel_inherit_absolute)
|
||||
vm_vec_normalize_safe(&info.vel, true);
|
||||
vm_vec_normalize_safe(&info.velocity, true);
|
||||
|
||||
info.vel *= (m_ignore_velocity_inherit_if_has_parent && parent >= 0) ? 0.f : m_vel_inherit.next() * inheritVelocityMultiplier;
|
||||
info.velocity *= (m_ignore_velocity_inherit_if_has_parent && parent >= 0) ? 0.f : m_vel_inherit.next() * inheritVelocityMultiplier;
|
||||
|
||||
vec3d localVelocity = velNoise;
|
||||
vec3d localPos = posNoise;
|
||||
@@ -325,32 +321,43 @@ auto ParticleEffect::processSourceInternal(float interp, const ParticleSource& s
|
||||
m_velocity_directional_scaling == VelocityScaling::DOT ? dot : 1.f / std::max(0.001f, dot));
|
||||
}
|
||||
|
||||
info.vel += localVelocity;
|
||||
info.pos += localPos + info.vel * (interp * f2fl(Frametime));
|
||||
info.velocity += localVelocity;
|
||||
info.pos += localPos + info.velocity * (interp * f2fl(Frametime));
|
||||
|
||||
info.bitmap = m_bitmap_list[m_bitmap_range.next()];
|
||||
|
||||
if (m_parentScale)
|
||||
// if we were spawned by a particle, parentRadius is the parent's radius and m_radius is a factor of that
|
||||
info.rad = parentRadius * m_radius.next() * radiusMultiplier;
|
||||
info.radius = parentRadius * m_radius.next() * radiusMultiplier;
|
||||
else
|
||||
info.rad = m_radius.next() * radiusMultiplier;
|
||||
info.radius = m_radius.next() * radiusMultiplier;
|
||||
|
||||
info.length = m_length.next() * lengthMultiplier;
|
||||
if (m_hasLifetime) {
|
||||
if (m_parentLifetime)
|
||||
// if we were spawned by a particle, parentLifetime is the parent's remaining lifetime and m_lifetime is a factor of that
|
||||
info.lifetime = parentLifetime * m_lifetime.next() * lifetimeMultiplier;
|
||||
else
|
||||
info.lifetime = m_lifetime.next() * lifetimeMultiplier;
|
||||
|
||||
info.lifetime_from_animation = m_keep_anim_length_if_available;
|
||||
int fps = 1;
|
||||
if (info.nframes < 0) {
|
||||
Assertion(bm_is_valid(info.bitmap), "Invalid bitmap handle passed to particle create.");
|
||||
bm_get_info(info.bitmap, nullptr, nullptr, nullptr, &info.nframes, &fps);
|
||||
}
|
||||
|
||||
if (m_hasLifetime) {
|
||||
if (m_keep_anim_length_if_available && info.nframes > 1) {
|
||||
// Recalculate max life for ani's
|
||||
info.max_life = i2fl(info.nframes) / i2fl(fps);
|
||||
}
|
||||
else {
|
||||
if (m_parentLifetime)
|
||||
// if we were spawned by a particle, parentLifetime is the parent's remaining lifetime and m_lifetime is a factor of that
|
||||
info.max_life = parentLifetime * m_lifetime.next() * lifetimeMultiplier;
|
||||
else
|
||||
info.max_life = m_lifetime.next() * lifetimeMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
info.starting_age = interp * f2fl(Frametime);
|
||||
|
||||
info.size_lifetime_curve = m_size_lifetime_curve;
|
||||
info.vel_lifetime_curve = m_vel_lifetime_curve;
|
||||
info.age = interp * f2fl(Frametime);
|
||||
info.looping = false;
|
||||
info.angle = frand_range(0.0f, PI2);
|
||||
info.parent_effect = m_self;
|
||||
|
||||
switch (m_rotation_type) {
|
||||
case RotationType::DEFAULT:
|
||||
@@ -367,7 +374,7 @@ auto ParticleEffect::processSourceInternal(float interp, const ParticleSource& s
|
||||
}
|
||||
|
||||
if (m_particleTrail.isValid()) {
|
||||
auto part = createPersistent(&info);
|
||||
auto part = createPersistent(std::move(info));
|
||||
|
||||
if constexpr (isPersistent)
|
||||
createdParticles.push_back(part);
|
||||
@@ -381,12 +388,12 @@ auto ParticleEffect::processSourceInternal(float interp, const ParticleSource& s
|
||||
}
|
||||
} else {
|
||||
if constexpr (isPersistent){
|
||||
auto part = createPersistent(&info);
|
||||
auto part = createPersistent(std::move(info));
|
||||
createdParticles.push_back(part);
|
||||
}
|
||||
else {
|
||||
// We don't have a trail so we don't need a persistent particle
|
||||
create(&info);
|
||||
create(std::move(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,15 +84,25 @@ public:
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
enum class ParticleLifetimeCurvesOutput : uint8_t {
|
||||
VELOCITY_MULT,
|
||||
RADIUS_MULT,
|
||||
LENGTH_MULT,
|
||||
ANIM_STATE,
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
private:
|
||||
friend struct ParticleParse;
|
||||
|
||||
friend class ParticleManager;
|
||||
friend int ::parse_weapon(int, bool, const char*);
|
||||
|
||||
friend ParticleEffectHandle scripting::api::getLegacyScriptingParticleEffect(int bitmap, bool reversed);
|
||||
|
||||
SCP_string m_name; //!< The name of this effect
|
||||
|
||||
ParticleSubeffectHandle m_self;
|
||||
|
||||
Duration m_duration;
|
||||
RotationType m_rotation_type;
|
||||
ShapeDirection m_direction;
|
||||
@@ -138,9 +148,6 @@ public:
|
||||
|
||||
ParticleEffectHandle m_particleTrail;
|
||||
|
||||
int m_size_lifetime_curve; //This is a curve of the particle, not of the particle effect, as such, it should not be part of the curve set
|
||||
int m_vel_lifetime_curve; //This is a curve of the particle, not of the particle effect, as such, it should not be part of the curve set
|
||||
|
||||
float m_particleChance; //Deprecated. Use particle num random ranges instead.
|
||||
float m_distanceCulled; //Kinda deprecated. Only used by the oldest of legacy effects.
|
||||
|
||||
@@ -251,7 +258,28 @@ public:
|
||||
ModularCurvesMathOperators::division>{}}
|
||||
);
|
||||
|
||||
constexpr static auto modular_curves_lifetime_definition = make_modular_curve_definition<particle, ParticleLifetimeCurvesOutput>(
|
||||
std::array {
|
||||
std::pair {"Radius", ParticleLifetimeCurvesOutput::RADIUS_MULT},
|
||||
std::pair {"Velocity", ParticleLifetimeCurvesOutput::VELOCITY_MULT},
|
||||
std::pair {"Length", ParticleLifetimeCurvesOutput::LENGTH_MULT},
|
||||
std::pair {"Anim State", ParticleLifetimeCurvesOutput::ANIM_STATE},
|
||||
},
|
||||
//Should you ever need to access something from the effect as a modular curve input:
|
||||
//std::pair {"", modular_curves_submember_input<&particle::parent_effect, &ParticleSubeffectHandle::getParticleEffect, &ParticleEffect::>{}}
|
||||
std::pair {"Age", modular_curves_submember_input<&particle::age>{}},
|
||||
std::pair {"Lifetime", modular_curves_math_input<
|
||||
modular_curves_submember_input<&particle::age>,
|
||||
modular_curves_submember_input<&particle::max_life>,
|
||||
ModularCurvesMathOperators::division>{}},
|
||||
std::pair {"Radius", modular_curves_submember_input<&particle::radius>{}},
|
||||
std::pair {"Velocity", modular_curves_submember_input<&particle::velocity, &vm_vec_mag_quick>{}})
|
||||
.derive_modular_curves_input_only_subset<float>(
|
||||
std::pair {"Post-Curves Velocity", modular_curves_self_input{}}
|
||||
);
|
||||
|
||||
MODULAR_CURVE_SET(m_modular_curves, modular_curves_definition);
|
||||
MODULAR_CURVE_SET(m_lifetime_curves, modular_curves_lifetime_definition);
|
||||
|
||||
private:
|
||||
float getCurrentFrequencyMult(decltype(modular_curves_definition)::input_type_t source) const;
|
||||
|
||||
@@ -135,9 +135,13 @@ ParticleEffectHandle ParticleManager::addEffect(SCP_vector<ParticleEffect>&& eff
|
||||
}
|
||||
#endif
|
||||
|
||||
m_effects.emplace_back(std::move(effect));
|
||||
auto& effect_after_emplace = m_effects.emplace_back(std::move(effect));
|
||||
|
||||
return ParticleEffectHandle(static_cast<ParticleEffectHandle::impl_type>(m_effects.size() - 1));
|
||||
auto handle = ParticleEffectHandle(static_cast<ParticleEffectHandle::impl_type>(m_effects.size() - 1));
|
||||
for (size_t i = 0; i < effect_after_emplace.size(); i++)
|
||||
effect_after_emplace[i].m_self = ParticleSubeffectHandle{handle, i};
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void ParticleManager::pageIn() {
|
||||
|
||||
@@ -283,20 +283,7 @@ namespace particle {
|
||||
}
|
||||
|
||||
static void parseModularCurvesLifetime(ParticleEffect& effect) {
|
||||
//TODO The following loop behaves as a true subset of how parsing will work once the particle modular curve set is implemented.
|
||||
//As such, once that's added the loop can be replaced with a modular_curve_set.parse without worry about breaking tables.
|
||||
while (optional_string("$Particle Lifetime Curve:")) {
|
||||
required_string("+Input: Lifetime");
|
||||
|
||||
required_string("+Output:");
|
||||
int output = required_string_one_of(2, "Radius", "Velocity");
|
||||
//The required string part enforces this to be either 0 or 1
|
||||
required_string(output == 0 ? "Radius" : "Velocity");
|
||||
int& curve = output == 0 ? effect.m_size_lifetime_curve : effect.m_vel_lifetime_curve;
|
||||
|
||||
required_string_either("+Curve Name:", "+Curve:", true);
|
||||
curve = curve_parse(" Unknown curve requested for modular curves!");
|
||||
}
|
||||
effect.m_lifetime_curves.parse("$Particle Lifetime Curve:");
|
||||
}
|
||||
|
||||
static void parseModularCurvesSource(ParticleEffect& effect) {
|
||||
@@ -368,13 +355,13 @@ namespace particle {
|
||||
|
||||
static void parseSizeLifetimeCurve(ParticleEffect &effect) {
|
||||
if (optional_string("+Size over lifetime curve:")) {
|
||||
effect.m_size_lifetime_curve = curve_parse("");
|
||||
effect.m_lifetime_curves.add_curve("Lifetime", ParticleEffect::ParticleLifetimeCurvesOutput::RADIUS_MULT, modular_curves_entry{curve_parse("")});
|
||||
}
|
||||
}
|
||||
|
||||
static void parseVelocityLifetimeCurve(ParticleEffect &effect) {
|
||||
if (optional_string("+Velocity scalar over lifetime curve:")) {
|
||||
effect.m_vel_lifetime_curve = curve_parse("");
|
||||
effect.m_lifetime_curves.add_curve("Lifetime", ParticleEffect::ParticleLifetimeCurvesOutput::VELOCITY_MULT, modular_curves_entry{curve_parse("")});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,6 @@ struct weapon_info;
|
||||
enum class WeaponState: uint32_t;
|
||||
|
||||
namespace particle {
|
||||
|
||||
class ParticleEffect;
|
||||
struct particle_effect_tag {
|
||||
};
|
||||
using ParticleEffectHandle = ::util::ID<particle_effect_tag, ptrdiff_t, -1>;
|
||||
|
||||
/**
|
||||
* @brief The orientation of a particle source
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "freespace.h"
|
||||
|
||||
#include "particle/ParticleEffect.h"
|
||||
|
||||
EffectHostParticle::EffectHostParticle(particle::WeakParticlePtr particle, matrix orientationOverride, bool orientationOverrideRelative) :
|
||||
EffectHost(orientationOverride, orientationOverrideRelative), m_particle(std::move(particle)) {}
|
||||
|
||||
@@ -16,10 +18,7 @@ std::pair<vec3d, matrix> EffectHostParticle::getPositionAndOrientation(bool /*re
|
||||
|
||||
vec3d pos;
|
||||
if (interp != 0.0f) {
|
||||
float vel_scalar = 1.0f;
|
||||
if (particle->vel_lifetime_curve >= 0) {
|
||||
vel_scalar = Curves[particle->vel_lifetime_curve].GetValue(particle->age / particle->max_life);
|
||||
}
|
||||
float vel_scalar = particle->parent_effect.getParticleEffect().m_lifetime_curves.get_output(particle::ParticleEffect::ParticleLifetimeCurvesOutput::VELOCITY_MULT, std::forward_as_tuple(*particle, vm_vec_mag_quick(&particle->velocity)));
|
||||
vec3d pos_last = particle->pos - (particle->velocity * vel_scalar * flFrametime);
|
||||
vm_vec_linear_interpolate(&pos, &particle->pos, &pos_last, interp);
|
||||
} else {
|
||||
@@ -52,11 +51,11 @@ float EffectHostParticle::getLifetime() const {
|
||||
|
||||
float EffectHostParticle::getScale() const {
|
||||
const auto& particle = m_particle.lock();
|
||||
int idx = particle->size_lifetime_curve;
|
||||
if (idx >= 0)
|
||||
return particle->radius * Curves[idx].GetValue(particle->age / particle->max_life);
|
||||
else
|
||||
return particle->radius;
|
||||
//For anything apart from the velocity curve, "Post-Curves Velocity" is well defined. This is needed to facilitate complex but common particle scaling and appearance curves.
|
||||
const auto& curve_input = std::forward_as_tuple(*particle,
|
||||
vm_vec_mag_quick(&particle->velocity) * particle->parent_effect.getParticleEffect().m_lifetime_curves.get_output(particle::ParticleEffect::ParticleLifetimeCurvesOutput::ANIM_STATE, std::forward_as_tuple(*particle, vm_vec_mag_quick(&particle->velocity))));
|
||||
|
||||
return particle->radius * particle->parent_effect.getParticleEffect().m_lifetime_curves.get_output(particle::ParticleEffect::ParticleLifetimeCurvesOutput::RADIUS_MULT, curve_input);
|
||||
}
|
||||
|
||||
bool EffectHostParticle::isValid() const {
|
||||
|
||||
+43
-98
@@ -106,6 +106,11 @@ namespace particle
|
||||
}
|
||||
}
|
||||
|
||||
const ParticleEffect& ParticleSubeffectHandle::getParticleEffect() const {
|
||||
//TODO possibly cache this!
|
||||
return ParticleManager::get()->getEffect(handle)[subeffect];
|
||||
}
|
||||
|
||||
// only call from game_shutdown()!!!
|
||||
void close()
|
||||
{
|
||||
@@ -134,118 +139,51 @@ namespace particle
|
||||
DCF_BOOL2(particles, Particles_enabled, "Turns particles on/off",
|
||||
"Usage: particles [bool]\nTurns particle system on/off. If nothing passed, then toggles it.\n");
|
||||
|
||||
bool init_particle(particle* part, particle_info* info) {
|
||||
static bool maybe_cull_particle(const particle& new_particle) {
|
||||
if (!Particles_enabled)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
vec3d world_pos = info->pos;
|
||||
if (info->attached_objnum >= 0) {
|
||||
vm_vec_unrotate(&world_pos, &world_pos, &Objects[info->attached_objnum].orient);
|
||||
world_pos += Objects[info->attached_objnum].pos;
|
||||
vec3d world_pos = new_particle.pos;
|
||||
if (new_particle.attached_objnum >= 0) {
|
||||
vm_vec_unrotate(&world_pos, &world_pos, &Objects[new_particle.attached_objnum].orient);
|
||||
world_pos += Objects[new_particle.attached_objnum].pos;
|
||||
}
|
||||
// treat particles on lower detail levels as 'further away' for the purposes of culling
|
||||
float adjusted_dist = vm_vec_dist(&Eye_position, &world_pos) * powf(2.5f, (float)(static_cast<int>(DefaultDetailPreset::Num_detail_presets) - Detail.num_particles));
|
||||
// treat bigger particles as 'closer'
|
||||
adjusted_dist /= info->rad;
|
||||
adjusted_dist /= new_particle.radius;
|
||||
float cull_start_dist = 1000.f;
|
||||
if (adjusted_dist > cull_start_dist) {
|
||||
if (frand() > 1.0f / (log2(adjusted_dist / cull_start_dist) + 1.0f))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int fps = 1;
|
||||
if (new_particle.nframes >= 0 && new_particle.bitmap < 0)
|
||||
return true;
|
||||
|
||||
part->pos = info->pos;
|
||||
part->velocity = info->vel;
|
||||
part->age = info->starting_age;
|
||||
part->max_life = info->lifetime;
|
||||
part->radius = info->rad;
|
||||
part->bitmap = info->bitmap;
|
||||
part->attached_objnum = info->attached_objnum;
|
||||
part->attached_sig = info->attached_sig;
|
||||
part->reverse = info->reverse;
|
||||
part->looping = false;
|
||||
part->length = info->length;
|
||||
part->angle = frand_range(0.0f, PI2);
|
||||
part->use_angle = info->use_angle;
|
||||
part->size_lifetime_curve = info->size_lifetime_curve;
|
||||
part->vel_lifetime_curve = info->vel_lifetime_curve;
|
||||
|
||||
if (info->nframes < 0) {
|
||||
Assertion(bm_is_valid(info->bitmap), "Invalid bitmap handle passed to particle create.");
|
||||
|
||||
bm_get_info(info->bitmap, nullptr, nullptr, nullptr, &part->nframes, &fps);
|
||||
|
||||
if (part->nframes > 1 && info->lifetime_from_animation)
|
||||
{
|
||||
// Recalculate max life for ani's
|
||||
part->max_life = i2fl(part->nframes) / i2fl(fps);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (part->bitmap < 0)
|
||||
return false;
|
||||
|
||||
part->nframes = info->nframes;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void create(particle_info* pinfo) {
|
||||
particle part;
|
||||
if (!init_particle(&part, pinfo)) {
|
||||
void create(particle&& new_particle) {
|
||||
if (maybe_cull_particle(new_particle))
|
||||
return;
|
||||
}
|
||||
|
||||
Particles.push_back(std::move(part));
|
||||
Particles.push_back(new_particle);
|
||||
}
|
||||
|
||||
// Creates a single particle. See the PARTICLE_?? defines for types.
|
||||
WeakParticlePtr createPersistent(particle_info* pinfo)
|
||||
WeakParticlePtr createPersistent(particle&& new_particle)
|
||||
{
|
||||
ParticlePtr new_particle = std::make_shared<particle>();
|
||||
if (maybe_cull_particle(new_particle))
|
||||
return {};
|
||||
|
||||
if (!init_particle(new_particle.get(), pinfo)) {
|
||||
return WeakParticlePtr();
|
||||
}
|
||||
ParticlePtr new_particle_ptr = std::make_shared<particle>(new_particle);
|
||||
|
||||
Persistent_particles.push_back(new_particle);
|
||||
Persistent_particles.push_back(new_particle_ptr);
|
||||
|
||||
return WeakParticlePtr(new_particle);
|
||||
}
|
||||
|
||||
void create(const vec3d* pos,
|
||||
const vec3d* vel,
|
||||
float lifetime,
|
||||
float rad,
|
||||
int bitmap,
|
||||
const object* objp,
|
||||
bool reverse) {
|
||||
particle_info pinfo;
|
||||
|
||||
// setup old data
|
||||
pinfo.pos = *pos;
|
||||
pinfo.vel = *vel;
|
||||
pinfo.lifetime = lifetime;
|
||||
pinfo.rad = rad;
|
||||
pinfo.bitmap = bitmap;
|
||||
pinfo.nframes = -1;
|
||||
|
||||
// setup new data
|
||||
if (objp == NULL) {
|
||||
pinfo.attached_objnum = -1;
|
||||
pinfo.attached_sig = -1;
|
||||
} else {
|
||||
pinfo.attached_objnum = OBJ_INDEX(objp);
|
||||
pinfo.attached_sig = objp->signature;
|
||||
}
|
||||
pinfo.reverse = reverse;
|
||||
|
||||
// lower level function
|
||||
create(&pinfo);
|
||||
return {new_particle_ptr};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,10 +230,9 @@ namespace particle
|
||||
return true;
|
||||
}
|
||||
|
||||
float vel_scalar = 1.0f;
|
||||
if (part->vel_lifetime_curve >= 0) {
|
||||
vel_scalar = Curves[part->vel_lifetime_curve].GetValue(part->age / part->max_life);
|
||||
}
|
||||
const auto& source_effect = part->parent_effect.getParticleEffect();
|
||||
|
||||
float vel_scalar = source_effect.m_lifetime_curves.get_output(ParticleEffect::ParticleLifetimeCurvesOutput::VELOCITY_MULT, std::forward_as_tuple(*part, vm_vec_mag_quick(&part->velocity)) );
|
||||
|
||||
// move as a regular particle
|
||||
part->pos += (part->velocity * vel_scalar) * frametime;
|
||||
@@ -406,12 +343,23 @@ namespace particle
|
||||
|
||||
g3_transfer_vertex(&pos, &p_pos);
|
||||
|
||||
const auto& source_effect = part->parent_effect.getParticleEffect();
|
||||
|
||||
//For anything apart from the velocity curve, "Post-Curves Velocity" is well defined. This is needed to facilitate complex but common particle scaling and appearance curves.
|
||||
const auto& curve_input = std::forward_as_tuple(*part,
|
||||
vm_vec_mag_quick(&part->velocity) * source_effect.m_lifetime_curves.get_output(ParticleEffect::ParticleLifetimeCurvesOutput::ANIM_STATE, std::forward_as_tuple(*part, vm_vec_mag_quick(&part->velocity))));
|
||||
|
||||
// figure out which frame we should be using
|
||||
int framenum;
|
||||
int cur_frame;
|
||||
if (part->nframes > 1) {
|
||||
framenum = bm_get_anim_frame(part->bitmap, part->age, part->max_life, part->looping);
|
||||
cur_frame = part->reverse ? (part->nframes - framenum - 1) : framenum;
|
||||
if (source_effect.m_lifetime_curves.has_curve(ParticleEffect::ParticleLifetimeCurvesOutput::ANIM_STATE)) {
|
||||
cur_frame = fl2i(i2fl(part->nframes - 1) * source_effect.m_lifetime_curves.get_output(ParticleEffect::ParticleLifetimeCurvesOutput::ANIM_STATE, curve_input));
|
||||
}
|
||||
else {
|
||||
framenum = bm_get_anim_frame(part->bitmap, part->age, part->max_life, part->looping);
|
||||
cur_frame = part->reverse ? (part->nframes - framenum - 1) : framenum;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -422,10 +370,7 @@ namespace particle
|
||||
|
||||
Assert( cur_frame < part->nframes );
|
||||
|
||||
float radius = part->radius;
|
||||
if (part->size_lifetime_curve >= 0) {
|
||||
radius *= Curves[part->size_lifetime_curve].GetValue(part->age / part->max_life);
|
||||
}
|
||||
float radius = part->radius * source_effect.m_lifetime_curves.get_output(ParticleEffect::ParticleLifetimeCurvesOutput::VELOCITY_MULT, curve_input);
|
||||
|
||||
if (part->length != 0.0f) {
|
||||
vec3d p0 = p_pos;
|
||||
@@ -435,7 +380,7 @@ namespace particle
|
||||
if (part->attached_objnum >= 0) {
|
||||
vm_vec_unrotate(&p1, &p1, &Objects[part->attached_objnum].orient);
|
||||
}
|
||||
p1 *= part->length;
|
||||
p1 *= part->length * source_effect.m_lifetime_curves.get_output(ParticleEffect::ParticleLifetimeCurvesOutput::LENGTH_MULT, curve_input);
|
||||
p1 += p_pos;
|
||||
|
||||
batching_add_laser(framenum + cur_frame, &p0, radius, &p1, radius);
|
||||
|
||||
+17
-42
@@ -21,6 +21,19 @@ extern bool Randomize_particle_rotation;
|
||||
|
||||
namespace particle
|
||||
{
|
||||
|
||||
class ParticleEffect;
|
||||
struct particle_effect_tag {
|
||||
};
|
||||
using ParticleEffectHandle = ::util::ID<particle_effect_tag, ptrdiff_t, -1>;
|
||||
|
||||
struct ParticleSubeffectHandle {
|
||||
ParticleEffectHandle handle;
|
||||
size_t subeffect;
|
||||
|
||||
const ParticleEffect& getParticleEffect() const;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
//==================== PARTICLE SYSTEM GAME SEQUENCING CODE ==================
|
||||
//============================================================================
|
||||
@@ -56,29 +69,6 @@ namespace particle
|
||||
extern int Anim_bitmap_id_smoke2;
|
||||
extern int Anim_num_frames_smoke2;
|
||||
|
||||
// particle creation stuff
|
||||
typedef struct particle_info {
|
||||
// old-style particle info
|
||||
vec3d pos = vmd_zero_vector;
|
||||
vec3d vel = vmd_zero_vector;
|
||||
float lifetime = -1.0f;
|
||||
float starting_age = 0.0f;
|
||||
float rad = -1.0f;
|
||||
int bitmap = -1;
|
||||
int nframes = -1;
|
||||
|
||||
// new-style particle info
|
||||
int attached_objnum = -1; // if these are set, the pos is relative to the pos of the origin of the attached object
|
||||
int attached_sig = -1; // to make sure the object hasn't changed or died. velocity is ignored in this case
|
||||
bool reverse = false; // play any animations in reverse
|
||||
bool lifetime_from_animation = true; // if the particle plays an animation then use the anim length for the particle life
|
||||
float length = 0.f; // if set, makes the particle render like a laser, oriented along its path
|
||||
bool use_angle = Randomize_particle_rotation; // whether particles created from this will use angles (i.e. can be rotated)
|
||||
// (the field is initialized to the game_settings variable here because not all particle creation goes through ParticleProperties::createParticle)
|
||||
int size_lifetime_curve = -1; // a curve idx for size over lifetime, if applicable
|
||||
int vel_lifetime_curve = -1; // a curve idx for velocity over lifetime, if applicable
|
||||
} particle_info;
|
||||
|
||||
typedef struct particle {
|
||||
// old style data
|
||||
vec3d pos; // position
|
||||
@@ -97,8 +87,8 @@ namespace particle
|
||||
float length; // the length of the particle for laser-style rendering
|
||||
float angle;
|
||||
bool use_angle; // whether this particle can be rotated
|
||||
int size_lifetime_curve;// a curve idx for size over lifetime, if applicable
|
||||
int vel_lifetime_curve; // a curve idx for velocity over lifetime, if applicable
|
||||
|
||||
ParticleSubeffectHandle parent_effect;
|
||||
} particle;
|
||||
|
||||
typedef std::weak_ptr<particle> WeakParticlePtr;
|
||||
@@ -113,22 +103,7 @@ namespace particle
|
||||
*
|
||||
* @param pinfo A structure containg information about how the particle should be created
|
||||
*/
|
||||
void create(particle_info* pinfo);
|
||||
|
||||
/**
|
||||
* @brief Convenience function for creating a non-persistent particle without explicitly creating a particle_info
|
||||
* structure.
|
||||
* @return The particle handle
|
||||
*
|
||||
* @see particle::create(particle_info* pinfo)
|
||||
*/
|
||||
void create(const vec3d* pos,
|
||||
const vec3d* vel,
|
||||
float lifetime,
|
||||
float rad,
|
||||
int bitmap = -1,
|
||||
const object* objp = nullptr,
|
||||
bool reverse = false);
|
||||
void create(particle&& new_particle);
|
||||
|
||||
/**
|
||||
* @brief Creates a persistent particle
|
||||
@@ -140,7 +115,7 @@ namespace particle
|
||||
* @param pinfo A structure containg information about how the particle should be created
|
||||
* @return A weak reference to the particle
|
||||
*/
|
||||
WeakParticlePtr createPersistent(particle_info* pinfo);
|
||||
WeakParticlePtr createPersistent(particle&& new_particle);
|
||||
}
|
||||
|
||||
#endif // _PARTICLE_H
|
||||
|
||||
@@ -39,12 +39,26 @@ struct modular_curves_submember_input {
|
||||
}
|
||||
//Pointer to static variable, i.e. used to index into things.
|
||||
else if constexpr (std::is_pointer_v<decltype(grabber)>) {
|
||||
static_assert(std::is_integral_v<std::remove_cv_t<std::remove_reference_t<input_type>>>, "Can only index into array from an integral input");
|
||||
using indexing_type = std::decay_t<decltype((*grabber)[input])>;
|
||||
if (input >= 0)
|
||||
return std::optional<std::reference_wrapper<const indexing_type>>{ std::cref((*grabber)[input]) };
|
||||
else
|
||||
return std::optional<std::reference_wrapper<const indexing_type>>(std::nullopt);
|
||||
if constexpr (std::is_invocable_v<decltype(grabber), decltype(input)>) {
|
||||
//Global func by ref
|
||||
return grabber(input);
|
||||
}
|
||||
else if constexpr (is_dereferenceable_pointer_v<std::remove_reference_t<input_type>> && std::is_invocable_v<decltype(grabber), std::remove_pointer_t<std::decay_t<decltype(input)>>>) {
|
||||
//Global func by ref from ptr
|
||||
return grabber(*input);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<decltype(grabber), decltype(&input)>) {
|
||||
//Global func by ptr
|
||||
return grabber(&input);
|
||||
}
|
||||
else {
|
||||
static_assert(std::is_integral_v<std::remove_cv_t<std::remove_reference_t<input_type>>>, "Can only index into array from an integral input");
|
||||
using indexing_type = std::decay_t<decltype((*grabber)[input])>;
|
||||
if (input >= 0)
|
||||
return std::optional<std::reference_wrapper<const indexing_type>>{std::cref((*grabber)[input])};
|
||||
else
|
||||
return std::optional<std::reference_wrapper<const indexing_type>>(std::nullopt);
|
||||
}
|
||||
}
|
||||
//Integer, used to index into tuples. Should be rarely used by actual users, but is required to do child-types.
|
||||
else if constexpr (std::is_integral_v<decltype(grabber)>) {
|
||||
|
||||
Reference in new issue
Block a user