1
// Copyright (C) 2002-2011 Nikolaus Gebhardt
2
// This file is part of the "Irrlicht Engine".
3
// For conditions of distribution and use, see copyright notice in irrlicht.h
5
#include "CParticleSystemSceneNode.h"
7
#include "ISceneManager.h"
8
#include "ICameraSceneNode.h"
9
#include "IVideoDriver.h"
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"
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)
40
setDebugName("CParticleSystemSceneNode");
43
Buffer = new SMeshBuffer();
44
if (createDefaultEmitter)
46
IParticleEmitter* e = createBoxEmitter();
54
CParticleSystemSceneNode::~CParticleSystemSceneNode()
65
//! Gets the particle emitter, which creates the particles.
66
IParticleEmitter* CParticleSystemSceneNode::getEmitter()
72
//! Sets the particle emitter, which creates the particles.
73
void CParticleSystemSceneNode::setEmitter(IParticleEmitter* emitter)
75
if (emitter == Emitter)
87
//! Adds new particle effector to the particle system.
88
void CParticleSystemSceneNode::addAffector(IParticleAffector* affector)
91
AffectorList.push_back(affector);
94
//! Get a list of all particle affectors.
95
const core::list<IParticleAffector*>& CParticleSystemSceneNode::getAffectors() const
100
//! Removes all particle affectors in the particle system.
101
void CParticleSystemSceneNode::removeAllAffectors()
103
core::list<IParticleAffector*>::Iterator it = AffectorList.begin();
104
while (it != AffectorList.end())
107
it = AffectorList.erase(it);
112
//! Returns the material based on the zero based index i.
113
video::SMaterial& CParticleSystemSceneNode::getMaterial(u32 i)
115
return Buffer->Material;
119
//! Returns amount of materials used by this scene node.
120
u32 CParticleSystemSceneNode::getMaterialCount() const
126
//! Creates a particle emitter for an animated mesh scene node
127
IParticleAnimatedMeshSceneNodeEmitter*
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 )
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 );
148
//! Creates a box particle emitter.
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 )
157
return new CParticleBoxEmitter(box, direction, minParticlesPerSecond,
158
maxParticlesPerSecond, minStartColor, maxStartColor,
159
lifeTimeMin, lifeTimeMax, maxAngleDegrees,
160
minStartSize, maxStartSize );
164
//! Creates a particle emitter for emitting from a cylinder
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 )
175
return new CParticleCylinderEmitter( center, radius, normal, length,
176
outlineOnly, direction,
177
minParticlesPerSecond, maxParticlesPerSecond,
178
minStartColor, maxStartColor,
179
lifeTimeMin, lifeTimeMax, maxAngleDegrees,
180
minStartSize, maxStartSize );
184
//! Creates a mesh particle emitter.
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)
195
return new CParticleMeshEmitter( mesh, useNormalDirection, direction,
196
normalDirectionModifier, mbNumber, everyMeshVertex,
197
minParticlesPerSecond, maxParticlesPerSecond,
198
minStartColor, maxStartColor,
199
lifeTimeMin, lifeTimeMax, maxAngleDegrees,
200
minStartSize, maxStartSize );
204
//! Creates a point particle emitter.
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 )
212
return new CParticlePointEmitter(direction, minParticlesPerSecond,
213
maxParticlesPerSecond, minStartColor, maxStartColor,
214
lifeTimeMin, lifeTimeMax, maxAngleDegrees,
215
minStartSize, maxStartSize );
219
//! Creates a ring particle emitter.
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 )
228
return new CParticleRingEmitter( center, radius, ringThickness, direction,
229
minParticlesPerSecond, maxParticlesPerSecond, minStartColor,
230
maxStartColor, lifeTimeMin, lifeTimeMax, maxAngleDegrees,
231
minStartSize, maxStartSize );
235
//! Creates a sphere particle emitter.
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 )
244
return new CParticleSphereEmitter(center, radius, direction,
245
minParticlesPerSecond, maxParticlesPerSecond,
246
minStartColor, maxStartColor,
247
lifeTimeMin, lifeTimeMax, maxAngleDegrees,
248
minStartSize, maxStartSize );
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.
254
IParticleAttractionAffector* CParticleSystemSceneNode::createAttractionAffector(
255
const core::vector3df& point, f32 speed, bool attract,
256
bool affectX, bool affectY, bool affectZ )
258
return new CParticleAttractionAffector( point, speed, attract, affectX, affectY, affectZ );
261
//! Creates a scale particle affector.
262
IParticleAffector* CParticleSystemSceneNode::createScaleParticleAffector(const core::dimension2df& scaleTo)
264
return new CParticleScaleAffector(scaleTo);
268
//! Creates a fade out particle affector.
269
IParticleFadeOutAffector* CParticleSystemSceneNode::createFadeOutParticleAffector(
270
const video::SColor& targetColor, u32 timeNeededToFadeOut)
272
return new CParticleFadeOutAffector(targetColor, timeNeededToFadeOut);
276
//! Creates a gravity affector.
277
IParticleGravityAffector* CParticleSystemSceneNode::createGravityAffector(
278
const core::vector3df& gravity, u32 timeForceLost)
280
return new CParticleGravityAffector(gravity, timeForceLost);
284
//! Creates a rotation affector. This affector rotates the particles around a specified pivot
285
//! point. The speed represents Degrees of rotation per second.
286
IParticleRotationAffector* CParticleSystemSceneNode::createRotationAffector(
287
const core::vector3df& speed, const core::vector3df& pivotPoint )
289
return new CParticleRotationAffector( speed, pivotPoint );
294
void CParticleSystemSceneNode::OnRegisterSceneNode()
296
doParticleSystem(os::Timer::getTime());
298
if (IsVisible && (Particles.size() != 0))
300
SceneManager->registerNodeForRendering(this);
301
ISceneNode::OnRegisterSceneNode();
307
void CParticleSystemSceneNode::render()
309
video::IVideoDriver* driver = SceneManager->getVideoDriver();
310
ICameraSceneNode* camera = SceneManager->getActiveCamera();
312
if (!camera || !driver)
317
// calculate vectors for letting particles look to camera
318
core::vector3df view(camera->getTarget() - camera->getAbsolutePosition());
325
const core::matrix4 &m = camera->getViewFrustum()->getTransform( video::ETS_VIEW );
327
const core::vector3df view ( -m[2], -m[6] , -m[10] );
331
// reallocate arrays, if they are too small
334
// create particle vertex data
336
for (u32 i=0; i<Particles.size(); ++i)
338
const SParticle& particle = Particles[i];
341
core::vector3df horizontal = camera->getUpVector().crossProduct(view);
342
horizontal.normalize();
343
horizontal *= 0.5f * particle.size.Width;
345
core::vector3df vertical = horizontal.crossProduct(view);
346
vertical.normalize();
347
vertical *= 0.5f * particle.size.Height;
352
f = 0.5f * particle.size.Width;
353
const core::vector3df horizontal ( m[0] * f, m[4] * f, m[8] * f );
355
f = -0.5f * particle.size.Height;
356
const core::vector3df vertical ( m[1] * f, m[5] * f, m[9] * f );
359
Buffer->Vertices[0+idx].Pos = particle.pos + horizontal + vertical;
360
Buffer->Vertices[0+idx].Color = particle.color;
361
Buffer->Vertices[0+idx].Normal = view;
363
Buffer->Vertices[1+idx].Pos = particle.pos + horizontal - vertical;
364
Buffer->Vertices[1+idx].Color = particle.color;
365
Buffer->Vertices[1+idx].Normal = view;
367
Buffer->Vertices[2+idx].Pos = particle.pos - horizontal - vertical;
368
Buffer->Vertices[2+idx].Color = particle.color;
369
Buffer->Vertices[2+idx].Normal = view;
371
Buffer->Vertices[3+idx].Pos = particle.pos - horizontal + vertical;
372
Buffer->Vertices[3+idx].Color = particle.color;
373
Buffer->Vertices[3+idx].Normal = view;
380
if (!ParticlesAreGlobal)
381
mat.setTranslation(AbsoluteTransformation.getTranslation());
382
driver->setTransform(video::ETS_WORLD, mat);
384
driver->setMaterial(Buffer->Material);
386
driver->drawVertexPrimitiveList(Buffer->getVertices(), Particles.size()*4,
387
Buffer->getIndices(), Particles.size()*2, video::EVT_STANDARD, EPT_TRIANGLES,Buffer->getIndexType());
389
// for debug purposes only:
390
if ( DebugDataVisible & scene::EDS_BBOX )
392
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
393
video::SMaterial deb_m;
394
deb_m.Lighting = false;
395
driver->setMaterial(deb_m);
396
driver->draw3DBox(Buffer->BoundingBox, video::SColor(0,255,255,255));
401
//! returns the axis aligned bounding box of this node
402
const core::aabbox3d<f32>& CParticleSystemSceneNode::getBoundingBox() const
404
return Buffer->getBoundingBox();
408
void CParticleSystemSceneNode::doParticleSystem(u32 time)
417
u32 timediff = time - LastEmitTime;
422
if (Emitter && IsVisible)
424
SParticle* array = 0;
425
s32 newParticles = Emitter->emitt(now, timediff, array);
427
if (newParticles && array)
429
s32 j=Particles.size();
430
if (newParticles > 16250-j)
431
newParticles=16250-j;
432
Particles.set_used(j+newParticles);
433
for (s32 i=j; i<j+newParticles; ++i)
435
Particles[i]=array[i-j];
436
AbsoluteTransformation.rotateVect(Particles[i].startVector);
437
if (ParticlesAreGlobal)
438
AbsoluteTransformation.transformVect(Particles[i].pos);
444
core::list<IParticleAffector*>::Iterator ait = AffectorList.begin();
445
for (; ait != AffectorList.end(); ++ait)
446
(*ait)->affect(now, Particles.pointer(), Particles.size());
448
if (ParticlesAreGlobal)
449
Buffer->BoundingBox.reset(AbsoluteTransformation.getTranslation());
451
Buffer->BoundingBox.reset(core::vector3df(0,0,0));
453
// animate all particles
454
f32 scale = (f32)timediff;
456
for (u32 i=0; i<Particles.size();)
458
// erase is pretty expensive!
459
if (now > Particles[i].endTime)
461
// Particle order does not seem to matter.
462
// So we can delete by switching with last particle and deleting that one.
463
// This is a lot faster and speed is very important here as the erase otherwise
464
// can cause noticable freezes.
465
Particles[i] = Particles[Particles.size()-1];
466
Particles.erase( Particles.size()-1 );
470
Particles[i].pos += (Particles[i].vector * scale);
471
Buffer->BoundingBox.addInternalPoint(Particles[i].pos);
476
const f32 m = (ParticleSize.Width > ParticleSize.Height ? ParticleSize.Width : ParticleSize.Height) * 0.5f;
477
Buffer->BoundingBox.MaxEdge.X += m;
478
Buffer->BoundingBox.MaxEdge.Y += m;
479
Buffer->BoundingBox.MaxEdge.Z += m;
481
Buffer->BoundingBox.MinEdge.X -= m;
482
Buffer->BoundingBox.MinEdge.Y -= m;
483
Buffer->BoundingBox.MinEdge.Z -= m;
485
if (ParticlesAreGlobal)
487
core::matrix4 absinv( AbsoluteTransformation, core::matrix4::EM4CONST_INVERSE );
488
absinv.transformBoxEx(Buffer->BoundingBox);
493
//! Sets if the particles should be global. If it is, the particles are affected by
494
//! the movement of the particle system scene node too, otherwise they completely
495
//! ignore it. Default is true.
496
void CParticleSystemSceneNode::setParticlesAreGlobal(bool global)
498
ParticlesAreGlobal = global;
501
//! Remove all currently visible particles
502
void CParticleSystemSceneNode::clearParticles()
504
Particles.set_used(0);
507
//! Sets the size of all particles.
508
void CParticleSystemSceneNode::setParticleSize(const core::dimension2d<f32> &size)
510
os::Printer::log("setParticleSize is deprecated, use setMinStartSize/setMaxStartSize in emitter.", irr::ELL_WARNING);
511
//A bit of a hack, but better here than in the particle code
514
Emitter->setMinStartSize(size);
515
Emitter->setMaxStartSize(size);
521
void CParticleSystemSceneNode::reallocateBuffers()
523
if (Particles.size() * 4 > Buffer->getVertexCount() ||
524
Particles.size() * 6 > Buffer->getIndexCount())
526
u32 oldSize = Buffer->getVertexCount();
527
Buffer->Vertices.set_used(Particles.size() * 4);
531
// fill remaining vertices
532
for (i=oldSize; i<Buffer->Vertices.size(); i+=4)
534
Buffer->Vertices[0+i].TCoords.set(0.0f, 0.0f);
535
Buffer->Vertices[1+i].TCoords.set(0.0f, 1.0f);
536
Buffer->Vertices[2+i].TCoords.set(1.0f, 1.0f);
537
Buffer->Vertices[3+i].TCoords.set(1.0f, 0.0f);
540
// fill remaining indices
541
u32 oldIdxSize = Buffer->getIndexCount();
542
u32 oldvertices = oldSize;
543
Buffer->Indices.set_used(Particles.size() * 6);
545
for (i=oldIdxSize; i<Buffer->Indices.size(); i+=6)
547
Buffer->Indices[0+i] = (u16)0+oldvertices;
548
Buffer->Indices[1+i] = (u16)2+oldvertices;
549
Buffer->Indices[2+i] = (u16)1+oldvertices;
550
Buffer->Indices[3+i] = (u16)0+oldvertices;
551
Buffer->Indices[4+i] = (u16)3+oldvertices;
552
Buffer->Indices[5+i] = (u16)2+oldvertices;
559
//! Writes attributes of the scene node.
560
void CParticleSystemSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
562
IParticleSystemSceneNode::serializeAttributes(out, options);
564
out->addBool("GlobalParticles", ParticlesAreGlobal);
565
out->addFloat("ParticleWidth", ParticleSize.Width);
566
out->addFloat("ParticleHeight", ParticleSize.Height);
570
E_PARTICLE_EMITTER_TYPE type = EPET_COUNT;
572
type = Emitter->getType();
574
out->addEnum("Emitter", (s32)type, ParticleEmitterTypeNames);
577
Emitter->serializeAttributes(out, options);
581
E_PARTICLE_AFFECTOR_TYPE atype = EPAT_NONE;
583
for (core::list<IParticleAffector*>::ConstIterator it = AffectorList.begin();
584
it != AffectorList.end(); ++it)
586
atype = (*it)->getType();
588
out->addEnum("Affector", (s32)atype, ParticleAffectorTypeNames);
590
(*it)->serializeAttributes(out);
593
// add empty affector to make it possible to add further affectors
595
if (options && options->Flags & io::EARWF_FOR_EDITOR)
596
out->addEnum("Affector", EPAT_NONE, ParticleAffectorTypeNames);
600
//! Reads attributes of the scene node.
601
void CParticleSystemSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
603
IParticleSystemSceneNode::deserializeAttributes(in, options);
605
ParticlesAreGlobal = in->getAttributeAsBool("GlobalParticles");
606
ParticleSize.Width = in->getAttributeAsFloat("ParticleWidth");
607
ParticleSize.Height = in->getAttributeAsFloat("ParticleHeight");
611
int emitterIdx = in->findAttribute("Emitter");
612
if (emitterIdx == -1)
619
E_PARTICLE_EMITTER_TYPE type = (E_PARTICLE_EMITTER_TYPE)
620
in->getAttributeAsEnumeration("Emitter", ParticleEmitterTypeNames);
625
Emitter = createPointEmitter();
627
case EPET_ANIMATED_MESH:
628
Emitter = createAnimatedMeshSceneNodeEmitter(NULL); // we can't set the node - the user will have to do this
631
Emitter = createBoxEmitter();
634
Emitter = createCylinderEmitter(core::vector3df(0,0,0), 10.f, core::vector3df(0,1,0), 10.f); // (values here don't matter)
637
Emitter = createMeshEmitter(NULL); // we can't set the mesh - the user will have to do this
640
Emitter = createRingEmitter(core::vector3df(0,0,0), 10.f, 10.f); // (values here don't matter)
643
Emitter = createSphereEmitter(core::vector3df(0,0,0), 10.f); // (values here don't matter)
653
idx = Emitter->deserializeAttributes(idx, in);
658
Emitter->deserializeAttributes(in);
663
removeAllAffectors();
664
u32 cnt = in->getAttributeCount();
668
const char* name = in->getAttributeName(idx);
670
if (!name || strcmp("Affector", name))
673
E_PARTICLE_AFFECTOR_TYPE atype =
674
(E_PARTICLE_AFFECTOR_TYPE)in->getAttributeAsEnumeration(idx, ParticleAffectorTypeNames);
676
IParticleAffector* aff = 0;
681
aff = createAttractionAffector(core::vector3df(0,0,0));
684
aff = createFadeOutParticleAffector();
687
aff = createGravityAffector();
690
aff = createRotationAffector();
693
aff = createScaleParticleAffector();
705
idx = aff->deserializeAttributes(idx, in, options);
708
aff->deserializeAttributes(in, options);
718
} // end namespace scene
719
} // end namespace irr