Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions code/globalincs/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,4 +517,10 @@ const T* coalesce(const T* possibly_null, const T* value_if_null)
return (possibly_null != nullptr) ? possibly_null : value_if_null;
}

template<typename... Ts>
struct overloads : Ts... {
constexpr overloads(Ts... t) : Ts(t)... {}
using Ts::operator()...;
};

#endif
244 changes: 244 additions & 0 deletions code/particle/EffectHost.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
#include "particle/EffectHost.h"

#include "freespace.h"
#include "ParticleEffect.h"
#include "globalincs/utility.h"
#include "particle/particle.h"

#include <variant>

namespace effects {
vec3d EffectAttachment::local_pos_to_global(const vec3d& local_pos, float interp) const {
return std::visit(overloads {
[&local_pos](const std::monostate&) {
return local_pos;
},
[&local_pos, interp](const attachment_object& obj) {
if (obj.objnum < 0)
return local_pos;

vec3d pos;
vm_vec_unrotate(&pos, &local_pos, &Objects[obj.objnum].orient);

vec3d parent_pos;
vm_vec_linear_interpolate(&parent_pos, &Objects[obj.objnum].pos, &Objects[obj.objnum].last_pos, interp);
pos += parent_pos;

return pos;
},
[&local_pos, interp, this](const attachment_particle& parent_part) {
const auto& parent = parent_part.particle.lock();
Assertion(!parent->parent_effect.getParticleEffect().m_parent_is_transitive, "Encountered live transitive parent in effect attachment.");
if (!parent)
return local_pos;

const auto& [parent_pos, parent_orient] = get_frame(interp);
vec3d pos;
vm_vec_unrotate(&pos, &local_pos, &parent_orient);

return pos + parent_pos;
},
}, m_variant);
}

vec3d EffectAttachment::global_pos_to_local(const vec3d& global_pos) const {
return std::visit(overloads {
[&global_pos](const std::monostate&) {
return global_pos;
},
[&global_pos](const attachment_object& obj) {
if (obj.objnum < 0)
return global_pos;

vec3d pos = global_pos - Objects[obj.objnum].pos;
vm_vec_rotate(&pos, &pos, &Objects[obj.objnum].orient);

return pos;
},
[&global_pos, this](const attachment_particle& parent_part) {
const auto& parent = parent_part.particle.lock();
Assertion(!parent->parent_effect.getParticleEffect().m_parent_is_transitive, "Encountered live transitive parent in effect attachment.");
if (!parent)
return global_pos;

const auto& [parent_pos, parent_orient] = get_frame();
vec3d pos = global_pos - parent_pos;
vm_vec_rotate(&pos, &pos, &parent_orient);

return pos;
},
}, m_variant);
}

vec3d EffectAttachment::local_vel_to_global(const vec3d& local_vel) const {
return std::visit(overloads {
[&local_vel](const std::monostate&) {
return local_vel;
},
[&local_vel](const attachment_object& obj) {
if (obj.objnum < 0)
return local_vel;

vec3d vel;
vm_vec_unrotate(&vel, &local_vel, &Objects[obj.objnum].orient);
return vel;
},
[&local_vel, this](const attachment_particle& parent_part) {
const auto& parent = parent_part.particle.lock();
Assertion(!parent->parent_effect.getParticleEffect().m_parent_is_transitive, "Encountered live transitive parent in effect attachment.");
if (!parent)
return local_vel;

const auto& [parent_pos, parent_orient] = get_frame();
vec3d vel;
vm_vec_unrotate(&vel, &local_vel, &parent_orient);

return vel + parent->attachment.local_vel_to_global(parent->velocity);
},
}, m_variant);
}

vec3d EffectAttachment::global_vel_to_local(const vec3d& global_vel) const {
return std::visit(overloads {
[&global_vel](const std::monostate&) {
return global_vel;
},
[&global_vel](const attachment_object& obj) {
if (obj.objnum < 0)
return global_vel;

vec3d vel;
vm_vec_rotate(&vel, &global_vel, &Objects[obj.objnum].orient);
return vel;
},
[&global_vel, this](const attachment_particle& parent_part) {
const auto& parent = parent_part.particle.lock();
Assertion(!parent->parent_effect.getParticleEffect().m_parent_is_transitive, "Encountered live transitive parent in effect attachment.");
if (!parent)
return global_vel;

const auto& [parent_pos, parent_orient] = get_frame();
vec3d relative_vel = global_vel - parent->attachment.local_vel_to_global(parent->velocity);
vm_vec_rotate(&relative_vel, &relative_vel, &parent_orient);

return relative_vel;
},
}, m_variant);
}

vec3d EffectAttachment::local_last_pos_to_global(const vec3d& last_pos) const {
return std::visit(overloads{
[&last_pos](const std::monostate&) {
return last_pos;
},
[&last_pos](const attachment_object& obj) {
vec3d pos = last_pos;
if (obj.objnum >= 0) {
vm_vec_unrotate(&pos, &pos, &Objects[obj.objnum].last_orient);
pos += Objects[obj.objnum].last_pos;
}
return pos;
},
[&last_pos, this](const attachment_particle& parent_part) {
const auto& parent = parent_part.particle.lock();
Assertion(!parent->parent_effect.getParticleEffect().m_parent_is_transitive, "Encountered live transitive parent in effect attachment.");
if (!parent)
return last_pos;

const auto& [parent_pos, parent_orient] = get_frame();
vec3d pos;
vm_vec_unrotate(&pos, &last_pos, &parent_orient);

float vel_scalar = parent->parent_effect.getParticleEffect().m_lifetime_curves.get_output(particle::ParticleEffect::ParticleLifetimeCurvesOutput::VELOCITY_MULT, std::forward_as_tuple(*parent, vm_vec_mag_quick(&parent->velocity)));
return pos + parent_pos - parent->attachment.local_vel_to_global(parent->velocity) * flFrametime * vel_scalar;
},
}, m_variant);
}

bool EffectAttachment::is_valid() const {
return std::visit(overloads{
[](const std::monostate&) {
return true;
},
[](const effects::attachment_object& obj) {
return obj.objnum >= 0 && obj.objnum < MAX_OBJECTS && Objects[obj.objnum].signature == obj.sig;
},
[](const effects::attachment_particle& parent_part) {
return !parent_part.particle.expired();
},
}, m_variant);
}

bool EffectAttachment::is_not_attached() const {
return std::holds_alternative<std::monostate>(m_variant);
}

std::pair<vec3d, matrix> EffectAttachment::get_frame(float interp) const {
return std::visit(overloads{
[](const std::monostate&) -> std::pair<vec3d, matrix> {
return {ZERO_VECTOR, vmd_identity_matrix};
},
[interp](const effects::attachment_object& obj) -> std::pair<vec3d, matrix> {
if (obj.objnum < 0)
return {ZERO_VECTOR, vmd_identity_matrix};
vec3d pos;
vm_vec_linear_interpolate(&pos, &Objects[obj.objnum].pos, &Objects[obj.objnum].last_pos, interp);
return {pos, Objects[obj.objnum].orient};
},
[interp](const effects::attachment_particle& parent_part) -> std::pair<vec3d, matrix> {
const auto& parent = parent_part.particle.lock();
Assertion(!parent->parent_effect.getParticleEffect().m_parent_is_transitive, "Encountered live transitive parent in effect attachment.");
if (!parent)
return {ZERO_VECTOR, vmd_identity_matrix};

auto [parent_pos, orient] = parent->attachment.get_frame(interp);

vec3d parent_global_orient_local_velocity;
vm_vec_unrotate(&parent_global_orient_local_velocity, &parent->velocity, &orient);
float vel_scalar = parent->parent_effect.getParticleEffect().m_lifetime_curves.get_output(particle::ParticleEffect::ParticleLifetimeCurvesOutput::VELOCITY_MULT, std::forward_as_tuple(*parent, vm_vec_mag_quick(&parent->velocity)));

vec3d pos_rotated;
vm_vec_unrotate(&pos_rotated, &parent->pos, &orient);

return {parent_pos + pos_rotated - parent_global_orient_local_velocity * flFrametime * interp * vel_scalar, orient};
},
}, m_variant);
}

std::optional<attachment_object> EffectAttachment::extract_object() const {
return std::visit(overloads{
[](const std::monostate&) -> std::optional<attachment_object> {
return std::nullopt;
},
[](const attachment_object& obj) -> std::optional<attachment_object> {
return obj;
},
[](const attachment_particle&) -> std::optional<attachment_object> {
return std::nullopt;
},
}, m_variant);
}

EffectAttachment EffectAttachment::resolve_true_parent() const {
return std::visit(overloads{
[](const std::monostate&) -> EffectAttachment {
return {};
},
[](const attachment_object& obj) -> EffectAttachment {
return {obj};
},
[](const attachment_particle& parent_part) -> EffectAttachment {
const auto& parent = parent_part.particle.lock();
const auto& effect = parent->parent_effect.getParticleEffect();
if (effect.m_parent_is_transitive) {
if (parent->attachment.is_valid())
return parent->attachment.resolve_true_parent();
else
return {};
}
else
return {parent_part};
},
}, m_variant);
}
}
41 changes: 40 additions & 1 deletion code/particle/EffectHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,48 @@

