Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / particle_system_model.cpp
blob7736b7233a0047272799d2425085a41f5f6924e1
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/misc/debug.h"
20 #include "nel/misc/common.h"
21 #include "nel/misc/hierarchical_timer.h"
22 #include "nel/3d/particle_system_model.h"
23 #include "nel/3d/particle_system_shape.h"
24 #include "nel/3d/particle_system.h"
25 #include "nel/3d/scene.h"
26 #include "nel/3d/anim_detail_trav.h"
27 #include "nel/3d/clip_trav.h"
28 #include "nel/3d/render_trav.h"
29 #include "nel/3d/skeleton_model.h"
31 #include "nel/3d/cluster.h" // ask trap
35 #ifdef DEBUG_NEW
36 #define new DEBUG_NEW
37 #endif
39 namespace NL3D {
42 uint64 PSStatsRegisterPSModelObserver = 0;
43 uint64 PSStatsRemovePSModelObserver = 0;
44 uint64 PSStatsUpdateOpacityInfos = 0;
45 uint64 PSStatsUpdateLightingInfos = 0;
46 uint64 PSStatsGetAABBox = 0;
47 uint64 PSStatsReallocRsc = 0;
48 uint64 PSStatsReleasePSPointer = 0;
49 uint64 PSStatsRefreshRscDeletion = 0;
50 uint64 PSStatsReleaseRsc = 0;
51 uint64 PSStatsReleaseRscAndInvalidate = 0;
52 uint64 PSStatsGetNumTriangles = 0;
53 uint64 PSStatsCheckAgainstPyramid = 0;
54 uint64 PSStatsTraverseAnimDetail = 0;
55 uint64 PSStatsDoAnimate = 0;
56 uint64 PSStatsDoAnimatePart1 = 0;
57 uint64 PSStatsDoAnimatePart2 = 0;
58 uint64 PSStatsDoAnimatePart3 = 0;
59 uint64 PSStatsTraverseRender = 0;
60 uint64 PSStatsTraverseClip = 0;
61 uint64 PSStatsClipSystemInstanciated = 0;
62 uint64 PSStatsClipSystemNotInstanciated = 0;
63 uint64 PSStatsClipSystemCheckAgainstPyramid = 0;
64 uint64 PSStatsInsertInVisibleList = 0;
65 uint64 PSStatsCheckDestroyCondition = 0;
66 uint64 PSStatsForceInstanciate = 0;
67 uint64 PSStatsTraverseAnimDetailPart1 = 0;
68 uint64 PSStatsTraverseAnimDetailPart2 = 0;
69 uint64 PSStatsTraverseAnimDetailPart3 = 0;
70 uint64 PSStatsTraverseAnimDetailPart4 = 0;
72 uint64 PSAnim1 = 0;
73 uint64 PSAnim2 = 0;
74 uint64 PSAnim3 = 0;
75 uint64 PSAnim4 = 0;
76 uint64 PSAnim5 = 0;
77 uint64 PSAnim6 = 0;
78 uint64 PSAnim7 = 0;
79 uint64 PSAnim8 = 0;
80 uint64 PSAnim9 = 0;
81 uint64 PSAnim10 = 0;
82 uint64 PSAnim11 = 0;
84 uint PSStatsNumDoAnimateCalls = 0;
85 float PSMaxET = 0.f;
86 uint PSMaxNBPass = 0;
88 uint64 PSStatsZonePlane = 0;
89 uint64 PSStatsZoneSphere = 0;
90 uint64 PSStatsZoneDisc = 0;
91 uint64 PSStatsZoneRectangle = 0;
92 uint64 PSStatsZoneCylinder = 0;
94 uint64 PSMotion1 = 0;
95 uint64 PSMotion2 = 0;
96 uint64 PSMotion3 = 0;
97 uint64 PSMotion4 = 0;
98 uint64 PSStatCollision = 0;
99 uint64 PSStatEmit = 0;
100 uint64 PSStatRender = 0;
108 ///=====================================================================================
109 /// ctor
110 CParticleSystemModel::CParticleSystemModel() : _ParticleSystem(NULL),
111 _Scene(NULL),
112 _EllapsedTime(0.01f),
113 _EllapsedTimeRatio(1.f),
114 _AnimType(CParticleSystem::AnimVisible),
115 _AutoGetEllapsedTime(true),
116 _ToolDisplayEnabled(false),
117 _TransparencyStateTouched(true),
118 _LightableStateTouched(true),
119 _EditionMode(false),
120 _Invalidated(false),
121 _InsertedInVisibleList(false),
122 _InClusterAndVisible(false),
123 _EmitterActive(true),
124 _SoundActive(true),
125 _BypassGlobalUserParam(0),
126 _UserColor(CRGBA::White),
127 _ZBias(0.f),
128 _LastVisibility(CHrcTrav::Show)
130 setOpacity(false);
131 setTransparency(true);
132 IAnimatable::resize(AnimValueLast);
133 _TriggerAnimatedValue.Value = true;
135 // AnimDetail behavior: Must be traversed in AnimDetail, even if no channel mixer registered
136 CTransform::setIsForceAnimDetail(true);
138 for(uint k = 0; k < MaxPSUserParam; ++k)
140 _UserParam[k].Value = 0.f;
143 // RenderFilter: We are a Landscape
144 _RenderFilterType= UScene::FilterPS;
147 ///=====================================================================================
148 void CParticleSystemModel::setEditionMode(bool enable /*= true*/)
150 if (enable)
152 /// we need to have the system resources instanciated if we want to work with it
153 if (!_ParticleSystem)
155 nlassert(_Scene);
156 nlassert(Shape);
157 reallocRsc();
160 _EditionMode = enable;
163 ///=====================================================================================
164 void CParticleSystemModel::registerPSModelObserver(IPSModelObserver *obs)
166 MINI_TIMER(PSStatsRegisterPSModelObserver)
167 nlassert(!isPSModelObserver(obs)); // this observer has already been registered
168 _Observers.push_back(obs);
171 ///=====================================================================================
172 void CParticleSystemModel::removePSModelObserver(IPSModelObserver *obs)
174 MINI_TIMER(PSStatsRemovePSModelObserver);
175 nlassert(isPSModelObserver(obs)); // the observer must have been registered
176 std::vector<IPSModelObserver *>::iterator it = std::find(_Observers.begin(), _Observers.end(), obs);
177 _Observers.erase(it);
181 ///=====================================================================================
182 bool CParticleSystemModel::isPSModelObserver(IPSModelObserver *obs)
184 return std::find(_Observers.begin(), _Observers.end(), obs) != _Observers.end();
188 ///=====================================================================================
189 void CParticleSystemModel::registerBasic()
191 // register the model
192 CScene::registerModel(ParticleSystemModelId, TransformShapeId, CParticleSystemModel::creator);
195 ///=====================================================================================
196 void CParticleSystemModel::updateOpacityInfos(void)
198 MINI_TIMER(PSStatsUpdateOpacityInfos);
199 nlassert(_ParticleSystem);
200 if (!_TransparencyStateTouched) return;
201 nlassert(_ParticleSystem);
202 setOpacity(_ParticleSystem->hasOpaqueObjects() || _ToolDisplayEnabled);
203 setTransparency(_ParticleSystem->hasTransparentObjects());
204 _TransparencyStateTouched = false;
207 ///=====================================================================================
208 void CParticleSystemModel::updateLightingInfos(void)
210 MINI_TIMER(PSStatsUpdateLightingInfos)
211 nlassert(_ParticleSystem);
212 if (!_LightableStateTouched) return;
213 CTransform::setIsLightable(_ParticleSystem->hasLightableObjects());
214 _LightableStateTouched = false;
217 ///=====================================================================================
218 void CParticleSystemModel::getAABBox(NLMISC::CAABBox &bbox) const
220 MINI_TIMER(PSStatsGetAABBox)
221 if (_ParticleSystem)
223 _ParticleSystem->computeBBox(bbox);
225 else
227 NLMISC::safe_cast<CParticleSystemShape *>((IShape *) Shape)->getAABBox(bbox);
231 ///=====================================================================================
232 CParticleSystemModel::~CParticleSystemModel()
234 nlassert(_Scene);
235 releaseRsc();
236 // Auto detach me from skeleton. Must do it here, not in ~CTransform().
237 if(_FatherSkeletonModel)
239 // detach me from the skeleton.
240 // clip and hrc hierarchy is modified.
241 _FatherSkeletonModel->detachSkeletonSon(this);
242 nlassert(_FatherSkeletonModel==NULL);
247 ///=====================================================================================
248 /// Called when the resource (attached system) for this system must be reallocated
249 void CParticleSystemModel::reallocRsc()
251 //MINI_TIMER(PSStatsReallocRsc)
252 nlassert(_ParticleSystem == NULL);
253 #ifdef PS_FAST_ALLOC
254 CParticleSystemShape *shape = NLMISC::safe_cast<CParticleSystemShape *>((IShape *) Shape);
255 if (shape->isShared())
257 // there's a single CparticleSystemInstance even if there are several models
258 _ParticleSystem = shape->instanciatePS(*_Scene, &shape->Allocator);
260 else
262 _ParticleSystem = shape->instanciatePS(*_Scene, &_Allocator);
264 #else
265 _ParticleSystem = NLMISC::safe_cast<CParticleSystemShape *>((IShape *) Shape)->instanciatePS(*_Scene);
266 #endif
267 nlassert(_ParticleSystem);
268 nlassert(_Scene);
269 CParticleSystemManager &psmgt = _Scene->getParticleSystemManager();
270 _ModelHandle = psmgt.addSystemModel(this);
271 _AnimType = _ParticleSystem->getAnimType();
272 if (_ParticleSystem->getAnimType() == CParticleSystem::AnimAlways)
274 _AnimatedModelHandle = psmgt.addPermanentlyAnimatedSystem(this);
276 // touch user params animated value. If the system rsc have been released before, this force to restore them
277 for (uint k = 0; k < MaxPSUserParam; ++k)
279 touch((uint)CParticleSystemModel::PSParam0 + k, OwnerBit);
281 _ParticleSystem->setUserColor(_UserColor);
283 if (!_EmitterActive) _ParticleSystem->activateEmitters(false);
284 if (!_SoundActive) _ParticleSystem->stopSound();
286 if (_ZBias != 0.f) _ParticleSystem->setZBias(_ZBias);
289 ///=====================================================================================
290 void CParticleSystemModel::releasePSPointer()
292 MINI_TIMER(PSStatsReleasePSPointer)
293 nlassert(_ParticleSystem != NULL);
294 sint numRefs = _ParticleSystem.getNbRef();
295 if (numRefs == 1)
297 // Backup user params (in animated value) so that they will be restored when the system is recreated
298 for (uint k = 0; k < MaxPSUserParam; ++k)
300 _UserParam[k].Value = _ParticleSystem->getUserParam(k);
304 nlassert(_Scene);
305 _Scene->getParticleSystemManager().removeSystemModel(_ModelHandle);
306 if (_ParticleSystem->getAnimType() == CParticleSystem::AnimAlways)
308 if (_AnimatedModelHandle.Valid)
310 _Scene->getParticleSystemManager().removePermanentlyAnimatedSystem(_AnimatedModelHandle);
314 _ParticleSystem = NULL; // one less ref with the smart ptr
315 #ifdef PS_FAST_ALLOC
316 CParticleSystemShape *shape = NLMISC::safe_cast<CParticleSystemShape *>((IShape *) Shape);
317 if (shape->isShared())
319 if (numRefs == 1)
321 // release allocator in the shape
322 shape->Allocator.release();
325 #endif
328 ///=====================================================================================
329 void CParticleSystemModel::refreshRscDeletion(const std::vector<CPlane> &worldFrustumPyramid, const NLMISC::CVector &viewerPos)
331 MINI_TIMER(PSStatsRefreshRscDeletion)
332 if (_EditionMode) return;
333 /** Here we test whether the system has not gone out of scope.
334 * Why do we test this here addtionnaly to the clip traversal ?
335 * Simply because the clip traversal is not called if the cluster it is inserted in is not parsed.
336 * This is not good, because we want to keep few CParticleSystem instance.
337 * This method solve that problem. This is called by the particle system manager when each scene has rendered
341 nlassert(_ParticleSystem);
342 CParticleSystemShape *shape = NLMISC::safe_cast<CParticleSystemShape *>((IShape *) Shape);
344 /* NLMISC::CVector sysPos = getTransformMode() == DirectMatrix ?
345 getMatrix().getPos() :
346 getPos(); */
348 NLMISC::CVector sysPos = getWorldMatrix().getPos();
350 NLMISC::CVector v = sysPos - viewerPos;
351 /// test if not too far
352 const float dist2 = v * v;
354 if (dist2 > shape->_MaxViewDist * shape->_MaxViewDist) // too far ?
356 releasePSPointer();
357 if (shape->_DestroyModelWhenOutOfRange)
359 _Invalidated = true;
361 return;
364 /// frustum test
365 if (shape->_DestroyWhenOutOfFrustum)
367 if (checkAgainstPyramid(worldFrustumPyramid) == false)
369 if (shape->_DestroyModelWhenOutOfRange)
371 _Invalidated = true;
373 releasePSPointer();
374 return;
378 return;
381 ///=====================================================================================
382 void CParticleSystemModel::releaseRsc()
384 MINI_TIMER(PSStatsReleaseRsc)
385 if (!_ParticleSystem) return;
386 releasePSPointer();
389 ///=====================================================================================
390 void CParticleSystemModel::releaseRscAndInvalidate()
392 MINI_TIMER(PSStatsReleaseRscAndInvalidate)
393 if (!_ParticleSystem) return;
394 releasePSPointer();
395 _Invalidated = true;
397 static std::vector<IPSModelObserver *> copyVect;
398 copyVect.resize(_Observers.size());
399 std::copy(_Observers.begin(), _Observers.end(), copyVect.begin());
401 for (std::vector<IPSModelObserver *>::iterator it = copyVect.begin(); it != copyVect.end(); ++it)
403 (*it)->invalidPS(this); // if this crash, then you forgot to call removePSModelObserver !
405 #ifdef PS_FAST_ALLOC
406 CParticleSystemShape *shape = NLMISC::safe_cast<CParticleSystemShape *>((IShape *) Shape);
407 if (!shape->isShared())
409 _Allocator.release();
411 // else ..
412 // if system if shared, the allocator is placed in the shape, so no-op there
413 #endif
416 ///=====================================================================================
417 IAnimatedValue* CParticleSystemModel::getValue (uint valueId)
419 nlassert(valueId < AnimValueLast);
420 if (valueId < OwnerBit) return CTransformShape::getValue(valueId);
421 if (valueId < PSTrigger)
424 return &_UserParam[valueId - (uint) PSParam0];
426 return &_TriggerAnimatedValue;
429 ///=====================================================================================
430 const char *CParticleSystemModel::getPSParamName (uint valueId)
432 nlassert(valueId < AnimValueLast);
433 const char *name[] = { "PSParam0", "PSParam1", "PSParam2", "PSParam3" };
434 return name[valueId - (uint) PSParam0];
437 ///=====================================================================================
438 const char *CParticleSystemModel::getValueName (uint valueId) const
440 nlassert(valueId < AnimValueLast);
441 if (valueId < OwnerBit) return CTransformShape::getValueName(valueId);
442 if (valueId < PSTrigger) return getPSParamName(valueId);
443 return "PSTrigger";
446 ///=====================================================================================
447 ITrack* CParticleSystemModel::getDefaultTrack (uint valueId)
449 nlassert(valueId < AnimValueLast);
450 nlassert(Shape);
452 CParticleSystemShape *pss = NLMISC::safe_cast<CParticleSystemShape *>((IShape *) Shape);
454 switch (valueId)
456 case PosValue: return pss->getDefaultPos();
457 case RotQuatValue: return pss->getDefaultRotQuat();
458 case ScaleValue: return pss->getDefaultScale();
460 if (valueId < OwnerBit) return CTransformShape::getDefaultTrack(valueId); // delegate to parent
462 // this value belong to us
463 if (valueId < PSTrigger)
465 return pss->getUserParamDefaultTrack(valueId - (uint) PSParam0);
467 return pss->getDefaultTriggerTrack();
470 ///=====================================================================================
471 void CParticleSystemModel::registerToChannelMixer(CChannelMixer *chanMixer, const std::string &prefix /* =std::string() */)
473 CTransformShape::registerToChannelMixer(chanMixer, prefix);
474 addValue(chanMixer, PSParam0, OwnerBit, prefix, true);
475 addValue(chanMixer, PSParam1, OwnerBit, prefix, true);
476 addValue(chanMixer, PSParam2, OwnerBit, prefix, true);
477 addValue(chanMixer, PSParam3, OwnerBit, prefix, true);
478 addValue(chanMixer, PSTrigger, OwnerBit, prefix, true);
482 ///=====================================================================================
483 float CParticleSystemModel::getNumTriangles (float distance)
485 MINI_TIMER(PSStatsGetNumTriangles)
486 if (!_ParticleSystem) return 0;
487 if (!_InsertedInVisibleList) return 0;
488 return (float) _ParticleSystem->getWantedNumTris(distance);
491 ///=========================================================================================
492 bool CParticleSystemModel::checkAgainstPyramid(const std::vector<CPlane> &pyramid) const
494 MINI_TIMER(PSStatsCheckAgainstPyramid)
495 nlassert(_ParticleSystem);
496 NLMISC::CAABBox bbox;
497 _ParticleSystem->computeBBox(bbox);
498 const CMatrix &mat = getWorldMatrix();
500 // Transform the pyramid in Object space.
501 for(sint i=0; i < (sint) pyramid.size(); i++)
503 // test whether the bbox is entirely in the neg side of the plane
504 if (!bbox.clipBack(pyramid[i] * mat ))
506 return false;
509 return true;
513 //////////////////////////////////////////////
514 // CParticleSystem AnimDetail implementation //
515 //////////////////////////////////////////////
517 ///=====================================================================================
518 void CParticleSystemModel::traverseAnimDetail()
520 MINI_TIMER(PSStatsTraverseAnimDetail)
521 CTransformShape::traverseAnimDetail();
522 CParticleSystem *ps = getPS();
523 if (!_WorldVis) return;
524 if (_Invalidated) return;
525 if (getVisibility() == CHrcTrav::Hide) return;
528 MINI_TIMER(PSStatsTraverseAnimDetailPart1)
529 if (!_EditionMode && !_InClusterAndVisible)
531 CParticleSystemShape *pss = NLMISC::safe_cast<CParticleSystemShape *>((IShape *)Shape);
532 if (pss->_DestroyWhenOutOfFrustum)
534 if (pss->_DestroyModelWhenOutOfRange)
536 releaseRscAndInvalidate();
538 else // remove rsc but do not invalidate the system
540 releaseRsc();
542 return;
544 if (!ps) return;
547 // check for trigger. If the trigger is false, and there is a system instanciated, we delete it.
548 if (!_EditionMode)
550 if (!_TriggerAnimatedValue.Value)
552 // system is off, or hasn't been instanciated now...
553 if (ps)
555 releaseRsc();
557 return;
563 MINI_TIMER(PSStatsTraverseAnimDetailPart2)
565 // the system or its center is in the view frustum, but it may not have been instanciated from its shape now
566 if (!ps)
568 nlassert(_Scene);
569 nlassert(Shape);
570 reallocRsc();
571 ps = _ParticleSystem;
575 CClipTrav &clipTrav= getOwnerScene()->getClipTrav();
578 MINI_TIMER(PSStatsTraverseAnimDetailPart3)
579 if (_InClusterAndVisible || ps->getAnimType() == CParticleSystem::AnimInCluster)
581 bool animate = true;
582 if (ps->isSharingEnabled()) /// with shared system, we only animate one version!
584 if (ps->_LastUpdateDate == clipTrav.CurrentDate)
586 animate = false;
588 else
590 ps->_LastUpdateDate = clipTrav.CurrentDate;
593 else
595 ps->_LastUpdateDate = clipTrav.CurrentDate;
597 ps->_LastUpdateDate = clipTrav.CurrentDate;
598 if (animate)
600 if (ps->getAnimType() != CParticleSystem::AnimAlways) // if the animation is always perfomred,
601 // then animation is done by the particle system manager
602 // just before the render trav
604 doAnimate();
611 MINI_TIMER(PSStatsTraverseAnimDetailPart4)
612 // add a render model if in cluster & not hidden
613 if (_InClusterAndVisible)
615 getOwnerScene()->getRenderTrav().addRenderModel(this);
620 ///=====================================================================================
621 void CParticleSystemModel::doAnimate()
623 ++ PSStatsNumDoAnimateCalls;
624 MINI_TIMER(PSStatsDoAnimate)
625 nlassert(!_Invalidated);
626 CParticleSystem *ps = getPS();
627 CClipTrav &clipTrav= getOwnerScene()->getClipTrav();
628 const CMatrix &mat= getWorldMatrix();
631 MINI_TIMER(PSStatsDoAnimatePart1)
633 // Set the 'hide' flag. This prevent trails from being created is the system is hidden, moved, and then showed in the next frame.
634 ps->hide(!this->isHrcVisible());
635 ps->setSysMat(&mat);
636 ps->setUserMatrix(&_UserMatrix);
637 ps->setViewMat(clipTrav.ViewMatrix);
638 updateOpacityInfos();
639 updateLightingInfos();
641 //ps->setSysMat(getWorldMatrix());
642 nlassert(ps->getScene());
646 MINI_TIMER(PSStatsDoAnimatePart2)
648 // setup the number of faces we allow
649 ps->setNumTris((uint) getNumTrianglesAfterLoadBalancing());
652 // set the global user param that are bypassed
653 nlctassert(MaxPSUserParam < 8); // there should be less than 8 parameters because of mask stored in a byte
654 ps->_BypassGlobalUserParam = _BypassGlobalUserParam;
656 // setup system user parameters for parameters that have been touched
657 for (uint k = 0; k < MaxPSUserParam; ++k)
659 if (isTouched((uint)CParticleSystemModel::PSParam0 + k))
661 ps->setUserParam(k, _UserParam[k].Value);
662 clearFlag((uint)CParticleSystemModel::PSParam0 + k);
665 if (isAutoGetEllapsedTimeEnabled())
667 setEllapsedTime(ps->getScene()->getEllapsedTime() * getEllapsedTimeRatio());
671 MINI_TIMER(PSStatsDoAnimatePart3)
672 TAnimationTime delay = getEllapsedTime();
673 // animate particles
674 CParticleSystemShape *pss= NLMISC::safe_cast<CParticleSystemShape *>((IShape *)Shape);
675 if (_EditionMode)
677 pss->_ProcessOrder.clear(); // force to eval each frame because ps could be modified
679 ps->step(CParticleSystem::Anim, delay, *pss, *this);
684 //////////////////////////////////////////////
685 // CParticleSystem Render implementation //
686 //////////////////////////////////////////////
687 void CParticleSystemModel::traverseRender()
689 MINI_TIMER(PSStatsTraverseRender)
691 if (!_OutOfFrustum)
693 if (_ParticleSystem)
695 if (CTransform::isLightable())
697 // affect global lighting color
698 const CLightContribution &lc = getLightContribution();
699 NLMISC::CRGBA lighting(0, 0, 0, 255);
700 for(uint k = 0; k < NL3D_MAX_LIGHT_CONTRIBUTION; ++k)
702 if (lc.PointLight[k] == NULL) break;
703 NLMISC::CRGBA currLightContrib;
704 currLightContrib.modulateFromui(lc.PointLight[k]->getDiffuse(), lc.AttFactor[k]);
705 lighting.add(lighting, currLightContrib);
707 // add local ambient
708 //lighting.add(lighting, lc.LocalAmbient);
709 //lighting.add(lighting,lc.MergedPointLight);
710 // add sun diffuse
711 nlassert(_Scene);
712 NLMISC::CRGBA sunDiffuse;
713 sunDiffuse.modulateFromui(_Scene->getSunDiffuse(), lc.SunContribution);
714 lighting.add(lighting, sunDiffuse);
715 NLMISC::CRGBA sunAmbient;
716 sunAmbient.modulateFromui(_Scene->getSunAmbient(), lc.SunContribution);
717 lighting.add(lighting, sunAmbient);
718 _ParticleSystem->setLightingColor(lighting);
720 CTransformShape::traverseRender();
727 * CParticleSystem Clip implementation
728 * IMPORTANT : the _Visible attribute is interpreted as 'in traversed clusters'. We need this because we want
729 * to know when a p.s is in clusters, but not visible. As a matter of fact we may need to have system that are animated
730 * as long as in cluster, but not visible.
733 void CParticleSystemModel::traverseClip()
735 MINI_TIMER(PSStatsTraverseClip)
736 // disable H_AUTO, because slowdown when lot of models (eg 1000-2000 tested in forest)
737 //H_AUTO ( NL3D_Particles_Clip );
739 // CTransformShape::traverseClip();
740 // Traverse the Clip sons.
741 uint numClipChildren= clipGetNumChildren();
742 for(uint i=0;i<numClipChildren;i++)
743 clipGetChild(i)->traverseClip();
745 if (!_WorldVis) return;
746 if (_Invalidated) return;
747 CClipTrav &clipTrav= getOwnerScene()->getClipTrav();
750 if (_ClipDate != clipTrav.CurrentDate)
752 _InsertedInVisibleList = false;
753 _InClusterAndVisible = false;
754 _ClipDate = clipTrav.CurrentDate;
756 if (_InClusterAndVisible) return; // already visible
759 CParticleSystem *ps = _ParticleSystem;
762 if (ps) // system instanciated
764 MINI_TIMER(PSStatsClipSystemInstanciated)
765 // if there are no more particles, no need to even clip..
766 if (checkDestroyCondition(ps)) return;
767 // check for anim mode change
768 if (_AnimType != ps->getAnimType())
770 CParticleSystemManager &psmgt = _Scene->getParticleSystemManager();
771 if (_AnimType == CParticleSystem::AnimAlways) // was previously always animated ?
773 if (_AnimatedModelHandle.Valid)
775 psmgt.removePermanentlyAnimatedSystem(_AnimatedModelHandle);
778 _AnimType = ps->getAnimType();
779 if (_AnimType == CParticleSystem::AnimAlways)
781 _AnimatedModelHandle = psmgt.addPermanentlyAnimatedSystem(this);
786 // check whether display filtered or not
787 if( !(_Scene->getFilterRenderFlags() & _RenderFilterType) )
789 _Visible = false;
790 return;
793 // special case : system sticked to a skeleton
794 if( _AncestorSkeletonModel!=NULL )
796 bool visible = _AncestorSkeletonModel->isClipVisible();
797 // Special test: if we are sticked to a skeletonModel, and if we are still visible, maybe we don't have to
798 if(_Visible && _FatherSkeletonModel)
800 // if our skeletonModel father is displayed with a Lod, maybe we are not to be displayed
801 if(_FatherSkeletonModel->isDisplayedAsLodCharacter())
803 // We are visible only if we where sticked to the skeleton with forceCLod==true.
804 // This is also true if we are actually a skeletonModel
805 if(!getShowWhenLODSticked())
806 // otherWise we are not visible. eg: this is the case of skins and some sticked object
807 visible = false;
811 if (visible)
814 MINI_TIMER(PSStatsInsertInVisibleList)
815 insertInVisibleList();
817 _InClusterAndVisible = true;
818 return;
820 else // not visible, may need animation however..
822 if (!ps) // no resc allocated
824 CParticleSystemShape *pss= NLMISC::safe_cast<CParticleSystemShape *>((IShape *)Shape);
825 nlassert(pss);
826 // invalidate the system if too far
827 const CVector pos = _AncestorSkeletonModel->getWorldMatrix().getPos();
828 const CVector d = pos - clipTrav.CamPos;
829 if (d * d > pss->_MaxViewDist * pss->_MaxViewDist)
831 _Visible = false;
832 if (pss->_DestroyModelWhenOutOfRange)
834 _Invalidated = true;
838 else
840 // NB : The test to see whether the system is not too far is performed by the particle system manager
841 if (!_EditionMode)
844 MINI_TIMER(PSStatsInsertInVisibleList)
845 insertInVisibleList();
850 return;
855 const std::vector<CPlane> &pyramid= clipTrav.WorldPyramid;
856 /** traverse the sons
857 * we must do this before us, because this object may delete himself from the scene
860 // now the pyramid is directly expressed in the world
861 const CMatrix &mat= getWorldMatrix();
864 // Transform the pyramid in Object space.
867 if(!ps) ///====================== system resource not allocated, test if it entered the scope
869 MINI_TIMER(PSStatsClipSystemNotInstanciated)
870 CParticleSystemShape *pss= NLMISC::safe_cast<CParticleSystemShape *>((IShape *)Shape);
871 nlassert(pss);
873 // the system wasn't present the last time, we use its center to see if it's back in the view frustum,
874 // or if it is near enough.
875 // if this is the case, we say it isn't clipped, so it will be reinstanciated from the shape
876 // during the DetailAnimTraversal
878 const CVector pos = getWorldMatrix().getPos();
880 const CVector d = pos - clipTrav.CamPos;
883 // check whether system not too far
884 if (d * d > pss->_MaxViewDist * pss->_MaxViewDist)
886 _Visible = false;
887 if (pss->_DestroyModelWhenOutOfRange)
889 _Invalidated = true;
891 return;
894 // test the shape to see whether we have a precomputed bbox
895 if (!pss->_UsePrecomputedBBox)
897 ///============================= the system has no precomputed bbox
898 /// frustum test
899 for(sint i=0; i < (sint)pyramid.size(); i++)
901 if ( (pyramid[i] * mat ).d > 0.0f ) // in its basis, the system is at the center
905 MINI_TIMER(PSStatsInsertInVisibleList)
906 insertInVisibleList();
908 return;
912 MINI_TIMER(PSStatsInsertInVisibleList)
913 insertInVisibleList();
915 _InClusterAndVisible = true;
916 return;
918 else
920 ///============================= the system has a precomputed bbox
921 /// frustum test
922 for(sint i=0; i < (sint)pyramid.size(); i++)
924 if ( !pss->_PrecomputedBBox.clipBack(pyramid[i] * mat ) )
927 MINI_TIMER(PSStatsInsertInVisibleList)
928 insertInVisibleList();
930 return;
934 MINI_TIMER(PSStatsInsertInVisibleList)
935 insertInVisibleList();
937 _InClusterAndVisible = true;
938 return;
943 //=========================================================================================================
944 // the system is already instanciated
946 nlassert(ps);
947 /// Pyramid test. IMPORTANT : The test to see whether the system is not too far is performed by the particle system manager
948 // In edition mode, it isn't done by the manager (system never removed), so we do it here in this case
949 if (_EditionMode)
951 CParticleSystemShape *pss= NLMISC::safe_cast<CParticleSystemShape *>((IShape *)Shape);
952 nlassert(pss);
953 const CVector pos = getWorldMatrix().getPos();
954 const CVector d = pos - clipTrav.CamPos;
955 // check whether system not too far
956 if (d * d > ps->getMaxViewDist() * ps->getMaxViewDist())
958 return; // not visible
961 if (checkAgainstPyramid(pyramid) == false)
963 MINI_TIMER(PSStatsClipSystemCheckAgainstPyramid)
964 if (!_EditionMode)
966 // system near, but maybe not in cluster..
968 MINI_TIMER(PSStatsInsertInVisibleList)
969 insertInVisibleList();
972 return;
977 MINI_TIMER(PSStatsInsertInVisibleList)
978 insertInVisibleList();
981 _InClusterAndVisible = true;
985 //===================================================================
986 bool CParticleSystemModel::clip()
988 // no-op clip() because all done in special traverse()
989 return true;
993 //===================================================================
994 bool CParticleSystemModel::checkDestroyCondition(CParticleSystem *ps)
996 MINI_TIMER(PSStatsCheckDestroyCondition)
997 nlassert(ps);
998 if (!_EditionMode)
1000 /** NB : we don't do this test here for always animated system, as it is done
1001 * by the CParticleSystemManager, because this code is not sure to be executed if the system has been clipped by a cluster
1003 if (ps->getAnimType() != CParticleSystem::AnimAlways)
1005 if (ps->isDestroyConditionVerified())
1007 releaseRscAndInvalidate();
1008 return true;
1012 return false;
1015 //===================================================================
1016 void CParticleSystemModel::bypassGlobalUserParamValue(uint userParamIndex,bool byPass /*=true*/)
1018 nlctassert(MaxPSUserParam < 8); // there should be less than 8 parameters because of mask stored in a byte
1019 nlassert(userParamIndex < MaxPSUserParam);
1020 if (byPass) _BypassGlobalUserParam |= (1 << userParamIndex);
1021 else _BypassGlobalUserParam &= ~(1 << userParamIndex);
1024 //===================================================================
1025 bool CParticleSystemModel::isGlobalUserParamValueBypassed(uint userParamIndex) const
1027 nlctassert(MaxPSUserParam < 8); // there should be less than 8 parameters because of mask stored in a byte
1028 nlassert(userParamIndex < MaxPSUserParam);
1029 return (_BypassGlobalUserParam & (1 << userParamIndex)) != 0;
1032 //===================================================================
1033 void CParticleSystemModel::enableDisplayTools(bool enable /*=true*/)
1035 _ToolDisplayEnabled = enable;
1036 touchTransparencyState();
1037 touchLightableState();
1040 //===================================================================
1041 void CParticleSystemModel::invalidateAutoAnimatedHandle()
1043 _AnimatedModelHandle.Valid = false;
1046 //===================================================================
1047 void CParticleSystemModel::activateEmitters(bool active)
1049 if (active == _EmitterActive) return;
1050 _EmitterActive = active;
1051 if (_ParticleSystem) _ParticleSystem->activateEmitters(active);
1054 //===================================================================
1055 bool CParticleSystemModel::hasActiveEmitters() const
1057 #ifdef NL_DEBUG
1058 if (_ParticleSystem)
1060 if (_ParticleSystem->hasEmittersTemplates())
1062 nlassert(_ParticleSystem->hasActiveEmitters() == _EmitterActive);
1065 #endif
1066 return _EmitterActive;
1069 //===================================================================
1070 void CParticleSystemModel::setUserColor(NLMISC::CRGBA userColor)
1072 if (_ParticleSystem) _ParticleSystem->setUserColor(userColor);
1073 _UserColor = userColor;
1076 //===================================================================
1077 void CParticleSystemModel::forceInstanciate()
1079 MINI_TIMER(PSStatsForceInstanciate)
1080 if (_Invalidated) return;
1081 if (_ParticleSystem) return;
1082 reallocRsc();
1085 //===================================================================
1086 void CParticleSystemModel::setZBias(float value)
1088 if (value == _ZBias) return;
1089 _ZBias = value;
1090 if (_ParticleSystem) _ParticleSystem->setZBias(_ZBias);
1093 //===================================================================
1094 void CParticleSystemModel::forceSetUserMatrix(const NLMISC::CMatrix &userMatrix)
1096 _UserMatrix = userMatrix;
1097 if (getPS())
1099 getPS()->setUserMatrix(&_UserMatrix);
1103 //===================================================================
1104 void CParticleSystemModel::stopSound()
1106 if (!_SoundActive) return;
1107 if (_ParticleSystem) _ParticleSystem->stopSound();
1108 _SoundActive = false;
1111 //===================================================================
1112 void CParticleSystemModel::reactivateSound()
1114 if (_SoundActive) return;
1115 if (_ParticleSystem) _ParticleSystem->reactivateSound();
1116 _SoundActive = true;
1119 //===================================================================
1120 void CParticleSystemModel::update()
1122 CTransformShape::update();
1123 if (_LocalVis != _LastVisibility)
1125 if (_ParticleSystem)
1127 _ParticleSystem->onShow(_LocalVis == CHrcTrav::Show);
1129 _LastVisibility = _LocalVis;
1134 } // NL3D