Files
fs2open.github.com/code/particle/ParticleSource.h
Asteroth 0f06bf279a Add velocity inheritance for particle effects (#3691)
* implement velocity inheritance for particles

* add vel inherit for single effects

* appease clang

* review feedback
2021-10-10 13:37:31 -04:00

342 lines
9.3 KiB
C++

#ifndef PARTICLE_SOURCE_H
#define PARTICLE_SOURCE_H
#pragma once
#include "globalincs/pstypes.h"
#include "object/object.h"
#include "particle/particle.h"
#include "particle/ParticleEffect.h"
#include "io/timer.h"
struct weapon;
struct weapon_info;
// Forward declaration so we don't need weapons.h
enum class WeaponState: uint32_t;
namespace particle {
/**
* The origin type
*/
enum class SourceOriginType {
NONE, //!< Invalid origin
VECTOR, //!< World-space offset
OBJECT, //!< An object
PARTICLE //!< A particle
};
class ParticleSource;
class ParticleEffect;
/**
* @brief A source origin
*
* The SourceOrigin class encapsualtes the information about where a source is located. It allows a source to be
* relative to an object or a particle and keeps track of whether the host objects are still valid.
*/
class SourceOrigin {
private:
SourceOriginType m_originType;
struct {
vec3d m_pos;
object_h m_object;
WeakParticlePtr m_particle;
} m_origin;
WeaponState m_weaponState;
vec3d m_offset;
vec3d m_velocity;
public:
/**
* @brief Initializes the origin with default values
*/
SourceOrigin();
/**
* @brief Gets the current, global position of the origin
* @param posOut The pointer where the location will be stored
*/
void getGlobalPosition(vec3d* posOut) const;
void getHostOrientation(matrix* matOut) const;
inline SourceOriginType getType() const { return m_originType; }
inline object* getObjectHost() const { return m_origin.m_object.objp; }
/**
* @brief Determines if the origin is valid
*
* This checks if the hosting object is valid or if the hosting particle is still valid.
*
* @return @c true if the origin is valid, @c false otherwise
*/
bool isValid() const;
/**
* @brief Applies the information to a particle info
*
* This can be used to initialize the position of a created particle from a source.
*
* @param info The particle_info this should be applied to
* @param allowRelative If @c true then the location of the particle can be relative to the host
*/
void applyToParticleInfo(particle_info& info, bool allowRelative = false) const;
/**
* @brief Gets the velocity of the origin host
* @return The velocity of the host
*/
vec3d getVelocity() const;
/**
* @brief Sets the weapon state in which this origin is valid
* @param state The weapon state to use
*/
void setWeaponState(WeaponState state);
/**
* @brief Moves the source to the specified world location
* @param pos The world position
*/
void moveTo(vec3d* pos);
/**
* @brief Moves the source to the specified object with an offset
* @param objp The hosting object
* @param offset The position relative to this object
*/
void moveToObject(object* objp, vec3d* offset);
/**
* @brief Moves the source to the specified particle
* @param weakParticlePtr The hosting particle
*/
void moveToParticle(const WeakParticlePtr& weakParticlePtr);
/**
* @brief Sets the velocity of the source, will not move the source, but particles created may inherit this velocity
* @param vel The world velocity
*/
void setVelocity(vec3d* vel);
friend class ParticleSource;
};
/**
* @brief The orientation of a particle source
*
* Currently only the forward direction vector is useful because the other vectors of the matrix are chosen pretty
* arbitrarily. This also contains a normal vector if it was specified when creating the source.
*
* An orientation can be either relative or global. In relative mode all transforms should be relative to the host
* object while in global mode all directions are in world-space. Normals are always in world-space.
*/
class SourceOrientation {
private:
matrix m_orientation;
bool m_hasNormal = false;
vec3d m_normal;
// By default the orientation of a source is relative to the host
bool m_isRelative = true;
public:
SourceOrientation();
/**
* @brief Sets the direction from a vector
*
* The vector doesn't have to be normalized before passing it to this function.
*
* @param vec The vector to create the matrix from
*/
void setFromVector(const vec3d& vec, bool relative = false);
/**
* @brief Sets the direction from an already normalized vector
*
* @param vec The normalized vector
*/
void setFromNormalizedVector(const vec3d& vec, bool relative = false);
void setNormal(const vec3d& normal);
void setFromMatrix(const matrix& mat, bool relative = false);
vec3d getDirectionVector(const SourceOrigin* origin) const;
bool isRelative() const { return m_isRelative; }
/**
* @brief Gets the normal of this orientation
* @param outNormal The pointer where the normal will be written
* @return @c true if there was a normal, @c false otherwise
*/
bool getNormal(vec3d* outNormal) const;
inline const matrix* getMatrix() const { return &m_orientation; }
friend class ParticleSource;
};
/**
* @brief Contains information about the timing of a source
*
* This can be used to control when a source is active and when it will be deleted. Since a source can be active at a
* later time the timing goes through three stages. In the first state the source is created but not active. #isActive()
* returns false but #isFinished() also return false. When it activates #isActive() will return true while #isFinished()
* stays @c false. Then #isActive() will be @c false and #isFinished() will be @c true. At this point the source is
* considered to have expired and it will be removed the next time it is processed.
*/
class SourceTiming {
private:
int m_creationTimestamp;
int m_nextCreation; //! The next time the source should generate a particle. Controlled by the effect of the source.
int m_beginTimestamp;
int m_endTimestamp;
public:
SourceTiming();
void setCreationTimestamp(int time);
int getCreationTime() const { return m_creationTimestamp; }
/**
* @brief Sets when the source is active
*
* The source will be active between the specified begin and end times
*
* @param begin The timestamp when the source will begin to be active
* @param end The timestamp when the source will not be active anymore
*/
void setLifetime(int begin, int end);
/**
* @brief Determines if the source is currently active
* @return @c true if the source is active
*/
bool isActive() const;
/**
* @brief Determines if the source has expired
* @return @c true if the source has expired.
*/
bool isFinished() const;
/**
* @brief Gets the progress of the source through its active time.
*
* If the source is not active or if the timestamps are not valid then -1 is returned
*
* @return The progress of the source through its lifetime
*/
float getLifeTimeProgress() const;
/**
* @brief Determine if the timestamp for the next particle creation has expired
* @return @c true if the timestamp has expired, false otherwise
*/
bool nextCreationTimeExpired() const;
/**
* @brief Move the internal timestamp for the next creation forward
*
* Use this for achieving a consistent amount of created particles regardless of FPS by setting time_diff_ms to a
* constant time and call nextCreationTimeExpired() until that returns false. Then all particles for that frame have
* been created.
*
* @param time_diff_ms
*/
void incrementNextCreationTime(int time_diff_ms);
friend class ParticleSource;
};
/**
* @brief A particle source
*
* A particle source contains the information about where and for how long particles are created. A particle effect uses
* this information to create new particles. A particle source has not effect-specific information which means that an
* effect can only use the information contained in this object.
*
* @ingroup particleSystems
*/
class ParticleSource {
private:
SourceOrigin m_origin; //!< The current position of this particle source
SourceOrientation m_effectOrientation; //!< The orientation of the particle source
SourceTiming m_timing; //!< The time informations of the particle source
ParticleEffect* m_effect; //!< The effect that is assigned to this source
size_t m_processingCount; //!< The number of times this effect has been processed
void initializeThrusterOffset(weapon* wp, weapon_info* wip);
public:
ParticleSource();
inline const ParticleEffect* getEffect() const { return m_effect; }
inline ParticleEffect* getEffect() { return m_effect; }
inline void setEffect(ParticleEffect* eff) {
Assert(eff != nullptr);
m_effect = eff;
}
inline const SourceOrigin* getOrigin() const { return &m_origin; }
inline SourceOrigin* getOrigin() { return &m_origin; }
inline const SourceOrientation* getOrientation() const { return &m_effectOrientation; }
inline SourceOrientation* getOrientation() { return &m_effectOrientation; }
inline const SourceTiming* getTiming() const { return &m_timing; }
inline SourceTiming* getTiming() { return &m_timing; }
inline size_t getProcessingCount() const { return m_processingCount; }
/**
* @brief Finishes the creation of a particle source
*
* This is used to initialize some status that is only available after everything has been set up.
*/
void finishCreation();
/**
* @brief Does one processing step for this source
* @return @c true if the source should continue to be processed
*/
bool process();
/**
* @brief Determines if the source is valid
* @return @c true if the source is valid, @c false otherwise.
*/
bool isValid() const;
};
}
#endif // PARTICLE_SOURCE_H