2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
2017-07-26 19:18:27 +00:00
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2017-07-26 21:45:10 +00:00
2012-09-19 15:15:01 +00:00
# include "platform/platform.h"
# include "T3D/fx/particleEmitter.h"
# include "scene/sceneManager.h"
# include "scene/sceneRenderState.h"
# include "console/consoleTypes.h"
2012-11-05 23:03:01 +00:00
# include "console/typeValidators.h"
2012-09-19 15:15:01 +00:00
# include "core/stream/bitStream.h"
# include "core/strings/stringUnit.h"
# include "math/mRandom.h"
# include "gfx/gfxDevice.h"
# include "gfx/primBuilder.h"
# include "gfx/gfxStringEnumTranslate.h"
# include "renderInstance/renderPassManager.h"
# include "T3D/gameBase/gameProcess.h"
# include "lighting/lightInfo.h"
# include "console/engineAPI.h"
2017-07-26 21:45:10 +00:00
# if defined(AFX_CAP_PARTICLE_POOLS)
# include "afx/util/afxParticlePool.h"
# endif
2012-09-19 15:15:01 +00:00
Point3F ParticleEmitter : : mWindVelocity ( 0.0 , 0.0 , 0.0 ) ;
const F32 ParticleEmitter : : AgedSpinToRadians = ( 1.0f / 1000.0f ) * ( 1.0f / 360.0f ) * M_PI_F * 2.0f ;
IMPLEMENT_CO_DATABLOCK_V1 ( ParticleEmitterData ) ;
IMPLEMENT_CONOBJECT ( ParticleEmitter ) ;
ConsoleDocClass ( ParticleEmitter ,
" @brief This object is responsible for spawning particles. \n \n "
" @note This class is not normally instantiated directly - to place a simple "
" particle emitting object in the scene, use a ParticleEmitterNode instead. \n \n "
" This class is the main interface for creating particles - though it is "
" usually only accessed from within another object like ParticleEmitterNode "
" or WheeledVehicle. If using this object class (via C++) directly, be aware "
" that it does <b>not</b> track changes in source axis or velocity over the "
" course of a single update, so emitParticles should be called at a fairly "
" fine grain. The emitter will potentially track the last particle to be "
" created into the next call to this function in order to create a uniformly "
" random time distribution of the particles. \n \n "
" If the object to which the emitter is attached is in motion, it should try "
" to ensure that for call (n+1) to this function, start is equal to the end "
" from call (n). This will ensure a uniform spatial distribution. \n \n "
" @ingroup FX \n "
" @see ParticleEmitterData \n "
" @see ParticleEmitterNode \n "
) ;
ConsoleDocClass ( ParticleEmitterData ,
" @brief Defines particle emission properties such as ejection angle, period "
" and velocity for a ParticleEmitter. \n \n "
" @tsexample \n "
" datablock ParticleEmitterData( GrenadeExpDustEmitter ) \n "
" { \n "
" ejectionPeriodMS = 1; \n "
" periodVarianceMS = 0; \n "
" ejectionVelocity = 15; \n "
" velocityVariance = 0.0; \n "
" ejectionOffset = 0.0; \n "
" thetaMin = 85; \n "
" thetaMax = 85; \n "
" phiReferenceVel = 0; \n "
" phiVariance = 360; \n "
" overrideAdvance = false; \n "
" lifetimeMS = 200; \n "
" particles = \" GrenadeExpDust \" ; \n "
" }; \n "
" @endtsexample \n \n "
" @ingroup FX \n "
" @see ParticleEmitter \n "
" @see ParticleData \n "
" @see ParticleEmitterNode \n "
) ;
2013-08-04 21:58:59 +00:00
static const F32 sgDefaultEjectionOffset = 0.f ;
static const F32 sgDefaultPhiReferenceVel = 0.f ;
static const F32 sgDefaultPhiVariance = 360.f ;
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// ParticleEmitterData
//-----------------------------------------------------------------------------
ParticleEmitterData : : ParticleEmitterData ( )
{
VECTOR_SET_ASSOCIATION ( particleDataBlocks ) ;
VECTOR_SET_ASSOCIATION ( dataBlockIds ) ;
ejectionPeriodMS = 100 ; // 10 Particles Per second
periodVarianceMS = 0 ; // exactly
ejectionVelocity = 2.0f ; // From 1.0 - 3.0 meters per sec
velocityVariance = 1.0f ;
ejectionOffset = sgDefaultEjectionOffset ; // ejection from the emitter point
2013-08-06 18:36:17 +00:00
ejectionOffsetVariance = 0.0f ;
2012-09-19 15:15:01 +00:00
thetaMin = 0.0f ; // All heights
thetaMax = 90.0f ;
phiReferenceVel = sgDefaultPhiReferenceVel ; // All directions
phiVariance = sgDefaultPhiVariance ;
softnessDistance = 1.0f ;
ambientFactor = 0.0f ;
lifetimeMS = 0 ;
lifetimeVarianceMS = 0 ;
overrideAdvance = true ;
orientParticles = false ;
orientOnVelocity = true ;
useEmitterSizes = false ;
useEmitterColors = false ;
particleString = NULL ;
partListInitSize = 0 ;
// These members added for support of user defined blend factors
// and optional particle sorting.
blendStyle = ParticleRenderInst : : BlendUndefined ;
sortParticles = false ;
renderReflection = true ;
2014-11-28 22:34:26 +00:00
glow = false ;
2012-09-19 15:15:01 +00:00
reverseOrder = false ;
textureName = 0 ;
textureHandle = 0 ;
highResOnly = true ;
alignParticles = false ;
alignDirection = Point3F ( 0.0f , 1.0f , 0.0f ) ;
2017-07-26 21:45:10 +00:00
ejectionInvert = false ;
fade_color = false ;
fade_alpha = false ;
fade_size = false ;
parts_per_eject = 1 ;
use_emitter_xfm = false ;
# if defined(AFX_CAP_PARTICLE_POOLS)
pool_datablock = 0 ;
pool_index = 0 ;
pool_depth_fade = false ;
pool_radial_fade = false ;
do_pool_id_convert = false ;
# endif
2012-09-19 15:15:01 +00:00
}
// Enum tables used for fields blendStyle, srcBlendFactor, dstBlendFactor.
// Note that the enums for srcBlendFactor and dstBlendFactor are consistent
// with the blending enums used in Torque Game Builder.
typedef ParticleRenderInst : : BlendStyle ParticleBlendStyle ;
DefineEnumType ( ParticleBlendStyle ) ;
ImplementEnumType ( ParticleBlendStyle ,
" The type of visual blending style to apply to the particles. \n "
" @ingroup FX \n \n " )
{ ParticleRenderInst : : BlendNormal , " NORMAL " , " No blending style. \n " } ,
{ ParticleRenderInst : : BlendAdditive , " ADDITIVE " , " Adds the color of the pixel to the frame buffer with full alpha for each pixel. \n " } ,
{ ParticleRenderInst : : BlendSubtractive , " SUBTRACTIVE " , " Subtractive Blending. Reverses the color model, causing dark colors to have a stronger visual effect. \n " } ,
{ ParticleRenderInst : : BlendPremultAlpha , " PREMULTALPHA " , " Color blends with the colors of the imagemap rather than the alpha. \n " } ,
EndImplementEnumType ;
2013-02-01 17:22:03 +00:00
IRangeValidator ejectPeriodIValidator ( 1 , 2047 ) ;
IRangeValidator periodVarianceIValidator ( 0 , 2047 ) ;
FRangeValidator ejectionFValidator ( 0.f , 655.35f ) ;
FRangeValidator velVarianceFValidator ( 0.f , 163.83f ) ;
FRangeValidator thetaFValidator ( 0.f , 180.f ) ;
FRangeValidator phiFValidator ( 0.f , 360.f ) ;
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// initPersistFields
//-----------------------------------------------------------------------------
void ParticleEmitterData : : initPersistFields ( )
{
addGroup ( " ParticleEmitterData " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " ejectionPeriodMS " , TYPEID < S32 > ( ) , Offset ( ejectionPeriodMS , ParticleEmitterData ) , & ejectPeriodIValidator ,
2012-09-19 15:15:01 +00:00
" Time (in milliseconds) between each particle ejection. " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " periodVarianceMS " , TYPEID < S32 > ( ) , Offset ( periodVarianceMS , ParticleEmitterData ) , & periodVarianceIValidator ,
2012-09-19 15:15:01 +00:00
" Variance in ejection period, from 1 - ejectionPeriodMS. " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " ejectionVelocity " , TYPEID < F32 > ( ) , Offset ( ejectionVelocity , ParticleEmitterData ) , & ejectionFValidator ,
2012-09-19 15:15:01 +00:00
" Particle ejection velocity. " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " velocityVariance " , TYPEID < F32 > ( ) , Offset ( velocityVariance , ParticleEmitterData ) , & velVarianceFValidator ,
2012-09-19 15:15:01 +00:00
" Variance for ejection velocity, from 0 - ejectionVelocity. " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " ejectionOffset " , TYPEID < F32 > ( ) , Offset ( ejectionOffset , ParticleEmitterData ) , & ejectionFValidator ,
2012-09-19 15:15:01 +00:00
" Distance along ejection Z axis from which to eject particles. " ) ;
2013-08-06 18:36:17 +00:00
addFieldV ( " ejectionOffsetVariance " , TYPEID < F32 > ( ) , Offset ( ejectionOffsetVariance , ParticleEmitterData ) , & ejectionFValidator ,
" Distance Padding along ejection Z axis from which to eject particles. " ) ;
2012-09-19 15:15:01 +00:00
2013-02-01 17:22:03 +00:00
addFieldV ( " thetaMin " , TYPEID < F32 > ( ) , Offset ( thetaMin , ParticleEmitterData ) , & thetaFValidator ,
2012-09-19 15:15:01 +00:00
" Minimum angle, from the horizontal plane, to eject from. " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " thetaMax " , TYPEID < F32 > ( ) , Offset ( thetaMax , ParticleEmitterData ) , & thetaFValidator ,
2012-09-19 15:15:01 +00:00
" Maximum angle, from the horizontal plane, to eject particles from. " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " phiReferenceVel " , TYPEID < F32 > ( ) , Offset ( phiReferenceVel , ParticleEmitterData ) , & phiFValidator ,
2012-09-19 15:15:01 +00:00
" Reference angle, from the vertical plane, to eject particles from. " ) ;
2013-02-01 17:22:03 +00:00
addFieldV ( " phiVariance " , TYPEID < F32 > ( ) , Offset ( phiVariance , ParticleEmitterData ) , & phiFValidator ,
2012-09-19 15:15:01 +00:00
" Variance from the reference angle, from 0 - 360. " ) ;
addField ( " softnessDistance " , TYPEID < F32 > ( ) , Offset ( softnessDistance , ParticleEmitterData ) ,
" For soft particles, the distance (in meters) where particles will be "
" faded based on the difference in depth between the particle and the "
" scene geometry. " ) ;
addField ( " ambientFactor " , TYPEID < F32 > ( ) , Offset ( ambientFactor , ParticleEmitterData ) ,
" Used to generate the final particle color by controlling interpolation "
" between the particle color and the particle color multiplied by the "
" ambient light color. " ) ;
addField ( " overrideAdvance " , TYPEID < bool > ( ) , Offset ( overrideAdvance , ParticleEmitterData ) ,
" If false, particles emitted in the same frame have their positions "
" adjusted. If true, adjustment is skipped and particles will clump "
" together. " ) ;
addField ( " orientParticles " , TYPEID < bool > ( ) , Offset ( orientParticles , ParticleEmitterData ) ,
" If true, Particles will always face the camera. " ) ;
addField ( " orientOnVelocity " , TYPEID < bool > ( ) , Offset ( orientOnVelocity , ParticleEmitterData ) ,
" If true, particles will be oriented to face in the direction they are moving. " ) ;
addField ( " particles " , TYPEID < StringTableEntry > ( ) , Offset ( particleString , ParticleEmitterData ) ,
" @brief List of space or TAB delimited ParticleData datablock names. \n \n "
" A random one of these datablocks is selected each time a particle is "
" emitted. " ) ;
addField ( " lifetimeMS " , TYPEID < S32 > ( ) , Offset ( lifetimeMS , ParticleEmitterData ) ,
" Lifetime of emitted particles (in milliseconds). " ) ;
addField ( " lifetimeVarianceMS " , TYPEID < S32 > ( ) , Offset ( lifetimeVarianceMS , ParticleEmitterData ) ,
" Variance in particle lifetime from 0 - lifetimeMS. " ) ;
addField ( " useEmitterSizes " , TYPEID < bool > ( ) , Offset ( useEmitterSizes , ParticleEmitterData ) ,
" @brief If true, use emitter specified sizes instead of datablock sizes. \n "
" Useful for Debris particle emitters that control the particle size. " ) ;
addField ( " useEmitterColors " , TYPEID < bool > ( ) , Offset ( useEmitterColors , ParticleEmitterData ) ,
" @brief If true, use emitter specified colors instead of datablock colors. \n \n "
" Useful for ShapeBase dust and WheeledVehicle wheel particle emitters that use "
" the current material to control particle color. " ) ;
/// These fields added for support of user defined blend factors and optional particle sorting.
//@{
addField ( " blendStyle " , TYPEID < ParticleRenderInst : : BlendStyle > ( ) , Offset ( blendStyle , ParticleEmitterData ) ,
" String value that controls how emitted particles blend with the scene. " ) ;
addField ( " sortParticles " , TYPEID < bool > ( ) , Offset ( sortParticles , ParticleEmitterData ) ,
" If true, particles are sorted furthest to nearest. " ) ;
addField ( " reverseOrder " , TYPEID < bool > ( ) , Offset ( reverseOrder , ParticleEmitterData ) ,
" @brief If true, reverses the normal draw order of particles. \n \n "
" Particles are normally drawn from newest to oldest, or in Z order "
" (furthest first) if sortParticles is true. Setting this field to "
" true will reverse that order: oldest first, or nearest first if "
" sortParticles is true. " ) ;
addField ( " textureName " , TYPEID < StringTableEntry > ( ) , Offset ( textureName , ParticleEmitterData ) ,
" Optional texture to override ParticleData::textureName. " ) ;
addField ( " alignParticles " , TYPEID < bool > ( ) , Offset ( alignParticles , ParticleEmitterData ) ,
" If true, particles always face along the axis defined by alignDirection. " ) ;
addProtectedField ( " alignDirection " , TYPEID < Point3F > ( ) , Offset ( alignDirection , ParticleEmitterData ) , & ParticleEmitterData : : _setAlignDirection , & defaultProtectedGetFn ,
" The direction aligned particles should face, only valid if alignParticles is true. " ) ;
addField ( " highResOnly " , TYPEID < bool > ( ) , Offset ( highResOnly , ParticleEmitterData ) ,
" This particle system should not use the mixed-resolution renderer. "
" If your particle system has large amounts of overdraw, consider "
" disabling this option. " ) ;
addField ( " renderReflection " , TYPEID < bool > ( ) , Offset ( renderReflection , ParticleEmitterData ) ,
" Controls whether particles are rendered onto reflective surfaces like water. " ) ;
2014-11-28 22:34:26 +00:00
addField ( " glow " , TYPEID < bool > ( ) , Offset ( glow , ParticleEmitterData ) ,
" If true, the particles are rendered to the glow buffer as well. " ) ;
2012-09-19 15:15:01 +00:00
//@}
endGroup ( " ParticleEmitterData " ) ;
2017-07-26 21:45:10 +00:00
addGroup ( " AFX " ) ;
addField ( " ejectionInvert " , TypeBool , Offset ( ejectionInvert , ParticleEmitterData ) ) ;
addField ( " fadeColor " , TypeBool , Offset ( fade_color , ParticleEmitterData ) ) ;
addField ( " fadeAlpha " , TypeBool , Offset ( fade_alpha , ParticleEmitterData ) ) ;
addField ( " fadeSize " , TypeBool , Offset ( fade_size , ParticleEmitterData ) ) ;
// useEmitterTransform currently does not work in TGEA or T3D
addField ( " useEmitterTransform " , TypeBool , Offset ( use_emitter_xfm , ParticleEmitterData ) ) ;
endGroup ( " AFX " ) ;
# if defined(AFX_CAP_PARTICLE_POOLS)
addGroup ( " AFX Pooled Particles " ) ;
addField ( " poolData " , TYPEID < afxParticlePoolData > ( ) , Offset ( pool_datablock , ParticleEmitterData ) ) ;
addField ( " poolIndex " , TypeS32 , Offset ( pool_index , ParticleEmitterData ) ) ;
addField ( " poolDepthFade " , TypeBool , Offset ( pool_depth_fade , ParticleEmitterData ) ) ;
addField ( " poolRadialFade " , TypeBool , Offset ( pool_radial_fade , ParticleEmitterData ) ) ;
endGroup ( " AFX Pooled Particles " ) ;
# endif
2017-07-26 19:18:27 +00:00
// disallow some field substitutions
disableFieldSubstitutions ( " particles " ) ;
onlyKeepClearSubstitutions ( " poolData " ) ; // subs resolving to "~~", or "~0" are OK
2012-09-19 15:15:01 +00:00
Parent : : initPersistFields ( ) ;
}
bool ParticleEmitterData : : _setAlignDirection ( void * object , const char * index , const char * data )
{
ParticleEmitterData * p = static_cast < ParticleEmitterData * > ( object ) ;
Con : : setData ( TypePoint3F , & p - > alignDirection , 0 , 1 , & data ) ;
p - > alignDirection . normalizeSafe ( ) ;
// we already set the field
return false ;
}
//-----------------------------------------------------------------------------
// packData
//-----------------------------------------------------------------------------
void ParticleEmitterData : : packData ( BitStream * stream )
{
Parent : : packData ( stream ) ;
2012-11-05 23:03:01 +00:00
stream - > writeInt ( ejectionPeriodMS , 11 ) ; // must match limit on valid range in ParticleEmitterData::initPersistFields
stream - > writeInt ( periodVarianceMS , 11 ) ;
2012-09-19 15:15:01 +00:00
stream - > writeInt ( ( S32 ) ( ejectionVelocity * 100 ) , 16 ) ;
stream - > writeInt ( ( S32 ) ( velocityVariance * 100 ) , 14 ) ;
if ( stream - > writeFlag ( ejectionOffset ! = sgDefaultEjectionOffset ) )
stream - > writeInt ( ( S32 ) ( ejectionOffset * 100 ) , 16 ) ;
2013-08-06 18:36:17 +00:00
if ( stream - > writeFlag ( ejectionOffsetVariance ! = 0.0f ) )
stream - > writeInt ( ( S32 ) ( ejectionOffsetVariance * 100 ) , 16 ) ;
2012-09-19 15:15:01 +00:00
stream - > writeRangedU32 ( ( U32 ) thetaMin , 0 , 180 ) ;
stream - > writeRangedU32 ( ( U32 ) thetaMax , 0 , 180 ) ;
if ( stream - > writeFlag ( phiReferenceVel ! = sgDefaultPhiReferenceVel ) )
stream - > writeRangedU32 ( ( U32 ) phiReferenceVel , 0 , 360 ) ;
if ( stream - > writeFlag ( phiVariance ! = sgDefaultPhiVariance ) )
stream - > writeRangedU32 ( ( U32 ) phiVariance , 0 , 360 ) ;
stream - > write ( softnessDistance ) ;
stream - > write ( ambientFactor ) ;
stream - > writeFlag ( overrideAdvance ) ;
stream - > writeFlag ( orientParticles ) ;
stream - > writeFlag ( orientOnVelocity ) ;
stream - > write ( lifetimeMS ) ;
stream - > write ( lifetimeVarianceMS ) ;
stream - > writeFlag ( useEmitterSizes ) ;
stream - > writeFlag ( useEmitterColors ) ;
stream - > write ( dataBlockIds . size ( ) ) ;
for ( U32 i = 0 ; i < dataBlockIds . size ( ) ; i + + )
stream - > write ( dataBlockIds [ i ] ) ;
stream - > writeFlag ( sortParticles ) ;
stream - > writeFlag ( reverseOrder ) ;
if ( stream - > writeFlag ( textureName ! = 0 ) )
stream - > writeString ( textureName ) ;
if ( stream - > writeFlag ( alignParticles ) )
{
stream - > write ( alignDirection . x ) ;
stream - > write ( alignDirection . y ) ;
stream - > write ( alignDirection . z ) ;
}
stream - > writeFlag ( highResOnly ) ;
stream - > writeFlag ( renderReflection ) ;
2014-11-28 22:34:26 +00:00
stream - > writeFlag ( glow ) ;
2012-09-19 15:15:01 +00:00
stream - > writeInt ( blendStyle , 4 ) ;
2017-07-26 21:45:10 +00:00
stream - > writeFlag ( ejectionInvert ) ;
stream - > writeFlag ( fade_color ) ;
stream - > writeFlag ( fade_alpha ) ;
stream - > writeFlag ( fade_size ) ;
stream - > writeFlag ( use_emitter_xfm ) ;
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( stream - > writeFlag ( pool_datablock ) )
{
stream - > writeRangedU32 ( packed ? SimObjectId ( ( uintptr_t ) pool_datablock ) : pool_datablock - > getId ( ) , DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
stream - > write ( pool_index ) ;
stream - > writeFlag ( pool_depth_fade ) ;
stream - > writeFlag ( pool_radial_fade ) ;
}
# endif
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
// unpackData
//-----------------------------------------------------------------------------
void ParticleEmitterData : : unpackData ( BitStream * stream )
{
Parent : : unpackData ( stream ) ;
2012-11-05 23:03:01 +00:00
ejectionPeriodMS = stream - > readInt ( 11 ) ;
periodVarianceMS = stream - > readInt ( 11 ) ;
2012-09-19 15:15:01 +00:00
ejectionVelocity = stream - > readInt ( 16 ) / 100.0f ;
velocityVariance = stream - > readInt ( 14 ) / 100.0f ;
if ( stream - > readFlag ( ) )
ejectionOffset = stream - > readInt ( 16 ) / 100.0f ;
else
ejectionOffset = sgDefaultEjectionOffset ;
2013-08-06 18:36:17 +00:00
if ( stream - > readFlag ( ) )
ejectionOffsetVariance = stream - > readInt ( 16 ) / 100.0f ;
else
ejectionOffsetVariance = 0.0f ;
2012-09-19 15:15:01 +00:00
thetaMin = ( F32 ) stream - > readRangedU32 ( 0 , 180 ) ;
thetaMax = ( F32 ) stream - > readRangedU32 ( 0 , 180 ) ;
if ( stream - > readFlag ( ) )
phiReferenceVel = ( F32 ) stream - > readRangedU32 ( 0 , 360 ) ;
else
phiReferenceVel = sgDefaultPhiReferenceVel ;
if ( stream - > readFlag ( ) )
phiVariance = ( F32 ) stream - > readRangedU32 ( 0 , 360 ) ;
else
phiVariance = sgDefaultPhiVariance ;
stream - > read ( & softnessDistance ) ;
stream - > read ( & ambientFactor ) ;
overrideAdvance = stream - > readFlag ( ) ;
orientParticles = stream - > readFlag ( ) ;
orientOnVelocity = stream - > readFlag ( ) ;
stream - > read ( & lifetimeMS ) ;
stream - > read ( & lifetimeVarianceMS ) ;
useEmitterSizes = stream - > readFlag ( ) ;
useEmitterColors = stream - > readFlag ( ) ;
U32 size ; stream - > read ( & size ) ;
dataBlockIds . setSize ( size ) ;
for ( U32 i = 0 ; i < dataBlockIds . size ( ) ; i + + )
stream - > read ( & dataBlockIds [ i ] ) ;
sortParticles = stream - > readFlag ( ) ;
reverseOrder = stream - > readFlag ( ) ;
textureName = ( stream - > readFlag ( ) ) ? stream - > readSTString ( ) : 0 ;
alignParticles = stream - > readFlag ( ) ;
if ( alignParticles )
{
stream - > read ( & alignDirection . x ) ;
stream - > read ( & alignDirection . y ) ;
stream - > read ( & alignDirection . z ) ;
}
highResOnly = stream - > readFlag ( ) ;
renderReflection = stream - > readFlag ( ) ;
2014-11-28 22:34:26 +00:00
glow = stream - > readFlag ( ) ;
2012-09-19 15:15:01 +00:00
blendStyle = stream - > readInt ( 4 ) ;
2017-07-26 21:45:10 +00:00
ejectionInvert = stream - > readFlag ( ) ;
fade_color = stream - > readFlag ( ) ;
fade_alpha = stream - > readFlag ( ) ;
fade_size = stream - > readFlag ( ) ;
use_emitter_xfm = stream - > readFlag ( ) ;
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( stream - > readFlag ( ) )
{
pool_datablock = ( afxParticlePoolData * ) stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
stream - > read ( & pool_index ) ;
pool_depth_fade = stream - > readFlag ( ) ;
pool_radial_fade = stream - > readFlag ( ) ;
do_pool_id_convert = true ;
}
# endif
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
// onAdd
//-----------------------------------------------------------------------------
bool ParticleEmitterData : : onAdd ( )
{
if ( Parent : : onAdd ( ) = = false )
return false ;
// if (overrideAdvance == true) {
// Con::errorf(ConsoleLogEntry::General, "ParticleEmitterData: Not going to work. Fix it!");
// return false;
// }
// Validate the parameters...
//
if ( ejectionPeriodMS < 1 )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) period < 1 ms " , getName ( ) ) ;
ejectionPeriodMS = 1 ;
}
if ( periodVarianceMS > = ejectionPeriodMS )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) periodVariance >= period " , getName ( ) ) ;
periodVarianceMS = ejectionPeriodMS - 1 ;
}
if ( ejectionVelocity < 0.0f )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) ejectionVelocity < 0.0f " , getName ( ) ) ;
ejectionVelocity = 0.0f ;
}
if ( velocityVariance < 0.0f )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) velocityVariance < 0.0f " , getName ( ) ) ;
velocityVariance = 0.0f ;
}
if ( velocityVariance > ejectionVelocity )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) velocityVariance > ejectionVelocity " , getName ( ) ) ;
velocityVariance = ejectionVelocity ;
}
if ( ejectionOffset < 0.0f )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) ejectionOffset < 0 " , getName ( ) ) ;
ejectionOffset = 0.0f ;
}
if ( thetaMin < 0.0f )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) thetaMin < 0.0 " , getName ( ) ) ;
thetaMin = 0.0f ;
}
if ( thetaMax > 180.0f )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) thetaMax > 180.0 " , getName ( ) ) ;
thetaMax = 180.0f ;
}
if ( thetaMin > thetaMax )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) thetaMin > thetaMax " , getName ( ) ) ;
thetaMin = thetaMax ;
}
if ( phiVariance < 0.0f | | phiVariance > 360.0f )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) invalid phiVariance " , getName ( ) ) ;
phiVariance = phiVariance < 0.0f ? 0.0f : 360.0f ;
}
if ( softnessDistance < 0.0f )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) invalid softnessDistance " , getName ( ) ) ;
softnessDistance = 0.0f ;
}
if ( particleString = = NULL & & dataBlockIds . size ( ) = = 0 )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) no particleString, invalid datablock " , getName ( ) ) ;
return false ;
}
if ( particleString & & particleString [ 0 ] = = ' \0 ' )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) no particleString, invalid datablock " , getName ( ) ) ;
return false ;
}
if ( particleString & & dStrlen ( particleString ) > 255 )
{
Con : : errorf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) particle string too long [> 255 chars] " , getName ( ) ) ;
return false ;
}
if ( lifetimeMS < 0 )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) lifetimeMS < 0.0f " , getName ( ) ) ;
lifetimeMS = 0 ;
}
if ( lifetimeVarianceMS > lifetimeMS )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) lifetimeVarianceMS >= lifetimeMS " , getName ( ) ) ;
lifetimeVarianceMS = lifetimeMS ;
}
// load the particle datablocks...
//
if ( particleString ! = NULL )
{
// particleString is once again a list of particle datablocks so it
// must be parsed to extract the particle references.
// First we parse particleString into a list of particle name tokens
Vector < char * > dataBlocks ( __FILE__ , __LINE__ ) ;
2018-03-09 01:59:40 +00:00
dsize_t tokLen = dStrlen ( particleString ) + 1 ;
char * tokCopy = new char [ tokLen ] ;
dStrcpy ( tokCopy , particleString , tokLen ) ;
2012-09-19 15:15:01 +00:00
char * currTok = dStrtok ( tokCopy , " \t " ) ;
while ( currTok ! = NULL )
{
dataBlocks . push_back ( currTok ) ;
currTok = dStrtok ( NULL , " \t " ) ;
}
if ( dataBlocks . size ( ) = = 0 )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) invalid particles string. No datablocks found " , getName ( ) ) ;
delete [ ] tokCopy ;
return false ;
}
// Now we convert the particle name tokens into particle datablocks and IDs
particleDataBlocks . clear ( ) ;
dataBlockIds . clear ( ) ;
for ( U32 i = 0 ; i < dataBlocks . size ( ) ; i + + )
{
ParticleData * pData = NULL ;
if ( Sim : : findObject ( dataBlocks [ i ] , pData ) = = false )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) unable to find particle datablock: %s " , getName ( ) , dataBlocks [ i ] ) ;
}
else
{
particleDataBlocks . push_back ( pData ) ;
dataBlockIds . push_back ( pData - > getId ( ) ) ;
}
}
// cleanup
delete [ ] tokCopy ;
// check that we actually found some particle datablocks
if ( particleDataBlocks . size ( ) = = 0 )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) unable to find any particle datablocks " , getName ( ) ) ;
return false ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// preload
//-----------------------------------------------------------------------------
bool ParticleEmitterData : : preload ( bool server , String & errorStr )
{
if ( Parent : : preload ( server , errorStr ) = = false )
return false ;
particleDataBlocks . clear ( ) ;
for ( U32 i = 0 ; i < dataBlockIds . size ( ) ; i + + )
{
ParticleData * pData = NULL ;
if ( Sim : : findObject ( dataBlockIds [ i ] , pData ) = = false )
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) unable to find particle datablock: %d " , getName ( ) , dataBlockIds [ i ] ) ;
else
particleDataBlocks . push_back ( pData ) ;
}
if ( ! server )
{
2017-07-26 21:45:10 +00:00
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( do_pool_id_convert )
{
SimObjectId db_id = ( SimObjectId ) ( uintptr_t ) pool_datablock ;
if ( db_id ! = 0 )
{
// try to convert id to pointer
if ( ! Sim : : findObject ( db_id , pool_datablock ) )
{
Con : : errorf ( " ParticleEmitterData::reload() -- bad datablockId: 0x%x (poolData) " , db_id ) ;
}
}
do_pool_id_convert = false ;
}
# endif
2012-09-19 15:15:01 +00:00
// load emitter texture if specified
if ( textureName & & textureName [ 0 ] )
{
2017-06-23 16:36:20 +00:00
textureHandle = GFXTexHandle ( textureName , & GFXStaticTextureSRGBProfile , avar ( " %s() - textureHandle (line %d) " , __FUNCTION__ , __LINE__ ) ) ;
2012-09-19 15:15:01 +00:00
if ( ! textureHandle )
{
errorStr = String : : ToString ( " Missing particle emitter texture: %s " , textureName ) ;
return false ;
}
}
// otherwise, check that all particles refer to the same texture
else if ( particleDataBlocks . size ( ) > 1 )
{
StringTableEntry txr_name = particleDataBlocks [ 0 ] - > textureName ;
for ( S32 i = 1 ; i < particleDataBlocks . size ( ) ; i + + )
{
// warn if particle textures are inconsistent
if ( particleDataBlocks [ i ] - > textureName ! = txr_name )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) particles reference different textures. " , getName ( ) ) ;
break ;
}
}
}
}
// if blend-style is undefined check legacy useInvAlpha settings
if ( blendStyle = = ParticleRenderInst : : BlendUndefined & & particleDataBlocks . size ( ) > 0 )
{
bool useInvAlpha = particleDataBlocks [ 0 ] - > useInvAlpha ;
for ( S32 i = 1 ; i < particleDataBlocks . size ( ) ; i + + )
{
// warn if blend-style legacy useInvAlpha settings are inconsistent
if ( particleDataBlocks [ i ] - > useInvAlpha ! = useInvAlpha )
{
Con : : warnf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) particles have inconsistent useInvAlpha settings. " , getName ( ) ) ;
break ;
}
}
blendStyle = ( useInvAlpha ) ? ParticleRenderInst : : BlendNormal : ParticleRenderInst : : BlendAdditive ;
}
if ( ! server )
{
allocPrimBuffer ( ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// alloc PrimitiveBuffer
// The datablock allocates this static index buffer because it's the same
// for all of the emitters - each particle quad uses the same index ordering
//-----------------------------------------------------------------------------
void ParticleEmitterData : : allocPrimBuffer ( S32 overrideSize )
{
// calculate particle list size
AssertFatal ( particleDataBlocks . size ( ) > 0 , " Error, no particles found. " ) ;
U32 maxPartLife = particleDataBlocks [ 0 ] - > lifetimeMS + particleDataBlocks [ 0 ] - > lifetimeVarianceMS ;
for ( S32 i = 1 ; i < particleDataBlocks . size ( ) ; i + + )
{
U32 mpl = particleDataBlocks [ i ] - > lifetimeMS + particleDataBlocks [ i ] - > lifetimeVarianceMS ;
if ( mpl > maxPartLife )
maxPartLife = mpl ;
}
partListInitSize = maxPartLife / ( ejectionPeriodMS - periodVarianceMS ) ;
partListInitSize + = 8 ; // add 8 as "fudge factor" to make sure it doesn't realloc if it goes over by 1
2017-07-26 21:45:10 +00:00
if ( parts_per_eject > 1 )
partListInitSize * = parts_per_eject ;
2012-09-19 15:15:01 +00:00
// if override size is specified, then the emitter overran its buffer and needs a larger allocation
if ( overrideSize ! = - 1 )
{
partListInitSize = overrideSize ;
}
// create index buffer based on that size
U32 indexListSize = partListInitSize * 6 ; // 6 indices per particle
U16 * indices = new U16 [ indexListSize ] ;
for ( U32 i = 0 ; i < partListInitSize ; i + + )
{
// this index ordering should be optimal (hopefully) for the vertex cache
U16 * idx = & indices [ i * 6 ] ;
volatile U32 offset = i * 4 ; // set to volatile to fix VC6 Release mode compiler bug
idx [ 0 ] = 0 + offset ;
idx [ 1 ] = 1 + offset ;
idx [ 2 ] = 3 + offset ;
idx [ 3 ] = 1 + offset ;
idx [ 4 ] = 3 + offset ;
idx [ 5 ] = 2 + offset ;
}
U16 * ibIndices ;
GFXBufferType bufferType = GFXBufferTypeStatic ;
primBuff . set ( GFX , indexListSize , 0 , bufferType ) ;
primBuff . lock ( & ibIndices ) ;
dMemcpy ( ibIndices , indices , indexListSize * sizeof ( U16 ) ) ;
primBuff . unlock ( ) ;
delete [ ] indices ;
}
2017-07-26 20:10:43 +00:00
//#define TRACK_PARTICLE_EMITTER_DATA_CLONES
# ifdef TRACK_PARTICLE_EMITTER_DATA_CLONES
static int emitter_data_clones = 0 ;
# endif
ParticleEmitterData : : ParticleEmitterData ( const ParticleEmitterData & other , bool temp_clone ) : GameBaseData ( other , temp_clone )
{
# ifdef TRACK_PARTICLE_EMITTER_DATA_CLONES
emitter_data_clones + + ;
if ( emitter_data_clones = = 1 )
Con : : errorf ( " ParticleEmitterData -- Clones are on the loose! " ) ;
# endif
ejectionPeriodMS = other . ejectionPeriodMS ;
periodVarianceMS = other . periodVarianceMS ;
ejectionVelocity = other . ejectionVelocity ;
velocityVariance = other . velocityVariance ;
ejectionOffset = other . ejectionOffset ;
ejectionOffsetVariance = other . ejectionOffsetVariance ;
thetaMin = other . thetaMin ;
thetaMax = other . thetaMax ;
phiReferenceVel = other . phiReferenceVel ;
phiVariance = other . phiVariance ;
softnessDistance = other . softnessDistance ;
ambientFactor = other . ambientFactor ;
lifetimeMS = other . lifetimeMS ;
lifetimeVarianceMS = other . lifetimeVarianceMS ;
overrideAdvance = other . overrideAdvance ;
orientParticles = other . orientParticles ;
orientOnVelocity = other . orientOnVelocity ;
useEmitterSizes = other . useEmitterSizes ;
useEmitterColors = other . useEmitterColors ;
alignParticles = other . alignParticles ;
alignDirection = other . alignDirection ;
particleString = other . particleString ;
particleDataBlocks = other . particleDataBlocks ; // -- derived from particleString
dataBlockIds = other . dataBlockIds ; // -- derived from particleString
partListInitSize = other . partListInitSize ; // -- approx calc from other fields
primBuff = other . primBuff ;
blendStyle = other . blendStyle ;
sortParticles = other . sortParticles ;
reverseOrder = other . reverseOrder ;
textureName = other . textureName ;
textureHandle = other . textureHandle ; // -- TextureHandle loads using textureName
highResOnly = other . highResOnly ;
renderReflection = other . renderReflection ;
fade_color = other . fade_color ;
fade_size = other . fade_size ;
fade_alpha = other . fade_alpha ;
ejectionInvert = other . ejectionInvert ;
parts_per_eject = other . parts_per_eject ; // -- set to 1 (used by subclasses)
use_emitter_xfm = other . use_emitter_xfm ;
# if defined(AFX_CAP_PARTICLE_POOLS)
pool_datablock = other . pool_datablock ;
pool_index = other . pool_index ;
pool_depth_fade = other . pool_depth_fade ;
pool_radial_fade = other . pool_radial_fade ;
do_pool_id_convert = other . do_pool_id_convert ; // -- flags pool id conversion need
# endif
}
ParticleEmitterData : : ~ ParticleEmitterData ( )
{
if ( ! isTempClone ( ) )
return ;
for ( S32 i = 0 ; i < particleDataBlocks . size ( ) ; i + + )
{
if ( particleDataBlocks [ i ] & & particleDataBlocks [ i ] - > isTempClone ( ) )
{
delete particleDataBlocks [ i ] ;
particleDataBlocks [ i ] = 0 ;
}
}
# ifdef TRACK_PARTICLE_EMITTER_DATA_CLONES
if ( emitter_data_clones > 0 )
{
emitter_data_clones - - ;
if ( emitter_data_clones = = 0 )
Con : : errorf ( " ParticleEmitterData -- Clones eliminated! " ) ;
}
else
Con : : errorf ( " ParticleEmitterData -- Too many clones deleted! " ) ;
# endif
}
ParticleEmitterData * ParticleEmitterData : : cloneAndPerformSubstitutions ( const SimObject * owner , S32 index )
{
if ( ! owner )
return this ;
bool clone_parts_db = false ;
// note -- this could be checked when the particle blocks are evaluated
for ( S32 i = 0 ; i < this - > particleDataBlocks . size ( ) ; i + + )
{
if ( this - > particleDataBlocks [ i ] & & ( this - > particleDataBlocks [ i ] - > getSubstitutionCount ( ) > 0 ) )
{
clone_parts_db = true ;
break ;
}
}
ParticleEmitterData * sub_emitter_db = this ;
if ( this - > getSubstitutionCount ( ) > 0 | | clone_parts_db )
{
sub_emitter_db = new ParticleEmitterData ( * this , true ) ;
performSubstitutions ( sub_emitter_db , owner , index ) ;
if ( clone_parts_db )
{
for ( S32 i = 0 ; i < sub_emitter_db - > particleDataBlocks . size ( ) ; i + + )
{
if ( sub_emitter_db - > particleDataBlocks [ i ] & & ( sub_emitter_db - > particleDataBlocks [ i ] - > getSubstitutionCount ( ) > 0 ) )
{
ParticleData * orig_db = sub_emitter_db - > particleDataBlocks [ i ] ;
sub_emitter_db - > particleDataBlocks [ i ] = new ParticleData ( * orig_db , true ) ;
orig_db - > performSubstitutions ( sub_emitter_db - > particleDataBlocks [ i ] , owner , index ) ;
}
}
}
}
return sub_emitter_db ;
}
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// ParticleEmitter
//-----------------------------------------------------------------------------
ParticleEmitter : : ParticleEmitter ( )
{
mDeleteWhenEmpty = false ;
mDeleteOnTick = false ;
mInternalClock = 0 ;
mNextParticleTime = 0 ;
mLastPosition . set ( 0 , 0 , 0 ) ;
mHasLastPosition = false ;
mLifetimeMS = 0 ;
mElapsedTimeMS = 0 ;
part_store = 0 ;
part_freelist = NULL ;
part_list_head . next = NULL ;
n_part_capacity = 0 ;
n_parts = 0 ;
mCurBuffSize = 0 ;
mDead = false ;
2013-04-15 16:54:34 +00:00
mDataBlock = NULL ;
2012-09-19 15:15:01 +00:00
// ParticleEmitter should be allocated on the client only.
mNetFlags . set ( IsGhost ) ;
2017-07-26 21:45:10 +00:00
fade_amt = 1.0f ;
forced_bbox = false ;
db_temp_clone = false ;
pos_pe . set ( 0 , 0 , 0 ) ;
sort_priority = 0 ;
2017-07-26 20:10:43 +00:00
mDataBlock = 0 ;
2017-07-26 21:45:10 +00:00
# if defined(AFX_CAP_PARTICLE_POOLS)
pool = 0 ;
# endif
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
// destructor
//-----------------------------------------------------------------------------
ParticleEmitter : : ~ ParticleEmitter ( )
{
for ( S32 i = 0 ; i < part_store . size ( ) ; i + + )
{
delete [ ] part_store [ i ] ;
}
2017-07-26 20:10:43 +00:00
if ( db_temp_clone & & mDataBlock & & mDataBlock - > isTempClone ( ) )
{
for ( S32 i = 0 ; i < mDataBlock - > particleDataBlocks . size ( ) ; i + + )
{
if ( mDataBlock - > particleDataBlocks [ i ] & & mDataBlock - > particleDataBlocks [ i ] - > isTempClone ( ) )
{
delete mDataBlock - > particleDataBlocks [ i ] ;
mDataBlock - > particleDataBlocks [ i ] = 0 ;
}
}
delete mDataBlock ;
mDataBlock = 0 ;
}
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
// onAdd
//-----------------------------------------------------------------------------
bool ParticleEmitter : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
// add to client side mission cleanup
SimGroup * cleanup = dynamic_cast < SimGroup * > ( Sim : : findObject ( " ClientMissionCleanup " ) ) ;
if ( cleanup ! = NULL )
{
cleanup - > addObject ( this ) ;
}
removeFromProcessList ( ) ;
F32 radius = 5.0 ;
mObjBox . minExtents = Point3F ( - radius , - radius , - radius ) ;
mObjBox . maxExtents = Point3F ( radius , radius , radius ) ;
resetWorldBox ( ) ;
2017-07-26 21:45:10 +00:00
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( pool )
pool - > addParticleEmitter ( this ) ;
# endif
2012-09-19 15:15:01 +00:00
return true ;
}
//-----------------------------------------------------------------------------
// onRemove
//-----------------------------------------------------------------------------
void ParticleEmitter : : onRemove ( )
{
2017-07-26 21:45:10 +00:00
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( pool )
{
pool - > removeParticleEmitter ( this ) ;
pool = 0 ;
}
# endif
2012-09-19 15:15:01 +00:00
removeFromScene ( ) ;
Parent : : onRemove ( ) ;
}
//-----------------------------------------------------------------------------
// onNewDataBlock
//-----------------------------------------------------------------------------
bool ParticleEmitter : : onNewDataBlock ( GameBaseData * dptr , bool reload )
{
mDataBlock = dynamic_cast < ParticleEmitterData * > ( dptr ) ;
if ( ! mDataBlock | | ! Parent : : onNewDataBlock ( dptr , reload ) )
return false ;
mLifetimeMS = mDataBlock - > lifetimeMS ;
if ( mDataBlock - > lifetimeVarianceMS )
{
mLifetimeMS + = S32 ( gRandGen . randI ( ) % ( 2 * mDataBlock - > lifetimeVarianceMS + 1 ) ) - S32 ( mDataBlock - > lifetimeVarianceMS ) ;
}
// Allocate particle structures and init the freelist. Member part_store
// is a Vector so that we can allocate more particles if partListInitSize
// turns out to be too small.
//
if ( mDataBlock - > partListInitSize > 0 )
{
for ( S32 i = 0 ; i < part_store . size ( ) ; i + + )
{
delete [ ] part_store [ i ] ;
}
part_store . clear ( ) ;
n_part_capacity = mDataBlock - > partListInitSize ;
Particle * store_block = new Particle [ n_part_capacity ] ;
part_store . push_back ( store_block ) ;
part_freelist = store_block ;
Particle * last_part = part_freelist ;
Particle * part = last_part + 1 ;
for ( S32 i = 1 ; i < n_part_capacity ; i + + , part + + , last_part + + )
{
last_part - > next = part ;
}
store_block [ n_part_capacity - 1 ] . next = NULL ;
part_list_head . next = NULL ;
n_parts = 0 ;
}
2017-07-26 20:10:43 +00:00
if ( mDataBlock - > isTempClone ( ) )
{
db_temp_clone = true ;
return true ;
}
2012-09-19 15:15:01 +00:00
scriptOnNewDataBlock ( ) ;
return true ;
}
//-----------------------------------------------------------------------------
// getCollectiveColor
//-----------------------------------------------------------------------------
2017-06-23 16:36:20 +00:00
LinearColorF ParticleEmitter : : getCollectiveColor ( )
2012-09-19 15:15:01 +00:00
{
U32 count = 0 ;
2017-06-23 16:36:20 +00:00
LinearColorF color = LinearColorF ( 0.0f , 0.0f , 0.0f ) ;
2012-09-19 15:15:01 +00:00
count = n_parts ;
for ( Particle * part = part_list_head . next ; part ! = NULL ; part = part - > next )
{
color + = part - > color ;
}
if ( count > 0 )
{
color / = F32 ( count ) ;
}
//if(color.red == 0.0f && color.green == 0.0f && color.blue == 0.0f)
// color = color;
return color ;
}
//-----------------------------------------------------------------------------
// prepRenderImage
//-----------------------------------------------------------------------------
void ParticleEmitter : : prepRenderImage ( SceneRenderState * state )
{
2017-07-26 21:45:10 +00:00
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( pool )
return ;
# endif
2012-09-19 15:15:01 +00:00
if ( state - > isReflectPass ( ) & & ! getDataBlock ( ) - > renderReflection )
return ;
// Never render into shadows.
if ( state - > isShadowPass ( ) )
return ;
PROFILE_SCOPE ( ParticleEmitter_prepRenderImage ) ;
if ( mDead | |
n_parts = = 0 | |
part_list_head . next = = NULL )
return ;
RenderPassManager * renderManager = state - > getRenderPass ( ) ;
const Point3F & camPos = state - > getCameraPosition ( ) ;
copyToVB ( camPos , state - > getAmbientLightColor ( ) ) ;
if ( ! mVertBuff . isValid ( ) )
return ;
ParticleRenderInst * ri = renderManager - > allocInst < ParticleRenderInst > ( ) ;
ri - > vertBuff = & mVertBuff ;
ri - > primBuff = & getDataBlock ( ) - > primBuff ;
ri - > translucentSort = true ;
ri - > type = RenderPassManager : : RIT_Particle ;
ri - > sortDistSq = getRenderWorldBox ( ) . getSqDistanceToPoint ( camPos ) ;
2017-07-26 21:45:10 +00:00
ri - > defaultKey = ( - sort_priority * 100 ) ;
2012-09-19 15:15:01 +00:00
// Draw the system offscreen unless the highResOnly flag is set on the datablock
ri - > systemState = ( getDataBlock ( ) - > highResOnly ? PSS_AwaitingHighResDraw : PSS_AwaitingOffscreenDraw ) ;
ri - > modelViewProj = renderManager - > allocUniqueXform ( GFX - > getProjectionMatrix ( ) *
GFX - > getViewMatrix ( ) *
GFX - > getWorldMatrix ( ) ) ;
// Update position on the matrix before multiplying it
mBBObjToWorld . setPosition ( mLastPosition ) ;
ri - > bbModelViewProj = renderManager - > allocUniqueXform ( * ri - > modelViewProj * mBBObjToWorld ) ;
ri - > count = n_parts ;
ri - > blendStyle = mDataBlock - > blendStyle ;
2014-11-28 22:34:26 +00:00
ri - > glow = mDataBlock - > glow ;
2012-09-19 15:15:01 +00:00
// use first particle's texture unless there is an emitter texture to override it
if ( mDataBlock - > textureHandle )
ri - > diffuseTex = & * ( mDataBlock - > textureHandle ) ;
else
ri - > diffuseTex = & * ( part_list_head . next - > dataBlock - > textureHandle ) ;
ri - > softnessDistance = mDataBlock - > softnessDistance ;
// Sort by texture too.
2015-01-24 21:08:26 +00:00
ri - > defaultKey = ri - > diffuseTex ? ( uintptr_t ) ri - > diffuseTex : ( uintptr_t ) ri - > vertBuff ;
2012-09-19 15:15:01 +00:00
renderManager - > addInst ( ri ) ;
}
//-----------------------------------------------------------------------------
// setSizes
//-----------------------------------------------------------------------------
void ParticleEmitter : : setSizes ( F32 * sizeList )
{
2013-08-04 21:26:01 +00:00
for ( S32 i = 0 ; i < ParticleData : : PDC_NUM_KEYS ; i + + )
2012-09-19 15:15:01 +00:00
{
sizes [ i ] = sizeList [ i ] ;
}
}
//-----------------------------------------------------------------------------
// setColors
//-----------------------------------------------------------------------------
2017-06-23 16:36:20 +00:00
void ParticleEmitter : : setColors ( LinearColorF * colorList )
2012-09-19 15:15:01 +00:00
{
2013-08-04 21:26:01 +00:00
for ( S32 i = 0 ; i < ParticleData : : PDC_NUM_KEYS ; i + + )
2012-09-19 15:15:01 +00:00
{
colors [ i ] = colorList [ i ] ;
}
}
//-----------------------------------------------------------------------------
// deleteWhenEmpty
//-----------------------------------------------------------------------------
void ParticleEmitter : : deleteWhenEmpty ( )
{
// if the following asserts fire, there is a reasonable chance that you are trying to delete a particle emitter
// that has already been deleted (possibly by ClientMissionCleanup). If so, use a SimObjectPtr to the emitter and check it
// for null before calling this function.
AssertFatal ( isProperlyAdded ( ) , " ParticleEmitter must be registed before calling deleteWhenEmpty " ) ;
AssertFatal ( ! mDead , " ParticleEmitter already deleted " ) ;
AssertFatal ( ! isDeleted ( ) , " ParticleEmitter already deleted " ) ;
AssertFatal ( ! isRemoved ( ) , " ParticleEmitter already removed " ) ;
// this check is for non debug case, so that we don't write in to freed memory
bool okToDelete = ! mDead & & isProperlyAdded ( ) & & ! isDeleted ( ) & & ! isRemoved ( ) ;
if ( okToDelete )
{
mDeleteWhenEmpty = true ;
if ( ! n_parts )
{
// We're already empty, so delete us now.
mDead = true ;
deleteObject ( ) ;
}
else
AssertFatal ( getSceneManager ( ) ! = NULL , " ParticleEmitter not on process list and won't get ticked to death " ) ;
}
}
//-----------------------------------------------------------------------------
// emitParticles
//-----------------------------------------------------------------------------
void ParticleEmitter : : emitParticles ( const Point3F & point ,
const bool useLastPosition ,
const Point3F & axis ,
const Point3F & velocity ,
const U32 numMilliseconds )
{
if ( mDead ) return ;
// lifetime over - no more particles
if ( mLifetimeMS > 0 & & mElapsedTimeMS > mLifetimeMS )
{
return ;
}
2017-07-26 21:45:10 +00:00
pos_pe = point ;
2012-09-19 15:15:01 +00:00
Point3F realStart ;
if ( useLastPosition & & mHasLastPosition )
realStart = mLastPosition ;
else
realStart = point ;
emitParticles ( realStart , point ,
axis ,
velocity ,
numMilliseconds ) ;
}
//-----------------------------------------------------------------------------
// emitParticles
//-----------------------------------------------------------------------------
void ParticleEmitter : : emitParticles ( const Point3F & start ,
const Point3F & end ,
const Point3F & axis ,
const Point3F & velocity ,
const U32 numMilliseconds )
{
if ( mDead ) return ;
if ( mDataBlock - > particleDataBlocks . empty ( ) )
return ;
// lifetime over - no more particles
if ( mLifetimeMS > 0 & & mElapsedTimeMS > mLifetimeMS )
{
return ;
}
U32 currTime = 0 ;
bool particlesAdded = false ;
Point3F axisx ;
if ( mFabs ( axis . z ) < 0.9f )
mCross ( axis , Point3F ( 0 , 0 , 1 ) , & axisx ) ;
else
mCross ( axis , Point3F ( 0 , 1 , 0 ) , & axisx ) ;
axisx . normalize ( ) ;
if ( mNextParticleTime ! = 0 )
{
// Need to handle next particle
//
if ( mNextParticleTime > numMilliseconds )
{
// Defer to next update
// (Note that this introduces a potential spatial irregularity if the owning
// object is accelerating, and updating at a low frequency)
//
mNextParticleTime - = numMilliseconds ;
mInternalClock + = numMilliseconds ;
mLastPosition = end ;
mHasLastPosition = true ;
return ;
}
else
{
currTime + = mNextParticleTime ;
mInternalClock + = mNextParticleTime ;
// Emit particle at curr time
// Create particle at the correct position
Point3F pos ;
pos . interpolate ( start , end , F32 ( currTime ) / F32 ( numMilliseconds ) ) ;
2017-07-26 21:45:10 +00:00
addParticle ( pos , axis , velocity , axisx , numMilliseconds - currTime ) ;
2012-09-19 15:15:01 +00:00
particlesAdded = true ;
mNextParticleTime = 0 ;
}
}
while ( currTime < numMilliseconds )
{
S32 nextTime = mDataBlock - > ejectionPeriodMS ;
if ( mDataBlock - > periodVarianceMS ! = 0 )
{
nextTime + = S32 ( gRandGen . randI ( ) % ( 2 * mDataBlock - > periodVarianceMS + 1 ) ) -
S32 ( mDataBlock - > periodVarianceMS ) ;
}
AssertFatal ( nextTime > 0 , " Error, next particle ejection time must always be greater than 0 " ) ;
if ( currTime + nextTime > numMilliseconds )
{
mNextParticleTime = ( currTime + nextTime ) - numMilliseconds ;
mInternalClock + = numMilliseconds - currTime ;
AssertFatal ( mNextParticleTime > 0 , " Error, should not have deferred this particle! " ) ;
break ;
}
currTime + = nextTime ;
mInternalClock + = nextTime ;
// Create particle at the correct position
Point3F pos ;
pos . interpolate ( start , end , F32 ( currTime ) / F32 ( numMilliseconds ) ) ;
2017-07-26 21:45:10 +00:00
addParticle ( pos , axis , velocity , axisx , numMilliseconds - currTime ) ;
2012-09-19 15:15:01 +00:00
particlesAdded = true ;
// This override-advance code is restored in order to correctly adjust
// animated parameters of particles allocated within the same frame
// update. Note that ordering is important and this code correctly
// adds particles in the same newest-to-oldest ordering of the link-list.
//
// NOTE: We are assuming that the just added particle is at the head of our
// list. If that changes, so must this...
U32 advanceMS = numMilliseconds - currTime ;
if ( mDataBlock - > overrideAdvance = = false & & advanceMS ! = 0 )
{
Particle * last_part = part_list_head . next ;
if ( advanceMS > last_part - > totalLifetime )
{
part_list_head . next = last_part - > next ;
n_parts - - ;
last_part - > next = part_freelist ;
part_freelist = last_part ;
}
else
{
if ( advanceMS ! = 0 )
{
2017-07-26 21:45:10 +00:00
F32 t = F32 ( advanceMS ) / 1000.0 ;
Point3F a = last_part - > acc ;
a - = last_part - > vel * last_part - > dataBlock - > dragCoefficient ;
a - = mWindVelocity * last_part - > dataBlock - > windCoefficient ;
//a += Point3F(0.0f, 0.0f, -9.81f) * last_part->dataBlock->gravityCoefficient;
a . z + = - 9.81f * last_part - > dataBlock - > gravityCoefficient ; // as long as gravity is a constant, this is faster
last_part - > vel + = a * t ;
//last_part->pos += last_part->vel * t;
last_part - > pos_local + = last_part - > vel * t ;
2012-09-19 15:15:01 +00:00
2017-07-26 21:45:10 +00:00
// AFX -- allow subclasses to adjust the particle params here
sub_particleUpdate ( last_part ) ;
2012-09-19 15:15:01 +00:00
2017-07-26 21:45:10 +00:00
if ( last_part - > dataBlock - > constrain_pos )
last_part - > pos = last_part - > pos_local + this - > pos_pe ;
else
last_part - > pos = last_part - > pos_local ;
2012-09-19 15:15:01 +00:00
2017-07-26 21:45:10 +00:00
updateKeyData ( last_part ) ;
2012-09-19 15:15:01 +00:00
}
}
}
}
// DMMFIX: Lame and slow...
if ( particlesAdded = = true )
updateBBox ( ) ;
if ( n_parts > 0 & & getSceneManager ( ) = = NULL )
{
gClientSceneGraph - > addObjectToScene ( this ) ;
ClientProcessList : : get ( ) - > addObject ( this ) ;
}
mLastPosition = end ;
mHasLastPosition = true ;
}
//-----------------------------------------------------------------------------
// emitParticles
//-----------------------------------------------------------------------------
void ParticleEmitter : : emitParticles ( const Point3F & rCenter ,
const Point3F & rNormal ,
const F32 radius ,
const Point3F & velocity ,
S32 count )
{
if ( mDead ) return ;
// lifetime over - no more particles
if ( mLifetimeMS > 0 & & mElapsedTimeMS > mLifetimeMS )
{
return ;
}
Point3F axisx , axisy ;
Point3F axisz = rNormal ;
if ( axisz . isZero ( ) )
{
axisz . set ( 0.0 , 0.0 , 1.0 ) ;
}
if ( mFabs ( axisz . z ) < 0.98 )
{
mCross ( axisz , Point3F ( 0 , 0 , 1 ) , & axisy ) ;
axisy . normalize ( ) ;
}
else
{
mCross ( axisz , Point3F ( 0 , 1 , 0 ) , & axisy ) ;
axisy . normalize ( ) ;
}
mCross ( axisz , axisy , & axisx ) ;
axisx . normalize ( ) ;
// Should think of a better way to distribute the
// particles within the hemisphere.
for ( S32 i = 0 ; i < count ; i + + )
{
Point3F pos = axisx * ( radius * ( 1 - ( 2 * gRandGen . randF ( ) ) ) ) ;
pos + = axisy * ( radius * ( 1 - ( 2 * gRandGen . randF ( ) ) ) ) ;
pos + = axisz * ( radius * gRandGen . randF ( ) ) ;
Point3F axis = pos ;
axis . normalize ( ) ;
pos + = rCenter ;
2017-07-26 21:45:10 +00:00
addParticle ( pos , axis , velocity , axisz , 0 ) ;
2012-09-19 15:15:01 +00:00
}
// Set world bounding box
mObjBox . minExtents = rCenter - Point3F ( radius , radius , radius ) ;
mObjBox . maxExtents = rCenter + Point3F ( radius , radius , radius ) ;
resetWorldBox ( ) ;
// Make sure we're part of the world
if ( n_parts > 0 & & getSceneManager ( ) = = NULL )
{
gClientSceneGraph - > addObjectToScene ( this ) ;
ClientProcessList : : get ( ) - > addObject ( this ) ;
}
mHasLastPosition = false ;
}
//-----------------------------------------------------------------------------
// updateBBox - SLOW, bad news
//-----------------------------------------------------------------------------
void ParticleEmitter : : updateBBox ( )
{
2017-07-26 21:45:10 +00:00
if ( forced_bbox )
return ;
2012-09-19 15:15:01 +00:00
Point3F minPt ( 1e10 , 1e10 , 1e10 ) ;
Point3F maxPt ( - 1e10 , - 1e10 , - 1e10 ) ;
for ( Particle * part = part_list_head . next ; part ! = NULL ; part = part - > next )
{
Point3F particleSize ( part - > size * 0.5f , 0.0f , part - > size * 0.5f ) ;
minPt . setMin ( part - > pos - particleSize ) ;
maxPt . setMax ( part - > pos + particleSize ) ;
}
mObjBox = Box3F ( minPt , maxPt ) ;
MatrixF temp = getTransform ( ) ;
setTransform ( temp ) ;
mBBObjToWorld . identity ( ) ;
Point3F boxScale = mObjBox . getExtents ( ) ;
boxScale . x = getMax ( boxScale . x , 1.0f ) ;
boxScale . y = getMax ( boxScale . y , 1.0f ) ;
boxScale . z = getMax ( boxScale . z , 1.0f ) ;
mBBObjToWorld . scale ( boxScale ) ;
2017-07-26 21:45:10 +00:00
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( pool )
pool - > updatePoolBBox ( this ) ;
# endif
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
// addParticle
//-----------------------------------------------------------------------------
2017-07-26 21:45:10 +00:00
void ParticleEmitter : : addParticle ( const Point3F & pos , const Point3F & axis , const Point3F & vel ,
const Point3F & axisx , const U32 age_offset )
2012-09-19 15:15:01 +00:00
{
n_parts + + ;
if ( n_parts > n_part_capacity | | n_parts > mDataBlock - > partListInitSize )
{
// In an emergency we allocate additional particles in blocks of 16.
// This should happen rarely.
Particle * store_block = new Particle [ 16 ] ;
part_store . push_back ( store_block ) ;
n_part_capacity + = 16 ;
for ( S32 i = 0 ; i < 16 ; i + + )
{
store_block [ i ] . next = part_freelist ;
part_freelist = & store_block [ i ] ;
}
mDataBlock - > allocPrimBuffer ( n_part_capacity ) ; // allocate larger primitive buffer or will crash
}
Particle * pNew = part_freelist ;
part_freelist = pNew - > next ;
pNew - > next = part_list_head . next ;
part_list_head . next = pNew ;
2017-07-26 21:45:10 +00:00
// for earlier access to constrain_pos, the ParticleData datablock is chosen here instead
// of later in the method.
U32 dBlockIndex = gRandGen . randI ( ) % mDataBlock - > particleDataBlocks . size ( ) ;
ParticleData * part_db = mDataBlock - > particleDataBlocks [ dBlockIndex ] ;
// set start position to world or local space
Point3F pos_start ;
if ( part_db - > constrain_pos )
pos_start . set ( 0 , 0 , 0 ) ;
else
pos_start = pos ;
2012-09-19 15:15:01 +00:00
Point3F ejectionAxis = axis ;
F32 theta = ( mDataBlock - > thetaMax - mDataBlock - > thetaMin ) * gRandGen . randF ( ) +
mDataBlock - > thetaMin ;
F32 ref = ( F32 ( mInternalClock ) / 1000.0 ) * mDataBlock - > phiReferenceVel ;
F32 phi = ref + gRandGen . randF ( ) * mDataBlock - > phiVariance ;
// Both phi and theta are in degs. Create axis angles out of them, and create the
// appropriate rotation matrix...
AngAxisF thetaRot ( axisx , theta * ( M_PI / 180.0 ) ) ;
AngAxisF phiRot ( axis , phi * ( M_PI / 180.0 ) ) ;
MatrixF temp ( true ) ;
thetaRot . setMatrix ( & temp ) ;
temp . mulP ( ejectionAxis ) ;
phiRot . setMatrix ( & temp ) ;
temp . mulP ( ejectionAxis ) ;
F32 initialVel = mDataBlock - > ejectionVelocity ;
initialVel + = ( mDataBlock - > velocityVariance * 2.0f * gRandGen . randF ( ) ) - mDataBlock - > velocityVariance ;
2017-07-26 21:45:10 +00:00
pNew - > pos = pos_start + ( ejectionAxis * ( mDataBlock - > ejectionOffset + mDataBlock - > ejectionOffsetVariance * gRandGen . randF ( ) ) ) ;
pNew - > pos_local = pNew - > pos ;
pNew - > vel = mDataBlock - > ejectionInvert ? ejectionAxis * - initialVel : ejectionAxis * initialVel ;
if ( mDataBlock - > orientParticles )
pNew - > orientDir = ejectionAxis ;
else
// note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
pNew - > orientDir . x = mDegToRad ( part_db - > start_angle + part_db - > angle_variance * 2.0f * gRandGen . randF ( ) - part_db - > angle_variance ) ;
2012-09-19 15:15:01 +00:00
pNew - > acc . set ( 0 , 0 , 0 ) ;
2017-07-26 21:45:10 +00:00
pNew - > currentAge = age_offset ;
pNew - > t_last = 0.0f ;
2012-09-19 15:15:01 +00:00
mDataBlock - > particleDataBlocks [ dBlockIndex ] - > initializeParticle ( pNew , vel ) ;
updateKeyData ( pNew ) ;
}
//-----------------------------------------------------------------------------
// processTick
//-----------------------------------------------------------------------------
void ParticleEmitter : : processTick ( const Move * )
{
if ( mDeleteOnTick = = true )
{
mDead = true ;
deleteObject ( ) ;
}
}
//-----------------------------------------------------------------------------
// advanceTime
//-----------------------------------------------------------------------------
void ParticleEmitter : : advanceTime ( F32 dt )
{
if ( dt < 0.00001 ) return ;
Parent : : advanceTime ( dt ) ;
if ( dt > 0.5 ) dt = 0.5 ;
if ( mDead ) return ;
mElapsedTimeMS + = ( S32 ) ( dt * 1000.0f ) ;
U32 numMSToUpdate = ( U32 ) ( dt * 1000.0f ) ;
if ( numMSToUpdate = = 0 ) return ;
// TODO: Prefetch
// remove dead particles
Particle * last_part = & part_list_head ;
for ( Particle * part = part_list_head . next ; part ! = NULL ; part = part - > next )
{
part - > currentAge + = numMSToUpdate ;
if ( part - > currentAge > part - > totalLifetime )
{
n_parts - - ;
last_part - > next = part - > next ;
part - > next = part_freelist ;
part_freelist = part ;
part = last_part ;
}
else
{
last_part = part ;
}
}
AssertFatal ( n_parts > = 0 , " ParticleEmitter: negative part count! " ) ;
if ( n_parts < 1 & & mDeleteWhenEmpty )
{
mDeleteOnTick = true ;
return ;
}
if ( numMSToUpdate ! = 0 & & n_parts > 0 )
{
update ( numMSToUpdate ) ;
}
}
//-----------------------------------------------------------------------------
// Update key related particle data
//-----------------------------------------------------------------------------
void ParticleEmitter : : updateKeyData ( Particle * part )
{
//Ensure that our lifetime is never below 0
if ( part - > totalLifetime < 1 )
part - > totalLifetime = 1 ;
2017-07-26 21:45:10 +00:00
if ( part - > currentAge > part - > totalLifetime )
part - > currentAge = part - > totalLifetime ;
F32 t = ( F32 ) part - > currentAge / ( F32 ) part - > totalLifetime ;
2012-09-19 15:15:01 +00:00
for ( U32 i = 1 ; i < ParticleData : : PDC_NUM_KEYS ; i + + )
{
if ( part - > dataBlock - > times [ i ] > = t )
{
F32 firstPart = t - part - > dataBlock - > times [ i - 1 ] ;
F32 total = part - > dataBlock - > times [ i ] -
part - > dataBlock - > times [ i - 1 ] ;
firstPart / = total ;
if ( mDataBlock - > useEmitterColors )
{
part - > color . interpolate ( colors [ i - 1 ] , colors [ i ] , firstPart ) ;
}
else
{
part - > color . interpolate ( part - > dataBlock - > colors [ i - 1 ] ,
part - > dataBlock - > colors [ i ] ,
firstPart ) ;
}
if ( mDataBlock - > useEmitterSizes )
{
part - > size = ( sizes [ i - 1 ] * ( 1.0 - firstPart ) ) +
( sizes [ i ] * firstPart ) ;
}
else
{
part - > size = ( part - > dataBlock - > sizes [ i - 1 ] * ( 1.0 - firstPart ) ) +
( part - > dataBlock - > sizes [ i ] * firstPart ) ;
2017-07-26 21:45:10 +00:00
part - > size * = part - > dataBlock - > sizeBias ;
2012-09-19 15:15:01 +00:00
}
2017-07-26 21:45:10 +00:00
if ( mDataBlock - > fade_color )
{
if ( mDataBlock - > fade_alpha )
part - > color * = fade_amt ;
else
{
part - > color . red * = fade_amt ;
part - > color . green * = fade_amt ;
part - > color . blue * = fade_amt ;
}
}
else if ( mDataBlock - > fade_alpha )
part - > color . alpha * = fade_amt ;
if ( mDataBlock - > fade_size )
part - > size * = fade_amt ;
2012-09-19 15:15:01 +00:00
break ;
}
}
}
//-----------------------------------------------------------------------------
// Update particles
//-----------------------------------------------------------------------------
void ParticleEmitter : : update ( U32 ms )
{
2017-07-26 21:45:10 +00:00
F32 t = F32 ( ms ) / 1000.0f ; // AFX -- moved outside loop, no need to recalculate this for every particle
2012-09-19 15:15:01 +00:00
for ( Particle * part = part_list_head . next ; part ! = NULL ; part = part - > next )
{
Point3F a = part - > acc ;
2017-07-26 21:45:10 +00:00
a - = part - > vel * part - > dataBlock - > dragCoefficient ;
2012-09-19 15:15:01 +00:00
a - = mWindVelocity * part - > dataBlock - > windCoefficient ;
2017-07-26 21:45:10 +00:00
a . z + = - 9.81f * part - > dataBlock - > gravityCoefficient ; // AFX -- as long as gravity is a constant, this is faster
2012-09-19 15:15:01 +00:00
part - > vel + = a * t ;
2017-07-26 21:45:10 +00:00
part - > pos_local + = part - > vel * t ;
// AFX -- allow subclasses to adjust the particle params here
sub_particleUpdate ( part ) ;
if ( part - > dataBlock - > constrain_pos )
part - > pos = part - > pos_local + this - > pos_pe ;
else
part - > pos = part - > pos_local ;
2012-09-19 15:15:01 +00:00
updateKeyData ( part ) ;
}
}
//-----------------------------------------------------------------------------
// Copy particles to vertex buffer
//-----------------------------------------------------------------------------
// structure used for particle sorting.
struct SortParticle
{
Particle * p ;
F32 k ;
} ;
// qsort callback function for particle sorting
2013-08-04 21:26:01 +00:00
S32 QSORT_CALLBACK cmpSortParticles ( const void * p1 , const void * p2 )
2012-09-19 15:15:01 +00:00
{
const SortParticle * sp1 = ( const SortParticle * ) p1 ;
const SortParticle * sp2 = ( const SortParticle * ) p2 ;
if ( sp2 - > k > sp1 - > k )
return 1 ;
else if ( sp2 - > k = = sp1 - > k )
return 0 ;
else
return - 1 ;
}
2017-06-23 16:36:20 +00:00
void ParticleEmitter : : copyToVB ( const Point3F & camPos , const LinearColorF & ambientColor )
2012-09-19 15:15:01 +00:00
{
static Vector < SortParticle > orderedVector ( __FILE__ , __LINE__ ) ;
PROFILE_START ( ParticleEmitter_copyToVB ) ;
PROFILE_START ( ParticleEmitter_copyToVB_Sort ) ;
// build sorted list of particles (far to near)
if ( mDataBlock - > sortParticles )
{
orderedVector . clear ( ) ;
MatrixF modelview = GFX - > getWorldMatrix ( ) ;
Point3F viewvec ; modelview . getRow ( 1 , & viewvec ) ;
// add each particle and a distance based sort key to orderedVector
for ( Particle * pp = part_list_head . next ; pp ! = NULL ; pp = pp - > next )
{
orderedVector . increment ( ) ;
orderedVector . last ( ) . p = pp ;
orderedVector . last ( ) . k = mDot ( pp - > pos , viewvec ) ;
}
// qsort the list into far to near ordering
dQsort ( orderedVector . address ( ) , orderedVector . size ( ) , sizeof ( SortParticle ) , cmpSortParticles ) ;
}
PROFILE_END ( ) ;
static Vector < ParticleVertexType > tempBuff ( 2048 ) ;
tempBuff . reserve ( n_parts * 4 + 64 ) ; // make sure tempBuff is big enough
ParticleVertexType * buffPtr = tempBuff . address ( ) ; // use direct pointer (faster)
if ( mDataBlock - > orientParticles )
{
PROFILE_START ( ParticleEmitter_copyToVB_Orient ) ;
if ( mDataBlock - > reverseOrder )
{
buffPtr + = 4 * ( n_parts - 1 ) ;
// do sorted-oriented particles
if ( mDataBlock - > sortParticles )
{
SortParticle * partPtr = orderedVector . address ( ) ;
for ( U32 i = 0 ; i < n_parts ; i + + , partPtr + + , buffPtr - = 4 )
setupOriented ( partPtr - > p , camPos , ambientColor , buffPtr ) ;
}
// do unsorted-oriented particles
else
{
for ( Particle * partPtr = part_list_head . next ; partPtr ! = NULL ; partPtr = partPtr - > next , buffPtr - = 4 )
setupOriented ( partPtr , camPos , ambientColor , buffPtr ) ;
}
}
else
{
// do sorted-oriented particles
if ( mDataBlock - > sortParticles )
{
SortParticle * partPtr = orderedVector . address ( ) ;
for ( U32 i = 0 ; i < n_parts ; i + + , partPtr + + , buffPtr + = 4 )
setupOriented ( partPtr - > p , camPos , ambientColor , buffPtr ) ;
}
// do unsorted-oriented particles
else
{
for ( Particle * partPtr = part_list_head . next ; partPtr ! = NULL ; partPtr = partPtr - > next , buffPtr + = 4 )
setupOriented ( partPtr , camPos , ambientColor , buffPtr ) ;
}
}
PROFILE_END ( ) ;
}
else if ( mDataBlock - > alignParticles )
{
PROFILE_START ( ParticleEmitter_copyToVB_Aligned ) ;
if ( mDataBlock - > reverseOrder )
{
buffPtr + = 4 * ( n_parts - 1 ) ;
// do sorted-oriented particles
if ( mDataBlock - > sortParticles )
{
SortParticle * partPtr = orderedVector . address ( ) ;
for ( U32 i = 0 ; i < n_parts ; i + + , partPtr + + , buffPtr - = 4 )
setupAligned ( partPtr - > p , ambientColor , buffPtr ) ;
}
// do unsorted-oriented particles
else
{
Particle * partPtr = part_list_head . next ;
for ( ; partPtr ! = NULL ; partPtr = partPtr - > next , buffPtr - = 4 )
setupAligned ( partPtr , ambientColor , buffPtr ) ;
}
}
else
{
// do sorted-oriented particles
if ( mDataBlock - > sortParticles )
{
SortParticle * partPtr = orderedVector . address ( ) ;
for ( U32 i = 0 ; i < n_parts ; i + + , partPtr + + , buffPtr + = 4 )
setupAligned ( partPtr - > p , ambientColor , buffPtr ) ;
}
// do unsorted-oriented particles
else
{
Particle * partPtr = part_list_head . next ;
for ( ; partPtr ! = NULL ; partPtr = partPtr - > next , buffPtr + = 4 )
setupAligned ( partPtr , ambientColor , buffPtr ) ;
}
}
PROFILE_END ( ) ;
}
else
{
PROFILE_START ( ParticleEmitter_copyToVB_NonOriented ) ;
// somewhat odd ordering so that texture coordinates match the oriented
// particles
Point3F basePoints [ 4 ] ;
basePoints [ 0 ] = Point3F ( - 1.0 , 0.0 , 1.0 ) ;
basePoints [ 1 ] = Point3F ( - 1.0 , 0.0 , - 1.0 ) ;
basePoints [ 2 ] = Point3F ( 1.0 , 0.0 , - 1.0 ) ;
basePoints [ 3 ] = Point3F ( 1.0 , 0.0 , 1.0 ) ;
MatrixF camView = GFX - > getWorldMatrix ( ) ;
camView . transpose ( ) ; // inverse - this gets the particles facing camera
if ( mDataBlock - > reverseOrder )
{
buffPtr + = 4 * ( n_parts - 1 ) ;
// do sorted-billboard particles
if ( mDataBlock - > sortParticles )
{
SortParticle * partPtr = orderedVector . address ( ) ;
for ( U32 i = 0 ; i < n_parts ; i + + , partPtr + + , buffPtr - = 4 )
setupBillboard ( partPtr - > p , basePoints , camView , ambientColor , buffPtr ) ;
}
// do unsorted-billboard particles
else
{
for ( Particle * partPtr = part_list_head . next ; partPtr ! = NULL ; partPtr = partPtr - > next , buffPtr - = 4 )
setupBillboard ( partPtr , basePoints , camView , ambientColor , buffPtr ) ;
}
}
else
{
// do sorted-billboard particles
if ( mDataBlock - > sortParticles )
{
SortParticle * partPtr = orderedVector . address ( ) ;
for ( U32 i = 0 ; i < n_parts ; i + + , partPtr + + , buffPtr + = 4 )
setupBillboard ( partPtr - > p , basePoints , camView , ambientColor , buffPtr ) ;
}
// do unsorted-billboard particles
else
{
for ( Particle * partPtr = part_list_head . next ; partPtr ! = NULL ; partPtr = partPtr - > next , buffPtr + = 4 )
setupBillboard ( partPtr , basePoints , camView , ambientColor , buffPtr ) ;
}
}
PROFILE_END ( ) ;
}
PROFILE_START ( ParticleEmitter_copyToVB_LockCopy ) ;
// create new VB if emitter size grows
if ( ! mVertBuff | | n_parts > mCurBuffSize )
{
mCurBuffSize = n_parts ;
mVertBuff . set ( GFX , n_parts * 4 , GFXBufferTypeDynamic ) ;
}
// lock and copy tempBuff to video RAM
ParticleVertexType * verts = mVertBuff . lock ( ) ;
dMemcpy ( verts , tempBuff . address ( ) , n_parts * 4 * sizeof ( ParticleVertexType ) ) ;
mVertBuff . unlock ( ) ;
PROFILE_END ( ) ;
PROFILE_END ( ) ;
}
//-----------------------------------------------------------------------------
// Set up particle for billboard style render
//-----------------------------------------------------------------------------
void ParticleEmitter : : setupBillboard ( Particle * part ,
Point3F * basePts ,
const MatrixF & camView ,
2017-06-23 16:36:20 +00:00
const LinearColorF & ambientColor ,
2012-09-19 15:15:01 +00:00
ParticleVertexType * lVerts )
{
F32 width = part - > size * 0.5f ;
F32 spinAngle = part - > spinSpeed * part - > currentAge * AgedSpinToRadians ;
F32 sy , cy ;
mSinCos ( spinAngle , sy , cy ) ;
const F32 ambientLerp = mClampF ( mDataBlock - > ambientFactor , 0.0f , 1.0f ) ;
2017-06-23 16:36:20 +00:00
LinearColorF partCol = mLerp ( part - > color , ( part - > color * ambientColor ) , ambientLerp ) ;
2012-09-19 15:15:01 +00:00
// fill four verts, use macro and unroll loop
# define fillVert(){ \
lVerts - > point . x = cy * basePts - > x - sy * basePts - > z ; \
lVerts - > point . y = 0.0f ; \
lVerts - > point . z = sy * basePts - > x + cy * basePts - > z ; \
camView . mulV ( lVerts - > point ) ; \
lVerts - > point * = width ; \
lVerts - > point + = part - > pos ; \
2017-06-23 16:36:20 +00:00
lVerts - > color = partCol . toColorI ( ) ; } \
2012-09-19 15:15:01 +00:00
// Here we deal with UVs for animated particle (billboard)
2016-07-07 03:30:19 +00:00
if ( part - > dataBlock - > animateTexture & & ! part - > dataBlock - > animTexFrames . empty ( ) )
2012-09-19 15:15:01 +00:00
{
S32 fm = ( S32 ) ( part - > currentAge * ( 1.0 / 1000.0 ) * part - > dataBlock - > framesPerSec ) ;
U8 fm_tile = part - > dataBlock - > animTexFrames [ fm % part - > dataBlock - > numFrames ] ;
S32 uv [ 4 ] ;
uv [ 0 ] = fm_tile + fm_tile / part - > dataBlock - > animTexTiling . x ;
uv [ 1 ] = uv [ 0 ] + ( part - > dataBlock - > animTexTiling . x + 1 ) ;
uv [ 2 ] = uv [ 1 ] + 1 ;
uv [ 3 ] = uv [ 0 ] + 1 ;
fillVert ( ) ;
// Here and below, we copy UVs from particle datablock's current frame's UVs (billboard)
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 0 ] ] ;
+ + lVerts ;
+ + basePts ;
fillVert ( ) ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 1 ] ] ;
+ + lVerts ;
+ + basePts ;
fillVert ( ) ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 2 ] ] ;
+ + lVerts ;
+ + basePts ;
fillVert ( ) ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 3 ] ] ;
+ + lVerts ;
+ + basePts ;
return ;
}
fillVert ( ) ;
// Here and below, we copy UVs from particle datablock's texCoords (billboard)
lVerts - > texCoord = part - > dataBlock - > texCoords [ 0 ] ;
+ + lVerts ;
+ + basePts ;
fillVert ( ) ;
lVerts - > texCoord = part - > dataBlock - > texCoords [ 1 ] ;
+ + lVerts ;
+ + basePts ;
fillVert ( ) ;
lVerts - > texCoord = part - > dataBlock - > texCoords [ 2 ] ;
+ + lVerts ;
+ + basePts ;
fillVert ( ) ;
lVerts - > texCoord = part - > dataBlock - > texCoords [ 3 ] ;
+ + lVerts ;
+ + basePts ;
}
//-----------------------------------------------------------------------------
// Set up oriented particle
//-----------------------------------------------------------------------------
void ParticleEmitter : : setupOriented ( Particle * part ,
const Point3F & camPos ,
2017-06-23 16:36:20 +00:00
const LinearColorF & ambientColor ,
2012-09-19 15:15:01 +00:00
ParticleVertexType * lVerts )
{
Point3F dir ;
if ( mDataBlock - > orientOnVelocity )
{
// don't render oriented particle if it has no velocity
if ( part - > vel . magnitudeSafe ( ) = = 0.0 ) return ;
dir = part - > vel ;
}
else
{
dir = part - > orientDir ;
}
Point3F dirFromCam = part - > pos - camPos ;
Point3F crossDir ;
mCross ( dirFromCam , dir , & crossDir ) ;
crossDir . normalize ( ) ;
dir . normalize ( ) ;
F32 width = part - > size * 0.5f ;
dir * = width ;
crossDir * = width ;
Point3F start = part - > pos - dir ;
Point3F end = part - > pos + dir ;
const F32 ambientLerp = mClampF ( mDataBlock - > ambientFactor , 0.0f , 1.0f ) ;
2017-06-23 16:36:20 +00:00
LinearColorF partCol = mLerp ( part - > color , ( part - > color * ambientColor ) , ambientLerp ) ;
const ColorI color = partCol . toColorI ( ) ;
2012-09-19 15:15:01 +00:00
// Here we deal with UVs for animated particle (oriented)
if ( part - > dataBlock - > animateTexture )
{
// Let particle compute the UV indices for current frame
S32 fm = ( S32 ) ( part - > currentAge * ( 1.0f / 1000.0f ) * part - > dataBlock - > framesPerSec ) ;
U8 fm_tile = part - > dataBlock - > animTexFrames [ fm % part - > dataBlock - > numFrames ] ;
S32 uv [ 4 ] ;
uv [ 0 ] = fm_tile + fm_tile / part - > dataBlock - > animTexTiling . x ;
uv [ 1 ] = uv [ 0 ] + ( part - > dataBlock - > animTexTiling . x + 1 ) ;
uv [ 2 ] = uv [ 1 ] + 1 ;
uv [ 3 ] = uv [ 0 ] + 1 ;
2017-06-23 16:36:20 +00:00
lVerts - > point = start + crossDir ;
lVerts - > color = color ;
// Here and below, we copy UVs from particle datablock's current frame's UVs (oriented)
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 0 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
2017-06-23 16:36:20 +00:00
lVerts - > point = start - crossDir ;
lVerts - > color = color ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 1 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
2017-06-23 16:36:20 +00:00
lVerts - > point = end - crossDir ;
lVerts - > color = color ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 2 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
2017-06-23 16:36:20 +00:00
lVerts - > point = end + crossDir ;
lVerts - > color = color ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 3 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
2017-06-23 16:36:20 +00:00
return ;
2012-09-19 15:15:01 +00:00
}
lVerts - > point = start + crossDir ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2012-09-19 15:15:01 +00:00
// Here and below, we copy UVs from particle datablock's texCoords (oriented)
2014-12-23 00:05:43 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 1 ] ;
2012-09-19 15:15:01 +00:00
+ + lVerts ;
lVerts - > point = start - crossDir ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2014-12-23 00:05:43 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 2 ] ;
2012-09-19 15:15:01 +00:00
+ + lVerts ;
lVerts - > point = end - crossDir ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2014-12-23 00:05:43 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 3 ] ;
2012-09-19 15:15:01 +00:00
+ + lVerts ;
lVerts - > point = end + crossDir ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2014-12-23 00:05:43 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 0 ] ;
2012-09-19 15:15:01 +00:00
+ + lVerts ;
}
void ParticleEmitter : : setupAligned ( const Particle * part ,
2017-06-23 16:36:20 +00:00
const LinearColorF & ambientColor ,
2012-09-19 15:15:01 +00:00
ParticleVertexType * lVerts )
{
// The aligned direction will always be normalized.
Point3F dir = mDataBlock - > alignDirection ;
// Find a right vector for this particle.
Point3F right ;
if ( mFabs ( dir . y ) > mFabs ( dir . z ) )
mCross ( Point3F : : UnitZ , dir , & right ) ;
else
mCross ( Point3F : : UnitY , dir , & right ) ;
right . normalize ( ) ;
// If we have a spin velocity.
if ( ! mIsZero ( part - > spinSpeed ) )
{
F32 spinAngle = part - > spinSpeed * part - > currentAge * AgedSpinToRadians ;
// This is an inline quaternion vector rotation which
// is faster that QuatF.mulP(), but generates different
// results and hence cannot replace it right now.
F32 sin , qw ;
mSinCos ( spinAngle * 0.5f , sin , qw ) ;
F32 qx = dir . x * sin ;
F32 qy = dir . y * sin ;
F32 qz = dir . z * sin ;
F32 vx = ( right . x * qw ) + ( right . z * qy ) - ( right . y * qz ) ;
F32 vy = ( right . y * qw ) + ( right . x * qz ) - ( right . z * qx ) ;
F32 vz = ( right . z * qw ) + ( right . y * qx ) - ( right . x * qy ) ;
F32 vw = ( right . x * qx ) + ( right . y * qy ) + ( right . z * qz ) ;
right . x = ( qw * vx ) + ( qx * vw ) + ( qy * vz ) - ( qz * vy ) ;
right . y = ( qw * vy ) + ( qy * vw ) + ( qz * vx ) - ( qx * vz ) ;
right . z = ( qw * vz ) + ( qz * vw ) + ( qx * vy ) - ( qy * vx ) ;
}
// Get the cross vector.
Point3F cross ;
mCross ( right , dir , & cross ) ;
F32 width = part - > size * 0.5f ;
right * = width ;
cross * = width ;
Point3F start = part - > pos - right ;
Point3F end = part - > pos + right ;
const F32 ambientLerp = mClampF ( mDataBlock - > ambientFactor , 0.0f , 1.0f ) ;
2017-06-23 16:36:20 +00:00
LinearColorF partCol = mLerp ( part - > color , ( part - > color * ambientColor ) , ambientLerp ) ;
const ColorI color = partCol . toColorI ( ) ;
2012-09-19 15:15:01 +00:00
// Here we deal with UVs for animated particle
if ( part - > dataBlock - > animateTexture )
{
// Let particle compute the UV indices for current frame
S32 fm = ( S32 ) ( part - > currentAge * ( 1.0f / 1000.0f ) * part - > dataBlock - > framesPerSec ) ;
U8 fm_tile = part - > dataBlock - > animTexFrames [ fm % part - > dataBlock - > numFrames ] ;
S32 uv [ 4 ] ;
uv [ 0 ] = fm_tile + fm_tile / part - > dataBlock - > animTexTiling . x ;
uv [ 1 ] = uv [ 0 ] + ( part - > dataBlock - > animTexTiling . x + 1 ) ;
uv [ 2 ] = uv [ 1 ] + 1 ;
uv [ 3 ] = uv [ 0 ] + 1 ;
2017-06-23 16:36:20 +00:00
lVerts - > point = start + cross ;
lVerts - > color = color ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 0 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
2017-06-23 16:36:20 +00:00
lVerts - > point = start - cross ;
lVerts - > color = color ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 1 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
2017-06-23 16:36:20 +00:00
lVerts - > point = end - cross ;
lVerts - > color = color ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 2 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
2017-06-23 16:36:20 +00:00
lVerts - > point = end + cross ;
lVerts - > color = color ;
lVerts - > texCoord = part - > dataBlock - > animTexUVs [ uv [ 3 ] ] ;
+ + lVerts ;
2012-09-19 15:15:01 +00:00
}
else
{
// Here and below, we copy UVs from particle datablock's texCoords
lVerts - > point = start + cross ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2012-09-19 15:15:01 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 0 ] ;
+ + lVerts ;
lVerts - > point = start - cross ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2012-09-19 15:15:01 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 1 ] ;
+ + lVerts ;
lVerts - > point = end - cross ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2012-09-19 15:15:01 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 2 ] ;
+ + lVerts ;
lVerts - > point = end + cross ;
2017-06-23 16:36:20 +00:00
lVerts - > color = color ;
2012-09-19 15:15:01 +00:00
lVerts - > texCoord = part - > dataBlock - > texCoords [ 3 ] ;
+ + lVerts ;
}
}
bool ParticleEmitterData : : reload ( )
{
// Clear out current particle data.
dataBlockIds . clear ( ) ;
particleDataBlocks . clear ( ) ;
// Parse out particle string.
U32 numUnits = 0 ;
if ( particleString )
numUnits = StringUnit : : getUnitCount ( particleString , " \t " ) ;
if ( ! particleString | | ! particleString [ 0 ] | | ! numUnits )
{
Con : : errorf ( " ParticleEmitterData(%s) has an empty particles string. " , getName ( ) ) ;
mReloadSignal . trigger ( ) ;
return false ;
}
for ( U32 i = 0 ; i < numUnits ; + + i )
{
const char * dbName = StringUnit : : getUnit ( particleString , i , " \t " ) ;
ParticleData * data = NULL ;
if ( ! Sim : : findObject ( dbName , data ) )
{
Con : : errorf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) unable to find particle datablock: %s " , getName ( ) , dbName ) ;
continue ;
}
particleDataBlocks . push_back ( data ) ;
dataBlockIds . push_back ( data - > getId ( ) ) ;
}
// Check that we actually found some particle datablocks.
if ( particleDataBlocks . empty ( ) )
{
Con : : errorf ( ConsoleLogEntry : : General , " ParticleEmitterData(%s) unable to find any particle datablocks " , getName ( ) ) ;
mReloadSignal . trigger ( ) ;
return false ;
}
// Trigger reload.
mReloadSignal . trigger ( ) ;
return true ;
}
DefineEngineMethod ( ParticleEmitterData , reload , void , ( ) , ,
" Reloads the ParticleData datablocks and other fields used by this emitter. \n "
" @tsexample \n "
" // Get the editor's current particle emitter \n "
" %emitter = PE_EmitterEditor.currEmitter \n \n "
" // Change a field value \n "
" %emitter.setFieldValue( %propertyField, %value ); \n \n "
" // Reload this emitter \n "
" %emitter.reload(); \n "
" @endtsexample \n " )
{
object - > reload ( ) ;
}
2017-07-26 21:45:10 +00:00
void ParticleEmitter : : emitParticlesExt ( const MatrixF & xfm , const Point3F & point ,
const Point3F & velocity , const U32 numMilliseconds )
{
if ( mDataBlock - > use_emitter_xfm )
{
Point3F zero_point ( 0.0f , 0.0f , 0.0f ) ;
this - > pos_pe = zero_point ;
this - > setTransform ( xfm ) ;
Point3F axis ( 0.0 , 0.0 , 1.0 ) ;
xfm . mulV ( axis ) ;
emitParticles ( zero_point , true , axis , velocity , numMilliseconds ) ;
}
else
{
this - > pos_pe = point ;
Point3F axis ( 0.0 , 0.0 , 1.0 ) ;
xfm . mulV ( axis ) ;
emitParticles ( point , true , axis , velocity , numMilliseconds ) ;
}
}
void ParticleEmitter : : setForcedObjBox ( Box3F & box )
{
mObjBox = box ;
forced_bbox = true ;
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( pool )
pool - > updatePoolBBox ( this ) ;
# endif
}
void ParticleEmitter : : setSortPriority ( S8 priority )
{
sort_priority = ( priority = = 0 ) ? 1 : priority ;
# if defined(AFX_CAP_PARTICLE_POOLS)
if ( pool )
pool - > setSortPriority ( sort_priority ) ;
# endif
}