#include "object/object.h"
#include "globalincs/pstypes.h"
#include "globalincs/utility.h"
#include "math/vecmat.h"

#include <optional>
#include <variant>

namespace particle {
struct particle;
typedef std::weak_ptr<particle> WeakParticlePtr;
}

namespace effects {
struct attachment_object {
int objnum = -1;
int sig = -1;
};
struct attachment_particle {
particle::WeakParticlePtr particle = particle::WeakParticlePtr();
};

struct EffectAttachment {
private:
using underlying_type = std::variant<std::monostate, attachment_object, attachment_particle>;

underlying_type m_variant;
public:
vec3d local_pos_to_global(const vec3d& local_pos, float interp = 0.0f) const;
vec3d global_pos_to_local(const vec3d& global_pos) const ;
vec3d local_vel_to_global(const vec3d& local_vel) const;
vec3d global_vel_to_local(const vec3d& global_vel) const;
vec3d local_last_pos_to_global(const vec3d& last_pos) const;
bool is_valid() const;
bool is_not_attached() const;
std::pair<vec3d, matrix> get_frame(float interp = 0.0f) const;
std::optional<attachment_object> extract_object() const;
EffectAttachment resolve_true_parent() const;

constexpr EffectAttachment() : m_variant(std::monostate()) {};
EffectAttachment(const underlying_type& variant) : m_variant(variant) {};
EffectAttachment(underlying_type&& variant) : m_variant(variant) {};
};
}

class EffectHost {

Expand All @@ -26,7 +65,7 @@ class EffectHost {
return vm_vec_mag_quick(&velocity);
}

virtual std::pair<int, int> getParentObjAndSig() const { return {-1, -1}; }
virtual effects::EffectAttachment getParentAttachment() const { return {}; }
virtual int getParentSubmodel() const { return -1; }

virtual float getLifetime() const { return -1.f; }
Expand Down
Loading
Loading