Change Encyclo button name and macros icon
[ryzomcore.git] / nel / src / 3d / ps_emitter.cpp
blob240be6f516860039e839abe2a351cf9fbf53eda2
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
19 #include "nel/3d/ps_emitter.h"
20 #include "nel/3d/material.h"
21 #include "nel/misc/line.h"
22 #include "nel/3d/dru.h"
23 #include "nel/3d/particle_system.h"
25 #ifdef DEBUG_NEW
26 #define new DEBUG_NEW
27 #endif
29 namespace NL3D {
34 static const uint EMITTER_BUFF_SIZE = 512; // number of emitter to be processed at once
35 static const float EMIT_PERIOD_THRESHOLD = 1.f / 75.f; // assuming the same behaviour than with a 75 hz rendering
36 bool CPSEmitter::_BypassEmitOnDeath = false;
39 //////////////////////
40 // STATIC FUNCTIONS //
41 //////////////////////
42 /** In an arrey of float, all value that are 0.f are replaced by EMIT_PERIOD_THRESHOLD
43 * A period of 0 is allowed for emitter and means "emit at each frame"
44 * This is deprecated now, and this helps to avoid that behaviour
46 static void replaceNullPeriodsByThreshold(float *tab, uint numElem)
48 NL_PS_FUNC(replaceNullPeriodsByThreshold)
49 const float *endTab = tab + numElem;
50 while (tab != endTab)
52 if (*tab == 0.f) *tab = EMIT_PERIOD_THRESHOLD;
53 ++ tab;
57 ///////////////////////////////
58 // CPSEmitter implementation //
59 ///////////////////////////////
60 CPSEmitter::CPSEmitter() : _EmittedType(NULL),
61 _SpeedInheritanceFactor(0.f),
62 _EmissionType(regular),
63 _Period(0.02f),
64 _PeriodScheme(NULL),
65 _GenNb(1),
66 _GenNbScheme(NULL),
67 _EmitDelay(0),
68 _MaxEmissionCount(0),
69 _SpeedBasisEmission(false),
70 _ConsistentEmission(true),
71 _BypassAutoLOD(false),
72 _UserMatrixModeForEmissionDirection(false),
73 _EmitTrigger(false),
74 _UserDirectionMatrixMode(PSFXWorldMatrix)
76 NL_PS_FUNC(CPSEmitter_CPSEmitter)
80 ///==========================================================================
81 CPSEmitter::~CPSEmitter()
83 NL_PS_FUNC(CPSEmitter_CPSEmitterDtor)
84 delete _PeriodScheme;
85 delete _GenNbScheme;
86 // if a located is emitted, unregister us as an observer
87 if (_EmittedType)
89 _EmittedType->unregisterDtorObserver(this);
93 ///==========================================================================
94 void CPSEmitter::releaseRefTo(const CParticleSystemProcess *other)
96 NL_PS_FUNC(CPSEmitter_releaseRefTo)
97 if (_EmittedType == other)
99 setEmittedType(NULL);
103 void CPSEmitter::releaseAllRef()
105 NL_PS_FUNC(CPSEmitter_releaseAllRef)
106 setEmittedType(NULL);
110 ///==========================================================================
111 void CPSEmitter::setOwner(CPSLocated *psl)
113 NL_PS_FUNC(CPSEmitter_setOwner)
114 CPSLocatedBindable::setOwner(psl);
115 updateMaxCountVect();
119 ///==========================================================================
120 inline void CPSEmitter::processEmit(uint32 index, sint nbToGenerate)
122 NL_PS_FUNC(CPSEmitter_processEmit)
123 NLMISC::CVector speed, pos;
124 nlassert(_Owner);
125 if (!_SpeedBasisEmission)
127 if (_SpeedInheritanceFactor == 0.f)
129 if (!_UserMatrixModeForEmissionDirection)
131 while (nbToGenerate > 0)
133 nbToGenerate --;
134 emit(_Owner->getPos()[index], index, pos, speed);
135 _EmittedType->postNewElement(pos, speed, *this->_Owner, index, _Owner->getMatrixMode(), 0.f);
138 else
140 while (nbToGenerate > 0)
142 nbToGenerate --;
143 emit(_Owner->getPos()[index], index, pos, speed);
144 _EmittedType->postNewElement(pos, speed, *this->_Owner, index, _UserDirectionMatrixMode, 0.f);
148 else
150 while (nbToGenerate --)
152 emit(_Owner->getPos()[index], index, pos, speed);
153 _EmittedType->postNewElement(pos, speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, 0, _Owner->getMatrixMode(), 0.f);
157 else
159 NLMISC::CMatrix m;
160 CPSUtil::buildSchmidtBasis(_Owner->getSpeed()[index], m);
161 if (_SpeedInheritanceFactor == 0.f)
163 while (nbToGenerate > 0)
165 nbToGenerate --;
166 emit(_Owner->getPos()[index], index, pos, speed);
167 _EmittedType->postNewElement(pos, m * speed, *this->_Owner, index, _Owner->getMatrixMode(), 0.f);
170 else
172 while (nbToGenerate --)
174 emit(_Owner->getPos()[index], index, pos, speed);
175 _EmittedType->postNewElement(pos, m * speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, index, _Owner->getMatrixMode(), 0.f);
182 ///==========================================================================
183 void CPSEmitter::processEmitOutsideSimLoop(uint32 index,sint nbToGenerate)
185 NL_PS_FUNC(CPSEmitter_processEmitOutsideSimLoop)
186 NLMISC::CVector speed, pos;
187 nlassert(_Owner);
188 if (!_SpeedBasisEmission)
190 if (_SpeedInheritanceFactor == 0.f)
192 if (!_UserMatrixModeForEmissionDirection)
194 while (nbToGenerate > 0)
196 nbToGenerate --;
197 emit(_Owner->getPos()[index], index, pos, speed);
198 _EmittedType->newElement(pos, speed, this->_Owner, index, _Owner->getMatrixMode(), true);
201 else
203 while (nbToGenerate > 0)
205 nbToGenerate --;
206 emit(_Owner->getPos()[index], index, pos, speed);
207 _EmittedType->newElement(pos, speed, this->_Owner, index, _UserDirectionMatrixMode);
211 else
213 while (nbToGenerate --)
215 emit(_Owner->getPos()[index], index, pos, speed);
216 _EmittedType->newElement(pos, speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], this->_Owner, 0, _Owner->getMatrixMode(), true);
220 else
222 NLMISC::CMatrix m;
223 CPSUtil::buildSchmidtBasis(_Owner->getSpeed()[index], m);
224 if (_SpeedInheritanceFactor == 0.f)
226 while (nbToGenerate > 0)
228 nbToGenerate --;
229 emit(_Owner->getPos()[index], index, pos, speed);
230 _EmittedType->newElement(pos, m * speed, this->_Owner, index, _Owner->getMatrixMode(), true);
233 else
235 while (nbToGenerate --)
237 emit(_Owner->getPos()[index], index, pos, speed);
238 _EmittedType->newElement(pos, m * speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], this->_Owner, index, _Owner->getMatrixMode(), true);
244 ///==========================================================================
245 inline void CPSEmitter::processEmitConsistent(const NLMISC::CVector &emitterPos,
246 uint32 index,
247 sint nbToGenerate,
248 TAnimationTime deltaT)
250 NL_PS_FUNC(CPSEmitter_processEmitConsistent)
251 static NLMISC::CVector speed, pos; /// speed and pos of emittee
252 nlassert(_Owner);
253 if (!_SpeedBasisEmission)
255 if (_SpeedInheritanceFactor == 0.f)
257 if (!_UserMatrixModeForEmissionDirection)
259 while (nbToGenerate > 0)
261 nbToGenerate --;
262 emit(emitterPos, index, pos, speed);
263 _EmittedType->postNewElement(pos, speed, *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
266 else
268 while (nbToGenerate > 0)
270 nbToGenerate --;
271 emit(emitterPos, index, pos, speed);
272 _EmittedType->postNewElement(pos, speed, *this->_Owner, index, _UserDirectionMatrixMode, deltaT);
276 else
278 while (nbToGenerate --)
280 emit(emitterPos, index, pos, speed);
281 _EmittedType->postNewElement(pos, speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
285 else
287 NLMISC::CMatrix m;
288 CPSUtil::buildSchmidtBasis(_Owner->getSpeed()[index], m);
289 if (_SpeedInheritanceFactor == 0.f)
291 while (nbToGenerate > 0)
293 nbToGenerate --;
294 emit(emitterPos, index, pos, speed);
295 _EmittedType->postNewElement(pos, m * speed, *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
298 else
300 while (nbToGenerate --)
302 emit(emitterPos, index, pos, speed);
303 _EmittedType->postNewElement(pos, m * speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
310 ///==========================================================================
311 bool CPSEmitter::setEmissionType(TEmissionType freqType)
313 NL_PS_FUNC(CPSEmitter_setEmissionType)
314 if (_Owner && _Owner->getOwner())
316 CParticleSystem *ps = _Owner->getOwner();
317 if (ps->getBypassMaxNumIntegrationSteps())
319 if (!_Owner)
321 nlwarning("<CPSEmitter::setEmissionType> The emitter should be inserted in a CPSLocated instance");
322 nlassert(0);
324 // check if the new value is valid
325 TEmissionType oldType = _EmissionType;
326 _EmissionType = freqType;
327 if (testEmitForever() == true)
329 _EmissionType = oldType;
330 std::string mess = "<CPSEmitter::setEmissionType> can't set emission type to '" +
331 NLMISC::toString(freqType) +
332 "' with the current configuration : the system has been flagged with \
333 'BypassMaxNumIntegrationSteps', and should have a finite duration. \
334 The flag is not set";
335 nlwarning(mess.c_str());
336 return false;
340 ps->systemDurationChanged();
342 _EmissionType = freqType;
343 return true;
346 ///==========================================================================
347 bool CPSEmitter::setEmittedType(CPSLocated *et)
349 NL_PS_FUNC(CPSEmitter_setEmittedType)
350 if (_EmittedType)
352 _EmittedType->unregisterDtorObserver(this);
354 if (et)
356 et->registerDtorObserver(this);
358 CPSLocated *oldType = _EmittedType;
359 _EmittedType = et;
360 if (_Owner && _Owner->getOwner())
362 CParticleSystem *ps = _Owner->getOwner();
363 if (_EmittedType)
365 bool ok = true;
366 if (ps->getBypassMaxNumIntegrationSteps())
368 ok = ps->canFinish();
370 else
372 ok = !ps->hasLoop();
374 if (!ok)
376 setEmittedType(oldType);
377 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");
378 return false;
381 ps->systemDurationChanged();
383 return true;
386 ///==========================================================================
387 void CPSEmitter::notifyTargetRemoved(CPSLocated *ptr)
389 NL_PS_FUNC(CPSEmitter_notifyTargetRemoved)
390 nlassert(ptr == _EmittedType && _EmittedType);
391 setEmittedType(NULL);
394 ///==========================================================================
395 void CPSEmitter::setPeriod(float period)
397 NL_PS_FUNC(CPSEmitter_setPeriod)
398 if (_PeriodScheme)
400 delete _PeriodScheme;
401 _PeriodScheme = NULL;
403 _Period = period;
404 if (_Owner && _Owner->getOwner())
406 _Owner->getOwner()->systemDurationChanged();
410 ///==========================================================================
411 void CPSEmitter::setPeriodScheme(CPSAttribMaker<float> *scheme)
413 NL_PS_FUNC(CPSEmitter_setPeriodScheme)
414 delete _PeriodScheme;
415 _PeriodScheme = scheme;
416 if (_Owner && scheme->hasMemory()) scheme->resize(_Owner->getMaxSize(), _Owner->getSize());
417 if (_Owner && _Owner->getOwner())
419 _Owner->getOwner()->systemDurationChanged();
423 ///==========================================================================
424 void CPSEmitter::setGenNb(uint32 genNb)
426 NL_PS_FUNC(CPSEmitter_setGenNb)
427 if (_GenNbScheme)
429 delete _GenNbScheme;
430 _GenNbScheme = NULL;
432 _GenNb = genNb;
435 ///==========================================================================
436 void CPSEmitter::setGenNbScheme(CPSAttribMaker<uint32> *scheme)
438 NL_PS_FUNC(CPSEmitter_setGenNbScheme)
439 delete _GenNbScheme;
440 _GenNbScheme = scheme;
441 if (_Owner && scheme->hasMemory()) scheme->resize(_Owner->getMaxSize(), _Owner->getSize());
444 ///==========================================================================
445 void CPSEmitter::showTool(void)
447 NL_PS_FUNC(CPSEmitter_showTool)
448 uint32 size = _Owner->getSize();
449 if (!size) return;
450 setupDriverModelMatrix();
452 const CVector I = computeI();
453 const CVector K = computeK();
455 // ugly slow code, but not for runtime
456 for (uint k = 0; k < size; ++k)
458 // center of the current particle
459 const CVector p = _Owner->getPos()[k];
460 const float sSize =0.1f;
461 std::vector<NLMISC::CLine> lines;
462 NLMISC::CLine l;
463 l.V0 = p - sSize * I; l.V1 = p + sSize * I; lines.push_back(l);
464 l.V0 = p - sSize * K; l.V1 = p + sSize * K; lines.push_back(l);
465 l.V0 = p - sSize * (I + K); l.V1 = p + sSize * (I + K); lines.push_back(l);
466 l.V0 = p - sSize * (I - K); l.V1 = p + sSize * (I - K); lines.push_back(l);
468 CMaterial mat;
469 mat.setBlendFunc(CMaterial::one, CMaterial::one);
470 mat.setZWrite(false);
471 mat.setLighting(false);
472 mat.setBlend(true);
473 mat.setZFunc(CMaterial::less);
476 CPSLocated *loc;
477 uint32 index;
478 CPSLocatedBindable *lb;
479 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
481 mat.setColor((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
484 CDRU::drawLinesUnlit(lines, mat, *getDriver() );
488 ///==========================================================================
489 void CPSEmitter::singleEmit(uint32 index, uint quantity)
491 NL_PS_FUNC(CPSEmitter_singleEmit)
492 nlassert(_Owner);
493 const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner,0) : _GenNb;
494 processEmitOutsideSimLoop(index, quantity * nbToGenerate);
499 ///==========================================================================
500 void CPSEmitter::processRegularEmissionWithNoLOD(uint firstInstanceIndex)
502 NL_PS_FUNC(CPSEmitter_processRegularEmissionWithNoLOD)
503 nlassert(_Owner);
504 nlassert(_Owner->getOwner());
506 const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
508 const uint size = _Owner->getSize();
509 nlassert(firstInstanceIndex < size);
510 uint leftToDo = size - firstInstanceIndex, toProcess;
511 float emitPeriod[EMITTER_BUFF_SIZE];
512 const float *currEmitPeriod;
513 uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
514 sint32 nbToGenerate;
516 TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
517 TPSAttribUInt8::iterator numEmitIt = _NumEmission.begin() + firstInstanceIndex;
519 // we don't use an iterator here
520 // because it could be invalidated if size change (a located could generate itself)
523 toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
526 if (_PeriodScheme)
528 // compute period
529 // 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
530 // so we may have a life counter that is > to 1
531 currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
532 if (emitThreshold)
535 /** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
536 * all null period have already been replaced by the threshold
538 if (currEmitPeriod == emitPeriod)
540 // if there possibility to have 0 in the scheme ?
541 if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
543 replaceNullPeriodsByThreshold(emitPeriod, toProcess);
548 else
550 if (_Period != 0.f || !emitThreshold)
552 currEmitPeriod = &_Period;
554 else
556 currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
560 endPhaseIt = phaseIt + toProcess;
562 if (_MaxEmissionCount == 0) // no emission count limit
564 /// is there an emission delay ?
565 if (_EmitDelay == 0.f) // no emission delay
569 *phaseIt += CParticleSystem::EllapsedTime;
570 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
572 if (*currEmitPeriod != 0)
574 *phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
576 const uint32 k = (uint32)(phaseIt - _Phase.begin());
577 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
578 processEmit(k, nbToGenerate);
581 ++phaseIt;
582 currEmitPeriod += currEmitPeriodPtrInc;
584 while (phaseIt != endPhaseIt);
586 else // there's an emission delay
590 *phaseIt += CParticleSystem::EllapsedTime;
591 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
593 if (*currEmitPeriod != 0)
595 *phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
597 const uint32 k = (uint32)(phaseIt - _Phase.begin());
598 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
599 processEmit(k, nbToGenerate);
602 ++phaseIt;
603 currEmitPeriod += currEmitPeriodPtrInc;
605 while (phaseIt != endPhaseIt);
608 else // there's an emission count limit
610 /// is there an emission delay ?
611 if (_EmitDelay == 0.f) // no emission delay
615 if (*numEmitIt < _MaxEmissionCount)
617 *phaseIt += CParticleSystem::EllapsedTime;
618 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
620 if (*currEmitPeriod != 0)
622 *phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
624 const uint32 k = (uint32)(phaseIt - _Phase.begin());
625 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
626 processEmit(k, nbToGenerate);
627 ++*numEmitIt;
630 ++phaseIt;
631 currEmitPeriod += currEmitPeriodPtrInc;
632 ++ numEmitIt;
634 while (phaseIt != endPhaseIt);
636 else // there's an emission delay
640 if (*numEmitIt < _MaxEmissionCount)
642 *phaseIt += CParticleSystem::EllapsedTime;
643 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
645 if (*currEmitPeriod != 0)
647 *phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
649 const uint32 k = (uint32)(phaseIt - _Phase.begin());
650 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
651 processEmit(k, nbToGenerate);
652 ++*numEmitIt;
655 ++phaseIt;
656 currEmitPeriod += currEmitPeriodPtrInc;
657 ++numEmitIt;
659 while (phaseIt != endPhaseIt);
663 leftToDo -= toProcess;
665 while (leftToDo);
669 ///==========================================================================
670 void CPSEmitter::processRegularEmission(uint firstInstanceIndex, float emitLOD)
672 NL_PS_FUNC(CPSEmitter_processRegularEmission)
673 nlassert(_Owner);
674 nlassert(_Owner->getOwner());
676 const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
678 const uint size = _Owner->getSize();
679 nlassert(firstInstanceIndex < size);
680 uint leftToDo = size - firstInstanceIndex, toProcess;
681 float emitPeriod[EMITTER_BUFF_SIZE];
682 const float *currEmitPeriod;
683 uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
684 sint32 nbToGenerate;
686 TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
687 TPSAttribUInt8::iterator numEmitIt = _NumEmission.begin() + firstInstanceIndex;
689 float ellapsedTimeLOD = emitLOD * CParticleSystem::EllapsedTime;
690 uint maxEmissionCountLOD = (uint8) (_MaxEmissionCount * emitLOD);
691 maxEmissionCountLOD = std::max(1u, maxEmissionCountLOD);
693 // we don't use an iterator here
694 // because it could be invalidated if size change (a located could generate itself)
697 toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
700 if (_PeriodScheme)
702 // compute period
703 // 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
704 // so we may have a life counter that is > to 1
705 currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
706 if (emitThreshold)
709 /** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
710 * all null period have already been replaced by the threshold
712 if (currEmitPeriod == emitPeriod)
714 // if there possibility to have 0 in the scheme ?
715 if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
717 replaceNullPeriodsByThreshold(emitPeriod, toProcess);
722 else
724 if (_Period != 0.f || !emitThreshold)
726 currEmitPeriod = &_Period;
728 else
730 currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
734 endPhaseIt = phaseIt + toProcess;
736 if (_MaxEmissionCount == 0) // no emission count limit
738 /// is there an emission delay ?
739 if (_EmitDelay == 0.f) // no emission delay
743 *phaseIt += ellapsedTimeLOD;
744 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
746 if (*currEmitPeriod != 0)
748 *phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
750 const uint32 k = (uint32)(phaseIt - _Phase.begin());
751 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
752 if (nbToGenerate)
754 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
755 if (!nbToGenerate) nbToGenerate = 1;
756 processEmit(k, nbToGenerate);
760 ++phaseIt;
761 currEmitPeriod += currEmitPeriodPtrInc;
763 while (phaseIt != endPhaseIt);
765 else // there's an emission delay
769 if (*phaseIt < _EmitDelay)
771 *phaseIt += CParticleSystem::EllapsedTime;
772 if (*phaseIt < _EmitDelay)
774 ++phaseIt;
775 currEmitPeriod += currEmitPeriodPtrInc;
776 continue;
778 else
780 *phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
783 else
785 *phaseIt += ellapsedTimeLOD;
788 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
790 if (*currEmitPeriod != 0)
792 *phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
794 const uint32 k = (uint32)(phaseIt - _Phase.begin());
795 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
796 if (nbToGenerate)
798 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
799 if (!nbToGenerate) nbToGenerate = 1;
800 processEmit(k, nbToGenerate);
804 ++phaseIt;
805 currEmitPeriod += currEmitPeriodPtrInc;
807 while (phaseIt != endPhaseIt);
810 else // there's an emission count limit
812 /// is there an emission delay ?
813 if (_EmitDelay == 0.f) // no emission delay
817 if (*numEmitIt < maxEmissionCountLOD)
819 *phaseIt += ellapsedTimeLOD;
820 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
822 if (*currEmitPeriod != 0)
824 *phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
826 const uint32 k = (uint32)(phaseIt - _Phase.begin());
827 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
828 if (nbToGenerate)
830 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
831 if (!nbToGenerate) nbToGenerate = 1;
832 processEmit(k, nbToGenerate);
834 ++*numEmitIt;
837 else
839 *numEmitIt = _MaxEmissionCount;
841 ++phaseIt;
842 currEmitPeriod += currEmitPeriodPtrInc;
843 ++ numEmitIt;
845 while (phaseIt != endPhaseIt);
847 else // there's an emission delay
851 if (*numEmitIt < maxEmissionCountLOD)
853 if (*phaseIt < _EmitDelay)
855 *phaseIt += CParticleSystem::EllapsedTime;
856 if (*phaseIt < _EmitDelay)
858 ++phaseIt;
859 currEmitPeriod += currEmitPeriodPtrInc;
860 ++numEmitIt;
861 currEmitPeriod += currEmitPeriodPtrInc;
862 continue;
864 else
866 *phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
869 else
871 *phaseIt += ellapsedTimeLOD;
874 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
876 if (*currEmitPeriod != 0)
878 *phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
880 const uint32 k = (uint32)(phaseIt - _Phase.begin());
881 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
882 if (nbToGenerate)
884 nbToGenerate = (sint32) (nbToGenerate * emitLOD);
885 if (!nbToGenerate) nbToGenerate = 1;
886 processEmit(k, nbToGenerate);
888 ++*numEmitIt;
891 else
893 *numEmitIt = _MaxEmissionCount;
895 ++phaseIt;
896 currEmitPeriod += currEmitPeriodPtrInc;
897 ++numEmitIt;
899 while (phaseIt != endPhaseIt);
903 leftToDo -= toProcess;
905 while (leftToDo);
908 /// private : generate the various position of an emitter in the given tab for the given slice of time,
909 // depending on whether its motion is parametric or incremental. This is used to create emittees at the right position
911 static
912 #ifndef NL_DEBUG
913 inline
914 #endif
915 uint GenEmitterPositions(CPSLocated *emitter,
916 CPSLocated *emittee,
917 uint emitterIndex,
918 uint numStep,
919 TAnimationTime deltaT, /* fraction of time needed to reach the first emission */
920 TAnimationTime step,
921 std::vector<NLMISC::CVector> &dest
924 NL_PS_FUNC(GenEmitterPositions)
925 const uint toProcess = std::max(1U, std::min(numStep, (uint) emittee->getMaxSize()));
926 dest.resize(toProcess);
929 if (!emitter->isParametricMotionEnabled()) // standard case : take current pos and integrate
931 if (toProcess == 1) // only one emission -> takes current pos
933 dest[0] = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
935 else
937 std::vector<NLMISC::CVector>::iterator outIt = dest.end();
938 std::vector<NLMISC::CVector>::iterator endIt = dest.begin();
939 NLMISC::CVector pos = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
940 NLMISC::CVector speed = step * emitter->getSpeed()[emitterIndex];
943 -- outIt;
944 *outIt = pos;
945 pos -= speed;
947 while (outIt != endIt);
950 else // compute parametric trajectory
952 emitter->integrateSingle(emitter->getOwner()->getSystemDate() + CParticleSystem::RealEllapsedTime - deltaT,
953 -step,
954 toProcess,
955 emitterIndex,
956 &dest[0]
960 return toProcess;
964 /** The same as GenEmitterPositions, but with LOD taken in account.
966 static inline uint GenEmitterPositionsWithLOD(CPSLocated *emitter,
967 CPSLocated *emittee,
968 uint emitterIndex,
969 uint numStep,
970 TAnimationTime deltaT, /* fraction of time needed to reach the first emission */
971 TAnimationTime step,
972 float invLODRatio,
973 std::vector<NLMISC::CVector> &dest
976 NL_PS_FUNC(GenEmitterPositionsWithLOD)
977 const uint toProcess = std::max(1U, std::min(numStep, (uint) emittee->getMaxSize()));
978 dest.resize(toProcess);
981 if (!emitter->isParametricMotionEnabled()) // standard case : take current pos and integrate
983 if (toProcess == 1) // only one emission -> takes current pos
985 dest[0] = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
987 else
989 std::vector<NLMISC::CVector>::iterator outIt = dest.end();
990 std::vector<NLMISC::CVector>::iterator endIt = dest.begin();
991 NLMISC::CVector pos = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
992 NLMISC::CVector speed = step * invLODRatio * emitter->getSpeed()[emitterIndex];
995 -- outIt;
996 *outIt = pos;
997 pos -= speed;
999 while (outIt != endIt);
1002 else // compute parametric trajectory
1004 emitter->integrateSingle(emitter->getOwner()->getSystemDate() - deltaT - step * toProcess,
1005 step,
1006 toProcess,
1007 emitterIndex,
1008 &dest[0]
1012 return toProcess;
1015 ///==========================================================================
1016 void CPSEmitter::processRegularEmissionConsistent(uint firstInstanceIndex, float emitLOD, float inverseEmitLOD)
1018 NL_PS_FUNC(CPSEmitter_processRegularEmissionConsistent)
1019 /// hmm some code factorisation would do no harm, but we want to keep tests outside the loops as much as possible...
1021 nlassert(_Owner);
1022 nlassert(_Owner->getOwner());
1024 const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
1028 static std::vector<NLMISC::CVector> emitterPositions;
1029 // Positions for the emitter. They are computed by using a parametric trajectory or by using integration
1031 const uint size = _Owner->getSize();
1032 nlassert(firstInstanceIndex < size);
1033 uint leftToDo = size - firstInstanceIndex, toProcess;
1034 float emitPeriod[EMITTER_BUFF_SIZE];
1035 const float *currEmitPeriod;
1036 uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
1037 sint32 nbToGenerate;
1040 TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
1041 TPSAttribUInt8::iterator numEmitIt;
1042 if(firstInstanceIndex < _NumEmission.getSize())
1044 numEmitIt = _NumEmission.begin() + firstInstanceIndex;
1046 else
1048 numEmitIt = _NumEmission.end();
1052 float ellapsedTimeLOD = CParticleSystem::EllapsedTime * emitLOD;
1053 uint maxEmissionCountLOD = (uint8) (_MaxEmissionCount * emitLOD);
1054 maxEmissionCountLOD = std::max(1u, maxEmissionCountLOD);
1055 // we don't use an iterator here
1056 // because it could be invalidated if size change (a located could generate itself)
1059 toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
1062 if (_PeriodScheme)
1064 // compute period
1065 // 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
1066 // so we may have a life counter that is > to 1
1067 currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
1068 if (emitThreshold)
1071 /** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
1072 * all null period have already been replaced by the threshold
1074 if (currEmitPeriod == emitPeriod)
1076 // if there possibility to have 0 in the scheme ?
1077 if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
1079 replaceNullPeriodsByThreshold(emitPeriod, toProcess);
1084 else
1086 if (_Period != 0.f || !emitThreshold)
1088 currEmitPeriod = &_Period;
1090 else
1092 currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
1096 endPhaseIt = phaseIt + toProcess;
1098 if (_MaxEmissionCount == 0) // no emission count limit
1100 /// is there an emission delay ?
1101 if (_EmitDelay == 0.f) // no emission delay
1105 *phaseIt += ellapsedTimeLOD;
1106 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
1108 if (*currEmitPeriod != 0)
1110 /** Must ensure phase is valid if period decrease over time
1112 *phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD);
1114 /// compute the number of emissions
1115 uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
1116 *phaseIt -= *currEmitPeriod * numEmissions;
1118 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1119 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1120 if (nbToGenerate)
1122 float deltaT = std::max(0.f, *phaseIt);
1124 /// compute the position of the emitter for the needed dates
1125 numEmissions = GenEmitterPositionsWithLOD(_Owner,
1126 _EmittedType,
1127 emitterIndex,
1128 numEmissions,
1129 deltaT,
1130 *currEmitPeriod,
1131 inverseEmitLOD,
1132 emitterPositions
1135 /// process each emission at the right pos at the right date
1136 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1137 if (!nbToGenerate) nbToGenerate = 1;
1138 uint k = numEmissions;
1139 float deltaTInc = *currEmitPeriod * inverseEmitLOD;
1142 --k;
1143 processEmitConsistent(emitterPositions[k],
1144 emitterIndex,
1145 nbToGenerate,
1146 deltaT
1148 deltaT += deltaTInc;
1150 while (k);
1153 else
1155 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1156 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1157 if (nbToGenerate)
1159 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1160 if (!nbToGenerate) nbToGenerate = 1;
1161 processEmit(emitterIndex, nbToGenerate);
1166 ++phaseIt;
1167 currEmitPeriod += currEmitPeriodPtrInc;
1169 while (phaseIt != endPhaseIt);
1171 else // thhere's an emission delay
1175 if (*phaseIt < _EmitDelay)
1177 *phaseIt += CParticleSystem::EllapsedTime;
1178 if (*phaseIt < _EmitDelay)
1180 ++phaseIt;
1181 currEmitPeriod += currEmitPeriodPtrInc;
1182 continue;
1184 else
1186 *phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
1189 else
1191 *phaseIt += ellapsedTimeLOD;
1194 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
1196 if (*currEmitPeriod != 0)
1198 /** Must ensure phase is valid if period decrease over time
1200 *phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD + _EmitDelay);
1202 uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
1203 *phaseIt -= *currEmitPeriod * numEmissions;
1204 float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
1205 //nlassert(deltaT >= 0.f);
1206 /// process each emission at the right pos at the right date
1207 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1208 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1209 if (nbToGenerate)
1212 /// compute the position of the emitter for the needed date
1213 numEmissions = GenEmitterPositionsWithLOD( _Owner,
1214 _EmittedType,
1215 emitterIndex,
1216 numEmissions,
1217 deltaT,
1218 *currEmitPeriod,
1219 inverseEmitLOD,
1220 emitterPositions
1223 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1224 if (!nbToGenerate) nbToGenerate = 1;
1225 uint k = numEmissions;
1226 float deltaTInc = *currEmitPeriod * inverseEmitLOD;
1229 --k;
1230 processEmitConsistent(emitterPositions[k],
1231 emitterIndex,
1232 nbToGenerate,
1233 deltaT);
1234 deltaT += deltaTInc;
1236 while (k);
1239 else
1241 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1242 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1243 if (nbToGenerate)
1245 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1246 if (!nbToGenerate) nbToGenerate = 1;
1247 processEmit(emitterIndex, nbToGenerate);
1252 ++phaseIt;
1253 currEmitPeriod += currEmitPeriodPtrInc;
1255 while (phaseIt != endPhaseIt);
1258 else // there's an emission count limit
1260 /// is there an emission delay ?
1261 if (_EmitDelay == 0.f) // no emission delay
1265 if (*numEmitIt < maxEmissionCountLOD)
1267 *phaseIt += ellapsedTimeLOD;
1268 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
1270 if (*currEmitPeriod != 0)
1272 /** Must ensure phase is valid if period decrease over time
1274 *phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD);
1276 uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
1277 *numEmitIt += numEmissions;
1278 *phaseIt -= *currEmitPeriod * numEmissions;
1279 float deltaT = std::max(*phaseIt, 0.f);
1280 //nlassert(deltaT >= 0.f);
1281 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1282 if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
1284 numEmissions -= *numEmitIt - _MaxEmissionCount;
1285 *numEmitIt = _MaxEmissionCount;
1287 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1288 if (nbToGenerate)
1290 /// compute the position of the emitter for the needed date
1291 numEmissions = GenEmitterPositionsWithLOD(_Owner,
1292 _EmittedType,
1293 emitterIndex,
1294 numEmissions,
1295 deltaT,
1296 *currEmitPeriod,
1297 inverseEmitLOD,
1298 emitterPositions
1300 uint k = numEmissions;
1301 /// process each emission at the right pos at the right date
1302 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1303 if (!nbToGenerate) nbToGenerate = 1;
1304 float deltaTInc = *currEmitPeriod * inverseEmitLOD;
1307 --k;
1308 processEmitConsistent(emitterPositions[k],
1309 emitterIndex,
1310 nbToGenerate,
1311 deltaT
1313 deltaT += deltaTInc;
1315 while (k);
1318 else
1320 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1321 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1322 if (nbToGenerate)
1324 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1325 if (!nbToGenerate) nbToGenerate = 1;
1326 processEmit(emitterIndex, nbToGenerate);
1327 ++*numEmitIt;
1332 else
1334 *numEmitIt = _MaxEmissionCount; // if the lod change, must ensure that the
1336 ++phaseIt;
1337 currEmitPeriod += currEmitPeriodPtrInc;
1338 ++ numEmitIt;
1340 while (phaseIt != endPhaseIt);
1342 else // there's an emission delay
1346 if (*numEmitIt < maxEmissionCountLOD)
1348 if (*phaseIt < _EmitDelay)
1350 *phaseIt += CParticleSystem::EllapsedTime;
1351 if (*phaseIt < _EmitDelay)
1353 ++phaseIt;
1354 currEmitPeriod += currEmitPeriodPtrInc;
1355 ++numEmitIt;
1356 continue;
1358 else
1360 *phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
1363 else
1365 *phaseIt += ellapsedTimeLOD;
1368 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
1370 if (*currEmitPeriod != 0)
1372 /** Must ensure phase is valid if period decrease over time
1374 *phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD + _EmitDelay);
1376 uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
1377 *numEmitIt += numEmissions;
1378 *phaseIt -= *currEmitPeriod * numEmissions;
1379 float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
1380 //nlassert(deltaT >= 0.f);
1381 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1382 if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
1384 numEmissions -= *numEmitIt - _MaxEmissionCount;
1385 *numEmitIt = _MaxEmissionCount;
1387 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1388 if (nbToGenerate)
1390 /// compute the position of the emitter for the needed date
1391 numEmissions = GenEmitterPositionsWithLOD(_Owner,
1392 _EmittedType,
1393 emitterIndex,
1394 numEmissions,
1395 deltaT,
1396 *currEmitPeriod,
1397 inverseEmitLOD,
1398 emitterPositions
1400 uint k = numEmissions;
1401 /// process each emission at the right pos at the right date
1402 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1403 if (!nbToGenerate) nbToGenerate = 1;
1404 float deltaTInc = *currEmitPeriod * inverseEmitLOD;
1407 --k;
1408 processEmitConsistent(emitterPositions[k],
1409 emitterIndex,
1410 nbToGenerate,
1411 deltaT);
1412 deltaT += deltaTInc;
1414 while (k);
1417 else
1419 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1420 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1421 if (nbToGenerate)
1423 nbToGenerate = (sint32) (emitLOD * nbToGenerate);
1424 if (!nbToGenerate) nbToGenerate = 1;
1425 processEmit(emitterIndex, nbToGenerate);
1426 ++*numEmitIt;
1431 else
1433 *numEmitIt = _MaxEmissionCount; // if the lod change, must ensure that the
1435 ++phaseIt;
1436 currEmitPeriod += currEmitPeriodPtrInc;
1437 ++numEmitIt;
1439 while (phaseIt != endPhaseIt);
1443 leftToDo -= toProcess;
1445 while (leftToDo);
1448 ///==========================================================================
1449 void CPSEmitter::processRegularEmissionConsistentWithNoLOD(uint firstInstanceIndex)
1451 NL_PS_FUNC(CPSEmitter_processRegularEmissionConsistentWithNoLOD)
1452 /// hum, some code factorization would do no harm, but we want to keep tests outside the loops as much as possible...
1454 nlassert(_Owner);
1455 nlassert(_Owner->getOwner());
1457 const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
1461 static std::vector<NLMISC::CVector> emitterPositions;
1462 // Positions for the emitter. They are computed by using a parametric trajectory or by using integration
1464 const uint size = _Owner->getSize();
1465 nlassert(firstInstanceIndex < size);
1466 uint leftToDo = size - firstInstanceIndex, toProcess;
1467 float emitPeriod[EMITTER_BUFF_SIZE];
1468 const float *currEmitPeriod;
1469 uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
1470 sint32 nbToGenerate;
1473 TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
1474 TPSAttribUInt8::iterator numEmitIt;
1475 if (firstInstanceIndex < _NumEmission.getSize())
1476 numEmitIt = _NumEmission.begin() + firstInstanceIndex;
1477 else
1478 numEmitIt = _NumEmission.end();
1481 toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
1484 if (_PeriodScheme)
1486 // compute period
1487 // 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
1488 // so we may have a life counter that is > to 1
1489 currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
1490 if (emitThreshold)
1493 /** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
1494 * all null period have already been replaced by the threshold
1496 if (currEmitPeriod == emitPeriod)
1498 // if there possibility to have 0 in the scheme ?
1499 if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
1501 replaceNullPeriodsByThreshold(emitPeriod, toProcess);
1506 else
1508 if (_Period != 0.f || !emitThreshold)
1510 currEmitPeriod = &_Period;
1512 else
1514 currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
1518 endPhaseIt = phaseIt + toProcess;
1520 if (_MaxEmissionCount == 0) // no emission count limit
1522 /// is there an emission delay ?
1523 if (_EmitDelay == 0.f) // no emission delay
1527 *phaseIt += CParticleSystem::EllapsedTime;
1528 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
1530 if (*currEmitPeriod != 0)
1532 /** Must ensure phase is valid if period decrease over time
1534 *phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime);
1536 /// compute the number of emissions
1537 uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
1538 *phaseIt -= *currEmitPeriod * numEmissions;
1539 float deltaT = std::max(0.f, *phaseIt);
1540 //nlassert(deltaT >= 0.f);
1541 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1543 /// compute the position of the emitter for the needed dates
1544 numEmissions = GenEmitterPositions(_Owner,
1545 _EmittedType,
1546 emitterIndex,
1547 numEmissions,
1548 deltaT,
1549 *currEmitPeriod,
1550 emitterPositions
1553 /// process each emission at the right pos at the right date
1554 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1555 uint k = numEmissions;
1558 --k;
1559 processEmitConsistent(emitterPositions[k],
1560 emitterIndex,
1561 nbToGenerate,
1562 deltaT);
1563 deltaT += *currEmitPeriod;
1565 while (k);
1567 else
1569 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1570 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1571 processEmit(emitterIndex, nbToGenerate);
1575 ++phaseIt;
1576 currEmitPeriod += currEmitPeriodPtrInc;
1578 while (phaseIt != endPhaseIt);
1580 else // thhere's an emission delay
1584 *phaseIt += CParticleSystem::EllapsedTime;
1585 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
1587 if (*currEmitPeriod != 0)
1589 /** Must ensure phase is valid if period decrease over time
1591 *phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime + _EmitDelay);
1593 uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
1594 *phaseIt -= *currEmitPeriod * numEmissions;
1595 float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
1596 //nlassert(deltaT >= 0.f);
1598 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1599 /// compute the position of the emitter for the needed date
1600 numEmissions = GenEmitterPositions(_Owner,
1601 _EmittedType,
1602 emitterIndex,
1603 numEmissions,
1604 deltaT,
1605 *currEmitPeriod,
1606 emitterPositions
1608 /// process each emission at the right pos at the right date
1609 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1610 uint k = numEmissions;
1613 --k;
1614 processEmitConsistent(emitterPositions[k],
1615 emitterIndex,
1616 nbToGenerate,
1617 deltaT);
1618 deltaT += *currEmitPeriod;
1620 while (k);
1622 else
1624 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1625 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1626 processEmit(emitterIndex, nbToGenerate);
1630 ++phaseIt;
1631 currEmitPeriod += currEmitPeriodPtrInc;
1633 while (phaseIt != endPhaseIt);
1636 else // there's an emission count limit
1638 /// is there an emission delay ?
1639 if (_EmitDelay == 0.f) // no emission delay
1643 if (*numEmitIt < _MaxEmissionCount)
1645 *phaseIt += CParticleSystem::EllapsedTime;
1646 if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
1648 if (*currEmitPeriod != 0)
1650 /** Must ensure phase is valid if period decrease over time
1652 *phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime);
1654 uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
1655 *numEmitIt += numEmissions;
1656 *phaseIt -= *currEmitPeriod * numEmissions;
1657 float deltaT = std::max(*phaseIt, 0.f);
1658 //nlassert(deltaT >= 0.f);
1659 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1660 if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
1662 numEmissions -= *numEmitIt - _MaxEmissionCount;
1663 *numEmitIt = _MaxEmissionCount;
1665 /// compute the position of the emitter for the needed date
1666 numEmissions = GenEmitterPositions(_Owner,
1667 _EmittedType,
1668 emitterIndex,
1669 numEmissions,
1670 deltaT,
1671 *currEmitPeriod,
1672 emitterPositions
1674 uint k = numEmissions;
1675 /// process each emission at the right pos at the right date
1676 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1679 --k;
1680 processEmitConsistent(emitterPositions[k],
1681 emitterIndex,
1682 nbToGenerate,
1683 deltaT);
1684 deltaT += *currEmitPeriod;
1686 while (k);
1688 else
1690 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1691 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1692 processEmit(emitterIndex, nbToGenerate);
1693 ++*numEmitIt;
1697 ++phaseIt;
1698 currEmitPeriod += currEmitPeriodPtrInc;
1699 ++ numEmitIt;
1701 while (phaseIt != endPhaseIt);
1703 else // there's an emission delay
1707 if (*numEmitIt < _MaxEmissionCount)
1709 *phaseIt += CParticleSystem::EllapsedTime;
1710 if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
1712 if (*currEmitPeriod != 0)
1714 /** Must ensure phase is valid if period decrease over time
1716 *phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime + _EmitDelay);
1718 uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
1719 *numEmitIt += numEmissions;
1720 *phaseIt -= *currEmitPeriod * numEmissions;
1721 float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
1722 //nlassert(deltaT >= 0.f);
1723 uint emitterIndex = (uint)(phaseIt - _Phase.begin());
1724 if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
1726 numEmissions -= *numEmitIt - _MaxEmissionCount;
1727 *numEmitIt = _MaxEmissionCount;
1729 /// compute the position of the emitter for the needed date
1730 numEmissions = GenEmitterPositions(_Owner,
1731 _EmittedType,
1732 emitterIndex,
1733 numEmissions,
1734 deltaT,
1735 *currEmitPeriod,
1736 emitterPositions
1738 uint k = numEmissions;
1739 /// process each emission at the right pos at the right date
1740 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1743 --k;
1744 processEmitConsistent(emitterPositions[k],
1745 emitterIndex,
1746 nbToGenerate,
1747 deltaT);
1748 deltaT += *currEmitPeriod;
1750 while (k);
1752 else
1754 const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
1755 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
1756 processEmit(emitterIndex, nbToGenerate);
1757 ++*numEmitIt;
1761 ++phaseIt;
1762 currEmitPeriod += currEmitPeriodPtrInc;
1763 ++numEmitIt;
1765 while (phaseIt != endPhaseIt);
1769 leftToDo -= toProcess;
1771 while (leftToDo);
1775 ///==========================================================================
1776 void CPSEmitter::step(TPSProcessPass pass)
1778 NL_PS_FUNC(CPSEmitter_step)
1779 if (pass == PSToolRender)
1781 showTool();
1785 ///==========================================================================
1786 void CPSEmitter::computeSpawns(uint firstInstanceIndex)
1788 NL_PS_FUNC(CPSEmitter_computeSpawns)
1789 if (!_EmittedType) return;
1790 nlassert(CParticleSystem::InsideSimLoop);
1791 const uint32 size = _Owner->getSize();
1792 if (!size) return;
1793 if (CParticleSystem::EllapsedTime == 0.f) return; // do nothing when paused
1794 CParticleSystem *ps = _Owner->getOwner();
1795 nlassert(ps);
1796 float emitLOD;
1797 if (ps->isAutoLODEnabled() && !ps->isSharingEnabled() && !_BypassAutoLOD)
1799 // temp test for auto lod
1800 emitLOD = ps->getAutoLODEmitRatio();
1802 else
1804 emitLOD = 1.f;
1806 nlassert(_EmissionType == CPSEmitter::regular);
1807 if (!_ConsistentEmission)
1809 if (emitLOD != 1.f)
1811 processRegularEmission(firstInstanceIndex, emitLOD);
1813 else
1815 processRegularEmissionWithNoLOD(firstInstanceIndex);
1818 else
1820 if (emitLOD != 1.f)
1822 if (emitLOD != 0.f)
1824 processRegularEmissionConsistent(firstInstanceIndex, emitLOD, 1.f / emitLOD);
1827 else
1829 processRegularEmissionConsistentWithNoLOD(firstInstanceIndex);
1835 ///==========================================================================
1836 void CPSEmitter::newElement(const CPSEmitterInfo &info)
1838 NL_PS_FUNC(CPSEmitter_newElement)
1839 nlassert(_Phase.getSize() != _Phase.getMaxSize());
1841 _Phase.insert(0.f);
1842 if (_MaxEmissionCount != 0)
1844 _NumEmission.insert(0);
1846 if (_PeriodScheme && _PeriodScheme->hasMemory()) _PeriodScheme->newElement(info);
1847 if (_GenNbScheme && _GenNbScheme->hasMemory()) _GenNbScheme->newElement(info);
1851 ///==========================================================================
1852 inline void CPSEmitter::deleteElementBase(uint32 index)
1854 NL_PS_FUNC(CPSEmitter_deleteElementBase)
1855 if (_PeriodScheme && _PeriodScheme->hasMemory()) _PeriodScheme->deleteElement(index);
1856 if (_GenNbScheme && _GenNbScheme->hasMemory()) _GenNbScheme->deleteElement(index);
1857 _Phase.remove(index);
1858 if (_MaxEmissionCount != 0)
1860 _NumEmission.remove(index);
1864 ///==========================================================================
1865 void CPSEmitter::deleteElement(uint32 index)
1867 NL_PS_FUNC(CPSEmitter_deleteElement)
1869 if (_EmissionType == CPSEmitter::onDeath && _EmittedType && _Active)
1871 if (!_BypassEmitOnDeath)
1873 const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, index) : _GenNb;
1874 processEmitOutsideSimLoop(index, nbToGenerate);
1877 deleteElementBase(index);
1880 ///==========================================================================
1881 void CPSEmitter::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
1883 NL_PS_FUNC(CPSEmitter_deleteElement)
1884 if (_EmissionType == CPSEmitter::onDeath && _EmittedType && _Active)
1886 const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, index) : _GenNb;
1887 processEmitConsistent(_Owner->getPos()[index], index, nbToGenerate, timeUntilNextSimStep);
1889 deleteElementBase(index);
1893 ///==========================================================================
1894 void CPSEmitter::resize(uint32 size)
1896 NL_PS_FUNC(CPSEmitter_resize)
1897 nlassert(size < (1 << 16));
1898 if (_PeriodScheme && _PeriodScheme->hasMemory()) _PeriodScheme->resize(size, _Owner->getSize());
1899 if (_GenNbScheme && _GenNbScheme->hasMemory()) _GenNbScheme->resize(size, _Owner->getSize());
1900 _Phase.resize(size);
1901 if (_MaxEmissionCount != 0)
1903 _NumEmission.resize(size);
1907 ///==========================================================================
1908 void CPSEmitter::bounceOccurred(uint32 index, TAnimationTime timeToNextSimStep)
1910 NL_PS_FUNC(CPSEmitter_bounceOccurred)
1911 // TODO : avoid duplication with deleteElement
1912 if (_EmittedType && _EmissionType == CPSEmitter::onBounce)
1914 const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, index) : _GenNb;
1915 processEmitConsistent(_Owner->getPos()[index], index, nbToGenerate, timeToNextSimStep);
1919 ///==========================================================================
1920 void CPSEmitter::serial(NLMISC::IStream &f)
1922 NL_PS_FUNC(CPSEmitter_serial)
1923 /// version 6 : the flag _EmitDirBasis no longer exist, it has been replaced by _UserMatrixModeForEmissionDirection
1925 /// version 5 : added _BypassAutoLOD
1926 /// version 4 : added consistent emissions
1927 sint ver = f.serialVersion(6);
1928 CPSLocatedBindable::serial(f);
1930 f.serialPolyPtr(_EmittedType);
1931 f.serial(_Phase);
1932 f.serial(_SpeedInheritanceFactor);
1934 bool speedBasisEmission = _SpeedBasisEmission; // tmp copy because of bitfield serialization scheme
1935 f.serial(speedBasisEmission);
1936 _SpeedBasisEmission = speedBasisEmission;
1938 f.serialEnum(_EmissionType);
1940 // this is for use with serial
1941 bool trueB = true, falseB = false;
1943 if (!f.isReading())
1945 switch (_EmissionType)
1947 case CPSEmitter::regular:
1948 if (_PeriodScheme)
1950 f.serial(trueB);
1951 f.serialPolyPtr(_PeriodScheme);
1953 else
1955 f.serial(falseB);
1956 f.serial(_Period);
1958 if (ver >= 3)
1960 f.serial(_EmitDelay, _MaxEmissionCount);
1962 break;
1963 default:
1964 break;
1966 if (_GenNbScheme)
1968 f.serial(trueB);
1969 f.serialPolyPtr(_GenNbScheme);
1971 else
1973 f.serial(falseB);
1974 f.serial(_GenNb);
1977 else
1979 bool useScheme;
1980 switch (_EmissionType)
1982 case CPSEmitter::regular:
1984 f.serial(useScheme);
1985 if (useScheme)
1987 delete _PeriodScheme;
1988 f.serialPolyPtr(_PeriodScheme);
1990 else
1992 f.serial(_Period);
1994 if (ver >= 3)
1996 f.serial(_EmitDelay, _MaxEmissionCount);
1997 updateMaxCountVect();
2000 break;
2001 default:
2002 break;
2005 f.serial(useScheme);
2006 if (useScheme)
2008 delete _GenNbScheme;
2009 f.serialPolyPtr(_GenNbScheme);
2011 else
2013 f.serial(_GenNb);
2016 if (ver > 1 && ver < 6)
2018 nlassert(f.isReading());
2019 bool emitDirBasis;
2020 f.serial(emitDirBasis);
2021 if (emitDirBasis)
2023 _UserMatrixModeForEmissionDirection = false;
2024 _UserDirectionMatrixMode = PSFXWorldMatrix;
2026 else
2028 _UserMatrixModeForEmissionDirection = true;
2029 if (_Owner)
2031 _UserDirectionMatrixMode = _Owner->getMatrixMode() == PSFXWorldMatrix ? PSIdentityMatrix : PSFXWorldMatrix;
2033 else
2035 _UserDirectionMatrixMode = PSFXWorldMatrix;
2039 if (ver >= 4)
2041 bool consistentEmission = _ConsistentEmission; // tmp copy because of bitfield serialization scheme
2042 f.serial(consistentEmission);
2043 _ConsistentEmission = consistentEmission;
2045 if (ver >= 5)
2047 bool byassAutoLOD = _BypassAutoLOD; // tmp copy because of bitfield serialization scheme
2048 f.serial(byassAutoLOD);
2049 _BypassAutoLOD = byassAutoLOD;
2051 if (ver >= 6)
2053 bool userMatrixModeForEmissionDirection = _UserMatrixModeForEmissionDirection; // tmp copy because of bitfield serialization scheme
2054 f.serial(userMatrixModeForEmissionDirection);
2055 _UserMatrixModeForEmissionDirection = userMatrixModeForEmissionDirection;
2056 f.serialEnum(_UserDirectionMatrixMode);
2060 ///==========================================================================
2061 void CPSEmitter::updateMaxCountVect()
2063 NL_PS_FUNC(CPSEmitter_updateMaxCountVect)
2064 if (!_MaxEmissionCount || !_Owner)
2066 _NumEmission.resize(0);
2068 else
2070 _NumEmission.resize(_Owner->getMaxSize());
2071 while (_NumEmission.getSize() != 0)
2073 _NumEmission.remove(0);
2075 while (_NumEmission.getSize() != _Owner->getSize())
2077 _NumEmission.insert(0);
2082 ///==========================================================================
2083 void CPSEmitter::setEmitDelay(float delay)
2085 NL_PS_FUNC(CPSEmitter_setEmitDelay)
2086 _EmitDelay = delay;
2087 if (_Owner && _Owner->getOwner())
2089 _Owner->getOwner()->systemDurationChanged();
2093 ///==========================================================================
2094 bool CPSEmitter::setMaxEmissionCount(uint8 count)
2096 NL_PS_FUNC(CPSEmitter_setMaxEmissionCount)
2097 if (count == _MaxEmissionCount) return true;
2098 nlassert(_Owner && _Owner->getOwner());
2099 CParticleSystem *ps = _Owner->getOwner();
2100 if (ps->getBypassMaxNumIntegrationSteps())
2102 uint8 oldEmissiontCount = _MaxEmissionCount;
2103 // should check that the new value is valid
2104 _MaxEmissionCount = count;
2105 if (testEmitForever())
2107 _MaxEmissionCount = oldEmissiontCount;
2108 nlwarning("<CPSEmitter::setMaxEmissionCount> can't set max emission count to %d \
2109 with the current configuration : the system has been flagged with \
2110 'BypassMaxNumIntegrationSteps', and should have a finite duration. \
2111 The new value is not set", (int) count);
2112 return false;
2115 ps->systemDurationChanged();
2116 _MaxEmissionCount = count;
2117 updateMaxCountVect();
2118 return true;
2121 ///==========================================================================
2122 bool CPSEmitter::checkLoop() const
2124 NL_PS_FUNC(CPSEmitter_checkLoop)
2125 nlassert(_Owner);
2126 nlassert(_Owner->getOwner());
2127 if (!_EmittedType) return false;
2128 std::set<const CPSLocated *> seenLocated; // the located we've already seen
2129 std::vector<const CPSLocated *> leftLoc(1); // the located that are left to see
2130 leftLoc[0] = _EmittedType;
2133 const CPSLocated *curr = leftLoc.back();
2134 if (curr == this->_Owner) return true;
2135 leftLoc.pop_back();
2136 seenLocated.insert(curr);
2137 for(uint32 k = 0; k < curr->getNbBoundObjects(); ++k)
2139 const CPSEmitter *emitter = dynamic_cast<const CPSEmitter *>(curr->getBoundObject(k));
2140 if (emitter && emitter->_EmittedType)
2142 if (seenLocated.find(emitter->_EmittedType) == seenLocated.end()) // not already seen this one ?
2144 leftLoc.push_back(emitter->_EmittedType);
2149 while (!leftLoc.empty());
2150 return false;
2153 ///==========================================================================
2154 bool CPSEmitter::testEmitForever() const
2156 NL_PS_FUNC(CPSEmitter_testEmitForever)
2157 if (!_Owner)
2159 nlwarning("<CPSEmitter::testEmitForever> The emitter should be inserted in a CPSLocated instance for this call to work.");
2160 nlassert(0);
2161 return true;
2163 if (!_Owner->getLastForever()) return false;
2164 switch(getEmissionType())
2166 case CPSEmitter::onBounce:
2167 case CPSEmitter::externEmit:
2168 case CPSEmitter::regular:
2169 // it is ok only if a limited number of located is emitted
2170 if (getMaxEmissionCount() == 0) return true;
2171 break;
2172 case CPSEmitter::onDeath: return true; // the emitter never dies, so ..
2173 case CPSEmitter::once: return false;
2174 break;
2175 default:
2176 nlassert(0); // not a known type
2177 break;
2179 return false;
2183 ////////////////////////////////////////////
2184 // implementation of CPSModulatedEmitter //
2185 ////////////////////////////////////////////
2187 void CPSModulatedEmitter::serialEmitteeSpeedScheme(NLMISC::IStream &f)
2189 NL_PS_FUNC(CPSModulatedEmitter_IStream )
2190 bool useScheme;
2191 if (!f.isReading())
2193 useScheme = useEmitteeSpeedScheme();
2195 f.serial(useScheme);
2196 if (useScheme)
2198 f.serialPolyPtr(_EmitteeSpeedScheme);
2200 else
2202 f.serial(_EmitteeSpeed);
2208 ////////////////////////////////////////////
2209 // implementation of CPSEmitterOmni //
2210 ////////////////////////////////////////////
2212 ///==========================================================================
2213 void CPSEmitterOmni::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
2215 NL_PS_FUNC(CPSEmitterOmni_emit)
2216 // TODO : verifier que ca marche si une particule s'emet elle-mem
2217 nlassert(_EmittedType);
2219 CVector v( ((rand() % 1000) - 500) / 500.0f
2220 , ((rand() % 1000) - 500) / 500.0f
2221 , ((rand() % 1000) - 500) / 500.0f);
2222 v.normalize();
2223 v *= _EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed;
2225 pos = srcPos;
2226 speed = v;
2230 ///==========================================================================
2231 void CPSEmitterOmni::serial(NLMISC::IStream &f)
2233 NL_PS_FUNC(CPSEmitterOmni_serial)
2234 f.serialVersion(1);
2235 CPSEmitter::serial(f);
2236 CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
2239 ///==========================================================================
2240 void CPSEmitterOmni::newElement(const CPSEmitterInfo &info)
2242 NL_PS_FUNC(CPSEmitterOmni_newElement)
2243 CPSEmitter::newElement(info);
2244 newEmitteeSpeedElement(info);
2247 ///==========================================================================
2248 inline void CPSEmitterOmni::deleteElementBase(uint32 index)
2250 NL_PS_FUNC(CPSEmitterOmni_deleteElementBase)
2251 deleteEmitteeSpeedElement(index);
2254 ///==========================================================================
2255 void CPSEmitterOmni::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
2257 NL_PS_FUNC(CPSEmitterOmni_deleteElement)
2258 CPSEmitter::deleteElement(index, timeUntilNextSimStep);
2259 deleteElementBase(index);
2262 ///==========================================================================
2263 void CPSEmitterOmni::deleteElement(uint32 index)
2265 NL_PS_FUNC(CPSEmitterOmni_deleteElement)
2266 CPSEmitter::deleteElement(index);
2267 deleteElementBase(index);
2270 ///==========================================================================
2271 void CPSEmitterOmni::resize(uint32 capacity)
2273 NL_PS_FUNC(CPSEmitterOmni_resize)
2274 nlassert(capacity < (1 << 16));
2275 CPSEmitter::resize(capacity);
2276 resizeEmitteeSpeed(capacity);
2279 ///==========================================================================
2280 void CPSEmitterDirectionnal::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
2282 NL_PS_FUNC(CPSEmitterDirectionnal_emit)
2283 // TODO : verifier que ca marche si une particule s'emet elle-mem
2284 nlassert(_EmittedType);
2287 speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed) * _Dir;
2288 pos = srcPos;
2291 ///==========================================================================
2292 void CPSEmitterDirectionnal::newElement(const CPSEmitterInfo &info)
2294 NL_PS_FUNC(CPSEmitterDirectionnal_newElement)
2295 CPSEmitter::newElement(info);
2296 newEmitteeSpeedElement(info);
2300 ///==========================================================================
2301 inline void CPSEmitterDirectionnal::deleteElementBase(uint32 index)
2303 NL_PS_FUNC(CPSEmitterDirectionnal_deleteElementBase)
2304 deleteEmitteeSpeedElement(index);
2307 ///==========================================================================
2308 void CPSEmitterDirectionnal::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
2310 NL_PS_FUNC(CPSEmitterDirectionnal_deleteElement)
2311 CPSEmitter::deleteElement(index, timeUntilNextSimStep);
2312 deleteElementBase(index);
2315 ///==========================================================================
2316 void CPSEmitterDirectionnal::deleteElement(uint32 index)
2318 NL_PS_FUNC(CPSEmitterDirectionnal_deleteElement)
2319 CPSEmitter::deleteElement(index);
2320 deleteElementBase(index);
2325 ///==========================================================================
2326 void CPSEmitterDirectionnal::resize(uint32 capacity)
2328 NL_PS_FUNC(CPSEmitterDirectionnal_resize)
2329 nlassert(capacity < (1 << 16));
2330 CPSEmitter::resize(capacity);
2331 resizeEmitteeSpeed(capacity);
2334 ///==========================================================================
2335 void CPSEmitterDirectionnal::serial(NLMISC::IStream &f)
2337 NL_PS_FUNC(CPSEmitterDirectionnal_IStream )
2338 f.serialVersion(1);
2339 CPSEmitter::serial(f);
2340 CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
2341 f.serial(_Dir);
2344 ////////////////////////////////////////////
2345 // implementation of CPSEmitterRectangle //
2346 ////////////////////////////////////////////
2348 ///==========================================================================
2349 void CPSEmitterRectangle::serial(NLMISC::IStream &f)
2351 NL_PS_FUNC(CPSEmitterRectangle_IStream )
2352 f.serialVersion(1);
2353 CPSEmitter::serial(f);
2354 CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
2355 f.serial(_Basis);
2356 f.serial(_Width);
2357 f.serial(_Height);
2358 f.serial(_Dir);
2362 ///==========================================================================
2363 void CPSEmitterRectangle::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
2365 NL_PS_FUNC(CPSEmitterRectangle_emit)
2366 CVector N = _Basis[index].X ^ _Basis[index].Y;
2367 pos = srcPos + ((rand() % 32000) * (1.f / 16000) - 1.f) * _Width[index] * _Basis[index].X
2368 + ((rand() % 32000) * (1.f / 16000) - 1.f) * _Height[index] * _Basis[index].Y;
2369 speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed)
2370 * (_Dir.x * _Basis[index].X+ _Dir.y * _Basis[index].Y + _Dir.z * N);
2373 ///==========================================================================
2374 void CPSEmitterRectangle::setMatrix(uint32 index, const CMatrix &m)
2376 NL_PS_FUNC(CPSEmitterRectangle_setMatrix)
2377 _Owner->getPos()[index] = m.getPos();
2380 _Basis[index].X = m.getI();
2381 _Basis[index].Y = m.getJ();
2384 ///==========================================================================
2385 CMatrix CPSEmitterRectangle::getMatrix(uint32 index) const
2387 NL_PS_FUNC(CPSEmitterRectangle_getMatrix)
2388 CMatrix m;
2389 m.setPos(_Owner->getPos()[index]);
2390 m.setRot(_Basis[index].X, _Basis[index].Y, _Basis[index].X ^ _Basis[index].Y, true);
2391 return m;
2394 ///==========================================================================
2395 void CPSEmitterRectangle::setScale(uint32 index, float scale)
2397 NL_PS_FUNC(CPSEmitterRectangle_setScale)
2398 _Width[index] = scale;
2399 _Height[index] = scale;
2402 ///==========================================================================
2403 void CPSEmitterRectangle::setScale(uint32 index, const CVector &s)
2405 NL_PS_FUNC(CPSEmitterRectangle_setScale)
2406 _Width[index] = s.x;
2407 _Height[index] = s.y;
2410 ///==========================================================================
2411 CVector CPSEmitterRectangle::getScale(uint32 index) const
2413 NL_PS_FUNC(CPSEmitterRectangle_getScale)
2414 return CVector(_Width[index], _Height[index], 1.f);
2417 ///==========================================================================
2418 void CPSEmitterRectangle::newElement(const CPSEmitterInfo &info)
2420 NL_PS_FUNC( CPSEmitterRectangle_newElement)
2421 CPSEmitter::newElement(info);
2422 newEmitteeSpeedElement(info);
2423 _Basis.insert(CPlaneBasis(CVector::K));
2424 _Width.insert(1.f);
2425 _Height.insert(1.f);
2428 ///==========================================================================
2429 inline void CPSEmitterRectangle::deleteElementBase(uint32 index)
2431 NL_PS_FUNC(CPSEmitterRectangle_deleteElementBase)
2432 deleteEmitteeSpeedElement(index);
2433 _Basis.remove(index);
2434 _Width.remove(index);
2435 _Height.remove(index);
2438 ///==========================================================================
2439 void CPSEmitterRectangle::deleteElement(uint32 index)
2441 NL_PS_FUNC(CPSEmitterRectangle_deleteElement)
2442 CPSEmitter::deleteElement(index);
2443 deleteElementBase(index);
2446 ///==========================================================================
2447 void CPSEmitterRectangle::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
2449 NL_PS_FUNC(CPSEmitterRectangle_deleteElement)
2450 CPSEmitter::deleteElement(index, timeUntilNextSimStep);
2451 deleteElementBase(index);
2454 ///==========================================================================
2455 void CPSEmitterRectangle::resize(uint32 size)
2457 NL_PS_FUNC(CPSEmitterRectangle_resize)
2458 nlassert(size < (1 << 16));
2459 CPSEmitter::resize(size);
2460 resizeEmitteeSpeed(size);
2461 _Basis.resize(size);
2462 _Width.resize(size);
2463 _Height.resize(size);
2466 ///==========================================================================
2467 void CPSEmitterRectangle::showTool(void)
2469 NL_PS_FUNC(CPSEmitterRectangle_showTool)
2470 nlassert(_Owner);
2471 const uint size = _Owner->getSize();
2472 if (!size) return;
2473 setupDriverModelMatrix();
2474 CMatrix mat;
2476 CPSLocated *loc;
2477 uint32 index;
2478 CPSLocatedBindable *lb;
2479 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
2481 for (uint k = 0; k < size; ++k)
2483 const CVector &I = _Basis[k].X;
2484 const CVector &J = _Basis[k].Y;
2485 mat.setRot(I, J , I ^J);
2486 mat.setPos(_Owner->getPos()[k]);
2487 CPSUtil::displayBasis(getDriver() ,getLocalToWorldMatrix(), mat, 1.f, *getFontGenerator(), *getFontManager());
2488 setupDriverModelMatrix();
2490 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
2494 const CVector &pos = _Owner->getPos()[k];
2495 CPSUtil::display3DQuad(*getDriver(), pos + I * _Width[k] + J * _Height[k]
2496 , pos + I * _Width[k] - J * _Height[k]
2497 , pos - I * _Width[k] - J * _Height[k]
2498 , pos - I * _Width[k] + J * _Height[k], col);
2504 ////////////////////////////////////
2505 // CPSEmitterconic implementation //
2506 ////////////////////////////////////
2508 ///==========================================================================
2509 void CPSEmitterConic::serial(NLMISC::IStream &f)
2511 NL_PS_FUNC(CPSEmitterConic_serial)
2512 f.serialVersion(1);
2513 CPSEmitterDirectionnal::serial(f);
2514 f.serial(_Radius);
2517 ///==========================================================================
2518 void CPSEmitterConic::setDir(const CVector &v)
2520 NL_PS_FUNC(CPSEmitterConic_setDir)
2521 CPSEmitterDirectionnal::setDir(v);
2525 ///==========================================================================
2526 void CPSEmitterConic::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
2528 NL_PS_FUNC(CPSEmitterConic_emit)
2529 // TODO : optimize that
2530 nlassert(_EmittedType);
2532 // we choose a custom direction like with omnidirectionnal emitter
2533 // then we force the direction vect to have the unit size
2535 static const double divRand = (2.0 / RAND_MAX);
2537 CVector dir((float) (rand() * divRand - 1)
2538 , (float) (rand() * divRand - 1)
2539 , (float) (rand() * divRand - 1) );
2541 const float n =dir.norm();
2543 dir *= _Radius / n;
2545 dir -= (_Dir * dir) * _Dir;
2546 dir += _Dir;
2547 dir.normalize();
2550 speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed)
2551 * dir;
2552 pos = srcPos;
2555 ////////////////////////////////////////
2556 // CPSSphericalEmitter implementation //
2557 ////////////////////////////////////////
2559 ///==========================================================================
2560 void CPSSphericalEmitter::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
2562 NL_PS_FUNC(CPSSphericalEmitter_emit)
2563 static const double divRand = (2.0 / RAND_MAX);
2564 CVector dir((float) (rand() * divRand - 1), (float) (rand() * divRand - 1) , (float) (rand() * divRand - 1) );
2565 dir.normalize();
2566 pos = srcPos + _Radius[index] * dir;
2567 speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed) * dir;
2571 ///==========================================================================
2572 void CPSSphericalEmitter::showTool(void)
2574 NL_PS_FUNC(CPSSphericalEmitter_showTool)
2575 CPSLocated *loc;
2576 uint32 index;
2577 CPSLocatedBindable *lb;
2578 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
2581 TPSAttribFloat::const_iterator radiusIt = _Radius.begin();
2582 TPSAttribVector::const_iterator posIt = _Owner->getPos().begin(), endPosIt = _Owner->getPos().end();
2583 setupDriverModelMatrix();
2584 for (uint k = 0; posIt != endPosIt; ++posIt, ++radiusIt, ++k)
2586 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
2587 CPSUtil::displaySphere(*getDriver(), *radiusIt, *posIt, 5, col);
2592 ///==========================================================================
2593 void CPSSphericalEmitter::setMatrix(uint32 index, const CMatrix &m)
2595 NL_PS_FUNC(CPSSphericalEmitter_setMatrix)
2596 nlassert(index < _Radius.getSize());
2597 // compute new pos
2598 _Owner->getPos()[index] = m.getPos();
2602 ///==========================================================================
2603 CMatrix CPSSphericalEmitter::getMatrix(uint32 index) const
2605 NL_PS_FUNC(CPSSphericalEmitter_getMatrix)
2606 nlassert(index < _Radius.getSize());
2607 CMatrix m;
2608 m.identity();
2609 m.translate(_Owner->getPos()[index]);
2610 return m;
2613 ///==========================================================================
2614 void CPSSphericalEmitter::serial(NLMISC::IStream &f)
2616 NL_PS_FUNC(CPSSphericalEmitter_serial)
2617 f.serialVersion(1);
2618 CPSEmitter::serial(f);
2619 CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
2620 f.serial(_Radius);
2623 ///==========================================================================
2624 void CPSSphericalEmitter::newElement(const CPSEmitterInfo &info)
2626 NL_PS_FUNC(CPSSphericalEmitter_newElement)
2627 CPSEmitter::newElement(info);
2628 newEmitteeSpeedElement(info);
2629 _Radius.insert(1.f);
2632 ///==========================================================================
2633 inline void CPSSphericalEmitter::deleteElementBase(uint32 index)
2635 NL_PS_FUNC(CPSSphericalEmitter_deleteElementBase)
2636 deleteEmitteeSpeedElement(index);
2637 _Radius.remove(index);
2640 ///==========================================================================
2641 void CPSSphericalEmitter::deleteElement(uint32 index)
2643 NL_PS_FUNC(CPSSphericalEmitter_deleteElement)
2644 CPSEmitter::deleteElement(index);
2645 deleteElementBase(index);
2648 ///==========================================================================
2649 void CPSSphericalEmitter::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
2651 NL_PS_FUNC(CPSSphericalEmitter_deleteElement)
2652 CPSEmitter::deleteElement(index, timeUntilNextSimStep);
2653 deleteElementBase(index);
2656 ///==========================================================================
2657 void CPSSphericalEmitter::resize(uint32 size)
2659 NL_PS_FUNC(CPSSphericalEmitter_resize)
2660 nlassert(size < (1 << 16));
2661 CPSEmitter::resize(size);
2662 resizeEmitteeSpeed(size);
2663 _Radius.resize(size);
2666 /////////////////////////////////////
2667 // CPSRadialEmitter implementation //
2668 /////////////////////////////////////
2670 ///==========================================================================
2671 void CPSRadialEmitter::serial(NLMISC::IStream &f)
2673 NL_PS_FUNC(CPSRadialEmitter_serial)
2674 f.serialVersion(1);
2675 CPSEmitterDirectionnal::serial(f);
2678 ///==========================================================================
2679 void CPSRadialEmitter::emit(const NLMISC::CVector &srcPos, uint32 index, NLMISC::CVector &pos, NLMISC::CVector &speed)
2681 NL_PS_FUNC(CPSRadialEmitter_emit)
2682 // TODO : verify if it works when a particle emits itself
2683 nlassert(_EmittedType);
2685 static const double divRand = (2.0 / RAND_MAX);
2686 CVector dir((float) (rand() * divRand - 1),
2687 (float) (rand() * divRand - 1),
2688 (float) (rand() * divRand - 1) );
2689 dir -= (dir * _Dir) * _Dir; //keep tangential direction
2690 dir.normalize();
2691 speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed) * dir;
2692 pos = srcPos;
2696 ///===============================================================================
2697 void CPSEmitter::enableSpeedBasisEmission(bool enabled /*=true*/)
2699 NL_PS_FUNC(CPSEmitter_enableSpeedBasisEmission)
2700 bool wasUserMatNeeded = isUserMatrixUsed();
2701 _SpeedBasisEmission = enabled;
2702 updatePSRefCountForUserMatrixUsage(isUserMatrixUsed(), wasUserMatNeeded);
2705 ///===============================================================================
2706 void CPSEmitter::enableUserMatrixModeForEmissionDirection(bool enable /*=true*/)
2708 NL_PS_FUNC(CPSEmitter_enableUserMatrixModeForEmissionDirection)
2709 bool wasUserMatNeeded = isUserMatrixUsed();
2710 _UserMatrixModeForEmissionDirection = enable;
2711 updatePSRefCountForUserMatrixUsage(isUserMatrixUsed(), wasUserMatNeeded);
2714 ///===============================================================================
2715 void CPSEmitter::setUserMatrixModeForEmissionDirection(TPSMatrixMode matrixMode)
2717 NL_PS_FUNC(CPSEmitter_setUserMatrixModeForEmissionDirection)
2718 bool wasUserMatNeeded = isUserMatrixUsed();
2719 _UserDirectionMatrixMode = matrixMode;
2720 updatePSRefCountForUserMatrixUsage(isUserMatrixUsed(), wasUserMatNeeded);
2724 ///==========================================================================
2725 void CPSEmitter::updatePSRefCountForUserMatrixUsage(bool matrixIsNeededNow, bool matrixWasNeededBefore)
2727 NL_PS_FUNC(CPSEmitter_updatePSRefCountForUserMatrixUsage)
2728 if (_Owner && _Owner->getOwner())
2730 if (matrixIsNeededNow && !matrixWasNeededBefore)
2732 _Owner->getOwner()->addRefForUserSysCoordInfo();
2734 else if (!matrixIsNeededNow && matrixWasNeededBefore)
2736 _Owner->getOwner()->releaseRefForUserSysCoordInfo();
2741 ///==========================================================================
2742 bool CPSEmitter::isUserMatrixUsed() const
2744 NL_PS_FUNC(CPSEmitter_isUserMatrixUsed)
2745 return !_SpeedBasisEmission && _UserMatrixModeForEmissionDirection && _UserDirectionMatrixMode == PSUserMatrix;
2748 ///==========================================================================
2749 bool CPSEmitter::getUserMatrixUsageCount() const
2751 NL_PS_FUNC(CPSEmitter_getUserMatrixUsageCount)
2752 return isUserMatrixUsed() ? 1 : 0;
2757 ///==========================================================================
2758 void CPSEmitter::doEmitOnce(uint firstInstanceIndex)
2760 NL_PS_FUNC(CPSEmitter_doEmitOnce)
2761 if (!_EmittedType) return;
2762 if (!_GenNbScheme && _GenNb == 0) return;
2763 nlassert(_Owner);
2764 nlassert(CParticleSystem::InsideSimLoop); // should only be called by the sim loop
2765 float emitLOD;
2766 nlassert(_Owner);
2767 nlassert(_Owner->getOwner());
2768 const CParticleSystem *ps = _Owner->getOwner();
2769 if (ps->isAutoLODEnabled() && !ps->isSharingEnabled() && !_BypassAutoLOD)
2771 // temp test for auto lod
2772 emitLOD = ps->getAutoLODEmitRatio();
2774 else
2776 emitLOD = 1.f;
2778 nlassert(emitLOD >= 0.f);
2779 if (_GenNbScheme)
2781 const uint BATCH_SIZE = 1024;
2782 uint32 numToEmit[BATCH_SIZE];
2783 uint k = firstInstanceIndex;
2784 nlassert(firstInstanceIndex < _Owner->getSize());
2785 uint leftToDo = _Owner->getSize() - firstInstanceIndex;
2787 while (leftToDo)
2789 uint toProcess = std::min((uint) BATCH_SIZE, leftToDo);
2790 uint32 *numToEmitPtr = (uint32 *) _GenNbScheme->make(_Owner, k, numToEmit, sizeof(uint32), true);
2791 leftToDo -= toProcess;
2792 while (toProcess)
2794 CVector startPos;
2795 if (!_Owner->isParametricMotionEnabled())
2797 startPos = _Owner->getPos()[k] - _Owner->getSpeed()[k] * CParticleSystem::EllapsedTime;
2799 else
2801 startPos = _Owner->getParametricInfos()[k].Pos;
2803 float currTime = _Owner->getTime()[k];
2804 _Owner->getTime()[k] = 0.f; // when emit occurred, time was 0
2805 sint32 nbToGenerate = (sint32) (emitLOD * *numToEmitPtr);
2806 if (nbToGenerate > 0)
2808 nbToGenerate = std::min(nbToGenerate, (sint32) _EmittedType->getMaxSize());
2809 processEmitConsistent(startPos, k, nbToGenerate, _Owner->getAgeInSeconds(k) / CParticleSystem::RealEllapsedTimeRatio);
2811 // restore time & pos
2812 _Owner->getTime()[k] = currTime;
2813 ++ k;
2814 ++ numToEmitPtr;
2815 -- toProcess;
2819 else
2821 sint nbToGenerate = (sint) (emitLOD * _GenNb);
2822 if (nbToGenerate <= 0) nbToGenerate = 1;
2823 nbToGenerate = std::min(nbToGenerate, (sint) _EmittedType->getMaxSize());
2824 for(uint k = firstInstanceIndex; k < _Owner->getSize(); ++k)
2826 // retrieve previous position (because motion step is done before spawn step)
2827 CVector startPos;
2828 if (!_Owner->isParametricMotionEnabled())
2830 startPos = _Owner->getPos()[k] - _Owner->getSpeed()[k] * CParticleSystem::EllapsedTime;
2832 else
2834 startPos = _Owner->getParametricInfos()[k].Pos;
2836 float currTime = _Owner->getTime()[k];
2837 _Owner->getTime()[k] = 0.f; // when emit occurred, time was 0
2838 processEmitConsistent(startPos, k, nbToGenerate, _Owner->getAgeInSeconds(k) / CParticleSystem::RealEllapsedTimeRatio);
2839 // restore time & pos
2840 _Owner->getTime()[k] = currTime;
2845 ///==========================================================================
2846 void CPSEmitter::updateEmitTrigger()
2848 NL_PS_FUNC(CPSEmitter_updateEmitTrigger)
2849 if (!_EmitTrigger) return;
2850 nlassert(_Owner);
2851 nlassert(_Owner->getOwner());
2852 const CParticleSystem *ps = _Owner->getOwner();
2853 float emitLOD;
2854 if (ps->isAutoLODEnabled() && !ps->isSharingEnabled() && !_BypassAutoLOD)
2856 // temp test for auto lod
2857 emitLOD = ps->getAutoLODEmitRatio();
2859 else
2861 emitLOD = 1.f;
2863 if (_GenNbScheme)
2865 const uint BATCH_SIZE = 1024;
2866 uint32 numToEmit[BATCH_SIZE];
2867 uint k = 0;
2868 uint leftToDo = _Owner->getSize();
2869 while (leftToDo)
2871 uint toProcess = std::min(BATCH_SIZE, leftToDo);
2872 uint32 *numToEmitPtr = (uint32 *) _GenNbScheme->make(_Owner, k, numToEmit, sizeof(uint32), true);
2873 while (toProcess)
2875 uint32 nbToGenerate = (sint32) (emitLOD * *numToEmitPtr);
2876 if (!nbToGenerate) nbToGenerate = 1;
2877 processEmit(k, nbToGenerate);
2878 ++ k;
2879 ++ numToEmitPtr;
2882 leftToDo -= toProcess;
2885 else
2887 uint nbToGenerate = (sint32) (emitLOD * _GenNb);
2888 if (!nbToGenerate) nbToGenerate = 1;
2889 for(uint k = 0; k < _Owner->getSize(); ++k)
2891 processEmit(k, nbToGenerate);
2894 _EmitTrigger = false;
2899 } // NL3D
2901 namespace NLMISC
2904 std::string toString(NL3D::CPSEmitter::TEmissionType type)
2906 NL_PS_FUNC(toString_CPSEmitter_TEmissionType)
2907 nlctassert(NL3D::CPSEmitter::numEmissionType == 5); // If this ct assertion is raised, the content of TEmissionType has changed, so should change this function !
2908 switch (type)
2910 case NL3D::CPSEmitter::regular: return "regular";
2911 case NL3D::CPSEmitter::onDeath: return "onDeath";
2912 case NL3D::CPSEmitter::once: return "once";
2913 case NL3D::CPSEmitter::onBounce: return "onBounce";
2914 case NL3D::CPSEmitter::externEmit: return "externEmit";
2915 default:
2916 nlassert(0);
2917 return "";
2918 break;
2923 } // NLMISC