mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
Add render path to dispatch Particles as Decals (#7507)
* Add path to dispatch particles as decals * Testing and bugfixing * Address feedback * Fix Typo * Fix obj attachment
This commit is contained in:
1 parent
446e8942ca
commit
856e676e79
6 files changed
+142
-30
No files matched your search
+24
-2
@@ -361,6 +361,10 @@ void pageInDecal(const creation_info& info) {
|
||||
DecalDefinitions[info.definition_handle].pageIn();
|
||||
}
|
||||
|
||||
bool decalSystemActive() {
|
||||
return Decal_system_active && Decal_option_active && gr_is_capable(gr_capability::CAPABILITY_INSTANCED_RENDERING);
|
||||
}
|
||||
|
||||
void initializeMission() {
|
||||
active_decals.clear();
|
||||
}
|
||||
@@ -459,7 +463,7 @@ inline static void renderDecal(graphics::decal_draw_list& draw_list, const Decal
|
||||
}
|
||||
|
||||
void renderAll() {
|
||||
if (!Decal_system_active || !Decal_option_active || !gr_is_capable(gr_capability::CAPABILITY_INSTANCED_RENDERING)) {
|
||||
if (!decalSystemActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -482,6 +486,24 @@ void renderAll() {
|
||||
++iter;
|
||||
}
|
||||
|
||||
for (auto iter = active_single_frame_decals.begin(); iter != active_single_frame_decals.end();) {
|
||||
if (!iter->isValid()) {
|
||||
// if we're sitting on the very last element, popping-back will invalidate the iterator!
|
||||
if (iter + 1 == active_single_frame_decals.end()) {
|
||||
active_single_frame_decals.pop_back();
|
||||
break;
|
||||
}
|
||||
|
||||
*iter = active_single_frame_decals.back();
|
||||
active_single_frame_decals.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// next decal, only increment the iterator if we found a valid value so nothing gets skipped
|
||||
// otherwise we may skip a decal which can then get through to the draw_list loop while being invalid
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (active_decals.empty() && active_single_frame_decals.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -542,7 +564,7 @@ void addDecal(creation_info& info, const object* host, int submodel, const vec3d
|
||||
}
|
||||
|
||||
void addSingleFrameDecal(Decal&& info) {
|
||||
active_single_frame_decals.push_back(info);
|
||||
active_single_frame_decals.push_back(std::move(info));
|
||||
}
|
||||
|
||||
void invalidateForShip(const ship* shipp) {
|
||||
|
||||
@@ -133,6 +133,8 @@ void loadBitmaps(const creation_info& info);
|
||||
*/
|
||||
void pageInDecal(const creation_info& info);
|
||||
|
||||
bool decalSystemActive();
|
||||
|
||||
/**
|
||||
* @brief Shut down the decal system. Must be called from game_shutdown
|
||||
*/
|
||||
|
||||
@@ -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),
|
||||
@@ -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_parent_is_transitive(true),
|
||||
m_bitmap_list({}),
|
||||
m_bitmap_range(::util::UniformRange<size_t>(0)),
|
||||
@@ -88,6 +91,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),
|
||||
@@ -98,6 +102,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_parent_is_transitive(true),
|
||||
m_bitmap_list({bitmap}),
|
||||
m_bitmap_range(::util::UniformRange<size_t>(0)),
|
||||
|
||||
@@ -69,6 +69,10 @@ public:
|
||||
SCREEN_ALIGNED
|
||||
};
|
||||
|
||||
enum class DecalOrientationMode : uint8_t {
|
||||
TOWARDS_CENTER
|
||||
};
|
||||
|
||||
enum class ParticleCurvesOutput : uint8_t {
|
||||
PARTICLE_NUM_MULT,
|
||||
PARTICLE_FREQ_MULT,
|
||||
@@ -131,6 +135,7 @@ public:
|
||||
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
|
||||
|
||||
@@ -140,6 +145,7 @@ public:
|
||||
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;
|
||||
@@ -151,6 +157,8 @@ public:
|
||||
bool m_vel_inherit_from_position_absolute;
|
||||
bool m_reverseAnimation;
|
||||
bool m_ignore_velocity_inherit_if_has_parent;
|
||||
bool m_renderAsDecal;
|
||||
bool m_decalEmissive;
|
||||
bool m_parent_is_transitive;
|
||||
|
||||
SCP_vector<int> m_bitmap_list;
|
||||
|
||||
@@ -348,6 +348,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:");
|
||||
}
|
||||
@@ -410,6 +439,7 @@ namespace particle {
|
||||
parseLifetime(effect);
|
||||
parseLocalPositionScaling(effect);
|
||||
parseParentLocal(effect);
|
||||
parseRenderAsDecal(effect);
|
||||
parseLightEmissionSettings(effect);
|
||||
|
||||
//Spawner Settings
|
||||
|
||||
+72
-28
@@ -10,6 +10,7 @@
|
||||
|
||||
|
||||
#include "bmpman/bmpman.h"
|
||||
#include "decals/decals.h"
|
||||
#include "particle/particle.h"
|
||||
|
||||
#include "freespace.h"
|
||||
@@ -387,26 +388,90 @@ namespace particle
|
||||
/**
|
||||
* @brief Renders a single particle
|
||||
* @param part The particle to render
|
||||
* @return @c true if the particle has been added to the rendering batch, @c false otherwise
|
||||
* @return @c true if the particle has been added to the rendering batch (notably, this only includes main-render pass, alternative dispatch through decals is not true), @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 = part->attachment.local_pos_to_global(part->pos);
|
||||
|
||||
bool part_has_length = part->length != 0.0f;
|
||||
|
||||
if (!part_has_length && vm_vec_dot_to_point(&Eye_matrix.vec.fvec, &Eye_position, &p_pos) <= 0.0f)
|
||||
const auto& source_effect = part->parent_effect.getParticleEffect();
|
||||
|
||||
if (!source_effect.m_renderAsDecal && !part_has_length && vm_vec_dot_to_point(&Eye_matrix.vec.fvec, &Eye_position, &p_pos) <= 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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::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 (!decals::decalSystemActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& obj = part->attachment.extract_object();
|
||||
|
||||
if (!obj || obj->objnum < 0 || Objects[obj->objnum].signature != obj->sig || Objects[obj->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[obj->objnum];
|
||||
decalInfo.submodel = -1;
|
||||
decalInfo.creation_time = f2fl(Missiontime);
|
||||
decalInfo.lifetime = 1.0f;
|
||||
decalInfo.position = part->pos;
|
||||
decalInfo.scale = {{{ radius, radius, radius }}};
|
||||
decalInfo.orig_obj_type = OBJ_SHIP;
|
||||
|
||||
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) {
|
||||
@@ -450,36 +515,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;
|
||||
|
||||
Reference in new issue
Block a user