1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4
5 #include "CParticleSystemSceneNode.h"
6 #include "os.h"
7 #include "ISceneManager.h"
8 #include "ICameraSceneNode.h"
9 #include "IVideoDriver.h"
10
11 #include "CParticleAnimatedMeshSceneNodeEmitter.h"
12 #include "CParticleBoxEmitter.h"
13 #include "CParticleCylinderEmitter.h"
14 #include "CParticleMeshEmitter.h"
15 #include "CParticlePointEmitter.h"
16 #include "CParticleRingEmitter.h"
17 #include "CParticleSphereEmitter.h"
18 #include "CParticleAttractionAffector.h"
19 #include "CParticleFadeOutAffector.h"
20 #include "CParticleGravityAffector.h"
21 #include "CParticleRotationAffector.h"
22 #include "CParticleScaleAffector.h"
23 #include "SViewFrustum.h"
24
25 namespace irr
26 {
27 namespace scene
28 {
29
30 //! constructor
CParticleSystemSceneNode(bool createDefaultEmitter,ISceneNode * parent,ISceneManager * mgr,s32 id,const core::vector3df & position,const core::vector3df & rotation,const core::vector3df & scale)31 CParticleSystemSceneNode::CParticleSystemSceneNode(bool createDefaultEmitter,
32 ISceneNode* parent, ISceneManager* mgr, s32 id,
33 const core::vector3df& position, const core::vector3df& rotation,
34 const core::vector3df& scale)
35 : IParticleSystemSceneNode(parent, mgr, id, position, rotation, scale),
36 Emitter(0), ParticleSize(core::dimension2d<f32>(5.0f, 5.0f)), LastEmitTime(0),
37 MaxParticles(0xffff), Buffer(0), ParticlesAreGlobal(true)
38 {
39 #ifdef _DEBUG
40 setDebugName("CParticleSystemSceneNode");
41 #endif
42
43 Buffer = new SMeshBuffer();
44 if (createDefaultEmitter)
45 {
46 IParticleEmitter* e = createBoxEmitter();
47 setEmitter(e);
48 e->drop();
49 }
50 }
51
52
53 //! destructor
~CParticleSystemSceneNode()54 CParticleSystemSceneNode::~CParticleSystemSceneNode()
55 {
56 if (Emitter)
57 Emitter->drop();
58 if (Buffer)
59 Buffer->drop();
60
61 removeAllAffectors();
62 }
63
64
65 //! Gets the particle emitter, which creates the particles.
getEmitter()66 IParticleEmitter* CParticleSystemSceneNode::getEmitter()
67 {
68 return Emitter;
69 }
70
71
72 //! Sets the particle emitter, which creates the particles.
setEmitter(IParticleEmitter * emitter)73 void CParticleSystemSceneNode::setEmitter(IParticleEmitter* emitter)
74 {
75 if (emitter == Emitter)
76 return;
77 if (Emitter)
78 Emitter->drop();
79
80 Emitter = emitter;
81
82 if (Emitter)
83 Emitter->grab();
84 }
85
86
87 //! Adds new particle effector to the particle system.
addAffector(IParticleAffector * affector)88 void CParticleSystemSceneNode::addAffector(IParticleAffector* affector)
89 {
90 affector->grab();
91 AffectorList.push_back(affector);
92 }
93
94 //! Get a list of all particle affectors.
getAffectors() const95 const core::list<IParticleAffector*>& CParticleSystemSceneNode::getAffectors() const
96 {
97 return AffectorList;
98 }
99
100 //! Removes all particle affectors in the particle system.
removeAllAffectors()101 void CParticleSystemSceneNode::removeAllAffectors()
102 {
103 core::list<IParticleAffector*>::Iterator it = AffectorList.begin();
104 while (it != AffectorList.end())
105 {
106 (*it)->drop();
107 it = AffectorList.erase(it);
108 }
109 }
110
111
112 //! Returns the material based on the zero based index i.
getMaterial(u32 i)113 video::SMaterial& CParticleSystemSceneNode::getMaterial(u32 i)
114 {
115 return Buffer->Material;
116 }
117
118
119 //! Returns amount of materials used by this scene node.
getMaterialCount() const120 u32 CParticleSystemSceneNode::getMaterialCount() const
121 {
122 return 1;
123 }
124
125
126 //! Creates a particle emitter for an animated mesh scene node
127 IParticleAnimatedMeshSceneNodeEmitter*
createAnimatedMeshSceneNodeEmitter(scene::IAnimatedMeshSceneNode * node,bool useNormalDirection,const core::vector3df & direction,f32 normalDirectionModifier,s32 mbNumber,bool everyMeshVertex,u32 minParticlesPerSecond,u32 maxParticlesPerSecond,const video::SColor & minStartColor,const video::SColor & maxStartColor,u32 lifeTimeMin,u32 lifeTimeMax,s32 maxAngleDegrees,const core::dimension2df & minStartSize,const core::dimension2df & maxStartSize)128 CParticleSystemSceneNode::createAnimatedMeshSceneNodeEmitter(
129 scene::IAnimatedMeshSceneNode* node, bool useNormalDirection,
130 const core::vector3df& direction, f32 normalDirectionModifier,
131 s32 mbNumber, bool everyMeshVertex,
132 u32 minParticlesPerSecond, u32 maxParticlesPerSecond,
133 const video::SColor& minStartColor, const video::SColor& maxStartColor,
134 u32 lifeTimeMin, u32 lifeTimeMax, s32 maxAngleDegrees,
135 const core::dimension2df& minStartSize,
136 const core::dimension2df& maxStartSize )
137 {
138 return new CParticleAnimatedMeshSceneNodeEmitter( node,
139 useNormalDirection, direction, normalDirectionModifier,
140 mbNumber, everyMeshVertex,
141 minParticlesPerSecond, maxParticlesPerSecond,
142 minStartColor, maxStartColor,
143 lifeTimeMin, lifeTimeMax, maxAngleDegrees,
144 minStartSize, maxStartSize );
145 }
146
147
148 //! Creates a box particle emitter.
createBoxEmitter(const core::aabbox3df & box,const core::vector3df & direction,u32 minParticlesPerSecond,u32 maxParticlesPerSecond,const video::SColor & minStartColor,const video::SColor & maxStartColor,u32 lifeTimeMin,u32 lifeTimeMax,s32 maxAngleDegrees,const core::dimension2df & minStartSize,const core::dimension2df & maxStartSize)149 IParticleBoxEmitter* CParticleSystemSceneNode::createBoxEmitter(
150 const core::aabbox3df& box, const core::vector3df& direction,
151 u32 minParticlesPerSecond, u32 maxParticlesPerSecond,
152 const video::SColor& minStartColor, const video::SColor& maxStartColor,
153 u32 lifeTimeMin, u32 lifeTimeMax,
154 s32 maxAngleDegrees, const core::dimension2df& minStartSize,
155 const core::dimension2df& maxStartSize )
156 {
157 return new CParticleBoxEmitter(box, direction, minParticlesPerSecond,
158 maxParticlesPerSecond, minStartColor, maxStartColor,
159 lifeTimeMin, lifeTimeMax, maxAngleDegrees,
160 minStartSize, maxStartSize );
161 }
162
163
164 //! Creates a particle emitter for emitting from a cylinder
createCylinderEmitter(const core::vector3df & center,f32 radius,const core::vector3df & normal,f32 length,bool outlineOnly,const core::vector3df & direction,u32 minParticlesPerSecond,u32 maxParticlesPerSecond,const video::SColor & minStartColor,const video::SColor & maxStartColor,u32 lifeTimeMin,u32 lifeTimeMax,s32 maxAngleDegrees,const core::dimension2df & minStartSize,const core::dimension2df & maxStartSize)165 IParticleCylinderEmitter* CParticleSystemSceneNode::createCylinderEmitter(
166 const core::vector3df& center, f32 radius,
167 const core::vector3df& normal, f32 length,
168 bool outlineOnly, const core::vector3df& direction,
169 u32 minParticlesPerSecond, u32 maxParticlesPerSecond,
170 const video::SColor& minStartColor, const video::SColor& maxStartColor,
171 u32 lifeTimeMin, u32 lifeTimeMax, s32 maxAngleDegrees,
172 const core::dimension2df& minStartSize,
173 const core::dimension2df& maxStartSize )
174 {
175 return new CParticleCylinderEmitter( center, radius, normal, length,
176 outlineOnly, direction,
177 minParticlesPerSecond, maxParticlesPerSecond,
178 minStartColor, maxStartColor,
179 lifeTimeMin, lifeTimeMax, maxAngleDegrees,
180 minStartSize, maxStartSize );
181 }
182
183
184 //! Creates a mesh particle emitter.
createMeshEmitter(scene::IMesh * mesh,bool useNormalDirection,const core::vector3df & direction,f32 normalDirectionModifier,s32 mbNumber,bool everyMeshVertex,u32 minParticlesPerSecond,u32 maxParticlesPerSecond,const video::SColor & minStartColor,const video::SColor & maxStartColor,u32 lifeTimeMin,u32 lifeTimeMax,s32 maxAngleDegrees,const core::dimension2df & minStartSize,const core::dimension2df & maxStartSize)185 IParticleMeshEmitter* CParticleSystemSceneNode::createMeshEmitter(
186 scene::IMesh* mesh, bool useNormalDirection,
187 const core::vector3df& direction, f32 normalDirectionModifier,
188 s32 mbNumber, bool everyMeshVertex,
189 u32 minParticlesPerSecond, u32 maxParticlesPerSecond,
190 const video::SColor& minStartColor, const video::SColor& maxStartColor,
191 u32 lifeTimeMin, u32 lifeTimeMax, s32 maxAngleDegrees,
192 const core::dimension2df& minStartSize,
193 const core::dimension2df& maxStartSize)
194 {
195 return new CParticleMeshEmitter( mesh, useNormalDirection, direction,
196 normalDirectionModifier, mbNumber, everyMeshVertex,
197 minParticlesPerSecond, maxParticlesPerSecond,
198 minStartColor, maxStartColor,
199 lifeTimeMin, lifeTimeMax, maxAngleDegrees,
200 minStartSize, maxStartSize );
201 }
202
203
204 //! Creates a point particle emitter.
createPointEmitter(const core::vector3df & direction,u32 minParticlesPerSecond,u32 maxParticlesPerSecond,const video::SColor & minStartColor,const video::SColor & maxStartColor,u32 lifeTimeMin,u32 lifeTimeMax,s32 maxAngleDegrees,const core::dimension2df & minStartSize,const core::dimension2df & maxStartSize)205 IParticlePointEmitter* CParticleSystemSceneNode::createPointEmitter(
206 const core::vector3df& direction, u32 minParticlesPerSecond,
207 u32 maxParticlesPerSecond, const video::SColor& minStartColor,
208 const video::SColor& maxStartColor, u32 lifeTimeMin, u32 lifeTimeMax,
209 s32 maxAngleDegrees, const core::dimension2df& minStartSize,
210 const core::dimension2df& maxStartSize )
211 {
212 return new CParticlePointEmitter(direction, minParticlesPerSecond,
213 maxParticlesPerSecond, minStartColor, maxStartColor,
214 lifeTimeMin, lifeTimeMax, maxAngleDegrees,
215 minStartSize, maxStartSize );
216 }
217
218
219 //! Creates a ring particle emitter.
createRingEmitter(const core::vector3df & center,f32 radius,f32 ringThickness,const core::vector3df & direction,u32 minParticlesPerSecond,u32 maxParticlesPerSecond,const video::SColor & minStartColor,const video::SColor & maxStartColor,u32 lifeTimeMin,u32 lifeTimeMax,s32 maxAngleDegrees,const core::dimension2df & minStartSize,const core::dimension2df & maxStartSize)220 IParticleRingEmitter* CParticleSystemSceneNode::createRingEmitter(
221 const core::vector3df& center, f32 radius, f32 ringThickness,
222 const core::vector3df& direction,
223 u32 minParticlesPerSecond, u32 maxParticlesPerSecond,
224 const video::SColor& minStartColor, const video::SColor& maxStartColor,
225 u32 lifeTimeMin, u32 lifeTimeMax, s32 maxAngleDegrees,
226 const core::dimension2df& minStartSize, const core::dimension2df& maxStartSize )
227 {
228 return new CParticleRingEmitter( center, radius, ringThickness, direction,
229 minParticlesPerSecond, maxParticlesPerSecond, minStartColor,
230 maxStartColor, lifeTimeMin, lifeTimeMax, maxAngleDegrees,
231 minStartSize, maxStartSize );
232 }
233
234
235 //! Creates a sphere particle emitter.
createSphereEmitter(const core::vector3df & center,f32 radius,const core::vector3df & direction,u32 minParticlesPerSecond,u32 maxParticlesPerSecond,const video::SColor & minStartColor,const video::SColor & maxStartColor,u32 lifeTimeMin,u32 lifeTimeMax,s32 maxAngleDegrees,const core::dimension2df & minStartSize,const core::dimension2df & maxStartSize)236 IParticleSphereEmitter* CParticleSystemSceneNode::createSphereEmitter(
237 const core::vector3df& center, f32 radius, const core::vector3df& direction,
238 u32 minParticlesPerSecond, u32 maxParticlesPerSecond,
239 const video::SColor& minStartColor, const video::SColor& maxStartColor,
240 u32 lifeTimeMin, u32 lifeTimeMax,
241 s32 maxAngleDegrees, const core::dimension2df& minStartSize,
242 const core::dimension2df& maxStartSize )
243 {
244 return new CParticleSphereEmitter(center, radius, direction,
245 minParticlesPerSecond, maxParticlesPerSecond,
246 minStartColor, maxStartColor,
247 lifeTimeMin, lifeTimeMax, maxAngleDegrees,
248 minStartSize, maxStartSize );
249 }
250
251
252 //! Creates a point attraction affector. This affector modifies the positions of the
253 //! particles and attracts them to a specified point at a specified speed per second.
createAttractionAffector(const core::vector3df & point,f32 speed,bool attract,bool affectX,bool affectY,bool affectZ)254 IParticleAttractionAffector* CParticleSystemSceneNode::createAttractionAffector(
255 const core::vector3df& point, f32 speed, bool attract,
256 bool affectX, bool affectY, bool affectZ )
257 {
258 return new CParticleAttractionAffector( point, speed, attract, affectX, affectY, affectZ );
259 }
260
261 //! Creates a scale particle affector.
createScaleParticleAffector(const core::dimension2df & scaleTo)262 IParticleAffector* CParticleSystemSceneNode::createScaleParticleAffector(const core::dimension2df& scaleTo)
263 {
264 return new CParticleScaleAffector(scaleTo);
265 }
266
267
268 //! Creates a fade out particle affector.
createFadeOutParticleAffector(const video::SColor & targetColor,u32 timeNeededToFadeOut)269 IParticleFadeOutAffector* CParticleSystemSceneNode::createFadeOutParticleAffector(
270 const video::SColor& targetColor, u32 timeNeededToFadeOut)
271 {
272 return new CParticleFadeOutAffector(targetColor, timeNeededToFadeOut);
273 }
274
275
276 //! Creates a gravity affector.
createGravityAffector(const core::vector3df & gravity,u32 timeForceLost)277 IParticleGravityAffector* CParticleSystemSceneNode::createGravityAffector(
278 const core::vector3df& gravity, u32 timeForceLost)
279 {
280 return new CParticleGravityAffector(gravity, timeForceLost);
281 }
282
283
284 //! Creates a rotation affector. This affector rotates the particles around a specified pivot
285 //! point. The speed represents Degrees of rotation per second.
createRotationAffector(const core::vector3df & speed,const core::vector3df & pivotPoint)286 IParticleRotationAffector* CParticleSystemSceneNode::createRotationAffector(
287 const core::vector3df& speed, const core::vector3df& pivotPoint )
288 {
289 return new CParticleRotationAffector( speed, pivotPoint );
290 }
291
292
293 //! pre render event
OnRegisterSceneNode()294 void CParticleSystemSceneNode::OnRegisterSceneNode()
295 {
296 doParticleSystem(os::Timer::getTime());
297
298 if (IsVisible && (Particles.size() != 0))
299 {
300 SceneManager->registerNodeForRendering(this);
301 ISceneNode::OnRegisterSceneNode();
302 }
303 }
304
305
306 //! render
render()307 void CParticleSystemSceneNode::render()
308 {
309 video::IVideoDriver* driver = SceneManager->getVideoDriver();
310 ICameraSceneNode* camera = SceneManager->getActiveCamera();
311
312 if (!camera || !driver)
313 return;
314
315
316 #if 0
317 // calculate vectors for letting particles look to camera
318 core::vector3df view(camera->getTarget() - camera->getAbsolutePosition());
319 view.normalize();
320
321 view *= -1.0f;
322
323 #else
324
325 const core::matrix4 &m = camera->getViewFrustum()->getTransform( video::ETS_VIEW );
326
327 const core::vector3df view ( -m[2], -m[6] , -m[10] );
328
329 #endif
330
331 // reallocate arrays, if they are too small
332 reallocateBuffers();
333
334 // create particle vertex data
335 s32 idx = 0;
336 for (u32 i=0; i<Particles.size(); ++i)
337 {
338 const SParticle& particle = Particles[i];
339
340 drawBillboardParticle(idx, particle, view, m);
341
342 idx +=4;
343 }
344
345 // render all
346 core::matrix4 mat;
347 if (!ParticlesAreGlobal)
348 mat.setTranslation(AbsoluteTransformation.getTranslation());
349 driver->setTransform(video::ETS_WORLD, mat);
350
351 driver->setMaterial(Buffer->Material);
352
353 driver->drawVertexPrimitiveList(Buffer->getVertices(), Particles.size()*4,
354 Buffer->getIndices(), Particles.size()*2, video::EVT_STANDARD, EPT_TRIANGLES,Buffer->getIndexType());
355
356 // for debug purposes only:
357 if ( DebugDataVisible & scene::EDS_BBOX )
358 {
359 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
360 video::SMaterial deb_m;
361 deb_m.Lighting = false;
362 driver->setMaterial(deb_m);
363 driver->draw3DBox(Buffer->BoundingBox, video::SColor(0,255,255,255));
364 }
365 }
366
367 //! Draw billboard particle
drawBillboardParticle(const irr::s32 & idx,const SParticle & particle,const core::vector3df & view,const irr::core::matrix4 & m)368 void CParticleSystemSceneNode::drawBillboardParticle(const irr::s32 &idx,
369 const SParticle &particle, const core::vector3df &view, const irr::core::matrix4 &m)
370 {
371
372 f32 f;
373
374 f = 0.5f * particle.size.Width;
375 const core::vector3df horizontal ( m[0] * f, m[4] * f, m[8] * f );
376
377 f = -0.5f * particle.size.Height;
378 const core::vector3df vertical ( m[1] * f, m[5] * f, m[9] * f );
379
380
381 Buffer->Vertices[0+idx].Pos = particle.pos + horizontal + vertical;
382 Buffer->Vertices[0+idx].Color = particle.color;
383 Buffer->Vertices[0+idx].Normal = view;
384
385 Buffer->Vertices[1+idx].Pos = particle.pos + horizontal - vertical;
386 Buffer->Vertices[1+idx].Color = particle.color;
387 Buffer->Vertices[1+idx].Normal = view;
388
389 Buffer->Vertices[2+idx].Pos = particle.pos - horizontal - vertical;
390 Buffer->Vertices[2+idx].Color = particle.color;
391 Buffer->Vertices[2+idx].Normal = view;
392
393 Buffer->Vertices[3+idx].Pos = particle.pos - horizontal + vertical;
394 Buffer->Vertices[3+idx].Color = particle.color;
395 Buffer->Vertices[3+idx].Normal = view;
396
397 }
398
399
400 //! returns the axis aligned bounding box of this node
getBoundingBox() const401 const core::aabbox3d<f32>& CParticleSystemSceneNode::getBoundingBox() const
402 {
403 return Buffer->getBoundingBox();
404 }
405
406
doParticleSystem(u32 time)407 void CParticleSystemSceneNode::doParticleSystem(u32 time)
408 {
409 if (LastEmitTime==0)
410 {
411 LastEmitTime = time;
412 return;
413 }
414
415 u32 now = time;
416 u32 timediff = time - LastEmitTime;
417 LastEmitTime = time;
418
419 // run emitter
420
421 if (Emitter && IsVisible)
422 {
423 SParticle* array = 0;
424 s32 newParticles = Emitter->emitt(now, timediff, array);
425
426 if (newParticles && array)
427 {
428 s32 j=Particles.size();
429 if (newParticles > 16250-j)
430 newParticles=16250-j;
431 Particles.set_used(j+newParticles);
432 for (s32 i=j; i<j+newParticles; ++i)
433 {
434 Particles[i]=array[i-j];
435 AbsoluteTransformation.rotateVect(Particles[i].startVector);
436 if (ParticlesAreGlobal)
437 AbsoluteTransformation.transformVect(Particles[i].pos);
438 }
439 }
440 }
441
442 // run affectors
443 core::list<IParticleAffector*>::Iterator ait = AffectorList.begin();
444 for (; ait != AffectorList.end(); ++ait)
445 (*ait)->affect(now, Particles.pointer(), Particles.size());
446
447 if (ParticlesAreGlobal)
448 Buffer->BoundingBox.reset(AbsoluteTransformation.getTranslation());
449 else
450 Buffer->BoundingBox.reset(core::vector3df(0,0,0));
451
452 // animate all particles
453 f32 scale = (f32)timediff;
454
455 for (u32 i=0; i<Particles.size();)
456 {
457 // erase is pretty expensive!
458 if (now > Particles[i].endTime)
459 {
460 // Particle order does not seem to matter.
461 // So we can delete by switching with last particle and deleting that one.
462 // This is a lot faster and speed is very important here as the erase otherwise
463 // can cause noticable freezes.
464 Particles[i] = Particles[Particles.size()-1];
465 Particles.erase( Particles.size()-1 );
466 }
467 else
468 {
469 Particles[i].pos += (Particles[i].vector * scale);
470 Buffer->BoundingBox.addInternalPoint(Particles[i].pos);
471 ++i;
472 }
473 }
474
475 const f32 m = (ParticleSize.Width > ParticleSize.Height ? ParticleSize.Width : ParticleSize.Height) * 0.5f;
476 Buffer->BoundingBox.MaxEdge.X += m;
477 Buffer->BoundingBox.MaxEdge.Y += m;
478 Buffer->BoundingBox.MaxEdge.Z += m;
479
480 Buffer->BoundingBox.MinEdge.X -= m;
481 Buffer->BoundingBox.MinEdge.Y -= m;
482 Buffer->BoundingBox.MinEdge.Z -= m;
483
484 if (ParticlesAreGlobal)
485 {
486 core::matrix4 absinv( AbsoluteTransformation, core::matrix4::EM4CONST_INVERSE );
487 absinv.transformBoxEx(Buffer->BoundingBox);
488 }
489 }
490
491
492 //! Sets if the particles should be global. If it is, the particles are affected by
493 //! the movement of the particle system scene node too, otherwise they completely
494 //! ignore it. Default is true.
setParticlesAreGlobal(bool global)495 void CParticleSystemSceneNode::setParticlesAreGlobal(bool global)
496 {
497 ParticlesAreGlobal = global;
498 }
499
500 //! Remove all currently visible particles
clearParticles()501 void CParticleSystemSceneNode::clearParticles()
502 {
503 Particles.set_used(0);
504 }
505
506 //! Sets the size of all particles.
setParticleSize(const core::dimension2d<f32> & size)507 void CParticleSystemSceneNode::setParticleSize(const core::dimension2d<f32> &size)
508 {
509 os::Printer::log("setParticleSize is deprecated, use setMinStartSize/setMaxStartSize in emitter.", irr::ELL_WARNING);
510 //A bit of a hack, but better here than in the particle code
511 if (Emitter)
512 {
513 Emitter->setMinStartSize(size);
514 Emitter->setMaxStartSize(size);
515 }
516 ParticleSize = size;
517 }
518
519
reallocateBuffers()520 void CParticleSystemSceneNode::reallocateBuffers()
521 {
522 if (Particles.size() * 4 > Buffer->getVertexCount() ||
523 Particles.size() * 6 > Buffer->getIndexCount())
524 {
525 u32 oldSize = Buffer->getVertexCount();
526 Buffer->Vertices.set_used(Particles.size() * 4);
527
528 u32 i;
529
530 // fill remaining vertices
531 for (i=oldSize; i<Buffer->Vertices.size(); i+=4)
532 {
533 Buffer->Vertices[0+i].TCoords.set(0.0f, 0.0f);
534 Buffer->Vertices[1+i].TCoords.set(0.0f, 1.0f);
535 Buffer->Vertices[2+i].TCoords.set(1.0f, 1.0f);
536 Buffer->Vertices[3+i].TCoords.set(1.0f, 0.0f);
537 }
538
539 // fill remaining indices
540 u32 oldIdxSize = Buffer->getIndexCount();
541 u32 oldvertices = oldSize;
542 Buffer->Indices.set_used(Particles.size() * 6);
543
544 for (i=oldIdxSize; i<Buffer->Indices.size(); i+=6)
545 {
546 Buffer->Indices[0+i] = (u16)0+oldvertices;
547 Buffer->Indices[1+i] = (u16)2+oldvertices;
548 Buffer->Indices[2+i] = (u16)1+oldvertices;
549 Buffer->Indices[3+i] = (u16)0+oldvertices;
550 Buffer->Indices[4+i] = (u16)3+oldvertices;
551 Buffer->Indices[5+i] = (u16)2+oldvertices;
552 oldvertices += 4;
553 }
554 }
555 }
556
557
558 //! Writes attributes of the scene node.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options) const559 void CParticleSystemSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
560 {
561 IParticleSystemSceneNode::serializeAttributes(out, options);
562
563 out->addBool("GlobalParticles", ParticlesAreGlobal);
564 out->addFloat("ParticleWidth", ParticleSize.Width);
565 out->addFloat("ParticleHeight", ParticleSize.Height);
566
567 // write emitter
568
569 E_PARTICLE_EMITTER_TYPE type = EPET_COUNT;
570 if (Emitter)
571 type = Emitter->getType();
572
573 out->addEnum("Emitter", (s32)type, ParticleEmitterTypeNames);
574
575 if (Emitter)
576 Emitter->serializeAttributes(out, options);
577
578 // write affectors
579
580 E_PARTICLE_AFFECTOR_TYPE atype = EPAT_NONE;
581
582 for (core::list<IParticleAffector*>::ConstIterator it = AffectorList.begin();
583 it != AffectorList.end(); ++it)
584 {
585 atype = (*it)->getType();
586
587 out->addEnum("Affector", (s32)atype, ParticleAffectorTypeNames);
588
589 (*it)->serializeAttributes(out);
590 }
591
592 // add empty affector to make it possible to add further affectors
593
594 if (options && options->Flags & io::EARWF_FOR_EDITOR)
595 out->addEnum("Affector", EPAT_NONE, ParticleAffectorTypeNames);
596 }
597
598
599 //! Reads attributes of the scene node.
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options)600 void CParticleSystemSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
601 {
602 IParticleSystemSceneNode::deserializeAttributes(in, options);
603
604 ParticlesAreGlobal = in->getAttributeAsBool("GlobalParticles");
605 ParticleSize.Width = in->getAttributeAsFloat("ParticleWidth");
606 ParticleSize.Height = in->getAttributeAsFloat("ParticleHeight");
607
608 // read emitter
609
610 int emitterIdx = in->findAttribute("Emitter");
611 if (emitterIdx == -1)
612 return;
613
614 if (Emitter)
615 Emitter->drop();
616 Emitter = 0;
617
618 E_PARTICLE_EMITTER_TYPE type = (E_PARTICLE_EMITTER_TYPE)
619 in->getAttributeAsEnumeration("Emitter", ParticleEmitterTypeNames);
620
621 switch(type)
622 {
623 case EPET_POINT:
624 Emitter = createPointEmitter();
625 break;
626 case EPET_ANIMATED_MESH:
627 Emitter = createAnimatedMeshSceneNodeEmitter(NULL); // we can't set the node - the user will have to do this
628 break;
629 case EPET_BOX:
630 Emitter = createBoxEmitter();
631 break;
632 case EPET_CYLINDER:
633 Emitter = createCylinderEmitter(core::vector3df(0,0,0), 10.f, core::vector3df(0,1,0), 10.f); // (values here don't matter)
634 break;
635 case EPET_MESH:
636 Emitter = createMeshEmitter(NULL); // we can't set the mesh - the user will have to do this
637 break;
638 case EPET_RING:
639 Emitter = createRingEmitter(core::vector3df(0,0,0), 10.f, 10.f); // (values here don't matter)
640 break;
641 case EPET_SPHERE:
642 Emitter = createSphereEmitter(core::vector3df(0,0,0), 10.f); // (values here don't matter)
643 break;
644 default:
645 break;
646 }
647
648 u32 idx = 0;
649
650 #if 0
651 if (Emitter)
652 idx = Emitter->deserializeAttributes(idx, in);
653
654 ++idx;
655 #else
656 if (Emitter)
657 Emitter->deserializeAttributes(in);
658 #endif
659
660 // read affectors
661
662 removeAllAffectors();
663 u32 cnt = in->getAttributeCount();
664
665 while(idx < cnt)
666 {
667 const char* name = in->getAttributeName(idx);
668
669 if (!name || strcmp("Affector", name))
670 return;
671
672 E_PARTICLE_AFFECTOR_TYPE atype =
673 (E_PARTICLE_AFFECTOR_TYPE)in->getAttributeAsEnumeration(idx, ParticleAffectorTypeNames);
674
675 IParticleAffector* aff = 0;
676
677 switch(atype)
678 {
679 case EPAT_ATTRACT:
680 aff = createAttractionAffector(core::vector3df(0,0,0));
681 break;
682 case EPAT_FADE_OUT:
683 aff = createFadeOutParticleAffector();
684 break;
685 case EPAT_GRAVITY:
686 aff = createGravityAffector();
687 break;
688 case EPAT_ROTATE:
689 aff = createRotationAffector();
690 break;
691 case EPAT_SCALE:
692 aff = createScaleParticleAffector();
693 break;
694 case EPAT_NONE:
695 default:
696 break;
697 }
698
699 ++idx;
700
701 if (aff)
702 {
703 #if 0
704 idx = aff->deserializeAttributes(idx, in, options);
705 ++idx;
706 #else
707 aff->deserializeAttributes(in, options);
708 #endif
709
710 addAffector(aff);
711 aff->drop();
712 }
713 }
714 }
715
716
717 } // end namespace scene
718 } // end namespace irr
719
720
721