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
1 change: 1 addition & 0 deletions code/graphics/2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ class vertex_buffer
size_t stride;
size_t vertex_offset;
size_t vertex_num_offset;
size_t n_verts;

poly_list *model_list;

Expand Down
1 change: 1 addition & 0 deletions code/model/model_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Model {
Is_live_debris, // whether current submodel is a live debris model
Is_thruster, // is an engine thruster submodel
Is_damaged, // is a submodel that represents a damaged submodel (e.g. a -destroyed version of some other submodel)
Is_lod, // is a submodel that is a lower LOD of a different submodel
Do_not_scale_detail_distances, // if set should not scale boxes or spheres based on 'model detail' settings
Gun_rotation, // for animated weapon models
Instant_rotate_accel, // rotating submodels instantly reach their desired velocity
Expand Down
8 changes: 7 additions & 1 deletion code/model/modelread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ SCP_vector<bsp_collision_tree> Bsp_collision_tree_list;

const ubyte* Macro_ubyte_bounds = nullptr;

//If true, CPU-side vertex buffers are deleted once the model is on-GPU.
//This is typically desired for memory reasons, but will prevent certain type of particles.
bool Model_load_clear_CPU_buffers = true;

static int model_initted = 0;

#ifndef NDEBUG
Expand Down Expand Up @@ -1138,7 +1142,8 @@ void create_vertex_buffer(polymodel *pm, const model_read_deferred_tasks& deferr
interp_pack_vertex_buffers(pm, i);

// release temporary memory
pm->submodel[i].buffer.release();
if (Model_load_clear_CPU_buffers)
pm->submodel[i].buffer.release();
pm->submodel[i].trans_buffer.release();
}

Expand Down Expand Up @@ -3457,6 +3462,7 @@ int model_load(const char* filename, ship_info* sip, ErrorType error_type, bool
if (dl2 >= sm1->num_details ) sm1->num_details = dl2+1;
sm1->details[dl2] = j;
mprintf(( "Submodel '%s' is detail level %d of '%s'\n", sm2->name, dl2 + 1, sm1->name ));
sm2->flags.set(Model::Submodel_flags::Is_lod);
lower_to_higher_detail_submodels.emplace(sm2->name, sm1->name);
}
}
Expand Down
2 changes: 2 additions & 0 deletions code/model/modelrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ extern color Wireframe_color;

extern int Lab_object_detail_level;

extern bool Model_load_clear_CPU_buffers;

