1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/3d/particle_system.h"
23 #include "nel/3d/particle_system_shape.h"
24 #include "nel/3d/particle_system_model.h"
25 #include "nel/3d/scene.h"
26 #include "nel/3d/driver.h"
27 #include "nel/3d/skeleton_model.h"
28 #include "nel/3d/texture_file.h"
29 #include "nel/3d/ps_allocator.h"
30 #include "nel/misc/file.h"
31 #include "nel/misc/mem_stream.h"
32 #include "nel/misc/hierarchical_timer.h"
33 #include "nel/misc/contiguous_block_allocator.h"
36 #include "nel/3d/ps_face_look_at.h"
37 #include "nel/3d/ps_force.h"
51 NLMISC::CMutex s_PSSMutex
;
53 } /* anonymous namespace */
55 // private usage : macro to check the memory integrity
56 #if defined(NL_DEBUG) && defined(NL_OS_WINDOWS)
57 // #define PARTICLES_CHECK_MEM NLMEMORY::CheckHeap(true);
58 #define PARTICLES_CHECK_MEM
60 #define PARTICLES_CHECK_MEM
63 // ***************************************************************************
64 // A singleton to define the TextureCategory of Particle system
65 class CPSTextureCategory
68 static NLMISC::CSmartPtr
<ITexture::CTextureCategory
> &get()
71 _Instance
= new CPSTextureCategory();
72 return _Instance
->_TextureCategory
;
75 static void releaseInstance()
83 NLMISC::CSmartPtr
<ITexture::CTextureCategory
> _TextureCategory
;
86 _TextureCategory
= new ITexture::CTextureCategory("PARTICLE SYSTEM");
88 static CPSTextureCategory
*_Instance
;
90 CPSTextureCategory
*CPSTextureCategory::_Instance
= NULL
;
92 ///===========================================================================
93 void CParticleSystemShape::releaseInstance()
95 CPSTextureCategory::releaseInstance();
98 ///===========================================================================
99 CParticleSystemShape::CParticleSystemShape() : _MaxViewDist(100.f
),
100 _DestroyWhenOutOfFrustum(false),
101 _DestroyModelWhenOutOfRange(false),
102 _UsePrecomputedBBox(false),
106 /* ***********************************************
107 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
108 * It can be loaded/called through CAsyncFileManager for instance
109 * ***********************************************/
111 for (uint k
= 0; k
< 4; ++k
)
113 _UserParamDefaultTrack
[k
].setDefaultValue(0);
115 _DefaultPos
.setDefaultValue(CVector::Null
);
116 _DefaultScale
.setDefaultValue( CVector(1, 1, 1) );
117 _DefaultRotQuat
.setDefaultValue(CQuat());
118 _DefaultTriggerTrack
.setDefaultValue(true); // by default, system start as soon as they are instanciated
122 ///===========================================================================
123 void CParticleSystemShape::serial(NLMISC::IStream
&f
)
125 /* ***********************************************
126 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
127 * It can be loaded/called through CAsyncFileManager for instance
128 * ***********************************************/
130 sint ver
= f
.serialVersion(6);
131 /// version 6 : added sharing flag
132 //NLMISC::CVector8 &buf = _ParticleSystemProto.bufferAsVector();
137 std::vector
<uint8
> buf
;
139 _ParticleSystemProto
.fill(&buf
[0], (uint32
)buf
.size());
143 f
.serialBufferWithSize ((uint8
*)_ParticleSystemProto
.buffer(), _ParticleSystemProto
.length());
148 // serial default tracks
149 for (uint k
= 0; k
< 4; ++k
)
151 f
.serial(_UserParamDefaultTrack
[k
]);
156 f
.serial (_DefaultPos
);
157 f
.serial (_DefaultScale
);
158 f
.serial (_DefaultRotQuat
);
162 f
.serial(_MaxViewDist
);
163 f
.serial(_DestroyWhenOutOfFrustum
);
164 f
.serial(_DestroyModelWhenOutOfRange
);
168 f
.serial(_UsePrecomputedBBox
);
169 if (_UsePrecomputedBBox
)
171 f
.serial(_PrecomputedBBox
);
180 ///===========================================================================
181 void CParticleSystemShape::buildFromPS(const CParticleSystem
&ps
)
183 // must be sure that we are writing in the stream
184 if (_ParticleSystemProto
.isReading())
186 _ParticleSystemProto
.invert();
189 // to have const correctness in the prototype, we must do this...
190 CParticleSystem
*myPs
= const_cast<CParticleSystem
*>(&ps
);
192 // build the prototype
193 _ParticleSystemProto
.serialPtr(myPs
);
195 // mirror some system values
196 _MaxViewDist
= myPs
->getMaxViewDist();
197 _DestroyWhenOutOfFrustum
= myPs
->doesDestroyWhenOutOfFrustum();
198 _DestroyModelWhenOutOfRange
= myPs
->getDestroyModelWhenOutOfRange();
199 if (!myPs
->getAutoComputeBBox())
201 _UsePrecomputedBBox
= true;
202 myPs
->computeBBox(_PrecomputedBBox
);
206 _UsePrecomputedBBox
= false;
208 _Sharing
= myPs
->isSharingEnabled();
211 ///===========================================================================
212 void CParticleSystemShape::getAABBox(NLMISC::CAABBox
&bbox
) const
214 if (!_UsePrecomputedBBox
)
216 bbox
.setCenter(NLMISC::CVector::Null
);
217 bbox
.setHalfSize(NLMISC::CVector(1, 1, 1));
221 bbox
= _PrecomputedBBox
;
226 ///===========================================================================
227 CParticleSystem
*CParticleSystemShape::instanciatePS(CScene
&scene
, NLMISC::CContiguousBlockAllocator
*blockAllocator
/*= NULL*/)
229 if (_Sharing
&& _SharedSystem
!= NULL
) // is sharing enabled, and is a system already instanciated
231 return _SharedSystem
;
234 // avoid prb with concurrent thread (may happen if an instance group containing ps is loaded in background)
239 nlassert(PSBlockAllocator
== NULL
);
242 // set new allocator for particle system memory
243 PSBlockAllocator
= blockAllocator
;
244 blockAllocator
->init(_NumBytesWanted
); // if size wanted is already known, set it
248 //NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
250 CParticleSystem
*myInstance
= NULL
;
252 // serialize from the memory stream
253 if (!_ParticleSystemProto
.isReading()) // we must be sure that we are reading the stream
255 _ParticleSystemProto
.invert();
258 _ParticleSystemProto
.resetPtrTable();
259 _ParticleSystemProto
.seek(0, NLMISC::IStream::begin
);
261 // NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
262 _ParticleSystemProto
.serialPtr(myInstance
); // instanciate the system
263 /* NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
264 nlinfo("instanciation time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start)));
267 myInstance
->setScene(&scene
);
269 if (_CachedTex
.empty() && scene
.getDriver())
271 //nlinfo("flushing texs");
272 // load && cache textures
273 myInstance
->enumTexs(_CachedTex
, *scene
.getDriver());
274 for(uint k
= 0; k
< _CachedTex
.size(); ++k
)
278 _CachedTex
[k
]->setTextureCategory(CPSTextureCategory::get());
279 scene
.getDriver()->setupTexture (*(ITexture
*)_CachedTex
[k
]);
286 for(uint k = 0; k < _CachedTex.size(); ++k)
288 nlinfo(_CachedTex[k]->getShareName().c_str());
297 _SharedSystem
= myInstance
; // set this as the first shared instance
303 _NumBytesWanted
= blockAllocator
->getNumAllocatedBytes(); // now we know the number of wanted bytes, subsequent alloc can be much faster
304 PSBlockAllocator
= NULL
;
310 /*NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
311 nlinfo("instanciation time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start))); */
315 ///===========================================================================
316 CTransformShape
*CParticleSystemShape::createInstance(CScene
&scene
)
318 CParticleSystemModel
*psm
= NLMISC::safe_cast
<CParticleSystemModel
*>(scene
.createModel(NL3D::ParticleSystemModelId
) );
320 psm
->_Scene
= &scene
; // the model needs the scene to recreate the particle system he holds
321 // by default, we don't instanciate the system. It will be instanciated only if visible and triggered
322 // psm->_ParticleSystem = instanciatePS(scene);
324 // Setup position with the default value
325 psm
->ITransformable::setPos( _DefaultPos
.getDefaultValue() );
326 psm
->ITransformable::setRotQuat( _DefaultRotQuat
.getDefaultValue() );
327 psm
->ITransformable::setScale( _DefaultScale
.getDefaultValue() );
329 // ParticleSystems are added to the "Fx" Load Balancing Group.
330 psm
->setLoadBalancingGroup("Fx");
335 ///===========================================================================
336 void CParticleSystemShape::render(IDriver
*drv
, CTransformShape
*trans
, bool passOpaque
)
338 H_AUTO ( NL3D_Particles_Render
);
340 CParticleSystemModel
*psm
= NLMISC::safe_cast
<CParticleSystemModel
*>(trans
);
341 if (psm
->_Invalidated
) return;
342 CParticleSystem
*ps
= psm
->getPS();
343 /// has the system been triggered yet ?
346 TAnimationTime delay
= psm
->getEllapsedTime();
347 nlassert(ps
->getScene());
349 ///////////////////////
350 // render particles //
351 ///////////////////////
353 /// if sharing is enabled, we should resetup the system matrix
354 if (ps
->isSharingEnabled())
356 ps
->setSysMat(&(psm
->getWorldMatrix()));
357 ps
->setUserMatrix(&(psm
->getUserMatrix()));
361 /// drv->setupModelMatrix(trans->getWorldMatrix());
368 PSLookAtRenderTime
= 0;
369 //NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
370 ps
->step(CParticleSystem::SolidRender
, delay
, *this, *psm
);
371 /*NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
372 nlinfo("Solid render time time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start)));
373 nlinfo("LookAt Render time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(PSLookAtRenderTime))); */
377 //PSLookAtRenderTime = 0;
378 //NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
379 ps
->step(CParticleSystem::BlendRender
, delay
, *this, *psm
);
380 /*NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
381 nlinfo("Blend render time time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start)));
382 nlinfo("LookAt Render time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(PSLookAtRenderTime))); */
385 if (psm
->isToolDisplayEnabled())
387 ps
->step(CParticleSystem::ToolRender
, delay
, *this, *psm
);
392 ///===========================================================================
393 void CParticleSystemShape::flushTextures(IDriver
&driver
, uint selectedTexture
)
395 // if textures are already flushed, no-op
396 if (!_CachedTex
.empty()) return;
399 _SharedSystem
->enumTexs(_CachedTex
, driver
);
405 // must create an instance just to flush the textures
406 CParticleSystem
*myInstance
= NULL
;
409 nlassert(PSBlockAllocator
== NULL
);
410 NLMISC::CContiguousBlockAllocator blockAllocator
;
411 PSBlockAllocator
= &blockAllocator
;
412 blockAllocator
.init(300000); // we release memory just after, and we don't want to fragment the memory, so provide large enough mem
414 // serialize from the memory stream
415 if (!_ParticleSystemProto
.isReading()) // we must be sure that we are reading the stream
417 _ParticleSystemProto
.invert();
419 _ParticleSystemProto
.resetPtrTable();
420 _ParticleSystemProto
.seek(0, NLMISC::IStream::begin
);
421 _ParticleSystemProto
.serialPtr(myInstance
); // instanciate the system
423 _NumBytesWanted
= blockAllocator
.getNumAllocatedBytes(); // next allocation will be fast because we know how much memory to allocate
425 myInstance
->enumTexs(_CachedTex
, driver
);
429 for(uint k = 0; k < myInstance->getNbProcess(); ++k)
431 CPSLocated *loc = (CPSLocated *) myInstance->getProcess(k);
432 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
434 if (dynamic_cast<CPSCentralGravity *>(loc->getBoundObject(l)))
436 nlwarning("PS %s uses central gravity", myInstance->getName().c_str());
442 // sort the process inside the fx
443 myInstance
->getSortingByEmitterPrecedence(_ProcessOrder
);
446 PSBlockAllocator
= NULL
;
450 for(uint k
= 0; k
< _CachedTex
.size(); ++k
)
452 //nlinfo(_CachedTex[k]->getShareName().c_str());
455 _CachedTex
[k
]->setTextureCategory(CPSTextureCategory::get());
456 driver
.setupTexture(*_CachedTex
[k
]);