mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
Particle Scripting API (#7061)
* Expose particle effects to scripting * Allow creation of particle sources * Add API for particle sources * bugfixes
This commit is contained in:
1 parent
2c04313c59
commit
dc50aaa86b
5 files changed
+289
No files matched your search
@@ -38,6 +38,8 @@ void ParticleManager::shutdown() {
|
||||
ParticleSource* ParticleManager::createSource() {
|
||||
ParticleSource* source;
|
||||
|
||||
m_sourceValidityCounter++;
|
||||
|
||||
// If we are currently in the onFrame function, adding stuff to the vector would invalidate the iterator currently in use
|
||||
if (m_processingSources) {
|
||||
m_deferredSourceAdding.emplace_back();
|
||||
@@ -80,9 +82,12 @@ void ParticleManager::doFrame(float) {
|
||||
TRACE_SCOPE(tracing::ProcessParticleEffects);
|
||||
|
||||
m_processingSources = true;
|
||||
bool changehappened = false;
|
||||
|
||||
for (auto source = std::begin(m_sources); source != std::end(m_sources);) {
|
||||
if (!source->isValid() || !source->process()) {
|
||||
changehappened = true;
|
||||
|
||||
// if we're sitting on the very last source, popping-back will invalidate the iterator!
|
||||
if (std::next(source) == m_sources.end()) {
|
||||
m_sources.pop_back();
|
||||
@@ -102,9 +107,13 @@ void ParticleManager::doFrame(float) {
|
||||
m_processingSources = false;
|
||||
|
||||
for (auto& source : m_deferredSourceAdding) {
|
||||
changehappened = true;
|
||||
m_sources.push_back(std::move(source));
|
||||
}
|
||||
m_deferredSourceAdding.clear();
|
||||
|
||||
if (changehappened)
|
||||
m_sourceValidityCounter++;
|
||||
}
|
||||
|
||||
ParticleEffectHandle ParticleManager::addEffect(ParticleEffect&& effect)
|
||||
@@ -179,6 +188,10 @@ void ParticleManager::clearSources() {
|
||||
m_deferredSourceAdding.clear();
|
||||
}
|
||||
|
||||
uint32_t ParticleManager::getSourceValidityCounter() const {
|
||||
return m_sourceValidityCounter;
|
||||
}
|
||||
|
||||
namespace util {
|
||||
ParticleEffectHandle parseEffect(const SCP_string& objectName)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,8 @@ class ParticleManager {
|
||||
SCP_vector<ParticleSource> m_sources; //!< The currently active sources
|
||||
|
||||
bool m_processingSources = false; //!< @c true if sources are currently being processed
|
||||
|
||||
uint32_t m_sourceValidityCounter = 0;
|
||||
/**
|
||||
* If the sources are currently being processed, no additional sources can be added. Instead, they are added to this
|
||||
* vector and then added to the main vector when processing is done.
|
||||
@@ -148,6 +150,8 @@ class ParticleManager {
|
||||
* @return A wrapper class which allows access to the created sources
|
||||
*/
|
||||
ParticleSource* createSource(ParticleEffectHandle index);
|
||||
|
||||
uint32_t getSourceValidityCounter() const;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "scripting/api/objs/team_colors.h"
|
||||
#include "scripting/api/objs/weaponclass.h"
|
||||
#include "scripting/api/objs/wingformation.h"
|
||||
#include "scripting/api/objs/particle.h"
|
||||
|
||||
#include "decals/decals.h"
|
||||
#include "fireball/fireballs.h"
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "mission/missionmessage.h"
|
||||
#include "ship/ship.h"
|
||||
#include "weapon/weapon.h"
|
||||
#include "particle/ParticleManager.h"
|
||||
|
||||
|
||||
extern bool Ships_inited;
|
||||
@@ -373,5 +375,16 @@ ADE_FUNC(__len, l_Tables_TeamColors, nullptr, "Number of team colors", "number"
|
||||
return ade_set_args(L, "i", static_cast<int>(Team_Names.size()));
|
||||
}
|
||||
|
||||
//*****SUBLIBRARY: Tables/ShipTypes
|
||||
ADE_LIB_DERIV(l_Tables_ParticleEffects, "ParticleEffects", nullptr, nullptr, l_Tables);
|
||||
ADE_INDEXER(l_Tables_ParticleEffects, "string Name", "Array of particle effects", "particle_effect", "Particle Effect handle, or invalid handle if name is invalid")
|
||||
{
|
||||
const char* name;
|
||||
if (!ade_get_args(L, "*s", &name))
|
||||
return ade_set_error(L, "o", l_ParticleEffect.Set(particle::ParticleEffectHandle::invalid()));
|
||||
|
||||
return ade_set_args(L, "o", l_ParticleEffect.Set(particle::ParticleManager::get()->getEffectByName(name)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@
|
||||
#include "particle.h"
|
||||
#include "vecmath.h"
|
||||
#include "object.h"
|
||||
#include "model.h"
|
||||
#include "particle/ParticleManager.h"
|
||||
#include "particle/ParticleEffect.h"
|
||||
|
||||
namespace scripting {
|
||||
namespace api {
|
||||
@@ -20,6 +23,18 @@ bool particle_h::isValid() const {
|
||||
return !part.expired();
|
||||
}
|
||||
|
||||
particle_source_h::particle_source_h(particle::ParticleSource* source_p, uint32_t sourceValidityCounter_p) {
|
||||
this->source = source_p;
|
||||
this->sourceValidityCounter = sourceValidityCounter_p;
|
||||
}
|
||||
|
||||
particle::ParticleSource* particle_source_h::Get() const {
|
||||
return isValid() ? this->source : nullptr;
|
||||
}
|
||||
|
||||
bool particle_source_h::isValid() const {
|
||||
return particle::ParticleManager::get()->getSourceValidityCounter() == sourceValidityCounter;
|
||||
}
|
||||
|
||||
//**********HANDLE: Particle
|
||||
ADE_OBJ(l_Particle, particle_h, "particle", "Handle to a particle");
|
||||
@@ -214,6 +229,230 @@ ADE_FUNC_DEPRECATED(setColor, l_Particle, "number r, number g, number b", "Sets
|
||||
}
|
||||
|
||||
|
||||
ADE_OBJ(l_ParticleEffect, ::particle::ParticleEffectHandle, "particle_effect", "Handle to a tabled particle effect");
|
||||
|
||||
ADE_FUNC(getName, l_ParticleEffect, nullptr, "Returns the name under which this effect is stored", "string", "the name of the particle effect, or an empty string for an invalid handle")
|
||||
{
|
||||
::particle::ParticleEffectHandle ph;
|
||||
if (!ade_get_args(L, "o", l_ParticleEffect.Get(&ph)))
|
||||
return ade_set_error(L, "s", "");
|
||||
|
||||
if (!ph.isValid())
|
||||
return ade_set_error(L, "s", "");
|
||||
|
||||
const auto& particle_effect = particle::ParticleManager::get()->getEffect(ph);
|
||||
if (particle_effect.empty())
|
||||
return ade_set_error(L, "s", "");
|
||||
|
||||
return ade_set_args(L, "s", particle_effect.front().getName().c_str());
|
||||
}
|
||||
|
||||
ADE_FUNC(createSource, l_ParticleEffect, nullptr, "Creates a new particle source, spawning particles as per this particle effect", "particle_source", "the particle source, or nil for an invalid handle")
|
||||
{
|
||||
::particle::ParticleEffectHandle ph;
|
||||
if (!ade_get_args(L, "o", l_ParticleEffect.Get(&ph)))
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
if (!ph.isValid())
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
auto source = particle::ParticleManager::get()->createSource(ph);
|
||||
return ade_set_args(L, "o", l_ParticleSource.Set(particle_source_h(source, particle::ParticleManager::get()->getSourceValidityCounter())));
|
||||
}
|
||||
|
||||
ADE_OBJ(l_ParticleSource, particle_source_h, "particle_source", "Handle to a particle source. Only valid immediately after acquiring.");
|
||||
|
||||
ADE_FUNC(setNormal, l_ParticleSource, "vector normal", "Sets the normal vector of this particle source.", nullptr, nullptr)
|
||||
{
|
||||
particle_source_h ps;
|
||||
vec3d normal;
|
||||
if (!ade_get_args(L, "oo", l_ParticleSource.Get(&ps), l_Vector.Get(&normal)))
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
vm_vec_normalize_safe(&normal);
|
||||
psp->setNormal(normal);
|
||||
|
||||
return ADE_RETURN_NIL;
|
||||
}
|
||||
|
||||
ADE_FUNC(setTriggerRadius, l_ParticleSource, "number triggerRadius", "Sets the trigger radius of this particle source.", nullptr, nullptr)
|
||||
{
|
||||
particle_source_h ps;
|
||||
float trigger;
|
||||
if (!ade_get_args(L, "of", l_ParticleSource.Get(&ps), &trigger))
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
psp->setTriggerRadius(trigger);
|
||||
|
||||
return ADE_RETURN_NIL;
|
||||
}
|
||||
|
||||
ADE_FUNC(setTriggerVelocity, l_ParticleSource, "number triggerVelocity", "Sets the trigger velocity of this particle source.", nullptr, nullptr)
|
||||
{
|
||||
particle_source_h ps;
|
||||
float trigger;
|
||||
if (!ade_get_args(L, "of", l_ParticleSource.Get(&ps), &trigger))
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
psp->setTriggerVelocity(trigger);
|
||||
|
||||
return ADE_RETURN_NIL;
|
||||
}
|
||||
|
||||
ADE_FUNC(createOnCoordinates, l_ParticleSource, "vector position, orientation orientation, [vector velocity = zero_vector, orientation orientationOverride = identity, boolean orientationOverrideIsRelative = true]", "Actually spawns this particle source at the specified position.", "boolean", "returns true if the source was successfully created, false otherwise")
|
||||
{
|
||||
particle_source_h ps;
|
||||
vec3d position;
|
||||
matrix_h orientation;
|
||||
vec3d velocity = ZERO_VECTOR;
|
||||
matrix_h orientationOverride(&vmd_identity_matrix);
|
||||
bool orientationOverrideRelative = true;
|
||||
|
||||
if (!ade_get_args(L, "ooo|oob", l_ParticleSource.Get(&ps), l_Vector.Get(&position), l_Matrix.Get(&orientation), l_Vector.Get(&velocity), l_Matrix.Get(&orientationOverride), &orientationOverrideRelative))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
psp->setHost(std::make_unique<EffectHostVector>(position, *orientation.GetMatrix(), velocity, *orientationOverride.GetMatrix(), orientationOverrideRelative));
|
||||
psp->finishCreation();
|
||||
|
||||
return ade_set_args(L, "b", true);
|
||||
}
|
||||
|
||||
ADE_FUNC(createOnObject, l_ParticleSource, "object object, vector offset, [orientation orientationOverride = identity, boolean orientationOverrideIsRelative = true]", "Actually spawns this particle source, attached to the specified object.", "boolean", "returns true if the source was successfully created, false otherwise")
|
||||
{
|
||||
particle_source_h ps;
|
||||
object_h objh;
|
||||
vec3d position;
|
||||
matrix_h orientationOverride(&vmd_identity_matrix);
|
||||
bool orientationOverrideRelative = true;
|
||||
|
||||
if (!ade_get_args(L, "ooo|ob", l_ParticleSource.Get(&ps), l_Object.Get(&objh), l_Vector.Get(&position), l_Matrix.Get(&orientationOverride), &orientationOverrideRelative))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
if (!objh.isValid())
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
psp->setHost(std::make_unique<EffectHostObject>(objh.objp(), position, *orientationOverride.GetMatrix(), orientationOverrideRelative));
|
||||
psp->finishCreation();
|
||||
|
||||
return ade_set_args(L, "b", true);
|
||||
}
|
||||
|
||||
ADE_FUNC(createOnSubmodel, l_ParticleSource, "object object, submodel submodel, vector offset, [orientation orientationOverride = identity, boolean orientationOverrideIsRelative = true]", "Actually spawns this particle source, attached to the specified submodel.", "boolean", "returns true if the source was successfully created, false otherwise")
|
||||
{
|
||||
particle_source_h ps;
|
||||
object_h objh;
|
||||
submodel_h subobjh;
|
||||
vec3d position;
|
||||
matrix_h orientationOverride(&vmd_identity_matrix);
|
||||
bool orientationOverrideRelative = true;
|
||||
|
||||
if (!ade_get_args(L, "oooo|ob", l_ParticleSource.Get(&ps), l_Object.Get(&objh), l_Submodel.Get(&subobjh), l_Vector.Get(&position), l_Matrix.Get(&orientationOverride), &orientationOverrideRelative))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
if (!(subobjh.isValid() && objh.isValid()))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
psp->setHost(std::make_unique<EffectHostSubmodel>(objh.objp(), subobjh.GetSubmodelIndex(), position, *orientationOverride.GetMatrix(), orientationOverrideRelative));
|
||||
psp->finishCreation();
|
||||
|
||||
return ade_set_args(L, "b", true);
|
||||
}
|
||||
|
||||
ADE_FUNC(createOnTurret, l_ParticleSource, "object object, submodel submodel, number firepoint, [orientation orientationOverride = identity, boolean orientationOverrideIsRelative = true]", "Actually spawns this particle source, attached to the specified turret firepoint.", "boolean", "returns true if the source was successfully created, false otherwise")
|
||||
{
|
||||
particle_source_h ps;
|
||||
object_h objh;
|
||||
submodel_h subobjh;
|
||||
int firepoint;
|
||||
matrix_h orientationOverride(&vmd_identity_matrix);
|
||||
bool orientationOverrideRelative = true;
|
||||
|
||||
if (!ade_get_args(L, "oooi|ob", l_ParticleSource.Get(&ps), l_Object.Get(&objh), l_Submodel.Get(&subobjh), &firepoint, l_Matrix.Get(&orientationOverride), &orientationOverrideRelative))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
if (!(subobjh.isValid() && objh.isValid()))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
psp->setHost(std::make_unique<EffectHostTurret>(objh.objp(), subobjh.GetSubmodelIndex(), firepoint, *orientationOverride.GetMatrix(), orientationOverrideRelative));
|
||||
psp->finishCreation();
|
||||
|
||||
return ade_set_args(L, "b", true);
|
||||
}
|
||||
|
||||
ADE_FUNC(createOnBeam, l_ParticleSource, "object object, submodel submodel, number firepoint, [orientation orientationOverride = identity, boolean orientationOverrideIsRelative = true]", "Actually spawns this particle source along the length of the beam.", "boolean", "returns true if the source was successfully created, false otherwise")
|
||||
{
|
||||
particle_source_h ps;
|
||||
object_h objh;
|
||||
matrix_h orientationOverride(&vmd_identity_matrix);
|
||||
bool orientationOverrideRelative = true;
|
||||
|
||||
if (!ade_get_args(L, "ooo|ob", l_ParticleSource.Get(&ps), l_Object.Get(&objh), l_Matrix.Get(&orientationOverride), &orientationOverrideRelative))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
if (!objh.isValid() || objh.objp()->type != OBJ_BEAM)
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
psp->setHost(std::make_unique<EffectHostBeam>(objh.objp(), *orientationOverride.GetMatrix(), orientationOverrideRelative));
|
||||
psp->finishCreation();
|
||||
|
||||
return ade_set_args(L, "b", true);
|
||||
}
|
||||
|
||||
ADE_FUNC(createOnParticle, l_ParticleSource, "particle particle, [orientation orientationOverride = identity, boolean orientationOverrideIsRelative = true]", "Actually spawns this particle source, attached to the specified persistent particle.", "boolean", "returns true if the source was successfully created, false otherwise")
|
||||
{
|
||||
particle_source_h ps;
|
||||
particle_h particle;
|
||||
matrix_h orientationOverride(&vmd_identity_matrix);
|
||||
bool orientationOverrideRelative = true;
|
||||
|
||||
if (!ade_get_args(L, "ooo|ob", l_ParticleSource.Get(&ps), l_Particle.Get(&particle), l_Matrix.Get(&orientationOverride), &orientationOverrideRelative))
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
if (!particle.isValid())
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
particle::ParticleSource* psp = ps.Get();
|
||||
if (psp == nullptr)
|
||||
return ade_set_args(L, "b", false);
|
||||
|
||||
psp->setHost(std::make_unique<EffectHostParticle>(particle.Get(), *orientationOverride.GetMatrix(), orientationOverrideRelative));
|
||||
psp->finishCreation();
|
||||
|
||||
return ade_set_args(L, "b", true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "scripting/ade_api.h"
|
||||
#include "particle/particle.h"
|
||||
#include "particle/ParticleSource.h"
|
||||
|
||||
namespace scripting {
|
||||
namespace api {
|
||||
@@ -22,6 +23,25 @@ class particle_h
|
||||
|
||||
DECLARE_ADE_OBJ(l_Particle, particle_h);
|
||||
|
||||
DECLARE_ADE_OBJ(l_ParticleEffect, particle::ParticleEffectHandle);
|
||||
|
||||
class particle_source_h
|
||||
{
|
||||
protected:
|
||||
particle::ParticleSource* source = nullptr;
|
||||
uint32_t sourceValidityCounter = 0;
|
||||
public:
|
||||
particle_source_h() = default;
|
||||
|
||||
explicit particle_source_h(particle::ParticleSource* source_p, uint32_t sourceValidityCounter_p);
|
||||
|
||||
particle::ParticleSource* Get() const;
|
||||
|
||||
bool isValid() const;
|
||||
};
|
||||
|
||||
DECLARE_ADE_OBJ(l_ParticleSource, particle_source_h);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user