typedef enum {
TECH_SHIP,
TECH_WEAPON,
Expand Down
1 change: 1 addition & 0 deletions code/particle/EffectHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class EffectHost {
}

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

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

Expand Down
29 changes: 26 additions & 3 deletions code/particle/ParticleEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,28 @@ void ParticleEffect::sampleNoise(vec3d& noiseTarget, const matrix* orientation,
vm_vec_unrotate(&noiseTarget, &noiseSampleLocal, orientation);
}

vec3d ParticleEffect::adaptPosition(const vec3d& pos, int parent) const {
if (parent < 0 || !m_local_position_scaling.has_value()) {
return pos;
}

vec3d pos_local = pos;

if (!m_parent_local) {
pos_local -= Objects[parent].pos;
vm_vec_rotate(&pos_local, &pos_local, &Objects[parent].orient);
}

pos_local *= m_local_position_scaling->next();

if (!m_parent_local) {
vm_vec_unrotate(&pos_local, &pos_local, &Objects[parent].orient);
vm_vec_add2(&pos_local, &Objects[parent].pos);
}

return pos_local;
}

/*
* In persistent mode (should only ever be used by scripting, really), this function returns pointers to the persistent particles
* In non-persistent mode, this function returns the multiplier for the next spawn time. This is because the source cannot know about the curve evaluation that is required to get this factor
Expand Down Expand Up @@ -213,7 +235,8 @@ auto ParticleEffect::processSourceInternal(float interp, const ParticleSource& s
}
}

const auto& [pos, hostOrientation] = source.m_host->getPositionAndOrientation(m_parent_local, interp, m_manual_offset);
const auto& [pos_hit, hostOrientation] = source.m_host->getPositionAndOrientation(m_parent_local, interp, m_manual_offset);
const vec3d& pos = adaptPosition(pos_hit, parent);

vec3d posGlobal = pos;
if (m_parent_local && parent >= 0) {
Expand Down Expand Up @@ -290,11 +313,11 @@ auto ParticleEffect::processSourceInternal(float interp, const ParticleSource& s
vec3d localPos = posNoise;

if (m_spawnVolume != nullptr) {
localPos += m_spawnVolume->sampleRandomPoint(orientation, modularCurvesInput, particleFraction);
localPos += m_spawnVolume->sampleRandomPoint(orientation, modularCurvesInput, particleFraction, *source.m_host);
}

if (m_velocityVolume != nullptr) {
localVelocity += m_velocityVolume->sampleRandomPoint(orientation, modularCurvesInput, particleFraction) * (m_velocity_scaling.next() * velocityVolumeMultiplier);
localVelocity += m_velocityVolume->sampleRandomPoint(orientation, modularCurvesInput, particleFraction, *source.m_host) * (m_velocity_scaling.next() * velocityVolumeMultiplier);
}

if (m_manual_velocity_offset.has_value()) {
Expand Down
2 changes: 2 additions & 0 deletions code/particle/ParticleEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class ParticleEffect {

std::optional<::util::ParsedRandomFloatRange> m_vel_inherit_from_orientation;
std::optional<::util::ParsedRandomFloatRange> m_vel_inherit_from_position;
std::optional<::util::ParsedRandomFloatRange> m_local_position_scaling;

std::shared_ptr<::particle::ParticleVolume> m_velocityVolume;
std::shared_ptr<::particle::ParticleVolume> m_spawnVolume;
Expand All @@ -186,6 +187,7 @@ class ParticleEffect {
float m_distanceCulled; //Kinda deprecated. Only used by the oldest of legacy effects.

matrix getNewDirection(const matrix& hostOrientation, const std::optional<vec3d>& normal) const;
vec3d adaptPosition(const vec3d& pos, int parent) const;

template<bool isPersistent>
auto processSourceInternal(float interp, const ParticleSource& source, size_t effectNumber, const vec3d& velParent, int parent, int parent_sig, float parentLifetime, float parentRadius, float particle_percent) const;
Expand Down
17 changes: 15 additions & 2 deletions code/particle/ParticleParse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include <anl.h>

#include "volumes/ModelSurfaceVolume.h"

namespace particle {

//
Expand Down Expand Up @@ -92,6 +94,12 @@ namespace particle {
}
}

static void parseLocalPositionScaling(ParticleEffect& effect) {
if (optional_string("+Local position scaling:")) {
effect.m_local_position_scaling = ::util::ParsedRandomFloatRange::parseRandomRange();
}
}

static void parseParentLocal(ParticleEffect& effect) {
if (optional_string("+Remain local to parent:")) {
stuff_boolean(&effect.m_parent_local);
Expand Down Expand Up @@ -125,7 +133,7 @@ namespace particle {

static std::shared_ptr<ParticleVolume> parseVolume() {

int type = required_string_one_of(4, "Spheroid", "Cone", "Ring", "Point"); //... and future volumes
int type = required_string_one_of(5, "Spheroid", "Cone", "Ring", "Point", "ModelSurface"); //... and future volumes
std::shared_ptr<ParticleVolume> volume;

switch (type) {
Expand All @@ -145,6 +153,10 @@ namespace particle {
required_string("Point");
volume = std::make_shared<PointVolume>();
break;
case 4:
required_string("ModelSurface");
volume = std::make_shared<ModelSurfaceVolume>();
break;
default:
UNREACHABLE("Invalid volume type specified!");
}
Expand Down Expand Up @@ -372,6 +384,7 @@ namespace particle {
parseRadius(effect);
parseLength(effect);
parseLifetime(effect);
parseLocalPositionScaling(effect);
parseParentLocal(effect);
parseLightEmissionSettings(effect);

Expand Down Expand Up @@ -723,4 +736,4 @@ namespace particle {
"Sphere",
"Volume"
};
}
}
3 changes: 2 additions & 1 deletion code/particle/ParticleVolume.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

#include "globalincs/pstypes.h"
#include "parse/parselo.h"
#include "particle/EffectHost.h"

#include <optional>

namespace particle {
class ParticleSource;
class ParticleVolume {
public:
virtual vec3d sampleRandomPoint(const matrix &orientation, const std::tuple<const ParticleSource&, const size_t&, const vec3d&>& source, float particlesFraction) = 0;
virtual vec3d sampleRandomPoint(const matrix &orientation, const std::tuple<const ParticleSource&, const size_t&, const vec3d&>& source, float particlesFraction, const EffectHost& host) = 0;

virtual void parse() = 0;

Expand Down
4 changes: 4 additions & 0 deletions code/particle/hosts/EffectHostSubmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ std::pair<int, int> EffectHostSubmodel::getParentObjAndSig() const {
return { m_objnum, m_objsig };
}

int EffectHostSubmodel::getParentSubmodel() const {
return m_submodel;
}

float EffectHostSubmodel::getHostRadius() const {
return Objects[m_objnum].radius;
}
Expand Down
1 change: 1 addition & 0 deletions code/particle/hosts/EffectHostSubmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class EffectHostSubmodel : public EffectHost {
vec3d getVelocity() const override;

std::pair<int, int> getParentObjAndSig() const override;
int getParentSubmodel() const override;

float getHostRadius() const override;

Expand Down
4 changes: 4 additions & 0 deletions code/particle/hosts/EffectHostTurret.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ std::pair<int, int> EffectHostTurret::getParentObjAndSig() const {
return { m_objnum, m_objsig };
}

int EffectHostTurret::getParentSubmodel() const {
return m_submodel;
}

float EffectHostTurret::getHostRadius() const {
return Objects[m_objnum].radius;
}
Expand Down
1 change: 1 addition & 0 deletions code/particle/hosts/EffectHostTurret.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class EffectHostTurret : public EffectHost {
vec3d getVelocity() const override;

std::pair<int, int> getParentObjAndSig() const override;
int getParentSubmodel() const override;

float getHostRadius() const override;

Expand Down
2 changes: 1 addition & 1 deletion code/particle/volumes/ConeVolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace particle {
ConeVolume::ConeVolume(::util::ParsedRandomFloatRange deviation, ::util::ParsedRandomFloatRange length) : m_deviation(deviation), m_length(length), m_modular_curve_instance(m_modular_curves.create_instance()) { }


vec3d ConeVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction) {
vec3d ConeVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& /*host*/) {
auto curveSource = std::tuple_cat(source, std::make_tuple(particlesFraction));

//It is surely possible to do this more efficiently.
Expand Down
2 changes: 1 addition & 1 deletion code/particle/volumes/ConeVolume.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace particle {
explicit ConeVolume(::util::ParsedRandomFloatRange deviation, float length);
explicit ConeVolume(::util::ParsedRandomFloatRange deviation, ::util::ParsedRandomFloatRange length);

vec3d sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction) override;
vec3d sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& host) override;
void parse() override;
};
}
2 changes: 1 addition & 1 deletion code/particle/volumes/LegacyAACuboidVolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace particle {
LegacyAACuboidVolume::LegacyAACuboidVolume(float normalVariance, float size, bool normalize) : m_normalVariance(normalVariance), m_size(size), m_normalize(normalize), m_modular_curve_instance(m_modular_curves.create_instance()) { }

vec3d LegacyAACuboidVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction) {
vec3d LegacyAACuboidVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& /*host*/) {
auto curveSource = std::tuple_cat(source, std::make_tuple(particlesFraction));
float variance = m_normalVariance * m_modular_curves.get_output(VolumeModularCurveOutput::VARIANCE, curveSource, &m_modular_curve_instance);

Expand Down
2 changes: 1 addition & 1 deletion code/particle/volumes/LegacyAACuboidVolume.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace particle {
public:
explicit LegacyAACuboidVolume(float normalVariance, float size, bool normalize);

vec3d sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction) override;
vec3d sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& host) override;
void parse() override {
UNREACHABLE("Cannot parse Legacy Particle Volume!");
};
Expand Down
55 changes: 55 additions & 0 deletions code/particle/volumes/ModelSurfaceVolume.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "ModelSurfaceVolume.h"

