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/particle/ParticleEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ParticleEffect::ParticleEffect(SCP_string name)
m_rotation_type(RotationType::DEFAULT),
m_direction(ShapeDirection::ALIGNED),
m_velocity_directional_scaling(VelocityScaling::NONE),
m_decalOrientationMode(DecalOrientationMode::TOWARDS_CENTER),
m_affectedByDetail(false),
m_parentLifetime(false),
m_parentScale(false),
Expand All @@ -26,6 +27,8 @@ ParticleEffect::ParticleEffect(SCP_string name)
m_vel_inherit_from_position_absolute(false),
m_reverseAnimation(false),
m_ignore_velocity_inherit_if_has_parent(false),
m_renderAsDecal(false),
m_decalEmissive(false),
m_bitmap_list({}),
m_bitmap_range(::util::UniformRange<size_t>(0)),
m_delayRange(::util::UniformFloatRange(0.0f)),
Expand Down Expand Up @@ -86,6 +89,7 @@ ParticleEffect::ParticleEffect(SCP_string name,
m_rotation_type(RotationType::DEFAULT),
m_direction(direction),
m_velocity_directional_scaling(velocity_directional_scaling),
m_decalOrientationMode(DecalOrientationMode::TOWARDS_CENTER),
m_affectedByDetail(affectedByDetail),
m_parentLifetime(false),
m_parentScale(false),
Expand All @@ -96,6 +100,8 @@ ParticleEffect::ParticleEffect(SCP_string name,
m_vel_inherit_from_position_absolute(velInheritFromPositionAbsolute),
m_reverseAnimation(reverseAnimation),
m_ignore_velocity_inherit_if_has_parent(ignoreVelocityInheritIfParented),
m_renderAsDecal(false),
m_decalEmissive(false),
m_bitmap_list({bitmap}),
m_bitmap_range(::util::UniformRange<size_t>(0)),
m_delayRange(::util::UniformFloatRange(0.0f)),
Expand Down
8 changes: 8 additions & 0 deletions code/particle/ParticleEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ class ParticleEffect {
SCREEN_ALIGNED
};

enum class DecalOrientationMode : uint8_t {
TOWARDS_CENTER
};

enum class ParticleCurvesOutput : uint8_t {
PARTICLE_NUM_MULT,
PARTICLE_FREQ_MULT,
Expand Down Expand Up @@ -130,6 +134,7 @@ class ParticleEffect {
friend int ::parse_weapon(int, bool, const char*);
friend ParticleEffectHandle scripting::api::getLegacyScriptingParticleEffect(int bitmap, bool reversed);
friend bool move_particle(float frametime, particle* part);
friend bool render_particle(particle* part);

SCP_string m_name; //!< The name of this effect

Expand All @@ -139,6 +144,7 @@ class ParticleEffect {
RotationType m_rotation_type;
ShapeDirection m_direction;
VelocityScaling m_velocity_directional_scaling;
DecalOrientationMode m_decalOrientationMode;

bool m_affectedByDetail; //Kinda deprecated. Only used by the oldest of legacy effects.
bool m_parentLifetime;
Expand All @@ -150,6 +156,8 @@ class ParticleEffect {
bool m_vel_inherit_from_position_absolute;
bool m_reverseAnimation;
bool m_ignore_velocity_inherit_if_has_parent;
bool m_renderAsDecal;
bool m_decalEmissive;

SCP_vector<int> m_bitmap_list;
::util::UniformRange<size_t> m_bitmap_range;
Expand Down
30 changes: 30 additions & 0 deletions code/particle/ParticleParse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,35 @@ namespace particle {
}
}

static void parseRenderAsDecal(ParticleEffect &effect) {
if (optional_string("$Render As Decal:")) {
stuff_boolean(&effect.m_renderAsDecal);

if (effect.m_renderAsDecal) {
if (!effect.m_parent_local) {
error_display(0, "Particle must be local to parent for decal rendering. Disabling decal rendering.");
effect.m_renderAsDecal = false;
//We still parse the rest to allow parsing to properly complete for the other effects.
}

if (optional_string("+Orientation Mode:")) {
switch (required_string_one_of(1, "Towards Center")) {
case 0:
required_string("Towards Center");
effect.m_decalOrientationMode = ParticleEffect::DecalOrientationMode::TOWARDS_CENTER;
break;
default:
UNREACHABLE("Unexpected orientation mode");
}
}

if (optional_string("+Emissive:")) {
stuff_boolean(&effect.m_decalEmissive);
}
}
}
}

static void parseModularCurvesLifetime(ParticleEffect& effect) {
effect.m_lifetime_curves.parse("$Particle Lifetime Curve:");
}
Expand Down Expand Up @@ -386,6 +415,7 @@ namespace particle {
parseLifetime(effect);
parseLocalPositionScaling(effect);
parseParentLocal(effect);
parseRenderAsDecal(effect);
parseLightEmissionSettings(effect);

//Spawner Settings
Expand Down
87 changes: 62 additions & 25 deletions code/particle/particle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@


#include "bmpman/bmpman.h"
#include "decals/decals.h"
#include "particle/particle.h"
#include "particle/ParticleManager.h"
#include "particle/ParticleEffect.h"
Expand Down Expand Up @@ -408,7 +409,7 @@ namespace particle
* @param part The particle to render
* @return @c true if the particle has been added to the rendering batch, @c false otherwise
*/
static bool render_particle(particle* part) {
bool render_particle(particle* part) {
// skip back-facing particles (ripped from fullneb code)
// Wanderer - add support for attached particles
vec3d p_pos;
Expand All @@ -434,7 +435,64 @@ namespace particle
//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::VELOCITY_MULT, 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) {
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
{
cur_frame = 0;
}

framenum = part->bitmap;
Assert( (cur_frame < part->nframes) || (part->nframes == 0 && cur_frame == 0) );

int actual_frame = cur_frame + framenum;

if (source_effect.m_renderAsDecal) {
if (part->attached_objnum < 0 || Objects[part->attached_objnum].type != OBJ_SHIP) {
return false;
}

float radius = part->radius * source_effect.m_lifetime_curves.get_output(ParticleEffect::ParticleLifetimeCurvesOutput::RADIUS_MULT, curve_input);

decals::Decal decalInfo;

if (source_effect.m_decalEmissive) {
decalInfo.definition_handle = std::tuple(-1, actual_frame, -1);
} else {
decalInfo.definition_handle = std::tuple(actual_frame, -1, -1);
}

decalInfo.object = &Objects[part->attached_objnum];
decalInfo.submodel = -1;
decalInfo.creation_time = f2fl(Missiontime);
decalInfo.lifetime = 1.0f;
decalInfo.position = part->pos;
decalInfo.scale = {{{ radius, radius, radius }}};

switch (source_effect.m_decalOrientationMode) {
case ParticleEffect::DecalOrientationMode::TOWARDS_CENTER:
vm_vector_2_matrix(&decalInfo.orientation, &part->pos, nullptr, nullptr);
break;
default:
decalInfo.orientation = vmd_identity_matrix;
break;
}

decals::addSingleFrameDecal(std::move(decalInfo));
return false;
}

vec3d p1 = vmd_x_vector;

if (part_has_length) {
Expand Down Expand Up @@ -480,36 +538,15 @@ namespace particle

g3_transfer_vertex(&pos, &p_pos);

// figure out which frame we should be using
int framenum;
int cur_frame;
if (part->nframes > 1) {
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
{
cur_frame = 0;
}

framenum = part->bitmap;

Assert( (cur_frame < part->nframes) || (part->nframes == 0 && cur_frame == 0) );

float radius = part->radius * source_effect.m_lifetime_curves.get_output(ParticleEffect::ParticleLifetimeCurvesOutput::RADIUS_MULT, curve_input);

if (part_has_length) {
vec3d p0 = p_pos;
batching_add_laser(framenum + cur_frame, &p0, radius, &p1, radius);
batching_add_laser(actual_frame, &p0, radius, &p1, radius);
}
else {
// it will subtract Physics_viewer_bank, so without the flag we counter that and make it screen-aligned again
batching_add_volume_bitmap_rotated(framenum + cur_frame, &pos, part->use_angle ? part->angle : Physics_viewer_bank, radius, alpha);
batching_add_volume_bitmap_rotated(actual_frame, &pos, part->use_angle ? part->angle : Physics_viewer_bank, radius, alpha);
}

return true;
Expand Down
Loading