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:
BMagnu authored and GitHub committed 2025-09-15 04:42:06 +02:00
1 parent b0166dd6a6
commit 1e7431a044
10 files changed
+165 -209

No files matched your search

+2
View File
@@ -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'
...
+33 -26
View File
@@ -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));
}
}
}
+33 -5
View File
@@ -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;
+6 -2
View File
@@ -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() {
+3 -16
View File
@@ -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("")});
}
}
-5
View File
@@ -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
+8 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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
+20 -6
View File
@@ -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)>) {