#include "math/vecmat.h"

namespace particle {
ModelSurfaceVolume::ModelSurfaceVolume() : m_modelScale(::util::UniformFloatRange(1.f)), m_modular_curve_instance(m_modular_curves.create_instance()) {
Model_load_clear_CPU_buffers = false;
};

vec3d ModelSurfaceVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& host) {
int obj_num = host.getParentObjAndSig().first;
int submodel = host.getParentSubmodel();

vec3d point = ZERO_VECTOR;

auto curveSource = std::tuple_cat(source, std::make_tuple(particlesFraction));

if (obj_num >= 0) {
const polymodel* pm = object_get_model(&Objects[obj_num]);
if (pm != nullptr) {
if (submodel < 0) {
SCP_vector<int> eligible_submodels;
for (int i = 0; i < pm->n_models; ++i) {
if (!pm->submodel[i].flags[Model::Submodel_flags::Is_lod, Model::Submodel_flags::Is_damaged, Model::Submodel_flags::Is_live_debris])
eligible_submodels.emplace_back(i);
}
submodel = eligible_submodels[::util::UniformUIntRange(0U, static_cast<unsigned int>(eligible_submodels.size()) - 1).next()];
}

const bsp_info* submodel_data = &pm->submodel[submodel];
const auto& geometry_data = *submodel_data->buffer.model_list;
size_t target_vertex = ::util::UniformUIntRange(0U, static_cast<unsigned int>(geometry_data.n_verts) - 1).next();

//This point is, despite its name, not in world space, but in model local space (NOT submodel local though!)
point = geometry_data.vert[target_vertex].world;

point *= m_modelScale.next() * m_modular_curves.get_output(VolumeModularCurveOutput::SCALE_MULT, curveSource, &m_modular_curve_instance);
}
}

return pointCompensateForOffsetAndRotOffset(point, orientation,
m_modular_curves.get_output(VolumeModularCurveOutput::OFFSET_ROT, curveSource, &m_modular_curve_instance),
m_modular_curves.get_output(VolumeModularCurveOutput::POINT_TO_ROT, curveSource, &m_modular_curve_instance));
}

