/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// ParticleSys.h //////////////////////////////////////////////////////////////////////////////////
// Particle System type definitions
// Author: Michael S. Booth, November 2001
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef _PARTICLE_SYS_H_
#define _PARTICLE_SYS_H_
#include
#include "Common/AsciiString.h"
#include "Common/GameMemory.h"
#include "Common/GameType.h"
#include "Common/Snapshot.h"
#include "Common/SubsystemInterface.h"
#include "GameClient/ClientRandomValue.h"
#include "WWMath/Matrix3D.h" ///< @todo Replace with our own matrix library
#include "Common/STLTypedefs.h"
/// @todo Once the client framerate is decoupled, the frame counters within will have to become time-based
class Particle;
class ParticleSystem;
class ParticleSystemManager;
class Drawable;
class Object;
struct FieldParse;
class INI;
class DebugWindowDialog; // really ParticleEditorDialog
class RenderInfoClass; // ick
enum ParticleSystemID
{
INVALID_PARTICLE_SYSTEM_ID = 0
};
#define MAX_VOLUME_PARTICLE_DEPTH ( 16 )
#define DEFAULT_VOLUME_PARTICLE_DEPTH ( 0 )//The Default is not to do the volume thing!
#define OPTIMUM_VOLUME_PARTICLE_DEPTH ( 6 )
//--------------------------------------------------------------------------------------------------------------
enum { MAX_KEYFRAMES=8 };
struct Keyframe
{
Real value;
UnsignedInt frame;
};
struct RGBColorKeyframe
{
RGBColor color;
UnsignedInt frame;
};
enum ParticlePriorityType
{
INVALID_PRIORITY = 0,
PARTICLE_PRIORITY_LOWEST = 1,
// FLUFF = PARTICLE_PRIORITY_LOWEST, ///< total and absolute fluff
// DEBRIS, ///< debris related particles
// NATURE, ///< neato effects we just might see in the world
// WEAPON, ///< Weapons firing and flying in the air
// DAMAGE, ///< taking damage/dying explosions
// SPECIAL, ///< super special top priority like a superweapon
WEAPON_EXPLOSION = PARTICLE_PRIORITY_LOWEST,
SCORCHMARK,
DUST_TRAIL,
BUILDUP,
DEBRIS_TRAIL,
UNIT_DAMAGE_FX,
DEATH_EXPLOSION,
SEMI_CONSTANT,
CONSTANT,
WEAPON_TRAIL,
AREA_EFFECT,
CRITICAL, ///< super special top priority like a superweapon
ALWAYS_RENDER, ///< used for logically important display (not just fluff), so must never be culled, regardless of particle cap, lod, etc
// !!! *Noting* goes here ... special is the top priority !!!
PARTICLE_PRIORITY_HIGHEST = ALWAYS_RENDER,
NUM_PARTICLE_PRIORITIES ///< Keep this last
};
/**
* This structure is filled out and passed to the constructor of a Particle to initialize it
*/
class ParticleInfo : public Snapshot
{
public:
ParticleInfo( void );
Coord3D m_vel; ///< initial velocity
Coord3D m_pos; ///< initial position
Coord3D m_emitterPos; ///< position of the emitter
Real m_velDamping; ///< velocity damping coefficient
Real m_angleX; ///< initial angle around X axis
Real m_angleY; ///< initial angle around Y axis
Real m_angleZ; ///< initial angle around Z axis
Real m_angularRateX; ///< initial angle around X axis
Real m_angularRateY; ///< initial angle around Y axis
Real m_angularRateZ; ///< initial angle around Z axis
Real m_angularDamping; ///< angular velocity damping coefficient
UnsignedInt m_lifetime; ///< lifetime of this particle
Real m_size; ///< size of the particle
Real m_sizeRate; ///< rate of change of size
Real m_sizeRateDamping; ///< damping of size change rate
Keyframe m_alphaKey[ MAX_KEYFRAMES ];
RGBColorKeyframe m_colorKey[ MAX_KEYFRAMES ];
Real m_colorScale; ///< color "scaling" coefficient
Real m_windRandomness; ///< multiplier for wind randomness per particle
Bool m_particleUpTowardsEmitter; ///< if this is true, then the 0.0 Z rotation should actually
///< correspond to the direction of the emitter.
protected:
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
};
/**
* An individual particle created by a ParticleSystem.
* NOTE: Particles cannot exist without a parent particle system.
*/
class Particle : public MemoryPoolObject,
public ParticleInfo
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( Particle, "ParticlePool" )
public:
Particle( ParticleSystem *system, const ParticleInfo *data );
Bool update( void ); ///< update this particle's behavior - return false if dead
void doWindMotion( void ); ///< do wind motion (if present) from particle system
void applyForce( const Coord3D *force ); ///< add the given acceleration
void detachDrawable( void ) { m_drawable = NULL; } ///< detach the Drawable pointer from this particle
inline const Coord3D *getPosition( void ) { return &m_pos; }
inline Real getSize( void ) { return m_size; }
inline Real getAngle( void ) { return m_angleZ; }
inline Real getAlpha( void ) { return m_alpha; }
inline const RGBColor *getColor( void ) { return &m_color; }
inline void setColor( RGBColor *color ) { m_color = *color; }
Bool isInvisible( void ); ///< return true if this particle is invisible
inline Bool isCulled (void) {return m_isCulled;} ///< return true if the particle falls off the edge of the screen
inline void setIsCulled (Bool enable) { m_isCulled = enable;} ///< set particle to not visible because it's outside view frustum
void controlParticleSystem( ParticleSystem *sys ) { m_systemUnderControl = sys; }
void detachControlledParticleSystem( void ) { m_systemUnderControl = NULL; }
// get priority of this particle ... which is the priority of the system it belongs to
ParticlePriorityType getPriority( void );
UnsignedInt getPersonality(void) { return m_personality; };
void setPersonality(UnsignedInt p) { m_personality = p; };
protected:
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
void computeAlphaRate( void ); ///< compute alpha rate to get to next key
void computeColorRate( void ); ///< compute color change to get to next key
public:
Particle * m_systemNext;
Particle * m_systemPrev;
Particle * m_overallNext;
Particle * m_overallPrev;
protected:
ParticleSystem * m_system; ///< the particle system this particle belongs to
UnsignedInt m_personality; ///< each new particle assigned a number one higher than the previous
// most of the particle data is derived from ParticleInfo
Coord3D m_accel; ///< current acceleration
Coord3D m_lastPos; ///< previous position
UnsignedInt m_lifetimeLeft; ///< lifetime remaining, if zero -> destroy
UnsignedInt m_createTimestamp; ///< frame this particle was created
Real m_alpha; ///< current alpha of this particle
Real m_alphaRate; ///< current rate of alpha change
Int m_alphaTargetKey; ///< next index into key array
RGBColor m_color; ///< current color of this particle
RGBColor m_colorRate; ///< current rate of color change
Int m_colorTargetKey; ///< next index into key array
Drawable * m_drawable; ///< drawable associated with this particle
Bool m_isCulled; ///< status of particle relative to screen bounds
public:
Bool m_inSystemList;
Bool m_inOverallList;
union
{
ParticleSystem * m_systemUnderControl; ///< the particle system attached to this particle (not the system that created us)
ParticleSystemID m_systemUnderControlID; ///< id of system attached to this particle (not the system that created us);
};
};
//--------------------------------------------------------------------------------------------------------------
#ifdef DEFINE_PARTICLE_SYSTEM_NAMES
/**** NOTE: These MUST be kept in sync with the enumerations below *****/
static char *ParticleShaderTypeNames[] =
{
"NONE", "ADDITIVE", "ALPHA", "ALPHA_TEST", "MULTIPLY", NULL
};
static char *ParticleTypeNames[] =
{
"NONE", "PARTICLE", "DRAWABLE", "STREAK", "VOLUME_PARTICLE", NULL
};
static char *EmissionVelocityTypeNames[] =
{
"NONE", "ORTHO", "SPHERICAL", "HEMISPHERICAL", "CYLINDRICAL", "OUTWARD", NULL
};
static char *EmissionVolumeTypeNames[] =
{
"NONE", "POINT", "LINE", "BOX", "SPHERE", "CYLINDER", NULL
};
//"NONE", "FLUFF", "DEBRIS", "NATURE", "WEAPON", "DAMAGE", "SPECIAL"
static char *ParticlePriorityNames[] =
{
"NONE", "WEAPON_EXPLOSION","SCORCHMARK","DUST_TRAIL","BUILDUP","DEBRIS_TRAIL","UNIT_DAMAGE_FX","DEATH_EXPLOSION","SEMI_CONSTANT","CONSTANT","WEAPON_TRAIL","AREA_EFFECT","CRITICAL", "ALWAYS_RENDER", NULL
};
static char *WindMotionNames[] =
{
"NONE", "Unused", "PingPong", "Circular", NULL
};
#endif
/**
* All of the properties of a particle system, used by both ParticleSystemTemplates
* and ParticleSystem classes.
*/
class ParticleSystemInfo : public Snapshot
{
public:
ParticleSystemInfo(); ///< to set defaults.
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
Bool m_isOneShot; ///< if true, destroy system after one burst has occurred
enum ParticleShaderType
{
INVALID_SHADER=0, ADDITIVE, ALPHA, ALPHA_TEST, MULTIPLY
}
m_shaderType; ///< how this particle is rendered
enum ParticleType
{
INVALID_TYPE=0, PARTICLE, DRAWABLE, STREAK, VOLUME_PARTICLE ///< is a particle a 2D-screen-facing particle, or a Drawable, or a Segment in a streak?
}
m_particleType;
AsciiString m_particleTypeName; ///< if PARTICLE, texture filename, if DRAWABLE, Drawable name
GameClientRandomVariable m_angleX; ///< initial angle around X axis
GameClientRandomVariable m_angleY; ///< initial angle around Y axis
GameClientRandomVariable m_angleZ; ///< initial angle around Z axis
GameClientRandomVariable m_angularRateX; ///< initial angle around X axis
GameClientRandomVariable m_angularRateY; ///< initial angle around Y axis
GameClientRandomVariable m_angularRateZ; ///< initial angle around Z axis
GameClientRandomVariable m_angularDamping; ///< angular velocity damping coefficient
GameClientRandomVariable m_velDamping; ///< velocity damping factor
GameClientRandomVariable m_lifetime; ///< lifetime of emitted particles
UnsignedInt m_systemLifetime; ///< lifetime of the particle system
GameClientRandomVariable m_startSize; ///< initial size of emitted particles
GameClientRandomVariable m_startSizeRate; ///< change in start size of emitted particles
GameClientRandomVariable m_sizeRate; ///< rate of change of size
GameClientRandomVariable m_sizeRateDamping; ///< damping of size change
UnsignedInt m_volumeParticleDepth; ///< how many layers deep to draw the particle, if >1
struct RandomKeyframe
{
GameClientRandomVariable var; ///< the range of values at this keyframe
UnsignedInt frame; ///< the frame number
};
RandomKeyframe m_alphaKey[ MAX_KEYFRAMES ];
RGBColorKeyframe m_colorKey[ MAX_KEYFRAMES ]; ///< color of particle
typedef Int Color;
void tintAllColors( Color tintColor );
GameClientRandomVariable m_colorScale; ///< color coefficient
GameClientRandomVariable m_burstDelay; ///< time between particle emissions
GameClientRandomVariable m_burstCount; ///< number of particles emitted per burst
GameClientRandomVariable m_initialDelay; ///< delay before particles begin emitting
Coord3D m_driftVelocity; ///< additional velocity added to all particles
Real m_gravity; ///< gravity acceleration (global Z) for particles in this system
AsciiString m_slaveSystemName; ///< if non-empty, create a system whose particles track this system's
Coord3D m_slavePosOffset; ///< positional offset of slave particles relative to master's
AsciiString m_attachedSystemName; ///< if non-empty, create a system attached to each particle of this system
//-------------------------------------------------------
// The direction and speed at which particles are emitted
enum EmissionVelocityType
{
INVALID_VELOCITY=0, ORTHO, SPHERICAL, HEMISPHERICAL, CYLINDRICAL, OUTWARD
}
m_emissionVelocityType;
ParticlePriorityType m_priority;
union
{
struct
{
GameClientRandomVariable x; ///< initial speed of particle along X axis
GameClientRandomVariable y; ///< initial speed of particle along Y axis
GameClientRandomVariable z; ///< initial speed of particle along Z axis
}
ortho;
struct
{
GameClientRandomVariable speed; ///< initial speed of particle along random radial direction
}
spherical, hemispherical;
struct
{
GameClientRandomVariable radial; ///< initial speed of particle in the disk
GameClientRandomVariable normal; ///< initial speed of particle perpendicular to disk
}
cylindrical;
struct
{
GameClientRandomVariable speed; ///< speed outward from emission volume
GameClientRandomVariable otherSpeed; ///< speed along "other" axis, such as cylinder length
}
outward;
}
m_emissionVelocity;
//----------------------------------------------------------
// The volume of space where particles are initially created
// Note that the volume is relative to the system's position and orientation
enum EmissionVolumeType
{
INVALID_VOLUME=0, POINT, LINE, BOX, SPHERE, CYLINDER
}
m_emissionVolumeType; ///< the type of volume where particles are created
union emissionVolumeUnion
{
// point just uses system's position
// line
struct
{
Coord3D start, end;
}
line;
// box
struct
{
Coord3D halfSize;
}
box;
// sphere
struct
{
Real radius;
}
sphere;
// cylinder
struct
{
Real radius;
Real length;
}
cylinder;
}
m_emissionVolume; ///< the dimensions of the emission volume
Bool m_isEmissionVolumeHollow; ///< if true, only create particles at boundary of volume
Bool m_isGroundAligned; ///< if true, align with the ground. if false, then do the normal billboarding.
Bool m_isEmitAboveGroundOnly; ///< if true, only emit particles when the system is above ground.
Bool m_isParticleUpTowardsEmitter; ///< if true, align the up direction to be towards the emitter.
enum WindMotion
{
WIND_MOTION_INVALID = 0,
WIND_MOTION_NOT_USED,
WIND_MOTION_PING_PONG,
WIND_MOTION_CIRCULAR
};
WindMotion m_windMotion; ///< motion of the wind angle updating
Real m_windAngle; ///< angle of the "wind" associated with this system
Real m_windAngleChange; ///< current how fast the angle changes (higher=faster change)
Real m_windAngleChangeMin; ///< min for angle change
Real m_windAngleChangeMax; ///< max for angle change
Real m_windMotionStartAngle; ///< (for ping pong) angle 1 of the ping pong
Real m_windMotionStartAngleMin; ///< (for ping pong) min angle for angle 1
Real m_windMotionStartAngleMax; ///< (for ping pong) max angle for angle 1
Real m_windMotionEndAngle; ///< (for ping pong) angle 2 of the ping pong
Real m_windMotionEndAngleMin; ///< (for ping pong) min angle for angle 2
Real m_windMotionEndAngleMax; ///< (for ping pong) max angel for angle 2
Byte m_windMotionMovingToEndAngle; ///< (for ping pong) TRUE if we're moving "towards" the end angle
};
/**
* A ParticleSystemTemplate, used by the ParticleSystemManager to instantiate ParticleSystems.
*/
class ParticleSystemTemplate : public MemoryPoolObject, protected ParticleSystemInfo
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ParticleSystemTemplate, "ParticleSystemTemplatePool" )
public:
ParticleSystemTemplate( const AsciiString &name );
AsciiString getName( void ) const { return m_name; }
// This function was made const because of update modules' module data being all const.
ParticleSystem *createSlaveSystem( Bool createSlaves = TRUE ) const ; ///< if returns non-NULL, it is a slave system for use
const FieldParse *getFieldParse( void ) const { return m_fieldParseTable; } ///< Parsing from INI access
static void parseRGBColorKeyframe( INI* ini, void *instance, void *store, const void* /*userData*/ );
static void parseRandomKeyframe( INI* ini, void *instance, void *store, const void* /*userData*/ );
static void parseRandomRGBColor( INI* ini, void *instance, void *store, const void* /*userData*/ );
static void parseRandomRGBColorRate( INI* ini, void *instance, void *store, const void* /*userData*/ );
protected:
friend class ParticleSystemManager; ///< @todo remove this friendship
friend class ParticleSystem; ///< @todo remove this friendship
// These friendships are naughty but necessary for particle editing.
friend class DebugWindowDialog; ///< @todo remove this friendship when no longer editing particles
friend void _updateAsciiStringParmsToSystem(ParticleSystemTemplate *particleTemplate);
friend void _updateAsciiStringParmsFromSystem(ParticleSystemTemplate *particleTemplate);
friend void _writeSingleParticleSystem( File *out, ParticleSystemTemplate *templ );
static const FieldParse m_fieldParseTable[]; ///< the parse table for INI definition
protected:
AsciiString m_name; ///< the name of this template
// This has to be mutable because of the delayed initialization thing in createSlaveSystem
mutable const ParticleSystemTemplate *m_slaveTemplate; ///< if non-NULL, use this to create a slave system
// template attribute data inherited from ParticleSystemInfo class
};
/**
* A particle system, responsible for creating Particles.
* If a particle system has finished, but still has particles "in the air", it must wait
* before destroying itself in order to ensure everything can be cleaned up if the system
* is reset.
*/
class ParticleSystem : public MemoryPoolObject,
public ParticleSystemInfo
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ParticleSystem, "ParticleSystemPool" )
public:
ParticleSystem( const ParticleSystemTemplate *sysTemplate,
ParticleSystemID id,
Bool createSlaves ); ///< create a particle system from a template and assign it this ID
inline ParticleSystemID getSystemID( void ) const { return m_systemID; } ///< get unique system ID
void setPosition( const Coord3D *pos ); ///< set the position of the particle system
void getPosition( Coord3D *pos ); ///< get the position of the particle system
void setLocalTransform( const Matrix3D *matrix ); ///< set the system's local transform
void rotateLocalTransformX( Real x ); ///< rotate local transform matrix
void rotateLocalTransformY( Real y ); ///< rotate local transform matrix
void rotateLocalTransformZ( Real z ); ///< rotate local transform matrix
const Coord3D *getDriftVelocity( void ) { return &m_driftVelocity; } ///< get the drift velocity of the system
void attachToDrawable( const Drawable *draw ); ///< attach this particle system to a Drawable
void attachToObject( const Object *obj ); ///< attach this particle system to an Object
virtual Bool update( Int localPlayerIndex ); ///< update this particle system, return false if dead
void updateWindMotion( void ); ///< update wind motion
void setControlParticle( Particle *p ); ///< set control particle
void start( void ); ///< (re)start a stopped particle system
void stop( void ); ///< stop a particle system from emitting
void destroy( void ); ///< stop emitting, and wait for particles to die, then destroy self
void setVelocityMultiplier( const Coord3D *value ) { m_velCoeff = *value; } ///< set the velocity multiplier coefficient
const Coord3D *getVelocityMultiplier( void ) { return &m_velCoeff; } ///< get the velocity multiplier coefficient
void setBurstCountMultiplier( Real value ) { m_countCoeff = value; }
Real getBurstCountMultiplier( void ) { return m_countCoeff; }
void setBurstDelayMultiplier( Real value ) { m_delayCoeff = value; }
Real getBurstDelayMultiplier( void ) { return m_delayCoeff; }
void setSizeMultiplier( Real value ) { m_sizeCoeff = value; }
Real getSizeMultiplier( void ) { return m_sizeCoeff; }
/// Force a burst of particles to be emitted immediately.
void trigger (void) { m_burstDelayLeft = 0; m_delayLeft = 0;}
void setInitialDelay( UnsignedInt delay ) { m_delayLeft = delay; }
AsciiString getParticleTypeName( void ) { return m_particleTypeName; } ///< return the name of the particles
Bool isUsingDrawables( void ) { return (m_particleType == DRAWABLE) ? true : false; }
Bool isUsingStreak( void ) { return (m_particleType == STREAK) ? true : false; }
UnsignedInt getVolumeParticleDepth( void ) { return ( m_particleType == VOLUME_PARTICLE ) ? OPTIMUM_VOLUME_PARTICLE_DEPTH : 0; }
Bool shouldBillboard( void ) { return !m_isGroundAligned; }
ParticleShaderType getShaderType( void ) { return m_shaderType; }
void setSlave( ParticleSystem *slave ); ///< set a slave system for us
ParticleSystem *getSlave( void ) { return m_slaveSystem; }
void setMaster( ParticleSystem *master ); ///< make this a slave with a master
ParticleSystem *getMaster( void ) { return m_masterSystem; }
const Coord3D *getSlavePositionOffset( void ) { return &m_slavePosOffset; }
void setSystemLifetime( UnsignedInt frames ) { m_systemLifetimeLeft = frames; }; ///< not the particle life, the system!... Lorenzen
void setLifetimeRange( Real min, Real max );
Bool isSystemForever() const {return m_isForever;}
UnsignedInt getStartFrame( void ) { return m_startTimestamp; } ///< return frame when this system was created
Bool isDestroyed( void ) const { return m_isDestroyed; }
void setSaveable( Bool b );
Bool isSaveable( void ) const { return m_isSaveable; }
/// called when the particle this system is controlled by dies
void detachControlParticle( Particle *p ) { m_controlParticle = NULL; }
/// called to merge two systems info. If slaveNeedsFullPromotion is true, then the slave needs to be aware of how many particles
/// to generate as well.
static ParticleInfo mergeRelatedParticleSystems( ParticleSystem *masterParticleSystem, ParticleSystem *slaveParticleSystem, Bool slaveNeedsFullPromotion);
/// @todo Const this (MSB)
const ParticleSystemTemplate *getTemplate( void ) { return m_template; } ///< get the template used to create this system
// @todo Const this jkmcd
Particle *getFirstParticle( void ) { return m_systemParticlesHead; }
void addParticle( Particle *particleToAdd );
/// when a particle dies, it calls this method - ONLY FOR USE BY PARTICLE
void removeParticle( Particle *p );
UnsignedInt getParticleCount( void ) const { return m_particleCount; }
inline ObjectID getAttachedObject( void ) { return m_attachedToObjectID; }
inline DrawableID getAttachedDrawable( void ) { return m_attachedToDrawableID; }
// Access to dynamically changing part of a particle system.
void setEmissionVolumeSphereRadius( Real newRadius ) { if (m_emissionVolumeType == SPHERE) m_emissionVolume.sphere.radius = newRadius; }
void setEmissionVolumeCylinderRadius( Real newRadius ) { if (m_emissionVolumeType == CYLINDER) m_emissionVolume.cylinder.radius = newRadius; }
EmissionVolumeType getEmisionVolumeType() const { return m_emissionVolumeType; }
ParticlePriorityType getPriority() const { return m_priority; }
// Access to wind motoin
Real getWindAngle( void ) { return m_windAngle; }
WindMotion getWindMotion( void ) { return m_windMotion; }
protected:
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
virtual Particle *createParticle( const ParticleInfo *data,
ParticlePriorityType priority,
Bool forceCreate = FALSE ); ///< factory method for particles
const ParticleInfo *generateParticleInfo( Int particleNum, Int particleCount ); ///< generate a new, random set of ParticleInfo
const Coord3D *computeParticlePosition( void ); ///< compute a position based on emission properties
const Coord3D *computeParticleVelocity( const Coord3D *pos ); ///< compute a velocity vector based on emission properties
const Coord3D *computePointOnUnitSphere( void ); ///< compute a random point on a unit sphere
protected:
Particle * m_systemParticlesHead;
Particle * m_systemParticlesTail;
UnsignedInt m_particleCount; ///< current count of particles for this system
ParticleSystemID m_systemID; ///< unique id given to this system from the particle system manager
DrawableID m_attachedToDrawableID; ///< if non-zero, system is parented to this Drawable
ObjectID m_attachedToObjectID; ///< if non-zero, system is parented to this Object
Matrix3D m_localTransform; ///< local orientation & position of system
Matrix3D m_transform; ///< composite transform of parent Drawable and local
UnsignedInt m_burstDelayLeft; ///< when zero, emit a particle burst
UnsignedInt m_delayLeft; ///< decremented until zero
UnsignedInt m_startTimestamp; ///< timestamp when this particle system was (re)started
UnsignedInt m_systemLifetimeLeft; ///< lifetime remaining for entire particle system
UnsignedInt m_personalityStore; ///< increments each time it is aggigned to each new particle
///< so that each particle gets an ever greater number
Real m_accumulatedSizeBonus; ///< For a system that wants to make particles start bigger and bigger. StartSizeRate
Coord3D m_velCoeff; ///< scalar value multiplied by all velocity parameters
Real m_countCoeff; ///< scalar value multiplied by burst count
Real m_delayCoeff; ///< scalar value multiplied by burst delay
Real m_sizeCoeff; ///< scalar value multiplied by initial size
Coord3D m_pos; ///< this is the position to emit at.
Coord3D m_lastPos; ///< this is the previous position we emitted at.
ParticleSystem * m_slaveSystem; ///< if non-NULL, another system this one has control of
ParticleSystemID m_slaveSystemID; ///< id of slave system (if present)
ParticleSystem * m_masterSystem; ///< if non-NULL, the system that controls this one
ParticleSystemID m_masterSystemID; ///< master system id (if present);
const ParticleSystemTemplate * m_template; ///< the template this system was constructed from
Particle * m_controlParticle; ///< if non-NULL, this system is controlled by this particle
Bool m_isLocalIdentity; ///< if true, the matrix can be ignored
Bool m_isIdentity; ///< if true, the matrix can be ignored
Bool m_isForever; ///< System has infinite lifetime
Bool m_isStopped; ///< if stopped, do not emit particles
Bool m_isDestroyed; ///< are we destroyed and waiting for particles to die
Bool m_isFirstPos; ///< true if this system hasn't been drawn before.
Bool m_isSaveable; ///< true if this system should be saved/loaded
// the actual particle system data is inherited from ParticleSystemInfo
};
//--------------------------------------------------------------------------------------------------------------
/**
* The particle system manager, responsible for maintaining all ParticleSystems
*/
class ParticleSystemManager : public SubsystemInterface,
public Snapshot
{
public:
typedef std::list ParticleSystemList;
typedef std::list::iterator ParticleSystemListIt;
typedef std::hash_map, rts::equal_to > TemplateMap;
ParticleSystemManager( void );
virtual ~ParticleSystemManager();
virtual void init( void ); ///< initialize the manager
virtual void reset( void ); ///< reset the manager and all particle systems
virtual void update( void ); ///< update all particle systems
virtual Int getOnScreenParticleCount( void ) = 0; ///< returns the number of particles on screen
virtual void setOnScreenParticleCount(int count);
ParticleSystemTemplate *findTemplate( const AsciiString &name ) const;
ParticleSystemTemplate *findParentTemplate( const AsciiString &name, int parentNum ) const;
ParticleSystemTemplate *newTemplate( const AsciiString &name );
/// given a template, instantiate a particle system
ParticleSystem *createParticleSystem( const ParticleSystemTemplate *sysTemplate,
Bool createSlaves = TRUE );
/** given a template, instantiate a particle system.
if attachTo is not null, attach the particle system to the given object.
return the particle system's ID, NOT its pointer.
*/
ParticleSystemID createAttachedParticleSystemID( const ParticleSystemTemplate *sysTemplate,
Object* attachTo,
Bool createSlaves = TRUE );
/// find a particle system given a unique system identifier
ParticleSystem *findParticleSystem( ParticleSystemID id );
/// destroy the particle system with the given id (if it still exists)
void destroyParticleSystemByID(ParticleSystemID id);
/// return iterators to the particle system template
TemplateMap::iterator beginParticleSystemTemplate() { return m_templateMap.begin(); }
TemplateMap::iterator endParticleSystemTemplate() { return m_templateMap.end(); }
TemplateMap::const_iterator beginParticleSystemTemplate() const { return m_templateMap.begin(); }
TemplateMap::const_iterator endParticleSystemTemplate() const { return m_templateMap.end(); }
/// destroy attached systems to object
void destroyAttachedSystems( Object *obj );
void setLocalPlayerIndex(Int index) {m_localPlayerIndex=index;}
void addParticle( Particle *particleToAdd, ParticlePriorityType priority );
void removeParticle( Particle *particleToRemove );
Int removeOldestParticles( UnsignedInt count, ParticlePriorityType priorityCap );
UnsignedInt getParticleCount( void ) const { return m_particleCount; }
UnsignedInt getFieldParticleCount( void ) const { return m_fieldParticleCount; }
UnsignedInt getParticleSystemCount( void ) const { return m_particleSystemCount; }
// @todo const this jkmcd
ParticleSystemList &getAllParticleSystems( void ) { return m_allParticleSystemList; }
virtual void doParticles(RenderInfoClass &rinfo) = 0;
virtual void queueParticleRender() = 0;
virtual void preloadAssets( TimeOfDay timeOfDay );
// these are only for use by partcle systems to link and unlink themselves
void friend_addParticleSystem( ParticleSystem *particleSystemToAdd );
void friend_removeParticleSystem( ParticleSystem *particleSystemToRemove );
protected:
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
Particle *m_allParticlesHead[ NUM_PARTICLE_PRIORITIES ];
Particle *m_allParticlesTail[ NUM_PARTICLE_PRIORITIES ];
ParticleSystemID m_uniqueSystemID; ///< unique system ID to assign to each system created
ParticleSystemList m_allParticleSystemList;
UnsignedInt m_particleCount;
UnsignedInt m_fieldParticleCount; ///< this does not need to be xfered, since it is evaluated every frame
UnsignedInt m_particleSystemCount;
Int m_onScreenParticleCount; ///< number of particles displayed on screen per frame
UnsignedInt m_lastLogicFrameUpdate;
Int m_localPlayerIndex; ///