// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "std3d.h"
# include <cstdlib>
# include "nel/3d/ps_emitter.h"
# include "nel/3d/material.h"
# include "nel/misc/line.h"
# include "nel/3d/dru.h"
# include "nel/3d/particle_system.h"
namespace NL3D {
static const uint EMITTER_BUFF_SIZE = 512 ; // number of emitter to be processed at once
static const float EMIT_PERIOD_THRESHOLD = 1.f / 75.f ; // assuming the same behaviour than with a 75 hz rendering
bool CPSEmitter : : _BypassEmitOnDeath = false ;
//////////////////////
// STATIC FUNCTIONS //
//////////////////////
/** In an arrey of float, all value that are 0.f are replaced by EMIT_PERIOD_THRESHOLD
* A period of 0 is allowed for emitter and means " emit at each frame "
* This is deprecated now , and this helps to avoid that behaviour
*/
static void replaceNullPeriodsByThreshold ( float * tab , uint numElem )
{
NL_PS_FUNC ( replaceNullPeriodsByThreshold )
const float * endTab = tab + numElem ;
while ( tab ! = endTab )
{
if ( * tab = = 0.f ) * tab = EMIT_PERIOD_THRESHOLD ;
+ + tab ;
}
}
///////////////////////////////
// CPSEmitter implementation //
///////////////////////////////
CPSEmitter : : CPSEmitter ( ) : _EmittedType ( NULL ) ,
_SpeedInheritanceFactor ( 0.f ) ,
_EmissionType ( regular ) ,
_Period ( 0.02f ) ,
_PeriodScheme ( NULL ) ,
_GenNb ( 1 ) ,
_GenNbScheme ( NULL ) ,
_EmitDelay ( 0 ) ,
_MaxEmissionCount ( 0 ) ,
_SpeedBasisEmission ( false ) ,
_ConsistentEmission ( true ) ,
_BypassAutoLOD ( false ) ,
_UserMatrixModeForEmissionDirection ( false ) ,
_EmitTrigger ( false ) ,
_UserDirectionMatrixMode ( PSFXWorldMatrix )
{
NL_PS_FUNC ( CPSEmitter_CPSEmitter )
}
///==========================================================================
CPSEmitter : : ~ CPSEmitter ( )
{
NL_PS_FUNC ( CPSEmitter_CPSEmitterDtor )
delete _PeriodScheme ;
delete _GenNbScheme ;
// if a located is emitted, unregister us as an observer
if ( _EmittedType )
{
_EmittedType - > unregisterDtorObserver ( this ) ;
}
}
///==========================================================================
void CPSEmitter : : releaseRefTo ( const CParticleSystemProcess * other )
{
NL_PS_FUNC ( CPSEmitter_releaseRefTo )
if ( _EmittedType = = other )
{
setEmittedType ( NULL ) ;
}
}
void CPSEmitter : : releaseAllRef ( )
{
NL_PS_FUNC ( CPSEmitter_releaseAllRef )
setEmittedType ( NULL ) ;
}
///==========================================================================
void CPSEmitter : : setOwner ( CPSLocated * psl )
{
NL_PS_FUNC ( CPSEmitter_setOwner )
CPSLocatedBindable : : setOwner ( psl ) ;
updateMaxCountVect ( ) ;
}
///==========================================================================
inline void CPSEmitter : : processEmit ( uint32 index , sint nbToGenerate )
{
NL_PS_FUNC ( CPSEmitter_processEmit )
NLMISC : : CVector speed , pos ;
nlassert ( _Owner ) ;
if ( ! _SpeedBasisEmission )
{
if ( _SpeedInheritanceFactor = = 0.f )
{
if ( ! _UserMatrixModeForEmissionDirection )
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , speed , * this - > _Owner , index , _Owner - > getMatrixMode ( ) , 0.f ) ;
}
}
else
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , speed , * this - > _Owner , index , _UserDirectionMatrixMode , 0.f ) ;
}
}
}
else
{
while ( nbToGenerate - - )
{
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , speed + _SpeedInheritanceFactor * _Owner - > getSpeed ( ) [ index ] , * this - > _Owner , 0 , _Owner - > getMatrixMode ( ) , 0.f ) ;
}
}
}
else
{
NLMISC : : CMatrix m ;
CPSUtil : : buildSchmidtBasis ( _Owner - > getSpeed ( ) [ index ] , m ) ;
if ( _SpeedInheritanceFactor = = 0.f )
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , m * speed , * this - > _Owner , index , _Owner - > getMatrixMode ( ) , 0.f ) ;
}
}
else
{
while ( nbToGenerate - - )
{
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , m * speed + _SpeedInheritanceFactor * _Owner - > getSpeed ( ) [ index ] , * this - > _Owner , index , _Owner - > getMatrixMode ( ) , 0.f ) ;
}
}
}
}
///==========================================================================
void CPSEmitter : : processEmitOutsideSimLoop ( uint32 index , sint nbToGenerate )
{
NL_PS_FUNC ( CPSEmitter_processEmitOutsideSimLoop )
NLMISC : : CVector speed , pos ;
nlassert ( _Owner ) ;
if ( ! _SpeedBasisEmission )
{
if ( _SpeedInheritanceFactor = = 0.f )
{
if ( ! _UserMatrixModeForEmissionDirection )
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > newElement ( pos , speed , this - > _Owner , index , _Owner - > getMatrixMode ( ) , true ) ;
}
}
else
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > newElement ( pos , speed , this - > _Owner , index , _UserDirectionMatrixMode ) ;
}
}
}
else
{
while ( nbToGenerate - - )
{
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > newElement ( pos , speed + _SpeedInheritanceFactor * _Owner - > getSpeed ( ) [ index ] , this - > _Owner , 0 , _Owner - > getMatrixMode ( ) , true ) ;
}
}
}
else
{
NLMISC : : CMatrix m ;
CPSUtil : : buildSchmidtBasis ( _Owner - > getSpeed ( ) [ index ] , m ) ;
if ( _SpeedInheritanceFactor = = 0.f )
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > newElement ( pos , m * speed , this - > _Owner , index , _Owner - > getMatrixMode ( ) , true ) ;
}
}
else
{
while ( nbToGenerate - - )
{
emit ( _Owner - > getPos ( ) [ index ] , index , pos , speed ) ;
_EmittedType - > newElement ( pos , m * speed + _SpeedInheritanceFactor * _Owner - > getSpeed ( ) [ index ] , this - > _Owner , index , _Owner - > getMatrixMode ( ) , true ) ;
}
}
}
}
///==========================================================================
inline void CPSEmitter : : processEmitConsistent ( const NLMISC : : CVector & emitterPos ,
uint32 index ,
sint nbToGenerate ,
TAnimationTime deltaT )
{
NL_PS_FUNC ( CPSEmitter_processEmitConsistent )
static NLMISC : : CVector speed , pos ; /// speed and pos of emittee
nlassert ( _Owner ) ;
if ( ! _SpeedBasisEmission )
{
if ( _SpeedInheritanceFactor = = 0.f )
{
if ( ! _UserMatrixModeForEmissionDirection )
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( emitterPos , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , speed , * this - > _Owner , index , _Owner - > getMatrixMode ( ) , deltaT ) ;
}
}
else
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( emitterPos , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , speed , * this - > _Owner , index , _UserDirectionMatrixMode , deltaT ) ;
}
}
}
else
{
while ( nbToGenerate - - )
{
emit ( emitterPos , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , speed + _SpeedInheritanceFactor * _Owner - > getSpeed ( ) [ index ] , * this - > _Owner , index , _Owner - > getMatrixMode ( ) , deltaT ) ;
}
}
}
else
{
NLMISC : : CMatrix m ;
CPSUtil : : buildSchmidtBasis ( _Owner - > getSpeed ( ) [ index ] , m ) ;
if ( _SpeedInheritanceFactor = = 0.f )
{
while ( nbToGenerate > 0 )
{
nbToGenerate - - ;
emit ( emitterPos , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , m * speed , * this - > _Owner , index , _Owner - > getMatrixMode ( ) , deltaT ) ;
}
}
else
{
while ( nbToGenerate - - )
{
emit ( emitterPos , index , pos , speed ) ;
_EmittedType - > postNewElement ( pos , m * speed + _SpeedInheritanceFactor * _Owner - > getSpeed ( ) [ index ] , * this - > _Owner , index , _Owner - > getMatrixMode ( ) , deltaT ) ;
}
}
}
}
///==========================================================================
bool CPSEmitter : : setEmissionType ( TEmissionType freqType )
{
NL_PS_FUNC ( CPSEmitter_setEmissionType )
if ( _Owner & & _Owner - > getOwner ( ) )
{
CParticleSystem * ps = _Owner - > getOwner ( ) ;
if ( ps - > getBypassMaxNumIntegrationSteps ( ) )
{
if ( ! _Owner )
{
nlwarning ( " <CPSEmitter::setEmissionType> The emitter should be inserted in a CPSLocated instance " ) ;
nlassert ( 0 ) ;
}
// check if the new value is valid
TEmissionType oldType = _EmissionType ;
_EmissionType = freqType ;
if ( testEmitForever ( ) = = true )
{
_EmissionType = oldType ;
std : : string mess = " <CPSEmitter::setEmissionType> can't set emission type to ' " +
NLMISC : : toString ( freqType ) +
" ' with the current configuration : the system has been flagged with \
' BypassMaxNumIntegrationSteps ' , and should have a finite duration . \
The flag is not set " ;
nlwarning ( mess . c_str ( ) ) ;
return false ;
}
}
ps - > systemDurationChanged ( ) ;
}
_EmissionType = freqType ;
return true ;
}
///==========================================================================
bool CPSEmitter : : setEmittedType ( CPSLocated * et )
{
NL_PS_FUNC ( CPSEmitter_setEmittedType )
if ( _EmittedType )
{
_EmittedType - > unregisterDtorObserver ( this ) ;
}
if ( et )
{
et - > registerDtorObserver ( this ) ;
}
CPSLocated * oldType = _EmittedType ;
_EmittedType = et ;
if ( _Owner & & _Owner - > getOwner ( ) )
{
CParticleSystem * ps = _Owner - > getOwner ( ) ;
if ( _EmittedType )
{
bool ok = true ;
if ( ps - > getBypassMaxNumIntegrationSteps ( ) )
{
ok = ps - > canFinish ( ) ;
}
else
{
ok = ! ps - > hasLoop ( ) ;
}
if ( ! ok )
{
setEmittedType ( oldType ) ;
nlwarning ( " <CPSLocated::setEmittedType> Can't set new emitted type : this causes the system to last forever, and it has been flagged with 'BypassMaxNumIntegrationSteps'. New emitted type is not set " ) ;
return false ;
}
}
ps - > systemDurationChanged ( ) ;
}
return true ;
}
///==========================================================================
void CPSEmitter : : notifyTargetRemoved ( CPSLocated * ptr )
{
NL_PS_FUNC ( CPSEmitter_notifyTargetRemoved )
nlassert ( ptr = = _EmittedType & & _EmittedType ) ;
setEmittedType ( NULL ) ;
}
///==========================================================================
void CPSEmitter : : setPeriod ( float period )
{
NL_PS_FUNC ( CPSEmitter_setPeriod )
if ( _PeriodScheme )
{
delete _PeriodScheme ;
_PeriodScheme = NULL ;
}
_Period = period ;
if ( _Owner & & _Owner - > getOwner ( ) )
{
_Owner - > getOwner ( ) - > systemDurationChanged ( ) ;
}
}
///==========================================================================
void CPSEmitter : : setPeriodScheme ( CPSAttribMaker < float > * scheme )
{
NL_PS_FUNC ( CPSEmitter_setPeriodScheme )
delete _PeriodScheme ;
_PeriodScheme = scheme ;
if ( _Owner & & scheme - > hasMemory ( ) ) scheme - > resize ( _Owner - > getMaxSize ( ) , _Owner - > getSize ( ) ) ;
if ( _Owner & & _Owner - > getOwner ( ) )
{
_Owner - > getOwner ( ) - > systemDurationChanged ( ) ;
}
}
///==========================================================================
void CPSEmitter : : setGenNb ( uint32 genNb )
{
NL_PS_FUNC ( CPSEmitter_setGenNb )
if ( _GenNbScheme )
{
delete _GenNbScheme ;
_GenNbScheme = NULL ;
}
_GenNb = genNb ;
}
///==========================================================================
void CPSEmitter : : setGenNbScheme ( CPSAttribMaker < uint32 > * scheme )
{
NL_PS_FUNC ( CPSEmitter_setGenNbScheme )
delete _GenNbScheme ;
_GenNbScheme = scheme ;
if ( _Owner & & scheme - > hasMemory ( ) ) scheme - > resize ( _Owner - > getMaxSize ( ) , _Owner - > getSize ( ) ) ;
}
///==========================================================================
void CPSEmitter : : showTool ( void )
{
NL_PS_FUNC ( CPSEmitter_showTool )
uint32 size = _Owner - > getSize ( ) ;
if ( ! size ) return ;
setupDriverModelMatrix ( ) ;
const CVector I = computeI ( ) ;
const CVector K = computeK ( ) ;
// ugly slow code, but not for runtime
for ( uint k = 0 ; k < size ; + + k )
{
// center of the current particle
const CVector p = _Owner - > getPos ( ) [ k ] ;
const float sSize = 0.1f ;
std : : vector < NLMISC : : CLine > lines ;
NLMISC : : CLine l ;
l . V0 = p - sSize * I ; l . V1 = p + sSize * I ; lines . push_back ( l ) ;
l . V0 = p - sSize * K ; l . V1 = p + sSize * K ; lines . push_back ( l ) ;
l . V0 = p - sSize * ( I + K ) ; l . V1 = p + sSize * ( I + K ) ; lines . push_back ( l ) ;
l . V0 = p - sSize * ( I - K ) ; l . V1 = p + sSize * ( I - K ) ; lines . push_back ( l ) ;
CMaterial mat ;
mat . setBlendFunc ( CMaterial : : one , CMaterial : : one ) ;
mat . setZWrite ( false ) ;
mat . setLighting ( false ) ;
mat . setBlend ( true ) ;
mat . setZFunc ( CMaterial : : less ) ;
CPSLocated * loc ;
uint32 index ;
CPSLocatedBindable * lb ;
_Owner - > getOwner ( ) - > getCurrentEditedElement ( loc , index , lb ) ;
mat . setColor ( ( lb = = NULL | | this = = lb ) & & loc = = _Owner & & index = = k ? CRGBA : : Red : CRGBA ( 127 , 127 , 127 ) ) ;
CDRU : : drawLinesUnlit ( lines , mat , * getDriver ( ) ) ;
}
}
///==========================================================================
void CPSEmitter : : singleEmit ( uint32 index , uint quantity )
{
NL_PS_FUNC ( CPSEmitter_singleEmit )
nlassert ( _Owner ) ;
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , 0 ) : _GenNb ;
processEmitOutsideSimLoop ( index , quantity * nbToGenerate ) ;
}
///==========================================================================
void CPSEmitter : : processRegularEmissionWithNoLOD ( uint firstInstanceIndex )
{
NL_PS_FUNC ( CPSEmitter_processRegularEmissionWithNoLOD )
nlassert ( _Owner ) ;
nlassert ( _Owner - > getOwner ( ) ) ;
//
const bool emitThreshold = _Owner - > getOwner ( ) - > isEmitThresholdEnabled ( ) ;
//
const uint size = _Owner - > getSize ( ) ;
nlassert ( firstInstanceIndex < size ) ;
uint leftToDo = size - firstInstanceIndex , toProcess ;
float emitPeriod [ EMITTER_BUFF_SIZE ] ;
const float * currEmitPeriod ;
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0 ;
sint32 nbToGenerate ;
TPSAttribTime : : iterator phaseIt = _Phase . begin ( ) + firstInstanceIndex , endPhaseIt ;
TPSAttribUInt8 : : iterator numEmitIt = _NumEmission . begin ( ) + firstInstanceIndex ;
// we don't use an iterator here
// because it could be invalidated if size change (a located could generate itself)
do
{
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE ;
if ( _PeriodScheme )
{
// compute period
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
// so we may have a life counter that is > to 1
currEmitPeriod = ( float * ) ( _PeriodScheme - > make ( _Owner , size - leftToDo , emitPeriod , sizeof ( float ) , toProcess , true , 1 < < 16 , true ) ) ;
if ( emitThreshold )
{
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
* all null period have already been replaced by the threshold
*/
if ( currEmitPeriod = = emitPeriod )
{
// if there possibility to have 0 in the scheme ?
if ( _PeriodScheme - > getMinValue ( ) < = 0.f & & _PeriodScheme - > getMaxValue ( ) > = 0.f )
{
replaceNullPeriodsByThreshold ( emitPeriod , toProcess ) ;
}
}
}
}
else
{
if ( _Period ! = 0.f | | ! emitThreshold )
{
currEmitPeriod = & _Period ;
}
else
{
currEmitPeriod = & EMIT_PERIOD_THRESHOLD ;
}
}
endPhaseIt = phaseIt + toProcess ;
if ( _MaxEmissionCount = = 0 ) // no emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( * phaseIt / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
processEmit ( k , nbToGenerate ) ;
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // there's an emission delay
{
do
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
processEmit ( k , nbToGenerate ) ;
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
else // there's an emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
if ( * numEmitIt < _MaxEmissionCount )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( * phaseIt / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
processEmit ( k , nbToGenerate ) ;
+ + * numEmitIt ;
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // there's an emission delay
{
do
{
if ( * numEmitIt < _MaxEmissionCount )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
processEmit ( k , nbToGenerate ) ;
+ + * numEmitIt ;
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
leftToDo - = toProcess ;
}
while ( leftToDo ) ;
}
///==========================================================================
void CPSEmitter : : processRegularEmission ( uint firstInstanceIndex , float emitLOD )
{
NL_PS_FUNC ( CPSEmitter_processRegularEmission )
nlassert ( _Owner ) ;
nlassert ( _Owner - > getOwner ( ) ) ;
//
const bool emitThreshold = _Owner - > getOwner ( ) - > isEmitThresholdEnabled ( ) ;
//
const uint size = _Owner - > getSize ( ) ;
nlassert ( firstInstanceIndex < size ) ;
uint leftToDo = size - firstInstanceIndex , toProcess ;
float emitPeriod [ EMITTER_BUFF_SIZE ] ;
const float * currEmitPeriod ;
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0 ;
sint32 nbToGenerate ;
TPSAttribTime : : iterator phaseIt = _Phase . begin ( ) + firstInstanceIndex , endPhaseIt ;
TPSAttribUInt8 : : iterator numEmitIt = _NumEmission . begin ( ) + firstInstanceIndex ;
float ellapsedTimeLOD = emitLOD * CParticleSystem : : EllapsedTime ;
uint maxEmissionCountLOD = ( uint8 ) ( _MaxEmissionCount * emitLOD ) ;
maxEmissionCountLOD = std : : max ( 1u , maxEmissionCountLOD ) ;
// we don't use an iterator here
// because it could be invalidated if size change (a located could generate itself)
do
{
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE ;
if ( _PeriodScheme )
{
// compute period
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
// so we may have a life counter that is > to 1
currEmitPeriod = ( float * ) ( _PeriodScheme - > make ( _Owner , size - leftToDo , emitPeriod , sizeof ( float ) , toProcess , true , 1 < < 16 , true ) ) ;
if ( emitThreshold )
{
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
* all null period have already been replaced by the threshold
*/
if ( currEmitPeriod = = emitPeriod )
{
// if there possibility to have 0 in the scheme ?
if ( _PeriodScheme - > getMinValue ( ) < = 0.f & & _PeriodScheme - > getMaxValue ( ) > = 0.f )
{
replaceNullPeriodsByThreshold ( emitPeriod , toProcess ) ;
}
}
}
}
else
{
if ( _Period ! = 0.f | | ! emitThreshold )
{
currEmitPeriod = & _Period ;
}
else
{
currEmitPeriod = & EMIT_PERIOD_THRESHOLD ;
}
}
endPhaseIt = phaseIt + toProcess ;
if ( _MaxEmissionCount = = 0 ) // no emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
* phaseIt + = ellapsedTimeLOD ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( * phaseIt / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( k , nbToGenerate ) ;
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // there's an emission delay
{
do
{
if ( * phaseIt < _EmitDelay )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt < _EmitDelay )
{
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
continue ;
}
else
{
* phaseIt = ( * phaseIt - _EmitDelay ) * emitLOD + _EmitDelay ;
}
}
else
{
* phaseIt + = ellapsedTimeLOD ;
}
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( k , nbToGenerate ) ;
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
else // there's an emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
if ( * numEmitIt < maxEmissionCountLOD )
{
* phaseIt + = ellapsedTimeLOD ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( * phaseIt / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( k , nbToGenerate ) ;
}
+ + * numEmitIt ;
}
}
else
{
* numEmitIt = _MaxEmissionCount ;
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // there's an emission delay
{
do
{
if ( * numEmitIt < maxEmissionCountLOD )
{
if ( * phaseIt < _EmitDelay )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt < _EmitDelay )
{
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
continue ;
}
else
{
* phaseIt = ( * phaseIt - _EmitDelay ) * emitLOD + _EmitDelay ;
}
}
else
{
* phaseIt + = ellapsedTimeLOD ;
}
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
* phaseIt - = : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) * * currEmitPeriod ;
}
const uint32 k = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , k ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( nbToGenerate * emitLOD ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( k , nbToGenerate ) ;
}
+ + * numEmitIt ;
}
}
else
{
* numEmitIt = _MaxEmissionCount ;
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
leftToDo - = toProcess ;
}
while ( leftToDo ) ;
}
/// private : generate the various position of an emitter in the given tab for the given slice of time,
// depending on whether its motion is parametric or incremental. This is used to create emittees at the right position
static
# ifndef NL_DEBUG
inline
# endif
uint GenEmitterPositions ( CPSLocated * emitter ,
CPSLocated * emittee ,
uint emitterIndex ,
uint numStep ,
TAnimationTime deltaT , /* fraction of time needed to reach the first emission */
TAnimationTime step ,
std : : vector < NLMISC : : CVector > & dest
)
{
NL_PS_FUNC ( GenEmitterPositions )
const uint toProcess = std : : max ( 1U , std : : min ( numStep , ( uint ) emittee - > getMaxSize ( ) ) ) ;
dest . resize ( toProcess ) ;
if ( ! emitter - > isParametricMotionEnabled ( ) ) // standard case : take current pos and integrate
{
if ( toProcess = = 1 ) // only one emission -> takes current pos
{
dest [ 0 ] = emitter - > getPos ( ) [ emitterIndex ] - deltaT * emitter - > getSpeed ( ) [ emitterIndex ] ;
}
else
{
std : : vector < NLMISC : : CVector > : : iterator outIt = dest . end ( ) ;
std : : vector < NLMISC : : CVector > : : iterator endIt = dest . begin ( ) ;
NLMISC : : CVector pos = emitter - > getPos ( ) [ emitterIndex ] - deltaT * emitter - > getSpeed ( ) [ emitterIndex ] ;
NLMISC : : CVector speed = step * emitter - > getSpeed ( ) [ emitterIndex ] ;
do
{
- - outIt ;
* outIt = pos ;
pos - = speed ;
}
while ( outIt ! = endIt ) ;
}
}
else // compute parametric trajectory
{
emitter - > integrateSingle ( emitter - > getOwner ( ) - > getSystemDate ( ) + CParticleSystem : : RealEllapsedTime - deltaT ,
- step ,
toProcess ,
emitterIndex ,
& dest [ 0 ]
) ;
}
return toProcess ;
}
/** The same as GenEmitterPositions, but with LOD taken in account.
*/
static inline uint GenEmitterPositionsWithLOD ( CPSLocated * emitter ,
CPSLocated * emittee ,
uint emitterIndex ,
uint numStep ,
TAnimationTime deltaT , /* fraction of time needed to reach the first emission */
TAnimationTime step ,
float invLODRatio ,
std : : vector < NLMISC : : CVector > & dest
)
{
NL_PS_FUNC ( GenEmitterPositionsWithLOD )
const uint toProcess = std : : max ( 1U , std : : min ( numStep , ( uint ) emittee - > getMaxSize ( ) ) ) ;
dest . resize ( toProcess ) ;
if ( ! emitter - > isParametricMotionEnabled ( ) ) // standard case : take current pos and integrate
{
if ( toProcess = = 1 ) // only one emission -> takes current pos
{
dest [ 0 ] = emitter - > getPos ( ) [ emitterIndex ] - deltaT * emitter - > getSpeed ( ) [ emitterIndex ] ;
}
else
{
std : : vector < NLMISC : : CVector > : : iterator outIt = dest . end ( ) ;
std : : vector < NLMISC : : CVector > : : iterator endIt = dest . begin ( ) ;
NLMISC : : CVector pos = emitter - > getPos ( ) [ emitterIndex ] - deltaT * emitter - > getSpeed ( ) [ emitterIndex ] ;
NLMISC : : CVector speed = step * invLODRatio * emitter - > getSpeed ( ) [ emitterIndex ] ;
do
{
- - outIt ;
* outIt = pos ;
pos - = speed ;
}
while ( outIt ! = endIt ) ;
}
}
else // compute parametric trajectory
{
emitter - > integrateSingle ( emitter - > getOwner ( ) - > getSystemDate ( ) - deltaT - step * toProcess ,
step ,
toProcess ,
emitterIndex ,
& dest [ 0 ]
) ;
}
return toProcess ;
}
///==========================================================================
void CPSEmitter : : processRegularEmissionConsistent ( uint firstInstanceIndex , float emitLOD , float inverseEmitLOD )
{
NL_PS_FUNC ( CPSEmitter_processRegularEmissionConsistent )
/// hmm some code factorisation would do no harm, but we want to keep tests outside the loops as much as possible...
nlassert ( _Owner ) ;
nlassert ( _Owner - > getOwner ( ) ) ;
//
const bool emitThreshold = _Owner - > getOwner ( ) - > isEmitThresholdEnabled ( ) ;
//
static std : : vector < NLMISC : : CVector > emitterPositions ;
// Positions for the emitter. They are computed by using a parametric trajectory or by using integration
const uint size = _Owner - > getSize ( ) ;
nlassert ( firstInstanceIndex < size ) ;
uint leftToDo = size - firstInstanceIndex , toProcess ;
float emitPeriod [ EMITTER_BUFF_SIZE ] ;
const float * currEmitPeriod ;
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0 ;
sint32 nbToGenerate ;
TPSAttribTime : : iterator phaseIt = _Phase . begin ( ) + firstInstanceIndex , endPhaseIt ;
TPSAttribUInt8 : : iterator numEmitIt ;
if ( firstInstanceIndex < _NumEmission . getSize ( ) )
{
numEmitIt = _NumEmission . begin ( ) + firstInstanceIndex ;
}
else
{
numEmitIt = _NumEmission . end ( ) ;
}
float ellapsedTimeLOD = CParticleSystem : : EllapsedTime * emitLOD ;
uint maxEmissionCountLOD = ( uint8 ) ( _MaxEmissionCount * emitLOD ) ;
maxEmissionCountLOD = std : : max ( 1u , maxEmissionCountLOD ) ;
// we don't use an iterator here
// because it could be invalidated if size change (a located could generate itself)
do
{
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE ;
if ( _PeriodScheme )
{
// compute period
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
// so we may have a life counter that is > to 1
currEmitPeriod = ( float * ) ( _PeriodScheme - > make ( _Owner , size - leftToDo , emitPeriod , sizeof ( float ) , toProcess , true , 1 < < 16 , true ) ) ;
if ( emitThreshold )
{
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
* all null period have already been replaced by the threshold
*/
if ( currEmitPeriod = = emitPeriod )
{
// if there possibility to have 0 in the scheme ?
if ( _PeriodScheme - > getMinValue ( ) < = 0.f & & _PeriodScheme - > getMaxValue ( ) > = 0.f )
{
replaceNullPeriodsByThreshold ( emitPeriod , toProcess ) ;
}
}
}
}
else
{
if ( _Period ! = 0.f | | ! emitThreshold )
{
currEmitPeriod = & _Period ;
}
else
{
currEmitPeriod = & EMIT_PERIOD_THRESHOLD ;
}
}
endPhaseIt = phaseIt + toProcess ;
if ( _MaxEmissionCount = = 0 ) // no emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
* phaseIt + = ellapsedTimeLOD ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + ellapsedTimeLOD ) ;
//
/// compute the number of emissions
uint numEmissions = ( uint ) : : floorf ( * phaseIt / * currEmitPeriod ) ;
* phaseIt - = * currEmitPeriod * numEmissions ;
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
float deltaT = std : : max ( 0.f , * phaseIt ) ;
/// compute the position of the emitter for the needed dates
numEmissions = GenEmitterPositionsWithLOD ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
inverseEmitLOD ,
emitterPositions
) ;
/// process each emission at the right pos at the right date
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
uint k = numEmissions ;
float deltaTInc = * currEmitPeriod * inverseEmitLOD ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT
) ;
deltaT + = deltaTInc ;
}
while ( k ) ;
}
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( emitterIndex , nbToGenerate ) ;
}
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // thhere's an emission delay
{
do
{
if ( * phaseIt < _EmitDelay )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt < _EmitDelay )
{
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
continue ;
}
else
{
* phaseIt = ( * phaseIt - _EmitDelay ) * emitLOD + _EmitDelay ;
}
}
else
{
* phaseIt + = ellapsedTimeLOD ;
}
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + ellapsedTimeLOD + _EmitDelay ) ;
//
uint numEmissions = ( uint ) : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) ;
* phaseIt - = * currEmitPeriod * numEmissions ;
float deltaT = std : : max ( * phaseIt - _EmitDelay , 0.f ) ;
//nlassert(deltaT >= 0.f);
/// process each emission at the right pos at the right date
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
/// compute the position of the emitter for the needed date
numEmissions = GenEmitterPositionsWithLOD ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
inverseEmitLOD ,
emitterPositions
) ;
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
uint k = numEmissions ;
float deltaTInc = * currEmitPeriod * inverseEmitLOD ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT ) ;
deltaT + = deltaTInc ;
}
while ( k ) ;
}
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( emitterIndex , nbToGenerate ) ;
}
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
else // there's an emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
if ( * numEmitIt < maxEmissionCountLOD )
{
* phaseIt + = ellapsedTimeLOD ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + ellapsedTimeLOD ) ;
//
uint numEmissions = ( uint ) : : floorf ( * phaseIt / * currEmitPeriod ) ;
* numEmitIt + = numEmissions ;
* phaseIt - = * currEmitPeriod * numEmissions ;
float deltaT = std : : max ( * phaseIt , 0.f ) ;
//nlassert(deltaT >= 0.f);
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
if ( * numEmitIt > _MaxEmissionCount ) // make sure we don't go over the emission limit
{
numEmissions - = * numEmitIt - _MaxEmissionCount ;
* numEmitIt = _MaxEmissionCount ;
}
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
/// compute the position of the emitter for the needed date
numEmissions = GenEmitterPositionsWithLOD ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
inverseEmitLOD ,
emitterPositions
) ;
uint k = numEmissions ;
/// process each emission at the right pos at the right date
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
float deltaTInc = * currEmitPeriod * inverseEmitLOD ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT
) ;
deltaT + = deltaTInc ;
}
while ( k ) ;
}
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( emitterIndex , nbToGenerate ) ;
+ + * numEmitIt ;
}
}
}
}
else
{
* numEmitIt = _MaxEmissionCount ; // if the lod change, must ensure that the
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // there's an emission delay
{
do
{
if ( * numEmitIt < maxEmissionCountLOD )
{
if ( * phaseIt < _EmitDelay )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt < _EmitDelay )
{
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
continue ;
}
else
{
* phaseIt = ( * phaseIt - _EmitDelay ) * emitLOD + _EmitDelay ;
}
}
else
{
* phaseIt + = ellapsedTimeLOD ;
}
//
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + ellapsedTimeLOD + _EmitDelay ) ;
//
uint numEmissions = ( uint ) : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) ;
* numEmitIt + = numEmissions ;
* phaseIt - = * currEmitPeriod * numEmissions ;
float deltaT = std : : max ( * phaseIt - _EmitDelay , 0.f ) ;
//nlassert(deltaT >= 0.f);
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
if ( * numEmitIt > _MaxEmissionCount ) // make sure we don't go over the emission limit
{
numEmissions - = * numEmitIt - _MaxEmissionCount ;
* numEmitIt = _MaxEmissionCount ;
}
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
/// compute the position of the emitter for the needed date
numEmissions = GenEmitterPositionsWithLOD ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
inverseEmitLOD ,
emitterPositions
) ;
uint k = numEmissions ;
/// process each emission at the right pos at the right date
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
float deltaTInc = * currEmitPeriod * inverseEmitLOD ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT ) ;
deltaT + = deltaTInc ;
}
while ( k ) ;
}
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
if ( nbToGenerate )
{
nbToGenerate = ( sint32 ) ( emitLOD * nbToGenerate ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( emitterIndex , nbToGenerate ) ;
+ + * numEmitIt ;
}
}
}
}
else
{
* numEmitIt = _MaxEmissionCount ; // if the lod change, must ensure that the
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
leftToDo - = toProcess ;
}
while ( leftToDo ) ;
}
///==========================================================================
void CPSEmitter : : processRegularEmissionConsistentWithNoLOD ( uint firstInstanceIndex )
{
NL_PS_FUNC ( CPSEmitter_processRegularEmissionConsistentWithNoLOD )
/// hum, some code factorization would do no harm, but we want to keep tests outside the loops as much as possible...
nlassert ( _Owner ) ;
nlassert ( _Owner - > getOwner ( ) ) ;
//
const bool emitThreshold = _Owner - > getOwner ( ) - > isEmitThresholdEnabled ( ) ;
//
static std : : vector < NLMISC : : CVector > emitterPositions ;
// Positions for the emitter. They are computed by using a parametric trajectory or by using integration
const uint size = _Owner - > getSize ( ) ;
nlassert ( firstInstanceIndex < size ) ;
uint leftToDo = size - firstInstanceIndex , toProcess ;
float emitPeriod [ EMITTER_BUFF_SIZE ] ;
const float * currEmitPeriod ;
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0 ;
sint32 nbToGenerate ;
TPSAttribTime : : iterator phaseIt = _Phase . begin ( ) + firstInstanceIndex , endPhaseIt ;
TPSAttribUInt8 : : iterator numEmitIt ;
if ( firstInstanceIndex < _NumEmission . getSize ( ) )
numEmitIt = _NumEmission . begin ( ) + firstInstanceIndex ;
else
numEmitIt = _NumEmission . end ( ) ;
do
{
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE ;
if ( _PeriodScheme )
{
// compute period
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
// so we may have a life counter that is > to 1
currEmitPeriod = ( float * ) ( _PeriodScheme - > make ( _Owner , size - leftToDo , emitPeriod , sizeof ( float ) , toProcess , true , 1 < < 16 , true ) ) ;
if ( emitThreshold )
{
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
* all null period have already been replaced by the threshold
*/
if ( currEmitPeriod = = emitPeriod )
{
// if there possibility to have 0 in the scheme ?
if ( _PeriodScheme - > getMinValue ( ) < = 0.f & & _PeriodScheme - > getMaxValue ( ) > = 0.f )
{
replaceNullPeriodsByThreshold ( emitPeriod , toProcess ) ;
}
}
}
}
else
{
if ( _Period ! = 0.f | | ! emitThreshold )
{
currEmitPeriod = & _Period ;
}
else
{
currEmitPeriod = & EMIT_PERIOD_THRESHOLD ;
}
}
endPhaseIt = phaseIt + toProcess ;
if ( _MaxEmissionCount = = 0 ) // no emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + CParticleSystem : : EllapsedTime ) ;
//
/// compute the number of emissions
uint numEmissions = ( uint ) : : floorf ( * phaseIt / * currEmitPeriod ) ;
* phaseIt - = * currEmitPeriod * numEmissions ;
float deltaT = std : : max ( 0.f , * phaseIt ) ;
//nlassert(deltaT >= 0.f);
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
/// compute the position of the emitter for the needed dates
numEmissions = GenEmitterPositions ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
emitterPositions
) ;
/// process each emission at the right pos at the right date
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
uint k = numEmissions ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT ) ;
deltaT + = * currEmitPeriod ;
}
while ( k ) ;
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
processEmit ( emitterIndex , nbToGenerate ) ;
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // thhere's an emission delay
{
do
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + CParticleSystem : : EllapsedTime + _EmitDelay ) ;
//
uint numEmissions = ( uint ) : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) ;
* phaseIt - = * currEmitPeriod * numEmissions ;
float deltaT = std : : max ( * phaseIt - _EmitDelay , 0.f ) ;
//nlassert(deltaT >= 0.f);
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
/// compute the position of the emitter for the needed date
numEmissions = GenEmitterPositions ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
emitterPositions
) ;
/// process each emission at the right pos at the right date
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
uint k = numEmissions ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT ) ;
deltaT + = * currEmitPeriod ;
}
while ( k ) ;
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
processEmit ( emitterIndex , nbToGenerate ) ;
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
else // there's an emission count limit
{
/// is there an emission delay ?
if ( _EmitDelay = = 0.f ) // no emission delay
{
do
{
if ( * numEmitIt < _MaxEmissionCount )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + CParticleSystem : : EllapsedTime ) ;
//
uint numEmissions = ( uint ) : : floorf ( * phaseIt / * currEmitPeriod ) ;
* numEmitIt + = numEmissions ;
* phaseIt - = * currEmitPeriod * numEmissions ;
float deltaT = std : : max ( * phaseIt , 0.f ) ;
//nlassert(deltaT >= 0.f);
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
if ( * numEmitIt > _MaxEmissionCount ) // make sure we don't go over the emission limit
{
numEmissions - = * numEmitIt - _MaxEmissionCount ;
* numEmitIt = _MaxEmissionCount ;
}
/// compute the position of the emitter for the needed date
numEmissions = GenEmitterPositions ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
emitterPositions
) ;
uint k = numEmissions ;
/// process each emission at the right pos at the right date
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT ) ;
deltaT + = * currEmitPeriod ;
}
while ( k ) ;
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
processEmit ( emitterIndex , nbToGenerate ) ;
+ + * numEmitIt ;
}
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
else // there's an emission delay
{
do
{
if ( * numEmitIt < _MaxEmissionCount )
{
* phaseIt + = CParticleSystem : : EllapsedTime ;
if ( * phaseIt > = * currEmitPeriod + _EmitDelay ) // phase is greater than period -> must emit
{
if ( * currEmitPeriod ! = 0 )
{
/** Must ensure phase is valid if period decrease over time
*/
* phaseIt = std : : min ( * phaseIt , * currEmitPeriod + CParticleSystem : : EllapsedTime + _EmitDelay ) ;
//
uint numEmissions = ( uint ) : : floorf ( ( * phaseIt - _EmitDelay ) / * currEmitPeriod ) ;
* numEmitIt + = numEmissions ;
* phaseIt - = * currEmitPeriod * numEmissions ;
float deltaT = std : : max ( * phaseIt - _EmitDelay , 0.f ) ;
//nlassert(deltaT >= 0.f);
uint emitterIndex = ( uint ) ( phaseIt - _Phase . begin ( ) ) ;
if ( * numEmitIt > _MaxEmissionCount ) // make sure we don't go over the emission limit
{
numEmissions - = * numEmitIt - _MaxEmissionCount ;
* numEmitIt = _MaxEmissionCount ;
}
/// compute the position of the emitter for the needed date
numEmissions = GenEmitterPositions ( _Owner ,
_EmittedType ,
emitterIndex ,
numEmissions ,
deltaT ,
* currEmitPeriod ,
emitterPositions
) ;
uint k = numEmissions ;
/// process each emission at the right pos at the right date
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
do
{
- - k ;
processEmitConsistent ( emitterPositions [ k ] ,
emitterIndex ,
nbToGenerate ,
deltaT ) ;
deltaT + = * currEmitPeriod ;
}
while ( k ) ;
}
else
{
const uint32 emitterIndex = ( uint32 ) ( phaseIt - _Phase . begin ( ) ) ;
nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , emitterIndex ) : _GenNb ;
processEmit ( emitterIndex , nbToGenerate ) ;
+ + * numEmitIt ;
}
}
}
+ + phaseIt ;
currEmitPeriod + = currEmitPeriodPtrInc ;
+ + numEmitIt ;
}
while ( phaseIt ! = endPhaseIt ) ;
}
}
leftToDo - = toProcess ;
}
while ( leftToDo ) ;
}
///==========================================================================
void CPSEmitter : : step ( TPSProcessPass pass )
{
NL_PS_FUNC ( CPSEmitter_step )
if ( pass = = PSToolRender )
{
showTool ( ) ;
}
}
///==========================================================================
void CPSEmitter : : computeSpawns ( uint firstInstanceIndex )
{
NL_PS_FUNC ( CPSEmitter_computeSpawns )
if ( ! _EmittedType ) return ;
nlassert ( CParticleSystem : : InsideSimLoop ) ;
const uint32 size = _Owner - > getSize ( ) ;
if ( ! size ) return ;
if ( CParticleSystem : : EllapsedTime = = 0.f ) return ; // do nothing when paused
CParticleSystem * ps = _Owner - > getOwner ( ) ;
nlassert ( ps ) ;
float emitLOD ;
if ( ps - > isAutoLODEnabled ( ) & & ! ps - > isSharingEnabled ( ) & & ! _BypassAutoLOD )
{
// temp test for auto lod
emitLOD = ps - > getAutoLODEmitRatio ( ) ;
}
else
{
emitLOD = 1.f ;
}
nlassert ( _EmissionType = = CPSEmitter : : regular ) ;
if ( ! _ConsistentEmission )
{
if ( emitLOD ! = 1.f )
{
processRegularEmission ( firstInstanceIndex , emitLOD ) ;
}
else
{
processRegularEmissionWithNoLOD ( firstInstanceIndex ) ;
}
}
else
{
if ( emitLOD ! = 1.f )
{
if ( emitLOD ! = 0.f )
{
processRegularEmissionConsistent ( firstInstanceIndex , emitLOD , 1.f / emitLOD ) ;
}
}
else
{
processRegularEmissionConsistentWithNoLOD ( firstInstanceIndex ) ;
}
}
}
///==========================================================================
void CPSEmitter : : newElement ( const CPSEmitterInfo & info )
{
NL_PS_FUNC ( CPSEmitter_newElement )
nlassert ( _Phase . getSize ( ) ! = _Phase . getMaxSize ( ) ) ;
_Phase . insert ( 0.f ) ;
if ( _MaxEmissionCount ! = 0 )
{
_NumEmission . insert ( 0 ) ;
}
if ( _PeriodScheme & & _PeriodScheme - > hasMemory ( ) ) _PeriodScheme - > newElement ( info ) ;
if ( _GenNbScheme & & _GenNbScheme - > hasMemory ( ) ) _GenNbScheme - > newElement ( info ) ;
}
///==========================================================================
inline void CPSEmitter : : deleteElementBase ( uint32 index )
{
NL_PS_FUNC ( CPSEmitter_deleteElementBase )
if ( _PeriodScheme & & _PeriodScheme - > hasMemory ( ) ) _PeriodScheme - > deleteElement ( index ) ;
if ( _GenNbScheme & & _GenNbScheme - > hasMemory ( ) ) _GenNbScheme - > deleteElement ( index ) ;
_Phase . remove ( index ) ;
if ( _MaxEmissionCount ! = 0 )
{
_NumEmission . remove ( index ) ;
}
}
///==========================================================================
void CPSEmitter : : deleteElement ( uint32 index )
{
NL_PS_FUNC ( CPSEmitter_deleteElement )
if ( _EmissionType = = CPSEmitter : : onDeath & & _EmittedType & & _Active )
{
if ( ! _BypassEmitOnDeath )
{
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , index ) : _GenNb ;
processEmitOutsideSimLoop ( index , nbToGenerate ) ;
}
}
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitter : : deleteElement ( uint32 index , TAnimationTime timeUntilNextSimStep )
{
NL_PS_FUNC ( CPSEmitter_deleteElement )
if ( _EmissionType = = CPSEmitter : : onDeath & & _EmittedType & & _Active )
{
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , index ) : _GenNb ;
processEmitConsistent ( _Owner - > getPos ( ) [ index ] , index , nbToGenerate , timeUntilNextSimStep ) ;
}
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitter : : resize ( uint32 size )
{
NL_PS_FUNC ( CPSEmitter_resize )
nlassert ( size < ( 1 < < 16 ) ) ;
if ( _PeriodScheme & & _PeriodScheme - > hasMemory ( ) ) _PeriodScheme - > resize ( size , _Owner - > getSize ( ) ) ;
if ( _GenNbScheme & & _GenNbScheme - > hasMemory ( ) ) _GenNbScheme - > resize ( size , _Owner - > getSize ( ) ) ;
_Phase . resize ( size ) ;
if ( _MaxEmissionCount ! = 0 )
{
_NumEmission . resize ( size ) ;
}
}
///==========================================================================
void CPSEmitter : : bounceOccured ( uint32 index , TAnimationTime timeToNextSimStep )
{
NL_PS_FUNC ( CPSEmitter_bounceOccured )
// TODO : avoid duplication with deleteElement
if ( _EmittedType & & _EmissionType = = CPSEmitter : : onBounce )
{
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme - > get ( _Owner , index ) : _GenNb ;
processEmitConsistent ( _Owner - > getPos ( ) [ index ] , index , nbToGenerate , timeToNextSimStep ) ;
}
}
///==========================================================================
void CPSEmitter : : serial ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSEmitter_serial )
/// version 6 : the flag _EmitDirBasis no longer exist, it has been replaced by _UserMatrixModeForEmissionDirection
//
/// version 5 : added _BypassAutoLOD
/// version 4 : added consistent emissions
sint ver = f . serialVersion ( 6 ) ;
CPSLocatedBindable : : serial ( f ) ;
f . serialPolyPtr ( _EmittedType ) ;
f . serial ( _Phase ) ;
f . serial ( _SpeedInheritanceFactor ) ;
bool speedBasisEmission = _SpeedBasisEmission ; // tmp copy because of bitfield serialization scheme
f . serial ( speedBasisEmission ) ;
_SpeedBasisEmission = speedBasisEmission ;
f . serialEnum ( _EmissionType ) ;
// this is for use with serial
bool trueB = true , falseB = false ;
if ( ! f . isReading ( ) )
{
switch ( _EmissionType )
{
case CPSEmitter : : regular :
if ( _PeriodScheme )
{
f . serial ( trueB ) ;
f . serialPolyPtr ( _PeriodScheme ) ;
}
else
{
f . serial ( falseB ) ;
f . serial ( _Period ) ;
}
if ( ver > = 3 )
{
f . serial ( _EmitDelay , _MaxEmissionCount ) ;
}
break ;
default :
break ;
}
if ( _GenNbScheme )
{
f . serial ( trueB ) ;
f . serialPolyPtr ( _GenNbScheme ) ;
}
else
{
f . serial ( falseB ) ;
f . serial ( _GenNb ) ;
}
}
else
{
bool useScheme ;
switch ( _EmissionType )
{
case CPSEmitter : : regular :
{
f . serial ( useScheme ) ;
if ( useScheme )
{
delete _PeriodScheme ;
f . serialPolyPtr ( _PeriodScheme ) ;
}
else
{
f . serial ( _Period ) ;
}
if ( ver > = 3 )
{
f . serial ( _EmitDelay , _MaxEmissionCount ) ;
updateMaxCountVect ( ) ;
}
}
break ;
default :
break ;
}
f . serial ( useScheme ) ;
if ( useScheme )
{
delete _GenNbScheme ;
f . serialPolyPtr ( _GenNbScheme ) ;
}
else
{
f . serial ( _GenNb ) ;
}
}
if ( ver > 1 & & ver < 6 )
{
nlassert ( f . isReading ( ) ) ;
bool emitDirBasis ;
f . serial ( emitDirBasis ) ;
if ( emitDirBasis )
{
_UserMatrixModeForEmissionDirection = false ;
_UserDirectionMatrixMode = PSFXWorldMatrix ;
}
else
{
_UserMatrixModeForEmissionDirection = true ;
if ( _Owner )
{
_UserDirectionMatrixMode = _Owner - > getMatrixMode ( ) = = PSFXWorldMatrix ? PSIdentityMatrix : PSFXWorldMatrix ;
}
else
{
_UserDirectionMatrixMode = PSFXWorldMatrix ;
}
}
}
if ( ver > = 4 )
{
bool consistentEmission = _ConsistentEmission ; // tmp copy because of bitfield serialization scheme
f . serial ( consistentEmission ) ;
_ConsistentEmission = consistentEmission ;
}
if ( ver > = 5 )
{
bool byassAutoLOD = _BypassAutoLOD ; // tmp copy because of bitfield serialization scheme
f . serial ( byassAutoLOD ) ;
_BypassAutoLOD = byassAutoLOD ;
}
if ( ver > = 6 )
{
bool userMatrixModeForEmissionDirection = _UserMatrixModeForEmissionDirection ; // tmp copy because of bitfield serialization scheme
f . serial ( userMatrixModeForEmissionDirection ) ;
_UserMatrixModeForEmissionDirection = userMatrixModeForEmissionDirection ;
f . serialEnum ( _UserDirectionMatrixMode ) ;
}
}
///==========================================================================
void CPSEmitter : : updateMaxCountVect ( )
{
NL_PS_FUNC ( CPSEmitter_updateMaxCountVect )
if ( ! _MaxEmissionCount | | ! _Owner )
{
_NumEmission . resize ( 0 ) ;
}
else
{
_NumEmission . resize ( _Owner - > getMaxSize ( ) ) ;
while ( _NumEmission . getSize ( ) ! = 0 )
{
_NumEmission . remove ( 0 ) ;
}
while ( _NumEmission . getSize ( ) ! = _Owner - > getSize ( ) )
{
_NumEmission . insert ( 0 ) ;
}
}
}
///==========================================================================
void CPSEmitter : : setEmitDelay ( float delay )
{
NL_PS_FUNC ( CPSEmitter_setEmitDelay )
_EmitDelay = delay ;
if ( _Owner & & _Owner - > getOwner ( ) )
{
_Owner - > getOwner ( ) - > systemDurationChanged ( ) ;
}
}
///==========================================================================
bool CPSEmitter : : setMaxEmissionCount ( uint8 count )
{
NL_PS_FUNC ( CPSEmitter_setMaxEmissionCount )
if ( count = = _MaxEmissionCount ) return true ;
nlassert ( _Owner & & _Owner - > getOwner ( ) ) ;
CParticleSystem * ps = _Owner - > getOwner ( ) ;
if ( ps - > getBypassMaxNumIntegrationSteps ( ) )
{
uint8 oldEmissiontCount = _MaxEmissionCount ;
// should check that the new value is valid
_MaxEmissionCount = count ;
if ( testEmitForever ( ) )
{
_MaxEmissionCount = oldEmissiontCount ;
nlwarning ( " <CPSEmitter::setMaxEmissionCount> can't set max emission count to %d \
with the current configuration : the system has been flagged with \
' BypassMaxNumIntegrationSteps ' , and should have a finite duration . \
The new value is not set " , (int) count);
return false ;
}
}
ps - > systemDurationChanged ( ) ;
_MaxEmissionCount = count ;
updateMaxCountVect ( ) ;
return true ;
}
///==========================================================================
bool CPSEmitter : : checkLoop ( ) const
{
NL_PS_FUNC ( CPSEmitter_checkLoop )
nlassert ( _Owner ) ;
nlassert ( _Owner - > getOwner ( ) ) ;
if ( ! _EmittedType ) return false ;
std : : set < const CPSLocated * > seenLocated ; // the located we've already seen
std : : vector < const CPSLocated * > leftLoc ( 1 ) ; // the located that are left to see
leftLoc [ 0 ] = _EmittedType ;
do
{
const CPSLocated * curr = leftLoc . back ( ) ;
if ( curr = = this - > _Owner ) return true ;
leftLoc . pop_back ( ) ;
seenLocated . insert ( curr ) ;
for ( uint32 k = 0 ; k < curr - > getNbBoundObjects ( ) ; + + k )
{
const CPSEmitter * emitter = dynamic_cast < const CPSEmitter * > ( curr - > getBoundObject ( k ) ) ;
if ( emitter & & emitter - > _EmittedType )
{
if ( seenLocated . find ( emitter - > _EmittedType ) = = seenLocated . end ( ) ) // not already seen this one ?
{
leftLoc . push_back ( emitter - > _EmittedType ) ;
}
}
}
}
while ( ! leftLoc . empty ( ) ) ;
return false ;
}
///==========================================================================
bool CPSEmitter : : testEmitForever ( ) const
{
NL_PS_FUNC ( CPSEmitter_testEmitForever )
if ( ! _Owner )
{
nlwarning ( " <CPSEmitter::testEmitForever> The emitter should be inserted in a CPSLocated instance for this call to work. " ) ;
nlassert ( 0 ) ;
return true ;
}
if ( ! _Owner - > getLastForever ( ) ) return false ;
switch ( getEmissionType ( ) )
{
case CPSEmitter : : onBounce :
case CPSEmitter : : externEmit :
case CPSEmitter : : regular :
// it is ok only if a limited number of located is emitted
if ( getMaxEmissionCount ( ) = = 0 ) return true ;
break ;
case CPSEmitter : : onDeath : return true ; // the emitter never dies, so ..
case CPSEmitter : : once : return false ;
break ;
default :
nlassert ( 0 ) ; // not a known type
break ;
}
return false ;
}
////////////////////////////////////////////
// implementation of CPSModulatedEmitter //
////////////////////////////////////////////
void CPSModulatedEmitter : : serialEmitteeSpeedScheme ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSModulatedEmitter_IStream )
bool useScheme ;
if ( ! f . isReading ( ) )
{
useScheme = useEmitteeSpeedScheme ( ) ;
}
f . serial ( useScheme ) ;
if ( useScheme )
{
f . serialPolyPtr ( _EmitteeSpeedScheme ) ;
}
else
{
f . serial ( _EmitteeSpeed ) ;
}
}
////////////////////////////////////////////
// implementation of CPSEmitterOmni //
////////////////////////////////////////////
///==========================================================================
void CPSEmitterOmni : : emit ( const NLMISC : : CVector & srcPos , uint32 index , CVector & pos , CVector & speed )
{
NL_PS_FUNC ( CPSEmitterOmni_emit )
// TODO : verifier que ca marche si une particule s'emet elle-mem
nlassert ( _EmittedType ) ;
CVector v ( ( ( rand ( ) % 1000 ) - 500 ) / 500.0f
, ( ( rand ( ) % 1000 ) - 500 ) / 500.0f
, ( ( rand ( ) % 1000 ) - 500 ) / 500.0f ) ;
v . normalize ( ) ;
v * = _EmitteeSpeedScheme ? _EmitteeSpeedScheme - > get ( _Owner , index ) : _EmitteeSpeed ;
pos = srcPos ;
speed = v ;
}
///==========================================================================
void CPSEmitterOmni : : serial ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSEmitterOmni_serial )
f . serialVersion ( 1 ) ;
CPSEmitter : : serial ( f ) ;
CPSModulatedEmitter : : serialEmitteeSpeedScheme ( f ) ;
}
///==========================================================================
void CPSEmitterOmni : : newElement ( const CPSEmitterInfo & info )
{
NL_PS_FUNC ( CPSEmitterOmni_newElement )
CPSEmitter : : newElement ( info ) ;
newEmitteeSpeedElement ( info ) ;
}
///==========================================================================
inline void CPSEmitterOmni : : deleteElementBase ( uint32 index )
{
NL_PS_FUNC ( CPSEmitterOmni_deleteElementBase )
deleteEmitteeSpeedElement ( index ) ;
}
///==========================================================================
void CPSEmitterOmni : : deleteElement ( uint32 index , TAnimationTime timeUntilNextSimStep )
{
NL_PS_FUNC ( CPSEmitterOmni_deleteElement )
CPSEmitter : : deleteElement ( index , timeUntilNextSimStep ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitterOmni : : deleteElement ( uint32 index )
{
NL_PS_FUNC ( CPSEmitterOmni_deleteElement )
CPSEmitter : : deleteElement ( index ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitterOmni : : resize ( uint32 capacity )
{
NL_PS_FUNC ( CPSEmitterOmni_resize )
nlassert ( capacity < ( 1 < < 16 ) ) ;
CPSEmitter : : resize ( capacity ) ;
resizeEmitteeSpeed ( capacity ) ;
}
///==========================================================================
void CPSEmitterDirectionnal : : emit ( const NLMISC : : CVector & srcPos , uint32 index , CVector & pos , CVector & speed )
{
NL_PS_FUNC ( CPSEmitterDirectionnal_emit )
// TODO : verifier que ca marche si une particule s'emet elle-mem
nlassert ( _EmittedType ) ;
speed = ( _EmitteeSpeedScheme ? _EmitteeSpeedScheme - > get ( _Owner , index ) : _EmitteeSpeed ) * _Dir ;
pos = srcPos ;
}
///==========================================================================
void CPSEmitterDirectionnal : : newElement ( const CPSEmitterInfo & info )
{
NL_PS_FUNC ( CPSEmitterDirectionnal_newElement )
CPSEmitter : : newElement ( info ) ;
newEmitteeSpeedElement ( info ) ;
}
///==========================================================================
inline void CPSEmitterDirectionnal : : deleteElementBase ( uint32 index )
{
NL_PS_FUNC ( CPSEmitterDirectionnal_deleteElementBase )
deleteEmitteeSpeedElement ( index ) ;
}
///==========================================================================
void CPSEmitterDirectionnal : : deleteElement ( uint32 index , TAnimationTime timeUntilNextSimStep )
{
NL_PS_FUNC ( CPSEmitterDirectionnal_deleteElement )
CPSEmitter : : deleteElement ( index , timeUntilNextSimStep ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitterDirectionnal : : deleteElement ( uint32 index )
{
NL_PS_FUNC ( CPSEmitterDirectionnal_deleteElement )
CPSEmitter : : deleteElement ( index ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitterDirectionnal : : resize ( uint32 capacity )
{
NL_PS_FUNC ( CPSEmitterDirectionnal_resize )
nlassert ( capacity < ( 1 < < 16 ) ) ;
CPSEmitter : : resize ( capacity ) ;
resizeEmitteeSpeed ( capacity ) ;
}
///==========================================================================
void CPSEmitterDirectionnal : : serial ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSEmitterDirectionnal_IStream )
f . serialVersion ( 1 ) ;
CPSEmitter : : serial ( f ) ;
CPSModulatedEmitter : : serialEmitteeSpeedScheme ( f ) ;
f . serial ( _Dir ) ;
}
////////////////////////////////////////////
// implementation of CPSEmitterRectangle //
////////////////////////////////////////////
///==========================================================================
void CPSEmitterRectangle : : serial ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSEmitterRectangle_IStream )
f . serialVersion ( 1 ) ;
CPSEmitter : : serial ( f ) ;
CPSModulatedEmitter : : serialEmitteeSpeedScheme ( f ) ;
f . serial ( _Basis ) ;
f . serial ( _Width ) ;
f . serial ( _Height ) ;
f . serial ( _Dir ) ;
}
///==========================================================================
void CPSEmitterRectangle : : emit ( const NLMISC : : CVector & srcPos , uint32 index , CVector & pos , CVector & speed )
{
NL_PS_FUNC ( CPSEmitterRectangle_emit )
CVector N = _Basis [ index ] . X ^ _Basis [ index ] . Y ;
pos = srcPos + ( ( rand ( ) % 32000 ) * ( 1.f / 16000 ) - 1.f ) * _Width [ index ] * _Basis [ index ] . X
+ ( ( rand ( ) % 32000 ) * ( 1.f / 16000 ) - 1.f ) * _Height [ index ] * _Basis [ index ] . Y ;
speed = ( _EmitteeSpeedScheme ? _EmitteeSpeedScheme - > get ( _Owner , index ) : _EmitteeSpeed )
* ( _Dir . x * _Basis [ index ] . X + _Dir . y * _Basis [ index ] . Y + _Dir . z * N ) ;
}
///==========================================================================
void CPSEmitterRectangle : : setMatrix ( uint32 index , const CMatrix & m )
{
NL_PS_FUNC ( CPSEmitterRectangle_setMatrix )
_Owner - > getPos ( ) [ index ] = m . getPos ( ) ;
_Basis [ index ] . X = m . getI ( ) ;
_Basis [ index ] . Y = m . getJ ( ) ;
}
///==========================================================================
CMatrix CPSEmitterRectangle : : getMatrix ( uint32 index ) const
{
NL_PS_FUNC ( CPSEmitterRectangle_getMatrix )
CMatrix m ;
m . setPos ( _Owner - > getPos ( ) [ index ] ) ;
m . setRot ( _Basis [ index ] . X , _Basis [ index ] . Y , _Basis [ index ] . X ^ _Basis [ index ] . Y , true ) ;
return m ;
}
///==========================================================================
void CPSEmitterRectangle : : setScale ( uint32 index , float scale )
{
NL_PS_FUNC ( CPSEmitterRectangle_setScale )
_Width [ index ] = scale ;
_Height [ index ] = scale ;
}
///==========================================================================
void CPSEmitterRectangle : : setScale ( uint32 index , const CVector & s )
{
NL_PS_FUNC ( CPSEmitterRectangle_setScale )
_Width [ index ] = s . x ;
_Height [ index ] = s . y ;
}
///==========================================================================
CVector CPSEmitterRectangle : : getScale ( uint32 index ) const
{
NL_PS_FUNC ( CPSEmitterRectangle_getScale )
return CVector ( _Width [ index ] , _Height [ index ] , 1.f ) ;
}
///==========================================================================
void CPSEmitterRectangle : : newElement ( const CPSEmitterInfo & info )
{
NL_PS_FUNC ( CPSEmitterRectangle_newElement )
CPSEmitter : : newElement ( info ) ;
newEmitteeSpeedElement ( info ) ;
_Basis . insert ( CPlaneBasis ( CVector : : K ) ) ;
_Width . insert ( 1.f ) ;
_Height . insert ( 1.f ) ;
}
///==========================================================================
inline void CPSEmitterRectangle : : deleteElementBase ( uint32 index )
{
NL_PS_FUNC ( CPSEmitterRectangle_deleteElementBase )
deleteEmitteeSpeedElement ( index ) ;
_Basis . remove ( index ) ;
_Width . remove ( index ) ;
_Height . remove ( index ) ;
}
///==========================================================================
void CPSEmitterRectangle : : deleteElement ( uint32 index )
{
NL_PS_FUNC ( CPSEmitterRectangle_deleteElement )
CPSEmitter : : deleteElement ( index ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitterRectangle : : deleteElement ( uint32 index , TAnimationTime timeUntilNextSimStep )
{
NL_PS_FUNC ( CPSEmitterRectangle_deleteElement )
CPSEmitter : : deleteElement ( index , timeUntilNextSimStep ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSEmitterRectangle : : resize ( uint32 size )
{
NL_PS_FUNC ( CPSEmitterRectangle_resize )
nlassert ( size < ( 1 < < 16 ) ) ;
CPSEmitter : : resize ( size ) ;
resizeEmitteeSpeed ( size ) ;
_Basis . resize ( size ) ;
_Width . resize ( size ) ;
_Height . resize ( size ) ;
}
///==========================================================================
void CPSEmitterRectangle : : showTool ( void )
{
NL_PS_FUNC ( CPSEmitterRectangle_showTool )
nlassert ( _Owner ) ;
const uint size = _Owner - > getSize ( ) ;
if ( ! size ) return ;
setupDriverModelMatrix ( ) ;
CMatrix mat ;
CPSLocated * loc ;
uint32 index ;
CPSLocatedBindable * lb ;
_Owner - > getOwner ( ) - > getCurrentEditedElement ( loc , index , lb ) ;
for ( uint k = 0 ; k < size ; + + k )
{
const CVector & I = _Basis [ k ] . X ;
const CVector & J = _Basis [ k ] . Y ;
mat . setRot ( I , J , I ^ J ) ;
mat . setPos ( _Owner - > getPos ( ) [ k ] ) ;
CPSUtil : : displayBasis ( getDriver ( ) , getLocalToWorldMatrix ( ) , mat , 1.f , * getFontGenerator ( ) , * getFontManager ( ) ) ;
setupDriverModelMatrix ( ) ;
const CRGBA col = ( ( lb = = NULL | | this = = lb ) & & loc = = _Owner & & index = = k ? CRGBA : : Red : CRGBA ( 127 , 127 , 127 ) ) ;
const CVector & pos = _Owner - > getPos ( ) [ k ] ;
CPSUtil : : display3DQuad ( * getDriver ( ) , pos + I * _Width [ k ] + J * _Height [ k ]
, pos + I * _Width [ k ] - J * _Height [ k ]
, pos - I * _Width [ k ] - J * _Height [ k ]
, pos - I * _Width [ k ] + J * _Height [ k ] , col ) ;
}
}
////////////////////////////////////
// CPSEmitterconic implementation //
////////////////////////////////////
///==========================================================================
void CPSEmitterConic : : serial ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSEmitterConic_serial )
f . serialVersion ( 1 ) ;
CPSEmitterDirectionnal : : serial ( f ) ;
f . serial ( _Radius ) ;
}
///==========================================================================
void CPSEmitterConic : : setDir ( const CVector & v )
{
NL_PS_FUNC ( CPSEmitterConic_setDir )
CPSEmitterDirectionnal : : setDir ( v ) ;
}
///==========================================================================
void CPSEmitterConic : : emit ( const NLMISC : : CVector & srcPos , uint32 index , CVector & pos , CVector & speed )
{
NL_PS_FUNC ( CPSEmitterConic_emit )
// TODO : optimize that
nlassert ( _EmittedType ) ;
// we choose a custom direction like with omnidirectionnal emitter
// then we force the direction vect to have the unit size
static const double divRand = ( 2.0 / RAND_MAX ) ;
CVector dir ( ( float ) ( rand ( ) * divRand - 1 )
, ( float ) ( rand ( ) * divRand - 1 )
, ( float ) ( rand ( ) * divRand - 1 ) ) ;
const float n = dir . norm ( ) ;
dir * = _Radius / n ;
dir - = ( _Dir * dir ) * _Dir ;
dir + = _Dir ;
dir . normalize ( ) ;
speed = ( _EmitteeSpeedScheme ? _EmitteeSpeedScheme - > get ( _Owner , index ) : _EmitteeSpeed )
* dir ;
pos = srcPos ;
}
////////////////////////////////////////
// CPSSphericalEmitter implementation //
////////////////////////////////////////
///==========================================================================
void CPSSphericalEmitter : : emit ( const NLMISC : : CVector & srcPos , uint32 index , CVector & pos , CVector & speed )
{
NL_PS_FUNC ( CPSSphericalEmitter_emit )
static const double divRand = ( 2.0 / RAND_MAX ) ;
CVector dir ( ( float ) ( rand ( ) * divRand - 1 ) , ( float ) ( rand ( ) * divRand - 1 ) , ( float ) ( rand ( ) * divRand - 1 ) ) ;
dir . normalize ( ) ;
pos = srcPos + _Radius [ index ] * dir ;
speed = ( _EmitteeSpeedScheme ? _EmitteeSpeedScheme - > get ( _Owner , index ) : _EmitteeSpeed ) * dir ;
}
///==========================================================================
void CPSSphericalEmitter : : showTool ( void )
{
NL_PS_FUNC ( CPSSphericalEmitter_showTool )
CPSLocated * loc ;
uint32 index ;
CPSLocatedBindable * lb ;
_Owner - > getOwner ( ) - > getCurrentEditedElement ( loc , index , lb ) ;
TPSAttribFloat : : const_iterator radiusIt = _Radius . begin ( ) ;
TPSAttribVector : : const_iterator posIt = _Owner - > getPos ( ) . begin ( ) , endPosIt = _Owner - > getPos ( ) . end ( ) ;
setupDriverModelMatrix ( ) ;
for ( uint k = 0 ; posIt ! = endPosIt ; + + posIt , + + radiusIt , + + k )
{
const CRGBA col = ( ( lb = = NULL | | this = = lb ) & & loc = = _Owner & & index = = k ? CRGBA : : Red : CRGBA ( 127 , 127 , 127 ) ) ;
CPSUtil : : displaySphere ( * getDriver ( ) , * radiusIt , * posIt , 5 , col ) ;
}
}
///==========================================================================
void CPSSphericalEmitter : : setMatrix ( uint32 index , const CMatrix & m )
{
NL_PS_FUNC ( CPSSphericalEmitter_setMatrix )
nlassert ( index < _Radius . getSize ( ) ) ;
// compute new pos
_Owner - > getPos ( ) [ index ] = m . getPos ( ) ;
}
///==========================================================================
CMatrix CPSSphericalEmitter : : getMatrix ( uint32 index ) const
{
NL_PS_FUNC ( CPSSphericalEmitter_getMatrix )
nlassert ( index < _Radius . getSize ( ) ) ;
CMatrix m ;
m . identity ( ) ;
m . translate ( _Owner - > getPos ( ) [ index ] ) ;
return m ;
}
///==========================================================================
void CPSSphericalEmitter : : serial ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSSphericalEmitter_serial )
f . serialVersion ( 1 ) ;
CPSEmitter : : serial ( f ) ;
CPSModulatedEmitter : : serialEmitteeSpeedScheme ( f ) ;
f . serial ( _Radius ) ;
}
///==========================================================================
void CPSSphericalEmitter : : newElement ( const CPSEmitterInfo & info )
{
NL_PS_FUNC ( CPSSphericalEmitter_newElement )
CPSEmitter : : newElement ( info ) ;
newEmitteeSpeedElement ( info ) ;
_Radius . insert ( 1.f ) ;
}
///==========================================================================
inline void CPSSphericalEmitter : : deleteElementBase ( uint32 index )
{
NL_PS_FUNC ( CPSSphericalEmitter_deleteElementBase )
deleteEmitteeSpeedElement ( index ) ;
_Radius . remove ( index ) ;
}
///==========================================================================
void CPSSphericalEmitter : : deleteElement ( uint32 index )
{
NL_PS_FUNC ( CPSSphericalEmitter_deleteElement )
CPSEmitter : : deleteElement ( index ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSSphericalEmitter : : deleteElement ( uint32 index , TAnimationTime timeUntilNextSimStep )
{
NL_PS_FUNC ( CPSSphericalEmitter_deleteElement )
CPSEmitter : : deleteElement ( index , timeUntilNextSimStep ) ;
deleteElementBase ( index ) ;
}
///==========================================================================
void CPSSphericalEmitter : : resize ( uint32 size )
{
NL_PS_FUNC ( CPSSphericalEmitter_resize )
nlassert ( size < ( 1 < < 16 ) ) ;
CPSEmitter : : resize ( size ) ;
resizeEmitteeSpeed ( size ) ;
_Radius . resize ( size ) ;
}
/////////////////////////////////////
// CPSRadialEmitter implementation //
/////////////////////////////////////
///==========================================================================
void CPSRadialEmitter : : serial ( NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
NL_PS_FUNC ( CPSRadialEmitter_serial )
f . serialVersion ( 1 ) ;
CPSEmitterDirectionnal : : serial ( f ) ;
}
///==========================================================================
void CPSRadialEmitter : : emit ( const NLMISC : : CVector & srcPos , uint32 index , NLMISC : : CVector & pos , NLMISC : : CVector & speed )
{
NL_PS_FUNC ( CPSRadialEmitter_emit )
// TODO : verifier que ca marche si une particule s'emet elle-mem
nlassert ( _EmittedType ) ;
static const double divRand = ( 2.0 / RAND_MAX ) ;
CVector dir ( ( float ) ( rand ( ) * divRand - 1 ) ,
( float ) ( rand ( ) * divRand - 1 ) ,
( float ) ( rand ( ) * divRand - 1 ) ) ;
dir - = ( dir * _Dir ) * _Dir ; //keep tangential direction
dir . normalize ( ) ;
speed = ( _EmitteeSpeedScheme ? _EmitteeSpeedScheme - > get ( _Owner , index ) : _EmitteeSpeed ) * dir ;
pos = srcPos ;
}
///===============================================================================
void CPSEmitter : : enableSpeedBasisEmission ( bool enabled /*=true*/ )
{
NL_PS_FUNC ( CPSEmitter_enableSpeedBasisEmission )
bool wasUserMatNeeded = isUserMatrixUsed ( ) ;
_SpeedBasisEmission = enabled ;
updatePSRefCountForUserMatrixUsage ( isUserMatrixUsed ( ) , wasUserMatNeeded ) ;
}
///===============================================================================
void CPSEmitter : : enableUserMatrixModeForEmissionDirection ( bool enable /*=true*/ )
{
NL_PS_FUNC ( CPSEmitter_enableUserMatrixModeForEmissionDirection )
bool wasUserMatNeeded = isUserMatrixUsed ( ) ;
_UserMatrixModeForEmissionDirection = enable ;
updatePSRefCountForUserMatrixUsage ( isUserMatrixUsed ( ) , wasUserMatNeeded ) ;
}
///===============================================================================
void CPSEmitter : : setUserMatrixModeForEmissionDirection ( TPSMatrixMode matrixMode )
{
NL_PS_FUNC ( CPSEmitter_setUserMatrixModeForEmissionDirection )
bool wasUserMatNeeded = isUserMatrixUsed ( ) ;
_UserDirectionMatrixMode = matrixMode ;
updatePSRefCountForUserMatrixUsage ( isUserMatrixUsed ( ) , wasUserMatNeeded ) ;
}
///==========================================================================
void CPSEmitter : : updatePSRefCountForUserMatrixUsage ( bool matrixIsNeededNow , bool matrixWasNeededBefore )
{
NL_PS_FUNC ( CPSEmitter_updatePSRefCountForUserMatrixUsage )
if ( _Owner & & _Owner - > getOwner ( ) )
{
if ( matrixIsNeededNow & & ! matrixWasNeededBefore )
{
_Owner - > getOwner ( ) - > addRefForUserSysCoordInfo ( ) ;
}
else if ( ! matrixIsNeededNow & & matrixWasNeededBefore )
{
_Owner - > getOwner ( ) - > releaseRefForUserSysCoordInfo ( ) ;
}
}
}
///==========================================================================
bool CPSEmitter : : isUserMatrixUsed ( ) const
{
NL_PS_FUNC ( CPSEmitter_isUserMatrixUsed )
return ! _SpeedBasisEmission & & _UserMatrixModeForEmissionDirection & & _UserDirectionMatrixMode = = PSUserMatrix ;
}
///==========================================================================
bool CPSEmitter : : getUserMatrixUsageCount ( ) const
{
NL_PS_FUNC ( CPSEmitter_getUserMatrixUsageCount )
return isUserMatrixUsed ( ) ? 1 : 0 ;
}
///==========================================================================
void CPSEmitter : : doEmitOnce ( uint firstInstanceIndex )
{
NL_PS_FUNC ( CPSEmitter_doEmitOnce )
if ( ! _EmittedType ) return ;
if ( ! _GenNbScheme & & _GenNb = = 0 ) return ;
nlassert ( _Owner ) ;
nlassert ( CParticleSystem : : InsideSimLoop ) ; // should only be called by the sim loop
float emitLOD ;
nlassert ( _Owner ) ;
nlassert ( _Owner - > getOwner ( ) ) ;
const CParticleSystem * ps = _Owner - > getOwner ( ) ;
if ( ps - > isAutoLODEnabled ( ) & & ! ps - > isSharingEnabled ( ) & & ! _BypassAutoLOD )
{
// temp test for auto lod
emitLOD = ps - > getAutoLODEmitRatio ( ) ;
}
else
{
emitLOD = 1.f ;
}
nlassert ( emitLOD > = 0.f ) ;
if ( _GenNbScheme )
{
const uint BATCH_SIZE = 1024 ;
uint32 numToEmit [ BATCH_SIZE ] ;
uint k = firstInstanceIndex ;
nlassert ( firstInstanceIndex < _Owner - > getSize ( ) ) ;
uint leftToDo = _Owner - > getSize ( ) - firstInstanceIndex ;
while ( leftToDo )
{
uint toProcess = std : : min ( ( uint ) BATCH_SIZE , leftToDo ) ;
uint32 * numToEmitPtr = ( uint32 * ) _GenNbScheme - > make ( _Owner , k , numToEmit , sizeof ( uint32 ) , true ) ;
leftToDo - = toProcess ;
while ( toProcess )
{
CVector startPos ;
if ( ! _Owner - > isParametricMotionEnabled ( ) )
{
startPos = _Owner - > getPos ( ) [ k ] - _Owner - > getSpeed ( ) [ k ] * CParticleSystem : : EllapsedTime ;
}
else
{
startPos = _Owner - > getParametricInfos ( ) [ k ] . Pos ;
}
float currTime = _Owner - > getTime ( ) [ k ] ;
_Owner - > getTime ( ) [ k ] = 0.f ; // when emit occured, time was 0
sint32 nbToGenerate = ( sint32 ) ( emitLOD * * numToEmitPtr ) ;
if ( nbToGenerate > 0 )
{
nbToGenerate = std : : min ( nbToGenerate , ( sint32 ) _EmittedType - > getMaxSize ( ) ) ;
processEmitConsistent ( startPos , k , nbToGenerate , _Owner - > getAgeInSeconds ( k ) / CParticleSystem : : RealEllapsedTimeRatio ) ;
}
// restore time & pos
_Owner - > getTime ( ) [ k ] = currTime ;
+ + k ;
+ + numToEmitPtr ;
- - toProcess ;
}
}
}
else
{
sint nbToGenerate = ( sint ) ( emitLOD * _GenNb ) ;
if ( nbToGenerate < = 0 ) nbToGenerate = 1 ;
nbToGenerate = std : : min ( nbToGenerate , ( sint ) _EmittedType - > getMaxSize ( ) ) ;
for ( uint k = firstInstanceIndex ; k < _Owner - > getSize ( ) ; + + k )
{
// retrieve previous position (because motion step is done before spawn step)
CVector startPos ;
if ( ! _Owner - > isParametricMotionEnabled ( ) )
{
startPos = _Owner - > getPos ( ) [ k ] - _Owner - > getSpeed ( ) [ k ] * CParticleSystem : : EllapsedTime ;
}
else
{
startPos = _Owner - > getParametricInfos ( ) [ k ] . Pos ;
}
float currTime = _Owner - > getTime ( ) [ k ] ;
_Owner - > getTime ( ) [ k ] = 0.f ; // when emit occured, time was 0
processEmitConsistent ( startPos , k , nbToGenerate , _Owner - > getAgeInSeconds ( k ) / CParticleSystem : : RealEllapsedTimeRatio ) ;
// restore time & pos
_Owner - > getTime ( ) [ k ] = currTime ;
}
}
}
///==========================================================================
void CPSEmitter : : updateEmitTrigger ( )
{
NL_PS_FUNC ( CPSEmitter_updateEmitTrigger )
if ( ! _EmitTrigger ) return ;
nlassert ( _Owner ) ;
nlassert ( _Owner - > getOwner ( ) ) ;
const CParticleSystem * ps = _Owner - > getOwner ( ) ;
float emitLOD ;
if ( ps - > isAutoLODEnabled ( ) & & ! ps - > isSharingEnabled ( ) & & ! _BypassAutoLOD )
{
// temp test for auto lod
emitLOD = ps - > getAutoLODEmitRatio ( ) ;
}
else
{
emitLOD = 1.f ;
}
if ( _GenNbScheme )
{
const uint BATCH_SIZE = 1024 ;
uint32 numToEmit [ BATCH_SIZE ] ;
uint k = 0 ;
uint leftToDo = _Owner - > getSize ( ) ;
while ( leftToDo )
{
uint toProcess = std : : min ( BATCH_SIZE , leftToDo ) ;
uint32 * numToEmitPtr = ( uint32 * ) _GenNbScheme - > make ( _Owner , k , numToEmit , sizeof ( uint32 ) , true ) ;
while ( toProcess )
{
uint32 nbToGenerate = ( sint32 ) ( emitLOD * * numToEmitPtr ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
processEmit ( k , nbToGenerate ) ;
+ + k ;
+ + numToEmitPtr ;
}
leftToDo - = toProcess ;
}
}
else
{
uint nbToGenerate = ( sint32 ) ( emitLOD * _GenNb ) ;
if ( ! nbToGenerate ) nbToGenerate = 1 ;
for ( uint k = 0 ; k < _Owner - > getSize ( ) ; + + k )
{
processEmit ( k , nbToGenerate ) ;
}
}
_EmitTrigger = false ;
}
} // NL3D
namespace NLMISC
{
std : : string toString ( NL3D : : CPSEmitter : : TEmissionType type )
{
NL_PS_FUNC ( toString_CPSEmitter_TEmissionType )
nlctassert ( NL3D : : CPSEmitter : : numEmissionType = = 5 ) ; // If this ct assertion is raised, the content of TEmissionType has changed, so should change this function !
switch ( type )
{
case NL3D : : CPSEmitter : : regular : return " regular " ;
case NL3D : : CPSEmitter : : onDeath : return " onDeath " ;
case NL3D : : CPSEmitter : : once : return " once " ;
case NL3D : : CPSEmitter : : onBounce : return " onBounce " ;
case NL3D : : CPSEmitter : : externEmit : return " externEmit " ;
default :
nlassert ( 0 ) ;
return " " ;
break ;
}
}
} // NLMISC