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