void ModelSurfaceVolume::parse() {
if (optional_string("+Scale:")) {
m_modelScale = ::util::ParsedRandomFloatRange::parseRandomRange();
}

ParticleVolume::parseCommon();

m_modular_curves.parse("$Volume Curve:");
}
}
31 changes: 31 additions & 0 deletions code/particle/volumes/ModelSurfaceVolume.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include "particle/ParticleVolume.h"
#include "particle/ParticleEffect.h"

namespace particle {
class ModelSurfaceVolume : public ParticleVolume {
::util::ParsedRandomFloatRange m_modelScale;
enum class VolumeModularCurveOutput : uint8_t {SCALE_MULT, OFFSET_ROT, POINT_TO_ROT, NUM_VALUES};

constexpr static auto modular_curve_definition = ParticleEffect::modular_curves_definition.derive_modular_curves_subset<float, VolumeModularCurveOutput>(
std::array {
std::pair { "Scale Mult", VolumeModularCurveOutput::SCALE_MULT },
std::pair { "Offset Rotate Around Fvec", VolumeModularCurveOutput::OFFSET_ROT },
std::pair { "Point To Rotate Around Fvec", VolumeModularCurveOutput::POINT_TO_ROT }
},
std::pair { "Fraction Particles Spawned", modular_curves_self_input{}});

public:
MODULAR_CURVE_SET(m_modular_curves, modular_curve_definition);

private:
modular_curves_entry_instance m_modular_curve_instance;

public:
explicit ModelSurfaceVolume();

vec3d sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& host) override;
void parse() override;
};
}
2 changes: 1 addition & 1 deletion code/particle/volumes/PointVolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace particle {
PointVolume::PointVolume() : m_modular_curve_instance(m_modular_curves.create_instance()) { };

vec3d PointVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction) {
vec3d PointVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& /*host*/) {
auto curveSource = std::tuple_cat(source, std::make_tuple(particlesFraction));

return pointCompensateForOffsetAndRotOffset(ZERO_VECTOR, orientation,
Expand Down
2 changes: 1 addition & 1 deletion code/particle/volumes/PointVolume.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace particle {
public:
explicit PointVolume();

vec3d sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction) override;
vec3d sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& host) override;
void parse() override;
};
}
2 changes: 1 addition & 1 deletion code/particle/volumes/RingVolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace particle {
RingVolume::RingVolume() : m_radius(1.f), m_onEdge(false), m_modular_curve_instance(m_modular_curves.create_instance()) { };
RingVolume::RingVolume(float radius, bool onEdge) : m_radius(radius), m_onEdge(onEdge), m_modular_curve_instance(m_modular_curves.create_instance()) { };

vec3d RingVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction) {
vec3d RingVolume::sampleRandomPoint(const matrix &orientation, decltype(ParticleEffect::modular_curves_definition)::input_type_t source, float particlesFraction, const EffectHost& /*host*/) {
auto curveSource = std::tuple_cat(source, std::make_tuple(particlesFraction));
vec3d pos;
// get an unbiased random point in the sphere
Expand Down
Loading
Loading