فهرست منبع

Moved most of the ParticleSystem code from modules/graphics/opengl/ to modules/graphics/

Alex Szpakowski 9 سال پیش
والد
کامیت
ac99a47386

+ 2 - 0
CMakeLists.txt

@@ -269,6 +269,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/Drawable.h
 	src/modules/graphics/Drawable.h
 	src/modules/graphics/Graphics.cpp
 	src/modules/graphics/Graphics.cpp
 	src/modules/graphics/Graphics.h
 	src/modules/graphics/Graphics.h
+	src/modules/graphics/ParticleSystem.cpp
+	src/modules/graphics/ParticleSystem.h
 	src/modules/graphics/Quad.cpp
 	src/modules/graphics/Quad.cpp
 	src/modules/graphics/Quad.h
 	src/modules/graphics/Quad.h
 	src/modules/graphics/Texture.cpp
 	src/modules/graphics/Texture.cpp

+ 8 - 0
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -871,6 +871,8 @@
 		FAB2D5AA1AABDD8A008224A4 /* TrueTypeRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */; };
 		FAB2D5AA1AABDD8A008224A4 /* TrueTypeRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */; };
 		FAB2D5AB1AABDD8A008224A4 /* TrueTypeRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */; };
 		FAB2D5AB1AABDD8A008224A4 /* TrueTypeRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */; };
 		FAB2D5AC1AABDD8A008224A4 /* TrueTypeRasterizer.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */; };
 		FAB2D5AC1AABDD8A008224A4 /* TrueTypeRasterizer.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */; };
+		FAE272521C05A15B00A67640 /* ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAE272501C05A15B00A67640 /* ParticleSystem.cpp */; };
+		FAE272531C05A15B00A67640 /* ParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE272511C05A15B00A67640 /* ParticleSystem.h */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
 /* Begin PBXCopyFilesBuildPhase section */
 /* Begin PBXCopyFilesBuildPhase section */
@@ -1519,6 +1521,8 @@
 		FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrueTypeRasterizer.h; sourceTree = "<group>"; };
 		FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrueTypeRasterizer.h; sourceTree = "<group>"; };
 		FAC734C11B2E021A00AB460A /* wrap_SoundData.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_SoundData.lua; sourceTree = "<group>"; };
 		FAC734C11B2E021A00AB460A /* wrap_SoundData.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_SoundData.lua; sourceTree = "<group>"; };
 		FAC734C21B2E628700AB460A /* wrap_ImageData.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_ImageData.lua; sourceTree = "<group>"; };
 		FAC734C21B2E628700AB460A /* wrap_ImageData.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_ImageData.lua; sourceTree = "<group>"; };
+		FAE272501C05A15B00A67640 /* ParticleSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParticleSystem.cpp; sourceTree = "<group>"; };
+		FAE272511C05A15B00A67640 /* ParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleSystem.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 /* End PBXFileReference section */
 
 
 /* Begin PBXFrameworksBuildPhase section */
 /* Begin PBXFrameworksBuildPhase section */
@@ -2204,6 +2208,8 @@
 				FA0B7B8A1A95902C000E1D17 /* Graphics.cpp */,
 				FA0B7B8A1A95902C000E1D17 /* Graphics.cpp */,
 				FA0B7B8B1A95902C000E1D17 /* Graphics.h */,
 				FA0B7B8B1A95902C000E1D17 /* Graphics.h */,
 				FA0B7B8C1A95902C000E1D17 /* opengl */,
 				FA0B7B8C1A95902C000E1D17 /* opengl */,
+				FAE272501C05A15B00A67640 /* ParticleSystem.cpp */,
+				FAE272511C05A15B00A67640 /* ParticleSystem.h */,
 				FA0B7BBC1A95902C000E1D17 /* Quad.cpp */,
 				FA0B7BBC1A95902C000E1D17 /* Quad.cpp */,
 				FA0B7BBD1A95902C000E1D17 /* Quad.h */,
 				FA0B7BBD1A95902C000E1D17 /* Quad.h */,
 				FA0B7BBE1A95902C000E1D17 /* Texture.cpp */,
 				FA0B7BBE1A95902C000E1D17 /* Texture.cpp */,
@@ -2874,6 +2880,7 @@
 				FA0B7B2D1A958EA3000E1D17 /* core.h in Headers */,
 				FA0B7B2D1A958EA3000E1D17 /* core.h in Headers */,
 				FAB17BF71ABFC4B100F9BA27 /* lz4hc.h in Headers */,
 				FAB17BF71ABFC4B100F9BA27 /* lz4hc.h in Headers */,
 				FA0B7E831A95902C000E1D17 /* Shape.h in Headers */,
 				FA0B7E831A95902C000E1D17 /* Shape.h in Headers */,
+				FAE272531C05A15B00A67640 /* ParticleSystem.h in Headers */,
 				FA0B7EDD1A95902D000E1D17 /* Touch.h in Headers */,
 				FA0B7EDD1A95902D000E1D17 /* Touch.h in Headers */,
 				FA0B7EDE1A95902D000E1D17 /* Touch.h in Headers */,
 				FA0B7EDE1A95902D000E1D17 /* Touch.h in Headers */,
 				FA0B7A541A958EA3000E1D17 /* b2Math.h in Headers */,
 				FA0B7A541A958EA3000E1D17 /* b2Math.h in Headers */,
@@ -3601,6 +3608,7 @@
 				FA0B7D391A95902C000E1D17 /* Graphics.cpp in Sources */,
 				FA0B7D391A95902C000E1D17 /* Graphics.cpp in Sources */,
 				FA0B79361A958E3B000E1D17 /* macosx.mm in Sources */,
 				FA0B79361A958E3B000E1D17 /* macosx.mm in Sources */,
 				FA0B7E121A95902C000E1D17 /* GearJoint.cpp in Sources */,
 				FA0B7E121A95902C000E1D17 /* GearJoint.cpp in Sources */,
+				FAE272521C05A15B00A67640 /* ParticleSystem.cpp in Sources */,
 				FA0B7DC11A95902C000E1D17 /* wrap_Joystick.cpp in Sources */,
 				FA0B7DC11A95902C000E1D17 /* wrap_Joystick.cpp in Sources */,
 				FA0B7CD31A95902C000E1D17 /* Source.cpp in Sources */,
 				FA0B7CD31A95902C000E1D17 /* Source.cpp in Sources */,
 				FA0B7A411A958EA3000E1D17 /* b2CircleShape.cpp in Sources */,
 				FA0B7A411A958EA3000E1D17 /* b2CircleShape.cpp in Sources */,

+ 986 - 0
src/modules/graphics/ParticleSystem.cpp

@@ -0,0 +1,986 @@
+/**
+ * Copyright (c) 2006-2015 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+//LOVE
+#include "common/config.h"
+#include "ParticleSystem.h"
+
+#include "common/math.h"
+#include "modules/math/RandomGenerator.h"
+
+// STD
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+
+namespace love
+{
+namespace graphics
+{
+
+namespace
+{
+
+love::math::RandomGenerator rng;
+
+float calculate_variation(float inner, float outer, float var)
+{
+	float low = inner - (outer/2.0f)*var;
+	float high = inner + (outer/2.0f)*var;
+	float r = (float) rng.random();
+	return low*(1-r)+high*r;
+}
+
+} // anonymous namespace
+
+ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
+	: pMem(nullptr)
+	, pFree(nullptr)
+	, pHead(nullptr)
+	, pTail(nullptr)
+	, texture(texture)
+	, active(true)
+	, insertMode(INSERT_MODE_TOP)
+	, maxParticles(0)
+	, activeParticles(0)
+	, emissionRate(0)
+	, emitCounter(0)
+	, areaSpreadDistribution(DISTRIBUTION_NONE)
+	, lifetime(-1)
+	, life(0)
+	, particleLifeMin(0)
+	, particleLifeMax(0)
+	, direction(0)
+	, spread(0)
+	, speedMin(0)
+	, speedMax(0)
+	, linearAccelerationMin(0, 0)
+	, linearAccelerationMax(0, 0)
+	, radialAccelerationMin(0)
+	, radialAccelerationMax(0)
+	, tangentialAccelerationMin(0)
+	, tangentialAccelerationMax(0)
+	, linearDampingMin(0.0f)
+	, linearDampingMax(0.0f)
+	, sizeVariation(0)
+	, rotationMin(0)
+	, rotationMax(0)
+	, spinStart(0)
+	, spinEnd(0)
+	, spinVariation(0)
+	, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
+	, defaultOffset(true)
+	, relativeRotation(false)
+{
+	if (size == 0 || size > MAX_PARTICLES)
+		throw love::Exception("Invalid ParticleSystem size.");
+
+	sizes.push_back(1.0f);
+	colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
+	setBufferSize(size);
+}
+
+ParticleSystem::ParticleSystem(const ParticleSystem &p)
+	: pMem(nullptr)
+	, pFree(nullptr)
+	, pHead(nullptr)
+	, pTail(nullptr)
+	, texture(p.texture)
+	, active(p.active)
+	, insertMode(p.insertMode)
+	, maxParticles(p.maxParticles)
+	, activeParticles(0)
+	, emissionRate(p.emissionRate)
+	, emitCounter(0.0f)
+	, position(p.position)
+	, prevPosition(p.prevPosition)
+	, areaSpreadDistribution(p.areaSpreadDistribution)
+	, areaSpread(p.areaSpread)
+	, lifetime(p.lifetime)
+	, life(p.lifetime) // Initialize with the maximum life time.
+	, particleLifeMin(p.particleLifeMin)
+	, particleLifeMax(p.particleLifeMax)
+	, direction(p.direction)
+	, spread(p.spread)
+	, speedMin(p.speedMin)
+	, speedMax(p.speedMax)
+	, linearAccelerationMin(p.linearAccelerationMin)
+	, linearAccelerationMax(p.linearAccelerationMax)
+	, radialAccelerationMin(p.radialAccelerationMin)
+	, radialAccelerationMax(p.radialAccelerationMax)
+	, tangentialAccelerationMin(p.tangentialAccelerationMin)
+	, tangentialAccelerationMax(p.tangentialAccelerationMax)
+	, linearDampingMin(p.linearDampingMin)
+	, linearDampingMax(p.linearDampingMax)
+	, sizes(p.sizes)
+	, sizeVariation(p.sizeVariation)
+	, rotationMin(p.rotationMin)
+	, rotationMax(p.rotationMax)
+	, spinStart(p.spinStart)
+	, spinEnd(p.spinEnd)
+	, spinVariation(p.spinVariation)
+	, offset(p.offset)
+	, defaultOffset(p.defaultOffset)
+	, colors(p.colors)
+	, quads(p.quads)
+	, relativeRotation(p.relativeRotation)
+{
+	setBufferSize(maxParticles);
+}
+
+ParticleSystem::~ParticleSystem()
+{
+	deleteBuffers();
+}
+
+void ParticleSystem::resetOffset()
+{
+	if (quads.empty())
+		offset = love::Vector(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f);
+	else
+	{
+		Quad::Viewport v = quads[0]->getViewport();
+		offset = love::Vector(v.x*0.5f, v.y*0.5f);
+	}
+}
+
+void ParticleSystem::createBuffers(size_t size)
+{
+	try
+	{
+		pFree = pMem = new Particle[size];
+		maxParticles = (uint32) size;
+	}
+	catch (std::bad_alloc &)
+	{
+		deleteBuffers();
+		throw love::Exception("Out of memory");
+	}
+}
+
+void ParticleSystem::deleteBuffers()
+{
+	// Clean up for great gracefulness!
+	delete[] pMem;
+
+	pMem = nullptr;
+	maxParticles = 0;
+	activeParticles = 0;
+}
+
+void ParticleSystem::setBufferSize(uint32 size)
+{
+	if (size == 0 || size > MAX_PARTICLES)
+		throw love::Exception("Invalid buffer size");
+	deleteBuffers();
+	createBuffers(size);
+	reset();
+}
+
+uint32 ParticleSystem::getBufferSize() const
+{
+	return maxParticles;
+}
+
+void ParticleSystem::addParticle(float t)
+{
+	if (isFull())
+		return;
+
+	// Gets a free particle and updates the allocation pointer.
+	Particle *p = pFree++;
+	initParticle(p, t);
+
+	switch (insertMode)
+	{
+	default:
+	case INSERT_MODE_TOP:
+		insertTop(p);
+		break;
+	case INSERT_MODE_BOTTOM:
+		insertBottom(p);
+		break;
+	case INSERT_MODE_RANDOM:
+		insertRandom(p);
+		break;
+	}
+
+	activeParticles++;
+}
+
+void ParticleSystem::initParticle(Particle *p, float t)
+{
+	float min,max;
+
+	// Linearly interpolate between the previous and current emitter position.
+	love::Vector pos = prevPosition + (position - prevPosition) * t;
+
+	min = particleLifeMin;
+	max = particleLifeMax;
+	if (min == max)
+		p->life = min;
+	else
+		p->life = (float) rng.random(min, max);
+	p->lifetime = p->life;
+
+	p->position = pos;
+
+	switch (areaSpreadDistribution)
+	{
+	case DISTRIBUTION_UNIFORM:
+		p->position.x += (float) rng.random(-areaSpread.getX(), areaSpread.getX());
+		p->position.y += (float) rng.random(-areaSpread.getY(), areaSpread.getY());
+		break;
+	case DISTRIBUTION_NORMAL:
+		p->position.x += (float) rng.randomNormal(areaSpread.getX());
+		p->position.y += (float) rng.randomNormal(areaSpread.getY());
+		break;
+	case DISTRIBUTION_NONE:
+	default:
+		break;
+	}
+
+	p->origin = pos;
+
+	min = speedMin;
+	max = speedMax;
+	float speed = (float) rng.random(min, max);
+
+	min = direction - spread/2.0f;
+	max = direction + spread/2.0f;
+	float dir = (float) rng.random(min, max);
+
+	p->velocity = love::Vector(cosf(dir), sinf(dir)) * speed;
+
+	p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x);
+	p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y);
+
+	min = radialAccelerationMin;
+	max = radialAccelerationMax;
+	p->radialAcceleration = (float) rng.random(min, max);
+
+	min = tangentialAccelerationMin;
+	max = tangentialAccelerationMax;
+	p->tangentialAcceleration = (float) rng.random(min, max);
+
+	min = linearDampingMin;
+	max = linearDampingMax;
+	p->linearDamping = (float) rng.random(min, max);
+
+	p->sizeOffset       = (float) rng.random(sizeVariation); // time offset for size change
+	p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset;
+	p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)];
+
+	min = rotationMin;
+	max = rotationMax;
+	p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
+	p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
+	p->rotation = (float) rng.random(min, max);
+
+	p->angle = p->rotation;
+	if (relativeRotation)
+		p->angle += atan2f(p->velocity.y, p->velocity.x);
+
+	p->color = colors[0];
+
+	p->quadIndex = 0;
+}
+
+void ParticleSystem::insertTop(Particle *p)
+{
+	if (pHead == nullptr)
+	{
+		pHead = p;
+		p->prev = nullptr;
+	}
+	else
+	{
+		pTail->next = p;
+		p->prev = pTail;
+	}
+	p->next = nullptr;
+	pTail = p;
+}
+
+void ParticleSystem::insertBottom(Particle *p)
+{
+	if (pTail == nullptr)
+	{
+		pTail = p;
+		p->next = nullptr;
+	}
+	else
+	{
+		pHead->prev = p;
+		p->next = pHead;
+	}
+	p->prev = nullptr;
+	pHead = p;
+}
+
+void ParticleSystem::insertRandom(Particle *p)
+{
+	// Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
+	uint64 pos = rng.rand() % ((int64) activeParticles + 1);
+
+	// Special case where the particle gets inserted before the head.
+	if (pos == activeParticles)
+	{
+		Particle *pA = pHead;
+		if (pA)
+			pA->prev = p;
+		p->prev = nullptr;
+		p->next = pA;
+		pHead = p;
+		return;
+	}
+
+	// Inserts the particle after the randomly selected particle.
+	Particle *pA = pMem + pos;
+	Particle *pB = pA->next;
+	pA->next = p;
+	if (pB)
+		pB->prev = p;
+	else
+		pTail = p;
+	p->prev = pA;
+	p->next = pB;
+}
+
+ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
+{
+	// The linked list is updated in this function and old pointers may be
+	// invalidated. The returned pointer will inform the caller of the new
+	// pointer to the next particle.
+	Particle *pNext = nullptr;
+
+	// Removes the particle from the linked list.
+	if (p->prev)
+		p->prev->next = p->next;
+	else
+		pHead = p->next;
+
+	if (p->next)
+	{
+		p->next->prev = p->prev;
+		pNext = p->next;
+	}
+	else
+		pTail = p->prev;
+
+	// The (in memory) last particle can now be moved into the free slot.
+	// It will skip the moving if it happens to be the removed particle.
+	pFree--;
+	if (p != pFree)
+	{
+		*p = *pFree;
+		if (pNext == pFree)
+			pNext = p;
+
+		if (p->prev)
+			p->prev->next = p;
+		else
+			pHead = p;
+
+		if (p->next)
+			p->next->prev = p;
+		else
+			pTail = p;
+	}
+
+	activeParticles--;
+	return pNext;
+}
+
+void ParticleSystem::setTexture(Texture *tex)
+{
+	texture.set(tex);
+
+	if (defaultOffset)
+		resetOffset();
+}
+
+Texture *ParticleSystem::getTexture() const
+{
+	return texture.get();
+}
+
+void ParticleSystem::setInsertMode(InsertMode mode)
+{
+	insertMode = mode;
+}
+
+ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
+{
+	return insertMode;
+}
+
+void ParticleSystem::setEmissionRate(float rate)
+{
+	if (rate < 0.0f)
+		throw love::Exception("Invalid emission rate");
+	emissionRate = rate;
+}
+
+float ParticleSystem::getEmissionRate() const
+{
+	return emissionRate;
+}
+
+void ParticleSystem::setEmitterLifetime(float life)
+{
+	this->life = lifetime = life;
+}
+
+float ParticleSystem::getEmitterLifetime() const
+{
+	return lifetime;
+}
+
+void ParticleSystem::setParticleLifetime(float min, float max)
+{
+	particleLifeMin = min;
+	if (max == 0)
+		particleLifeMax = min;
+	else
+		particleLifeMax = max;
+}
+
+void ParticleSystem::getParticleLifetime(float &min, float &max) const
+{
+	min = particleLifeMin;
+	max = particleLifeMax;
+}
+
+void ParticleSystem::setPosition(float x, float y)
+{
+	position = love::Vector(x, y);
+	prevPosition = position;
+}
+
+const love::Vector &ParticleSystem::getPosition() const
+{
+	return position;
+}
+
+void ParticleSystem::moveTo(float x, float y)
+{
+	position = love::Vector(x, y);
+}
+
+void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
+{
+	areaSpread = love::Vector(x, y);
+	areaSpreadDistribution = distribution;
+}
+
+ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
+{
+	return areaSpreadDistribution;
+}
+
+const love::Vector &ParticleSystem::getAreaSpreadParameters() const
+{
+	return areaSpread;
+}
+
+void ParticleSystem::setDirection(float direction)
+{
+	this->direction = direction;
+}
+
+float ParticleSystem::getDirection() const
+{
+	return direction;
+}
+
+void ParticleSystem::setSpread(float spread)
+{
+	this->spread = spread;
+}
+
+float ParticleSystem::getSpread() const
+{
+	return spread;
+}
+
+void ParticleSystem::setSpeed(float speed)
+{
+	speedMin = speedMax = speed;
+}
+
+void ParticleSystem::setSpeed(float min, float max)
+{
+	speedMin = min;
+	speedMax = max;
+}
+
+void ParticleSystem::getSpeed(float &min, float &max) const
+{
+	min = speedMin;
+	max = speedMax;
+}
+
+void ParticleSystem::setLinearAcceleration(float x, float y)
+{
+	linearAccelerationMin.x = linearAccelerationMax.x = x;
+	linearAccelerationMin.y = linearAccelerationMax.y = y;
+}
+
+void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax)
+{
+	linearAccelerationMin = love::Vector(xmin, ymin);
+	linearAccelerationMax = love::Vector(xmax, ymax);
+}
+
+void ParticleSystem::getLinearAcceleration(love::Vector &min, love::Vector &max) const
+{
+	min = linearAccelerationMin;
+	max = linearAccelerationMax;
+}
+
+void ParticleSystem::setRadialAcceleration(float acceleration)
+{
+	radialAccelerationMin = radialAccelerationMax = acceleration;
+}
+
+void ParticleSystem::setRadialAcceleration(float min, float max)
+{
+	radialAccelerationMin = min;
+	radialAccelerationMax = max;
+}
+
+void ParticleSystem::getRadialAcceleration(float &min, float &max) const
+{
+	min = radialAccelerationMin;
+	max = radialAccelerationMax;
+}
+
+void ParticleSystem::setTangentialAcceleration(float acceleration)
+{
+	tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
+}
+
+void ParticleSystem::setTangentialAcceleration(float min, float max)
+{
+	tangentialAccelerationMin = min;
+	tangentialAccelerationMax = max;
+}
+
+void ParticleSystem::getTangentialAcceleration(float &min, float &max) const
+{
+	min = tangentialAccelerationMin;
+	max = tangentialAccelerationMax;
+}
+
+void ParticleSystem::setLinearDamping(float min, float max)
+{
+	linearDampingMin = min;
+	linearDampingMax = max;
+}
+
+void ParticleSystem::getLinearDamping(float &min, float &max) const
+{
+	min = linearDampingMin;
+	max = linearDampingMax;
+}
+
+void ParticleSystem::setSize(float size)
+{
+	sizes.resize(1);
+	sizes[0] = size;
+}
+
+void ParticleSystem::setSizes(const std::vector<float> &newSizes)
+{
+	sizes = newSizes;
+}
+
+const std::vector<float> &ParticleSystem::getSizes() const
+{
+	return sizes;
+}
+
+void ParticleSystem::setSizeVariation(float variation)
+{
+	sizeVariation = variation;
+}
+
+float ParticleSystem::getSizeVariation() const
+{
+	return sizeVariation;
+}
+
+void ParticleSystem::setRotation(float rotation)
+{
+	rotationMin = rotationMax = rotation;
+}
+
+void ParticleSystem::setRotation(float min, float max)
+{
+	rotationMin = min;
+	rotationMax = max;
+}
+
+void ParticleSystem::getRotation(float &min, float &max) const
+{
+	min = rotationMin;
+	max = rotationMax;
+}
+
+void ParticleSystem::setSpin(float spin)
+{
+	spinStart = spin;
+	spinEnd = spin;
+}
+
+void ParticleSystem::setSpin(float start, float end)
+{
+	spinStart = start;
+	spinEnd = end;
+}
+
+void ParticleSystem::getSpin(float &start, float &end) const
+{
+	start = spinStart;
+	end = spinEnd;
+}
+
+void ParticleSystem::setSpinVariation(float variation)
+{
+	spinVariation = variation;
+}
+
+float ParticleSystem::getSpinVariation() const
+{
+	return spinVariation;
+}
+
+void ParticleSystem::setOffset(float x, float y)
+{
+	offset = love::Vector(x, y);
+	defaultOffset = false;
+}
+
+love::Vector ParticleSystem::getOffset() const
+{
+	return offset;
+}
+
+void ParticleSystem::setColor(const std::vector<Colorf> &newColors)
+{
+	colors = newColors;
+
+	for (Colorf &c : colors)
+	{
+		// We want to store the colors as [0, 1], rather than [0, 255].
+		c.r /= 255.0f;
+		c.g /= 255.0f;
+		c.b /= 255.0f;
+		c.a /= 255.0f;
+	}
+}
+
+std::vector<Colorf> ParticleSystem::getColor() const
+{
+	// The particle system stores colors in the range of [0, 1]...
+	std::vector<Colorf> ncolors(colors);
+
+	for (Colorf &c : ncolors)
+	{
+		c.r *= 255.0f;
+		c.g *= 255.0f;
+		c.b *= 255.0f;
+		c.a *= 255.0f;
+	}
+
+	return ncolors;
+}
+
+void ParticleSystem::setQuads(const std::vector<Quad *> &newQuads)
+{
+	std::vector<StrongRef<Quad>> quadlist;
+	quadlist.reserve(newQuads.size());
+
+	for (Quad *q : newQuads)
+		quadlist.push_back(q);
+
+	quads = quadlist;
+
+	if (defaultOffset)
+		resetOffset();
+}
+
+void ParticleSystem::setQuads()
+{
+	quads.clear();
+}
+
+std::vector<Quad *> ParticleSystem::getQuads() const
+{
+	std::vector<Quad *> quadlist;
+	quadlist.reserve(quads.size());
+
+	for (const StrongRef<Quad> &q : quads)
+		quadlist.push_back(q.get());
+
+	return quadlist;
+}
+
+void ParticleSystem::setRelativeRotation(bool enable)
+{
+	relativeRotation = enable;
+}
+
+bool ParticleSystem::hasRelativeRotation() const
+{
+	return relativeRotation;
+}
+
+uint32 ParticleSystem::getCount() const
+{
+	return activeParticles;
+}
+
+void ParticleSystem::start()
+{
+	active = true;
+}
+
+void ParticleSystem::stop()
+{
+	active = false;
+	life = lifetime;
+	emitCounter = 0;
+}
+
+void ParticleSystem::pause()
+{
+	active = false;
+}
+
+void ParticleSystem::reset()
+{
+	if (pMem == nullptr)
+		return;
+
+	pFree = pMem;
+	pHead = nullptr;
+	pTail = nullptr;
+	activeParticles = 0;
+	life = lifetime;
+	emitCounter = 0;
+}
+
+void ParticleSystem::emit(uint32 num)
+{
+	if (!active)
+		return;
+
+	num = std::min(num, maxParticles - activeParticles);
+
+	while (num--)
+		addParticle(1.0f);
+}
+
+bool ParticleSystem::isActive() const
+{
+	return active;
+}
+
+bool ParticleSystem::isPaused() const
+{
+	return !active && life < lifetime;
+}
+
+bool ParticleSystem::isStopped() const
+{
+	return !active && life >= lifetime;
+}
+
+bool ParticleSystem::isEmpty() const
+{
+	return activeParticles == 0;
+}
+
+bool ParticleSystem::isFull() const
+{
+	return activeParticles == maxParticles;
+}
+
+void ParticleSystem::update(float dt)
+{
+	if (pMem == nullptr || dt == 0.0f)
+		return;
+
+	// Traverse all particles and update.
+	Particle *p = pHead;
+
+	while (p)
+	{
+		// Decrease lifespan.
+		p->life -= dt;
+
+		if (p->life <= 0)
+			p = removeParticle(p);
+		else
+		{
+			// Temp variables.
+			love::Vector radial, tangential;
+			love::Vector ppos = p->position;
+
+			// Get vector from particle center to particle.
+			radial = ppos - p->origin;
+			radial.normalize();
+			tangential = radial;
+
+			// Resize radial acceleration.
+			radial *= p->radialAcceleration;
+
+			// Calculate tangential acceleration.
+			{
+				float a = tangential.getX();
+				tangential.setX(-tangential.getY());
+				tangential.setY(a);
+			}
+
+			// Resize tangential.
+			tangential *= p->tangentialAcceleration;
+
+			// Update velocity.
+			p->velocity += (radial + tangential + p->linearAcceleration) * dt;
+
+			// Apply damping.
+			p->velocity *= 1.0f / (1.0f + p->linearDamping * dt);
+
+			// Modify position.
+			ppos += p->velocity * dt;
+
+			p->position = ppos;
+
+			const float t = 1.0f - p->life / p->lifetime;
+
+			// Rotate.
+			p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt;
+
+			p->angle = p->rotation;
+
+			if (relativeRotation)
+				p->angle += atan2f(p->velocity.y, p->velocity.x);
+
+			// Change size according to given intervals:
+			// i = 0       1       2      3          n-1
+			//     |-------|-------|------|--- ... ---|
+			// t = 0    1/(n-1)        3/(n-1)        1
+			//
+			// `s' is the interpolation variable scaled to the current
+			// interval width, e.g. if n = 5 and t = 0.3, then the current
+			// indices are 1,2 and s = 0.3 - 0.25 = 0.05
+			float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation
+			s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size()
+			size_t i = (size_t)s;
+			size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f)
+			s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1
+			p->size = sizes[i] * (1.0f - s) + sizes[k] * s;
+
+			// Update color according to given intervals (as above)
+			s = t * (float)(colors.size() - 1);
+			i = (size_t)s;
+			k = (i == colors.size() - 1) ? i : i + 1;
+			s -= (float)i;                            // 0 <= s <= 1
+			p->color = colors[i] * (1.0f - s) + colors[k] * s;
+
+			// Update the quad index.
+			k = quads.size();
+			if (k > 0)
+			{
+				s = t * (float) k; // [0:numquads-1] (clamped below)
+				i = (s > 0.0f) ? (size_t) s : 0;
+				p->quadIndex = (int) ((i < k) ? i : k - 1);
+			}
+
+			// Next particle.
+			p = p->next;
+		}
+	}
+
+	// Make some more particles.
+	if (active)
+	{
+		float rate = 1.0f / emissionRate; // the amount of time between each particle emit
+		emitCounter += dt;
+		float total = emitCounter - rate;
+		while (emitCounter > rate)
+		{
+			addParticle(1.0f - (emitCounter - rate) / total);
+			emitCounter -= rate;
+		}
+		/*int particles = (int)(emissionRate * dt);
+		 for (int i = 0; i != particles; i++)
+		 add();*/
+
+		life -= dt;
+		if (lifetime != -1 && life < 0)
+			stop();
+	}
+
+	prevPosition = position;
+}
+
+bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
+{
+	return distributions.find(in, out);
+}
+
+bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
+{
+	return distributions.find(in, out);
+}
+
+bool ParticleSystem::getConstant(const char *in, InsertMode &out)
+{
+	return insertModes.find(in, out);
+}
+
+bool ParticleSystem::getConstant(InsertMode in, const char *&out)
+{
+	return insertModes.find(in, out);
+}
+
+StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] =
+{
+	{ "none",    DISTRIBUTION_NONE },
+	{ "uniform", DISTRIBUTION_UNIFORM },
+	{ "normal",  DISTRIBUTION_NORMAL },
+};
+
+StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));
+
+StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM>::Entry ParticleSystem::insertModesEntries[] =
+{
+	{ "top",    INSERT_MODE_TOP },
+	{ "bottom", INSERT_MODE_BOTTOM },
+	{ "random", INSERT_MODE_RANDOM },
+};
+
+StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM> ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries));
+
+} // graphics
+} // love

+ 672 - 0
src/modules/graphics/ParticleSystem.h

@@ -0,0 +1,672 @@
+/**
+ * Copyright (c) 2006-2015 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_GRAPHICS_PARTICLE_SYSTEM_H
+#define LOVE_GRAPHICS_PARTICLE_SYSTEM_H
+
+// LOVE
+#include "common/int.h"
+#include "common/math.h"
+#include "common/Vector.h"
+#include "Drawable.h"
+#include "Color.h"
+#include "Quad.h"
+#include "Texture.h"
+
+// STL
+#include <vector>
+
+namespace love
+{
+namespace graphics
+{
+
+/**
+ * A class for creating, moving and drawing particles.
+ * A big thanks to bobthebloke.org
+ **/
+class ParticleSystem : public Drawable
+{
+public:
+	/**
+	 * Type of distribution new particles are drawn from: None, uniform, normal.
+	 */
+	enum AreaSpreadDistribution
+	{
+		DISTRIBUTION_NONE,
+		DISTRIBUTION_UNIFORM,
+		DISTRIBUTION_NORMAL,
+		DISTRIBUTION_MAX_ENUM
+	};
+
+	/**
+	 * Insertion modes of new particles in the list: top, bottom, random.
+	 */
+	enum InsertMode
+	{
+		INSERT_MODE_TOP,
+		INSERT_MODE_BOTTOM,
+		INSERT_MODE_RANDOM,
+		INSERT_MODE_MAX_ENUM
+	};
+
+	/**
+	 * Maximum numbers of particles in a ParticleSystem.
+	 * This limit comes from the fact that a quad requires four vertices and the
+	 * OpenGL API where GLsizei is a signed int.
+	 **/
+	static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
+
+	/**
+	 * Creates a particle system with the specified buffer size and texture.
+	 **/
+	ParticleSystem(Texture *texture, uint32 buffer);
+	ParticleSystem(const ParticleSystem &p);
+
+	/**
+	 * Deletes any allocated memory.
+	 **/
+	virtual ~ParticleSystem();
+
+	/**
+	 * Creates an identical copy of this ParticleSystem. The clone does not
+	 * duplicate any existing particles from this ParticleSystem, just the
+	 * settable parameters.
+	 **/
+	virtual ParticleSystem *clone() = 0;
+
+	/**
+	 * Sets the texture used in the particle system.
+	 * @param texture The new texture.
+	 **/
+	void setTexture(Texture *texture);
+
+	/**
+	 * Returns the texture used when drawing the particle system.
+	 **/
+	Texture *getTexture() const;
+
+	/**
+	 * Clears the current buffer and allocates the appropriate amount of space for the buffer.
+	 * @param size The new buffer size.
+	 **/
+	virtual void setBufferSize(uint32 size);
+
+	/**
+	 * Returns the total amount of particles this ParticleSystem can have active
+	 * at any given point in time.
+	 **/
+	uint32 getBufferSize() const;
+
+	/**
+	 * Sets the insert mode for new particles.
+	 * @param mode The new insert mode.
+	 */
+	void setInsertMode(InsertMode mode);
+
+	/**
+	 * Returns the current insert mode.
+	 */
+	InsertMode getInsertMode() const;
+
+	/**
+	 * Sets the emission rate.
+	 * @param rate The amount of particles per second.
+	 **/
+	void setEmissionRate(float rate);
+
+	/**
+	 * Returns the number of particles created per second.
+	 **/
+	float getEmissionRate() const;
+
+	/**
+	 * Sets the lifetime of the particle emitter (-1 means eternal)
+	 * @param life The lifetime (in seconds).
+	 **/
+	void setEmitterLifetime(float life);
+
+	/**
+	 * Returns the lifetime of the particle emitter.
+	 **/
+	float getEmitterLifetime() const;
+
+	/**
+	 * Sets the life range of the particles.
+	 * @param min The minimum life.
+	 * @param max The maximum life (if 0, then becomes the same as minimum life).
+	 **/
+	void setParticleLifetime(float min, float max = 0);
+
+	/**
+	 * Gets the lifetime of a particle.
+	 * @param[out] min The minimum life.
+	 * @param[out] max The maximum life.
+	 **/
+	void getParticleLifetime(float &min, float &max) const;
+
+	/**
+	 * Sets the position of the center of the emitter.
+	 * Used to move the emitter without changing the position of already existing particles.
+	 * @param x The x-coordinate.
+	 * @param y The y-coordinate.
+	 **/
+	void setPosition(float x, float y);
+
+	/**
+	 * Returns the position of the emitter.
+	 **/
+	const love::Vector &getPosition() const;
+
+	/**
+	 * Moves the position of the center of the emitter.
+	 * When update is called, newly spawned particles will appear in a line
+	 * between the old emitter position and where the emitter was moved to,
+	 * resulting in a smoother-feeling particle system if moveTo is called
+	 * repeatedly.
+	 **/
+	void moveTo(float x, float y);
+
+	/**
+	 * Sets the emission area spread parameters and distribution type. The interpretation of
+	 * the parameters depends on the distribution type:
+	 *
+	 * * None:    Parameters are ignored. No area spread.
+	 * * Uniform: Parameters denote maximal (symmetric) displacement from emitter position.
+	 * * Normal:  Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
+	 * @param x First parameter. Interpretation depends on distribution type.
+	 * @param y Second parameter. Interpretation depends on distribution type.
+	 * @param distribution Distribution type
+	 **/
+	void setAreaSpread(AreaSpreadDistribution distribution, float x, float y);
+
+	/**
+	 * Returns area spread distribution type.
+	 **/
+	AreaSpreadDistribution getAreaSpreadDistribution() const;
+
+	/**
+	 * Returns area spread parameters.
+	 **/
+	const love::Vector &getAreaSpreadParameters() const;
+
+	/**
+	 * Sets the direction of the particle emitter.
+	 * @param direction The direction (in degrees).
+	 **/
+	void setDirection(float direction);
+
+	/**
+	 * Returns the direction of the particle emitter (in radians).
+	 **/
+	float getDirection() const;
+
+	/**
+	 * Sets the spread of the particle emitter.
+	 * @param spread The spread (in radians).
+	 **/
+	void setSpread(float spread);
+
+	/**
+	 * Returns the directional spread of the emitter (in radians).
+	 **/
+	float getSpread() const;
+
+	/**
+	 * Sets the speed of the particles.
+	 * @param speed The speed.
+	 **/
+	void setSpeed(float speed);
+
+	/**
+	 * Sets the speed of the particles.
+	 * @param min The minimum speed.
+	 * @param max The maximum speed.
+	 **/
+	void setSpeed(float min, float max);
+
+	/**
+	 * Gets the speed of the particles.
+	 * @param[out] min The minimum speed.
+	 * @param[out] max The maximum speed.
+	 **/
+	void getSpeed(float &min, float &max) const;
+
+	/**
+	 * Sets the linear acceleration (the acceleration along the x and y axes).
+	 * @param x The acceleration along the x-axis.
+	 * @param y The acceleration along the y-axis.
+	 **/
+	void setLinearAcceleration(float x, float y);
+
+	/**
+	 * Sets the linear acceleration (the acceleration along the x and y axes).
+	 * @param xmin The minimum amount of acceleration along the x-axis.
+	 * @param ymin The minimum amount of acceleration along the y-axis.
+	 * @param xmax The maximum amount of acceleration along the x-axis.
+	 * @param ymax The maximum amount of acceleration along the y-axis.
+	 **/
+	void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax);
+
+	/**
+	 * Gets the linear acceleration of the particles.
+	 * @param[out] min The minimum acceleration.
+	 * @param[out] max The maximum acceleration.
+	 **/
+	void getLinearAcceleration(love::Vector &min, love::Vector &max) const;
+
+	/**
+	 * Sets the radial acceleration (the acceleration towards the particle emitter).
+	 * @param acceleration The amount of acceleration.
+	 **/
+	void setRadialAcceleration(float acceleration);
+
+	/**
+	 * Sets the radial acceleration (the acceleration towards the particle emitter).
+	 * @param min The minimum acceleration.
+	 * @param max The maximum acceleration.
+	 **/
+	void setRadialAcceleration(float min, float max);
+
+	/**
+	 * Gets the radial acceleration.
+	 * @param[out] min The minimum amount of radial acceleration.
+	 * @param[out] max The maximum amount of radial acceleration.
+	 **/
+	void getRadialAcceleration(float &min, float &max) const;
+
+	/**
+	 * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
+	 * @param acceleration The amount of acceleration.
+	 **/
+	void setTangentialAcceleration(float acceleration);
+
+	/**
+	 * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
+	 * @param min The minimum acceleration.
+	 * @param max The maximum acceleration.
+	 **/
+	void setTangentialAcceleration(float min, float max);
+
+	/**
+	 * Gets the tangential acceleration.
+	 * @param[out] min The minimum tangential acceleration.
+	 * @param[out] max The maximum tangential acceleration.
+	 **/
+	void getTangentialAcceleration(float &min, float &max) const;
+
+	/**
+	 * Sets the amount of linear damping. Damping reduces the velocity of
+	 * particles over time. A value of 0 corresponds to no damping.
+	 **/
+	void setLinearDamping(float min, float max);
+
+	/**
+	 * Gets the current amount of linear damping.
+	 **/
+	void getLinearDamping(float &min, float &max) const;
+
+	/**
+	 * Sets the size of the sprite (1.0 being the default size).
+	 * @param size The size of the sprite.
+	 **/
+	void setSize(float size);
+
+	/**
+	 * Sets the sizes of the sprite upon creation and upon death (1.0 being the default size).
+	 * @param newSizes Array of sizes
+	 **/
+	void setSizes(const std::vector<float> &newSizes);
+
+	/**
+	 * Returns the sizes of the particle sprites.
+	 **/
+	const std::vector<float> &getSizes() const;
+
+	/**
+	 * Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end).
+	 * @param variation The amount of variation.
+	 **/
+	void setSizeVariation(float variation);
+
+	/**
+	 * Returns the amount of initial size variation between particles.
+	 **/
+	float getSizeVariation() const;
+
+	/**
+	 * Sets the amount of rotation a sprite starts out with.
+	 * @param rotation The amount of rotation.
+	 **/
+	void setRotation(float rotation);
+
+	/**
+	 * Sets the amount of rotation a sprite starts out with (a random value between min and max).
+	 * @param min The minimum amount of rotation.
+	 * @param max The maximum amount of rotation.
+	 **/
+	void setRotation(float min, float max);
+
+	/**
+	 * Gets the initial amount of rotation of a particle, in radians.
+	 * @param[out] min The minimum initial rotation.
+	 * @param[out] max The maximum initial rotation.
+	 **/
+	void getRotation(float &min, float &max) const;
+
+	/**
+	 * Sets the spin of the sprite.
+	 * @param spin The spin of the sprite (in degrees).
+	 **/
+	void setSpin(float spin);
+
+	/**
+	 * Sets the spin of the sprite upon particle creation and death.
+	 * @param start The spin of the sprite upon creation (in radians / second).
+	 * @param end The spin of the sprite upon death (in radians / second).
+	 **/
+	void setSpin(float start, float end);
+
+	/**
+	 * Gets the amount of spin of a particle during its lifetime.
+	 * @param[out] start The initial spin, in radians / s.
+	 * @param[out] end The final spin, in radians / s.
+	 **/
+	void getSpin(float &start, float &end) const;
+
+	/**
+	 * Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end).
+	 * @param variation The variation.
+	 **/
+	void setSpinVariation(float variation);
+
+	/**
+	 * Returns the amount of variation of the start spin of a particle.
+	 **/
+	float getSpinVariation() const;
+
+	/**
+	 * Sets the particles' offsets for rotation.
+	 * @param x The x offset.
+	 * @param y The y offset.
+	 **/
+	void setOffset(float x, float y);
+
+	/**
+	 * Returns of the particle offset.
+	 **/
+	love::Vector getOffset() const;
+
+	/**
+	 * Sets the color of the particles.
+	 * @param newColors Array of colors
+	 **/
+	void setColor(const std::vector<Colorf> &newColors);
+
+	/**
+	 * Returns the color of the particles.
+	 **/
+	std::vector<Colorf> getColor() const;
+
+	/**
+	 * Sets a list of Quads to use for particles over their lifetime.
+	 **/
+	void setQuads(const std::vector<Quad *> &newQuads);
+	void setQuads();
+
+	/**
+	 * Gets the Quads used when drawing the particles.
+	 **/
+	std::vector<Quad *> getQuads() const;
+
+	/**
+	 * sets whether particle angles & rotations are relative to their velocities.
+	 **/
+	void setRelativeRotation(bool enable);
+	bool hasRelativeRotation() const;
+
+	/**
+	 * Returns the amount of particles that are currently active in the system.
+	 **/
+	uint32 getCount() const;
+
+	/**
+	 * Starts/resumes the particle emitter.
+	 **/
+	void start();
+
+	/**
+	 * Stops the particle emitter and resets.
+	 **/
+	void stop();
+
+	/**
+	 * Pauses the particle emitter.
+	 **/
+	void pause();
+
+	/**
+	 * Resets the particle emitter.
+	 **/
+	void reset();
+
+	/**
+	 * Instantly emits a number of particles.
+	 * @param num The number of particles to emit.
+	 **/
+	void emit(uint32 num);
+
+	/**
+	 * Returns whether the particle emitter is active.
+	 **/
+	bool isActive() const;
+
+	/**
+	 * Returns whether the particle emitter is paused.
+	 **/
+	bool isPaused() const;
+
+	bool isStopped() const;
+
+	/**
+	 * Returns whether the particle system is empty of particles or not.
+	 **/
+	bool isEmpty() const;
+
+	/**
+	 * Returns whether the amount of particles has reached the buffer limit or not.
+	 **/
+	bool isFull() const;
+
+	/**
+	 * Updates the particle system.
+	 * @param dt Time since last update.
+	 **/
+	void update(float dt);
+
+	static bool getConstant(const char *in, AreaSpreadDistribution &out);
+	static bool getConstant(AreaSpreadDistribution in, const char *&out);
+
+	static bool getConstant(const char *in, InsertMode &out);
+	static bool getConstant(InsertMode in, const char *&out);
+
+protected:
+
+	// Represents a single particle.
+	struct Particle
+	{
+		Particle *prev;
+		Particle *next;
+
+		float lifetime;
+		float life;
+
+		love::Vector position;
+
+		// Particles gravitate towards this point.
+		love::Vector origin;
+
+		love::Vector velocity;
+		love::Vector linearAcceleration;
+		float radialAcceleration;
+		float tangentialAcceleration;
+
+		float linearDamping;
+
+		float size;
+		float sizeOffset;
+		float sizeIntervalSize;
+
+		float rotation; // Amount of rotation applied to the final angle.
+		float angle;
+		float spinStart;
+		float spinEnd;
+
+		Colorf color;
+
+		int quadIndex;
+	};
+
+	// Pointer to the beginning of the allocated memory.
+	Particle *pMem;
+
+	// Pointer to a free particle.
+	Particle *pFree;
+
+	// Pointer to the start of the linked list.
+	Particle *pHead;
+
+	// Pointer to the end of the linked list.
+	Particle *pTail;
+
+	// The texture to be drawn.
+	StrongRef<Texture> texture;
+
+	// Whether the particle emitter is active.
+	bool active;
+
+	// Insert mode of new particles.
+	InsertMode insertMode;
+
+	// The maximum number of particles.
+	uint32 maxParticles;
+
+	// The number of active particles.
+	uint32 activeParticles;
+
+	// The emission rate (particles/sec).
+	float emissionRate;
+
+	// Used to determine when a particle should be emitted.
+	float emitCounter;
+
+	// The relative position of the particle emitter.
+	love::Vector position;
+	love::Vector prevPosition;
+
+	// Emission area spread.
+	AreaSpreadDistribution areaSpreadDistribution;
+	love::Vector areaSpread;
+
+	// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
+	float lifetime;
+	float life;
+
+	// The particle life.
+	float particleLifeMin;
+	float particleLifeMax;
+
+	// The direction (and spread) the particles will be emitted in. Measured in radians.
+	float direction;
+	float spread;
+
+	// The speed.
+	float speedMin;
+	float speedMax;
+
+	// Acceleration along the x and y axes.
+	love::Vector linearAccelerationMin;
+	love::Vector linearAccelerationMax;
+
+	// Acceleration towards the emitter's center
+	float radialAccelerationMin;
+	float radialAccelerationMax;
+
+	// Acceleration perpendicular to the particle's direction.
+	float tangentialAccelerationMin;
+	float tangentialAccelerationMax;
+
+	float linearDampingMin;
+	float linearDampingMax;
+
+	// Size.
+	std::vector<float> sizes;
+	float sizeVariation;
+
+	// Rotation
+	float rotationMin;
+	float rotationMax;
+
+	// Spin.
+	float spinStart;
+	float spinEnd;
+	float spinVariation;
+
+	// Offsets
+	love::Vector offset;
+
+	// Is the ParticleSystem using a default offset?
+	bool defaultOffset;
+
+	// Color.
+	std::vector<Colorf> colors;
+
+	// Quads.
+	std::vector<StrongRef<Quad>> quads;
+
+	bool relativeRotation;
+
+private:
+
+	void resetOffset();
+
+	void createBuffers(size_t size);
+	void deleteBuffers();
+
+	void addParticle(float t);
+	Particle *removeParticle(Particle *p);
+
+	// Called by addParticle.
+	void initParticle(Particle *p, float t);
+	void insertTop(Particle *p);
+	void insertBottom(Particle *p);
+	void insertRandom(Particle *p);
+
+	static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
+	static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
+
+	static StringMap<InsertMode, INSERT_MODE_MAX_ENUM>::Entry insertModesEntries[];
+	static StringMap<InsertMode, INSERT_MODE_MAX_ENUM> insertModes;
+};
+
+} // graphics
+} // love
+
+#endif // LOVE_GRAPHICS_PARTICLE_SYSTEM_H

+ 18 - 931
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -22,8 +22,6 @@
 #include "common/config.h"
 #include "common/config.h"
 #include "ParticleSystem.h"
 #include "ParticleSystem.h"
 
 
-#include "common/math.h"
-#include "modules/math/RandomGenerator.h"
 #include "OpenGL.h"
 #include "OpenGL.h"
 
 
 // STD
 // STD
@@ -38,812 +36,58 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
-namespace
-{
-
-love::math::RandomGenerator rng;
-
-float calculate_variation(float inner, float outer, float var)
-{
-	float low = inner - (outer/2.0f)*var;
-	float high = inner + (outer/2.0f)*var;
-	float r = (float) rng.random();
-	return low*(1-r)+high*r;
-}
-
-} // anonymous namespace
-
 ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
 ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
-	: pMem(nullptr)
-	, pFree(nullptr)
-	, pHead(nullptr)
-	, pTail(nullptr)
+	: love::graphics::ParticleSystem(texture, size)
 	, particleVerts(nullptr)
 	, particleVerts(nullptr)
-	, quadIndices(1)
-	, texture(texture)
-	, active(true)
-	, insertMode(INSERT_MODE_TOP)
-	, maxParticles(0)
-	, activeParticles(0)
-	, emissionRate(0)
-	, emitCounter(0)
-	, areaSpreadDistribution(DISTRIBUTION_NONE)
-	, lifetime(-1)
-	, life(0)
-	, particleLifeMin(0)
-	, particleLifeMax(0)
-	, direction(0)
-	, spread(0)
-	, speedMin(0)
-	, speedMax(0)
-	, linearAccelerationMin(0, 0)
-	, linearAccelerationMax(0, 0)
-	, radialAccelerationMin(0)
-	, radialAccelerationMax(0)
-	, tangentialAccelerationMin(0)
-	, tangentialAccelerationMax(0)
-	, linearDampingMin(0.0f)
-	, linearDampingMax(0.0f)
-	, sizeVariation(0)
-	, rotationMin(0)
-	, rotationMax(0)
-	, spinStart(0)
-	, spinEnd(0)
-	, spinVariation(0)
-	, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
-	, defaultOffset(true)
-	, relativeRotation(false)
+	, quadIndices(size)
 {
 {
-	if (size == 0 || size > MAX_PARTICLES)
-		throw love::Exception("Invalid ParticleSystem size.");
-
-	sizes.push_back(1.0f);
-	colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
-	setBufferSize(size);
+	createVertices(size);
 }
 }
 
 
 ParticleSystem::ParticleSystem(const ParticleSystem &p)
 ParticleSystem::ParticleSystem(const ParticleSystem &p)
-	: pMem(nullptr)
-	, pFree(nullptr)
-	, pHead(nullptr)
-	, pTail(nullptr)
+	: love::graphics::ParticleSystem(p)
 	, particleVerts(nullptr)
 	, particleVerts(nullptr)
 	, quadIndices(p.quadIndices)
 	, quadIndices(p.quadIndices)
-	, texture(p.texture)
-	, active(p.active)
-	, insertMode(p.insertMode)
-	, maxParticles(p.maxParticles)
-	, activeParticles(0)
-	, emissionRate(p.emissionRate)
-	, emitCounter(0.0f)
-	, position(p.position)
-	, prevPosition(p.prevPosition)
-	, areaSpreadDistribution(p.areaSpreadDistribution)
-	, areaSpread(p.areaSpread)
-	, lifetime(p.lifetime)
-	, life(p.lifetime) // Initialize with the maximum life time.
-	, particleLifeMin(p.particleLifeMin)
-	, particleLifeMax(p.particleLifeMax)
-	, direction(p.direction)
-	, spread(p.spread)
-	, speedMin(p.speedMin)
-	, speedMax(p.speedMax)
-	, linearAccelerationMin(p.linearAccelerationMin)
-	, linearAccelerationMax(p.linearAccelerationMax)
-	, radialAccelerationMin(p.radialAccelerationMin)
-	, radialAccelerationMax(p.radialAccelerationMax)
-	, tangentialAccelerationMin(p.tangentialAccelerationMin)
-	, tangentialAccelerationMax(p.tangentialAccelerationMax)
-	, linearDampingMin(p.linearDampingMin)
-	, linearDampingMax(p.linearDampingMax)
-	, sizes(p.sizes)
-	, sizeVariation(p.sizeVariation)
-	, rotationMin(p.rotationMin)
-	, rotationMax(p.rotationMax)
-	, spinStart(p.spinStart)
-	, spinEnd(p.spinEnd)
-	, spinVariation(p.spinVariation)
-	, offset(p.offset)
-	, defaultOffset(p.defaultOffset)
-	, colors(p.colors)
-	, quads(p.quads)
-	, relativeRotation(p.relativeRotation)
 {
 {
-	setBufferSize(maxParticles);
+	createVertices(maxParticles);
 }
 }
 
 
 ParticleSystem::~ParticleSystem()
 ParticleSystem::~ParticleSystem()
 {
 {
-	deleteBuffers();
-}
-
-ParticleSystem *ParticleSystem::clone()
-{
-	return new ParticleSystem(*this);
-}
-
-void ParticleSystem::resetOffset()
-{
-	if (quads.empty())
-		offset = love::Vector(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f);
-	else
-	{
-		Quad::Viewport v = quads[0]->getViewport();
-		offset = love::Vector(v.x*0.5f, v.y*0.5f);
-	}
+	delete[] particleVerts;
 }
 }
 
 
-void ParticleSystem::createBuffers(size_t size)
+void ParticleSystem::createVertices(size_t numparticles)
 {
 {
 	try
 	try
 	{
 	{
-		pFree = pMem = new Particle[size];
-		particleVerts = new love::Vertex[size * 4];
-		maxParticles = (uint32) size;
+		love::Vertex *pverts = new love::Vertex[numparticles * 4];
+		delete[] particleVerts;
+		particleVerts = pverts;
 	}
 	}
-	catch (std::bad_alloc &)
+	catch (std::exception &)
 	{
 	{
-		deleteBuffers();
-		throw love::Exception("Out of memory");
+		throw love::Exception("Out of memory.");
 	}
 	}
 }
 }
 
 
-void ParticleSystem::deleteBuffers()
+ParticleSystem *ParticleSystem::clone()
 {
 {
-	// Clean up for great gracefulness!
-	delete[] pMem;
-	delete[] particleVerts;
-
-	pMem = nullptr;
-	particleVerts = nullptr;
-	maxParticles = 0;
-	activeParticles = 0;
+	return new ParticleSystem(*this);
 }
 }
 
 
 void ParticleSystem::setBufferSize(uint32 size)
 void ParticleSystem::setBufferSize(uint32 size)
 {
 {
-	if (size == 0 || size > MAX_PARTICLES)
-		throw love::Exception("Invalid buffer size");
-	quadIndices = QuadIndices(size);
-	deleteBuffers();
-	createBuffers(size);
-	reset();
-}
-
-uint32 ParticleSystem::getBufferSize() const
-{
-	return maxParticles;
-}
-
-void ParticleSystem::addParticle(float t)
-{
-	if (isFull())
-		return;
-
-	// Gets a free particle and updates the allocation pointer.
-	Particle *p = pFree++;
-	initParticle(p, t);
-
-	switch (insertMode)
-	{
-	default:
-	case INSERT_MODE_TOP:
-		insertTop(p);
-		break;
-	case INSERT_MODE_BOTTOM:
-		insertBottom(p);
-		break;
-	case INSERT_MODE_RANDOM:
-		insertRandom(p);
-		break;
-	}
-
-	activeParticles++;
-}
-
-void ParticleSystem::initParticle(Particle *p, float t)
-{
-	float min,max;
-
-	// Linearly interpolate between the previous and current emitter position.
-	love::Vector pos = prevPosition + (position - prevPosition) * t;
-
-	min = particleLifeMin;
-	max = particleLifeMax;
-	if (min == max)
-		p->life = min;
-	else
-		p->life = (float) rng.random(min, max);
-	p->lifetime = p->life;
-
-	p->position = pos;
-
-	switch (areaSpreadDistribution)
-	{
-	case DISTRIBUTION_UNIFORM:
-		p->position.x += (float) rng.random(-areaSpread.getX(), areaSpread.getX());
-		p->position.y += (float) rng.random(-areaSpread.getY(), areaSpread.getY());
-		break;
-	case DISTRIBUTION_NORMAL:
-		p->position.x += (float) rng.randomNormal(areaSpread.getX());
-		p->position.y += (float) rng.randomNormal(areaSpread.getY());
-		break;
-	case DISTRIBUTION_NONE:
-	default:
-		break;
-	}
-
-	p->origin = pos;
-
-	min = speedMin;
-	max = speedMax;
-	float speed = (float) rng.random(min, max);
-
-	min = direction - spread/2.0f;
-	max = direction + spread/2.0f;
-	float dir = (float) rng.random(min, max);
-
-	p->velocity = love::Vector(cosf(dir), sinf(dir)) * speed;
-
-	p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x);
-	p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y);
-
-	min = radialAccelerationMin;
-	max = radialAccelerationMax;
-	p->radialAcceleration = (float) rng.random(min, max);
-
-	min = tangentialAccelerationMin;
-	max = tangentialAccelerationMax;
-	p->tangentialAcceleration = (float) rng.random(min, max);
-
-	min = linearDampingMin;
-	max = linearDampingMax;
-	p->linearDamping = (float) rng.random(min, max);
-
-	p->sizeOffset       = (float) rng.random(sizeVariation); // time offset for size change
-	p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset;
-	p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)];
-
-	min = rotationMin;
-	max = rotationMax;
-	p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
-	p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
-	p->rotation = (float) rng.random(min, max);
-
-	p->angle = p->rotation;
-	if (relativeRotation)
-		p->angle += atan2f(p->velocity.y, p->velocity.x);
-
-	p->color = colors[0];
-
-	p->quadIndex = 0;
-}
-
-void ParticleSystem::insertTop(Particle *p)
-{
-	if (pHead == nullptr)
-	{
-		pHead = p;
-		p->prev = nullptr;
-	}
-	else
-	{
-		pTail->next = p;
-		p->prev = pTail;
-	}
-	p->next = nullptr;
-	pTail = p;
-}
-
-void ParticleSystem::insertBottom(Particle *p)
-{
-	if (pTail == nullptr)
-	{
-		pTail = p;
-		p->next = nullptr;
-	}
-	else
-	{
-		pHead->prev = p;
-		p->next = pHead;
-	}
-	p->prev = nullptr;
-	pHead = p;
-}
-
-void ParticleSystem::insertRandom(Particle *p)
-{
-	// Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
-	uint64 pos = rng.rand() % ((int64) activeParticles + 1);
-
-	// Special case where the particle gets inserted before the head.
-	if (pos == activeParticles)
-	{
-		Particle *pA = pHead;
-		if (pA)
-			pA->prev = p;
-		p->prev = nullptr;
-		p->next = pA;
-		pHead = p;
-		return;
-	}
-
-	// Inserts the particle after the randomly selected particle.
-	Particle *pA = pMem + pos;
-	Particle *pB = pA->next;
-	pA->next = p;
-	if (pB)
-		pB->prev = p;
-	else
-		pTail = p;
-	p->prev = pA;
-	p->next = pB;
-}
-
-ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
-{
-	// The linked list is updated in this function and old pointers may be
-	// invalidated. The returned pointer will inform the caller of the new
-	// pointer to the next particle.
-	Particle *pNext = nullptr;
-
-	// Removes the particle from the linked list.
-	if (p->prev)
-		p->prev->next = p->next;
-	else
-		pHead = p->next;
-
-	if (p->next)
-	{
-		p->next->prev = p->prev;
-		pNext = p->next;
-	}
-	else
-		pTail = p->prev;
-
-	// The (in memory) last particle can now be moved into the free slot.
-	// It will skip the moving if it happens to be the removed particle.
-	pFree--;
-	if (p != pFree)
-	{
-		*p = *pFree;
-		if (pNext == pFree)
-			pNext = p;
-
-		if (p->prev)
-			p->prev->next = p;
-		else
-			pHead = p;
-
-		if (p->next)
-			p->next->prev = p;
-		else
-			pTail = p;
-	}
-
-	activeParticles--;
-	return pNext;
-}
-
-void ParticleSystem::setTexture(Texture *tex)
-{
-	texture.set(tex);
-
-	if (defaultOffset)
-		resetOffset();
-}
-
-Texture *ParticleSystem::getTexture() const
-{
-	return texture.get();
-}
-
-void ParticleSystem::setInsertMode(InsertMode mode)
-{
-	insertMode = mode;
-}
-
-ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
-{
-	return insertMode;
-}
-
-void ParticleSystem::setEmissionRate(float rate)
-{
-	if (rate < 0.0f)
-		throw love::Exception("Invalid emission rate");
-	emissionRate = rate;
-}
-
-float ParticleSystem::getEmissionRate() const
-{
-	return emissionRate;
-}
-
-void ParticleSystem::setEmitterLifetime(float life)
-{
-	this->life = lifetime = life;
-}
-
-float ParticleSystem::getEmitterLifetime() const
-{
-	return lifetime;
-}
-
-void ParticleSystem::setParticleLifetime(float min, float max)
-{
-	particleLifeMin = min;
-	if (max == 0)
-		particleLifeMax = min;
-	else
-		particleLifeMax = max;
-}
-
-void ParticleSystem::getParticleLifetime(float &min, float &max) const
-{
-	min = particleLifeMin;
-	max = particleLifeMax;
-}
-
-void ParticleSystem::setPosition(float x, float y)
-{
-	position = love::Vector(x, y);
-	prevPosition = position;
-}
-
-const love::Vector &ParticleSystem::getPosition() const
-{
-	return position;
-}
-
-void ParticleSystem::moveTo(float x, float y)
-{
-	position = love::Vector(x, y);
-}
-
-void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
-{
-	areaSpread = love::Vector(x, y);
-	areaSpreadDistribution = distribution;
-}
-
-ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
-{
-	return areaSpreadDistribution;
-}
-
-const love::Vector &ParticleSystem::getAreaSpreadParameters() const
-{
-	return areaSpread;
-}
-
-void ParticleSystem::setDirection(float direction)
-{
-	this->direction = direction;
-}
-
-float ParticleSystem::getDirection() const
-{
-	return direction;
-}
-
-void ParticleSystem::setSpread(float spread)
-{
-	this->spread = spread;
-}
-
-float ParticleSystem::getSpread() const
-{
-	return spread;
-}
-
-void ParticleSystem::setSpeed(float speed)
-{
-	speedMin = speedMax = speed;
-}
-
-void ParticleSystem::setSpeed(float min, float max)
-{
-	speedMin = min;
-	speedMax = max;
-}
-
-void ParticleSystem::getSpeed(float &min, float &max) const
-{
-	min = speedMin;
-	max = speedMax;
-}
-
-void ParticleSystem::setLinearAcceleration(float x, float y)
-{
-	linearAccelerationMin.x = linearAccelerationMax.x = x;
-	linearAccelerationMin.y = linearAccelerationMax.y = y;
-}
-
-void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax)
-{
-	linearAccelerationMin = love::Vector(xmin, ymin);
-	linearAccelerationMax = love::Vector(xmax, ymax);
-}
-
-void ParticleSystem::getLinearAcceleration(love::Vector &min, love::Vector &max) const
-{
-	min = linearAccelerationMin;
-	max = linearAccelerationMax;
-}
+	love::graphics::ParticleSystem::setBufferSize(size);
 
 
-void ParticleSystem::setRadialAcceleration(float acceleration)
-{
-	radialAccelerationMin = radialAccelerationMax = acceleration;
-}
-
-void ParticleSystem::setRadialAcceleration(float min, float max)
-{
-	radialAccelerationMin = min;
-	radialAccelerationMax = max;
-}
-
-void ParticleSystem::getRadialAcceleration(float &min, float &max) const
-{
-	min = radialAccelerationMin;
-	max = radialAccelerationMax;
-}
-
-void ParticleSystem::setTangentialAcceleration(float acceleration)
-{
-	tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
-}
-
-void ParticleSystem::setTangentialAcceleration(float min, float max)
-{
-	tangentialAccelerationMin = min;
-	tangentialAccelerationMax = max;
-}
-
-void ParticleSystem::getTangentialAcceleration(float &min, float &max) const
-{
-	min = tangentialAccelerationMin;
-	max = tangentialAccelerationMax;
-}
-
-void ParticleSystem::setLinearDamping(float min, float max)
-{
-	linearDampingMin = min;
-	linearDampingMax = max;
-}
-
-void ParticleSystem::getLinearDamping(float &min, float &max) const
-{
-	min = linearDampingMin;
-	max = linearDampingMax;
-}
-
-void ParticleSystem::setSize(float size)
-{
-	sizes.resize(1);
-	sizes[0] = size;
-}
-
-void ParticleSystem::setSizes(const std::vector<float> &newSizes)
-{
-	sizes = newSizes;
-}
-
-const std::vector<float> &ParticleSystem::getSizes() const
-{
-	return sizes;
-}
-
-void ParticleSystem::setSizeVariation(float variation)
-{
-	sizeVariation = variation;
-}
-
-float ParticleSystem::getSizeVariation() const
-{
-	return sizeVariation;
-}
-
-void ParticleSystem::setRotation(float rotation)
-{
-	rotationMin = rotationMax = rotation;
-}
-
-void ParticleSystem::setRotation(float min, float max)
-{
-	rotationMin = min;
-	rotationMax = max;
-}
-
-void ParticleSystem::getRotation(float &min, float &max) const
-{
-	min = rotationMin;
-	max = rotationMax;
-}
-
-void ParticleSystem::setSpin(float spin)
-{
-	spinStart = spin;
-	spinEnd = spin;
-}
-
-void ParticleSystem::setSpin(float start, float end)
-{
-	spinStart = start;
-	spinEnd = end;
-}
-
-void ParticleSystem::getSpin(float &start, float &end) const
-{
-	start = spinStart;
-	end = spinEnd;
-}
-
-void ParticleSystem::setSpinVariation(float variation)
-{
-	spinVariation = variation;
-}
-
-float ParticleSystem::getSpinVariation() const
-{
-	return spinVariation;
-}
-
-void ParticleSystem::setOffset(float x, float y)
-{
-	offset = love::Vector(x, y);
-	defaultOffset = false;
-}
-
-love::Vector ParticleSystem::getOffset() const
-{
-	return offset;
-}
-
-void ParticleSystem::setColor(const std::vector<Colorf> &newColors)
-{
-	colors = newColors;
-
-	for (Colorf &c : colors)
-	{
-		// We want to store the colors as [0, 1], rather than [0, 255].
-		c.r /= 255.0f;
-		c.g /= 255.0f;
-		c.b /= 255.0f;
-		c.a /= 255.0f;
-	}
-}
-
-std::vector<Colorf> ParticleSystem::getColor() const
-{
-	// The particle system stores colors in the range of [0, 1]...
-	std::vector<Colorf> ncolors(colors);
-
-	for (Colorf &c : ncolors)
-	{
-		c.r *= 255.0f;
-		c.g *= 255.0f;
-		c.b *= 255.0f;
-		c.a *= 255.0f;
-	}
-
-	return ncolors;
-}
-
-void ParticleSystem::setQuads(const std::vector<Quad *> &newQuads)
-{
-	std::vector<StrongRef<Quad>> quadlist;
-	quadlist.reserve(newQuads.size());
-
-	for (Quad *q : newQuads)
-		quadlist.push_back(q);
-
-	quads = quadlist;
-
-	if (defaultOffset)
-		resetOffset();
-}
-
-void ParticleSystem::setQuads()
-{
-	quads.clear();
-}
-
-std::vector<Quad *> ParticleSystem::getQuads() const
-{
-	std::vector<Quad *> quadlist;
-	quadlist.reserve(quads.size());
-
-	for (const StrongRef<Quad> &q : quads)
-		quadlist.push_back(q.get());
-
-	return quadlist;
-}
-
-void ParticleSystem::setRelativeRotation(bool enable)
-{
-	relativeRotation = enable;
-}
-
-bool ParticleSystem::hasRelativeRotation() const
-{
-	return relativeRotation;
-}
-
-uint32 ParticleSystem::getCount() const
-{
-	return activeParticles;
-}
-
-void ParticleSystem::start()
-{
-	active = true;
-}
-
-void ParticleSystem::stop()
-{
-	active = false;
-	life = lifetime;
-	emitCounter = 0;
-}
-
-void ParticleSystem::pause()
-{
-	active = false;
-}
-
-void ParticleSystem::reset()
-{
-	if (pMem == nullptr)
-		return;
-
-	pFree = pMem;
-	pHead = nullptr;
-	pTail = nullptr;
-	activeParticles = 0;
-	life = lifetime;
-	emitCounter = 0;
-}
-
-void ParticleSystem::emit(uint32 num)
-{
-	if (!active)
-		return;
-
-	num = std::min(num, maxParticles - activeParticles);
-
-	while(num--)
-		addParticle(1.0f);
-}
-
-bool ParticleSystem::isActive() const
-{
-	return active;
-}
-
-bool ParticleSystem::isPaused() const
-{
-	return !active && life < lifetime;
-}
-
-bool ParticleSystem::isStopped() const
-{
-	return !active && life >= lifetime;
-}
-
-bool ParticleSystem::isEmpty() const
-{
-	return activeParticles == 0;
-}
-
-bool ParticleSystem::isFull() const
-{
-	return activeParticles == maxParticles;
+	quadIndices = QuadIndices(size);
+	createVertices(size);
 }
 }
 
 
 void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 {
 {
 	uint32 pCount = getCount();
 	uint32 pCount = getCount();
+
 	if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr)
 	if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr)
 		return;
 		return;
 
 
@@ -904,163 +148,6 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	}
 	}
 }
 }
 
 
-void ParticleSystem::update(float dt)
-{
-	if (pMem == nullptr || dt == 0.0f)
-		return;
-
-	// Traverse all particles and update.
-	Particle *p = pHead;
-
-	while (p)
-	{
-		// Decrease lifespan.
-		p->life -= dt;
-
-		if (p->life <= 0)
-			p = removeParticle(p);
-		else
-		{
-			// Temp variables.
-			love::Vector radial, tangential;
-			love::Vector ppos = p->position;
-
-			// Get vector from particle center to particle.
-			radial = ppos - p->origin;
-			radial.normalize();
-			tangential = radial;
-
-			// Resize radial acceleration.
-			radial *= p->radialAcceleration;
-
-			// Calculate tangential acceleration.
-			{
-				float a = tangential.getX();
-				tangential.setX(-tangential.getY());
-				tangential.setY(a);
-			}
-
-			// Resize tangential.
-			tangential *= p->tangentialAcceleration;
-
-			// Update velocity.
-			p->velocity += (radial + tangential + p->linearAcceleration) * dt;
-
-			// Apply damping.
-			p->velocity *= 1.0f / (1.0f + p->linearDamping * dt);
-
-			// Modify position.
-			ppos += p->velocity * dt;
-
-			p->position = ppos;
-
-			const float t = 1.0f - p->life / p->lifetime;
-
-			// Rotate.
-			p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt;
-
-			p->angle = p->rotation;
-
-			if (relativeRotation)
-				p->angle += atan2f(p->velocity.y, p->velocity.x);
-
-			// Change size according to given intervals:
-			// i = 0       1       2      3          n-1
-			//     |-------|-------|------|--- ... ---|
-			// t = 0    1/(n-1)        3/(n-1)        1
-			//
-			// `s' is the interpolation variable scaled to the current
-			// interval width, e.g. if n = 5 and t = 0.3, then the current
-			// indices are 1,2 and s = 0.3 - 0.25 = 0.05
-			float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation
-			s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size()
-			size_t i = (size_t)s;
-			size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f)
-			s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1
-			p->size = sizes[i] * (1.0f - s) + sizes[k] * s;
-
-			// Update color according to given intervals (as above)
-			s = t * (float)(colors.size() - 1);
-			i = (size_t)s;
-			k = (i == colors.size() - 1) ? i : i + 1;
-			s -= (float)i;                            // 0 <= s <= 1
-			p->color = colors[i] * (1.0f - s) + colors[k] * s;
-
-			// Update the quad index.
-			k = quads.size();
-			if (k > 0)
-			{
-				s = t * (float) k; // [0:numquads-1] (clamped below)
-				i = (s > 0.0f) ? (size_t) s : 0;
-				p->quadIndex = (int) ((i < k) ? i : k - 1);
-			}
-
-			// Next particle.
-			p = p->next;
-		}
-	}
-
-	// Make some more particles.
-	if (active)
-	{
-		float rate = 1.0f / emissionRate; // the amount of time between each particle emit
-		emitCounter += dt;
-		float total = emitCounter - rate;
-		while (emitCounter > rate)
-		{
-			addParticle(1.0f - (emitCounter - rate) / total);
-			emitCounter -= rate;
-		}
-		/*int particles = (int)(emissionRate * dt);
-		 for (int i = 0; i != particles; i++)
-		 add();*/
-
-		life -= dt;
-		if (lifetime != -1 && life < 0)
-			stop();
-	}
-
-	prevPosition = position;
-}
-
-bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
-{
-	return distributions.find(in, out);
-}
-
-bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
-{
-	return distributions.find(in, out);
-}
-
-bool ParticleSystem::getConstant(const char *in, InsertMode &out)
-{
-	return insertModes.find(in, out);
-}
-
-bool ParticleSystem::getConstant(InsertMode in, const char *&out)
-{
-	return insertModes.find(in, out);
-}
-
-StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] =
-{
-	{ "none",    ParticleSystem::DISTRIBUTION_NONE },
-	{ "uniform", ParticleSystem::DISTRIBUTION_UNIFORM },
-	{ "normal",  ParticleSystem::DISTRIBUTION_NORMAL },
-};
-
-StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));
-
-StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM>::Entry ParticleSystem::insertModesEntries[] =
-{
-	{ "top",    ParticleSystem::INSERT_MODE_TOP },
-	{ "bottom", ParticleSystem::INSERT_MODE_BOTTOM },
-	{ "random", ParticleSystem::INSERT_MODE_RANDOM },
-};
-
-StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM> ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries));
-
 } // opengl
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

+ 7 - 631
src/modules/graphics/opengl/ParticleSystem.h

@@ -22,18 +22,9 @@
 #define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
 #define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
 
 
 // LOVE
 // LOVE
-#include "common/int.h"
-#include "common/math.h"
-#include "common/Vector.h"
-#include "graphics/Drawable.h"
-#include "graphics/Color.h"
-#include "graphics/Quad.h"
-#include "graphics/Texture.h"
+#include "graphics/ParticleSystem.h"
 #include "GLBuffer.h"
 #include "GLBuffer.h"
 
 
-// STL
-#include <vector>
-
 namespace love
 namespace love
 {
 {
 namespace graphics
 namespace graphics
@@ -41,643 +32,28 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
-/**
- * A class for creating, moving and drawing particles.
- * A big thanks to bobthebloke.org
- **/
-class ParticleSystem : public Drawable
+class ParticleSystem : public love::graphics::ParticleSystem
 {
 {
 public:
 public:
-	/**
-	 * Type of distribution new particles are drawn from: None, uniform, normal.
-	 */
-	enum AreaSpreadDistribution
-	{
-		DISTRIBUTION_NONE,
-		DISTRIBUTION_UNIFORM,
-		DISTRIBUTION_NORMAL,
-		DISTRIBUTION_MAX_ENUM
-	};
-
-	/**
-	 * Insertion modes of new particles in the list: top, bottom, random.
-	 */
-	enum InsertMode
-	{
-		INSERT_MODE_TOP,
-		INSERT_MODE_BOTTOM,
-		INSERT_MODE_RANDOM,
-		INSERT_MODE_MAX_ENUM
-	};
 
 
-	/**
-	 * Maximum numbers of particles in a ParticleSystem.
-	 * This limit comes from the fact that a quad requires four vertices and the
-	 * OpenGL API where GLsizei is a signed int.
-	 **/
-	static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
-
-	/**
-	 * Creates a particle system with the specified buffer size and texture.
-	 **/
 	ParticleSystem(Texture *texture, uint32 buffer);
 	ParticleSystem(Texture *texture, uint32 buffer);
 	ParticleSystem(const ParticleSystem &p);
 	ParticleSystem(const ParticleSystem &p);
 
 
-	/**
-	 * Deletes any allocated memory.
-	 **/
 	virtual ~ParticleSystem();
 	virtual ~ParticleSystem();
 
 
-	/**
-	 * Creates an identical copy of this ParticleSystem. The clone does not
-	 * duplicate any existing particles from this ParticleSystem, just the
-	 * settable parameters.
-	 **/
-	ParticleSystem *clone();
-
-	/**
-	 * Sets the texture used in the particle system.
-	 * @param texture The new texture.
-	 **/
-	void setTexture(Texture *texture);
-
-	/**
-	 * Returns the texture used when drawing the particle system.
-	 **/
-	Texture *getTexture() const;
-
-	/**
-	 * Clears the current buffer and allocates the appropriate amount of space for the buffer.
-	 * @param size The new buffer size.
-	 **/
-	void setBufferSize(uint32 size);
-
-	/**
-	 * Returns the total amount of particles this ParticleSystem can have active
-	 * at any given point in time.
-	 **/
-	uint32 getBufferSize() const;
-
-	/**
-	 * Sets the insert mode for new particles.
-	 * @param mode The new insert mode.
-	 */
-	void setInsertMode(InsertMode mode);
-
-	/**
-	 * Returns the current insert mode.
-	 */
-	InsertMode getInsertMode() const;
-
-	/**
-	 * Sets the emission rate.
-	 * @param rate The amount of particles per second.
-	 **/
-	void setEmissionRate(float rate);
-
-	/**
-	 * Returns the number of particles created per second.
-	 **/
-	float getEmissionRate() const;
-
-	/**
-	 * Sets the lifetime of the particle emitter (-1 means eternal)
-	 * @param life The lifetime (in seconds).
-	 **/
-	void setEmitterLifetime(float life);
-
-	/**
-	 * Returns the lifetime of the particle emitter.
-	 **/
-	float getEmitterLifetime() const;
-
-	/**
-	 * Sets the life range of the particles.
-	 * @param min The minimum life.
-	 * @param max The maximum life (if 0, then becomes the same as minimum life).
-	 **/
-	void setParticleLifetime(float min, float max = 0);
-
-	/**
-	 * Gets the lifetime of a particle.
-	 * @param[out] min The minimum life.
-	 * @param[out] max The maximum life.
-	 **/
-	void getParticleLifetime(float &min, float &max) const;
-
-	/**
-	 * Sets the position of the center of the emitter.
-	 * Used to move the emitter without changing the position of already existing particles.
-	 * @param x The x-coordinate.
-	 * @param y The y-coordinate.
-	 **/
-	void setPosition(float x, float y);
-
-	/**
-	 * Returns the position of the emitter.
-	 **/
-	const love::Vector &getPosition() const;
-
-	/**
-	 * Moves the position of the center of the emitter.
-	 * When update is called, newly spawned particles will appear in a line
-	 * between the old emitter position and where the emitter was moved to,
-	 * resulting in a smoother-feeling particle system if moveTo is called
-	 * repeatedly.
-	 **/
-	void moveTo(float x, float y);
-
-	/**
-	 * Sets the emission area spread parameters and distribution type. The interpretation of
-	 * the parameters depends on the distribution type:
-	 *
-	 * * None:    Parameters are ignored. No area spread.
-	 * * Uniform: Parameters denote maximal (symmetric) displacement from emitter position.
-	 * * Normal:  Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
-	 * @param x First parameter. Interpretation depends on distribution type.
-	 * @param y Second parameter. Interpretation depends on distribution type.
-	 * @param distribution Distribution type
-	 **/
-	void setAreaSpread(AreaSpreadDistribution distribution, float x, float y);
-
-	/**
-	 * Returns area spread distribution type.
-	 **/
-	AreaSpreadDistribution getAreaSpreadDistribution() const;
-
-	/**
-	 * Returns area spread parameters.
-	 **/
-	const love::Vector &getAreaSpreadParameters() const;
-
-	/**
-	 * Sets the direction of the particle emitter.
-	 * @param direction The direction (in degrees).
-	 **/
-	void setDirection(float direction);
-
-	/**
-	 * Returns the direction of the particle emitter (in radians).
-	 **/
-	float getDirection() const;
-
-	/**
-	 * Sets the spread of the particle emitter.
-	 * @param spread The spread (in radians).
-	 **/
-	void setSpread(float spread);
-
-	/**
-	 * Returns the directional spread of the emitter (in radians).
-	 **/
-	float getSpread() const;
-
-	/**
-	 * Sets the speed of the particles.
-	 * @param speed The speed.
-	 **/
-	void setSpeed(float speed);
-
-	/**
-	 * Sets the speed of the particles.
-	 * @param min The minimum speed.
-	 * @param max The maximum speed.
-	 **/
-	void setSpeed(float min, float max);
-
-	/**
-	 * Gets the speed of the particles.
-	 * @param[out] min The minimum speed.
-	 * @param[out] max The maximum speed.
-	 **/
-	void getSpeed(float &min, float &max) const;
-
-	/**
-	 * Sets the linear acceleration (the acceleration along the x and y axes).
-	 * @param x The acceleration along the x-axis.
-	 * @param y The acceleration along the y-axis.
-	 **/
-	void setLinearAcceleration(float x, float y);
-
-	/**
-	 * Sets the linear acceleration (the acceleration along the x and y axes).
-	 * @param xmin The minimum amount of acceleration along the x-axis.
-	 * @param ymin The minimum amount of acceleration along the y-axis.
-	 * @param xmax The maximum amount of acceleration along the x-axis.
-	 * @param ymax The maximum amount of acceleration along the y-axis.
-	 **/
-	void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax);
-
-	/**
-	 * Gets the linear acceleration of the particles.
-	 * @param[out] min The minimum acceleration.
-	 * @param[out] max The maximum acceleration.
-	 **/
-	void getLinearAcceleration(love::Vector &min, love::Vector &max) const;
-
-	/**
-	 * Sets the radial acceleration (the acceleration towards the particle emitter).
-	 * @param acceleration The amount of acceleration.
-	 **/
-	void setRadialAcceleration(float acceleration);
-
-	/**
-	 * Sets the radial acceleration (the acceleration towards the particle emitter).
-	 * @param min The minimum acceleration.
-	 * @param max The maximum acceleration.
-	 **/
-	void setRadialAcceleration(float min, float max);
-
-	/**
-	 * Gets the radial acceleration.
-	 * @param[out] min The minimum amount of radial acceleration.
-	 * @param[out] max The maximum amount of radial acceleration.
-	 **/
-	void getRadialAcceleration(float &min, float &max) const;
-
-	/**
-	 * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
-	 * @param acceleration The amount of acceleration.
-	 **/
-	void setTangentialAcceleration(float acceleration);
-
-	/**
-	 * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
-	 * @param min The minimum acceleration.
-	 * @param max The maximum acceleration.
-	 **/
-	void setTangentialAcceleration(float min, float max);
-
-	/**
-	 * Gets the tangential acceleration.
-	 * @param[out] min The minimum tangential acceleration.
-	 * @param[out] max The maximum tangential acceleration.
-	 **/
-	void getTangentialAcceleration(float &min, float &max) const;
-
-	/**
-	 * Sets the amount of linear damping. Damping reduces the velocity of
-	 * particles over time. A value of 0 corresponds to no damping.
-	 **/
-	void setLinearDamping(float min, float max);
-
-	/**
-	 * Gets the current amount of linear damping.
-	 **/
-	void getLinearDamping(float &min, float &max) const;
-
-	/**
-	 * Sets the size of the sprite (1.0 being the default size).
-	 * @param size The size of the sprite.
-	 **/
-	void setSize(float size);
-
-	/**
-	 * Sets the sizes of the sprite upon creation and upon death (1.0 being the default size).
-	 * @param newSizes Array of sizes
-	 **/
-	void setSizes(const std::vector<float> &newSizes);
-
-	/**
-	 * Returns the sizes of the particle sprites.
-	 **/
-	const std::vector<float> &getSizes() const;
-
-	/**
-	 * Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end).
-	 * @param variation The amount of variation.
-	 **/
-	void setSizeVariation(float variation);
-
-	/**
-	 * Returns the amount of initial size variation between particles.
-	 **/
-	float getSizeVariation() const;
-
-	/**
-	 * Sets the amount of rotation a sprite starts out with.
-	 * @param rotation The amount of rotation.
-	 **/
-	void setRotation(float rotation);
-
-	/**
-	 * Sets the amount of rotation a sprite starts out with (a random value between min and max).
-	 * @param min The minimum amount of rotation.
-	 * @param max The maximum amount of rotation.
-	 **/
-	void setRotation(float min, float max);
-
-	/**
-	 * Gets the initial amount of rotation of a particle, in radians.
-	 * @param[out] min The minimum initial rotation.
-	 * @param[out] max The maximum initial rotation.
-	 **/
-	void getRotation(float &min, float &max) const;
-
-	/**
-	 * Sets the spin of the sprite.
-	 * @param spin The spin of the sprite (in degrees).
-	 **/
-	void setSpin(float spin);
-
-	/**
-	 * Sets the spin of the sprite upon particle creation and death.
-	 * @param start The spin of the sprite upon creation (in radians / second).
-	 * @param end The spin of the sprite upon death (in radians / second).
-	 **/
-	void setSpin(float start, float end);
-
-	/**
-	 * Gets the amount of spin of a particle during its lifetime.
-	 * @param[out] start The initial spin, in radians / s.
-	 * @param[out] end The final spin, in radians / s.
-	 **/
-	void getSpin(float &start, float &end) const;
-
-	/**
-	 * Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end).
-	 * @param variation The variation.
-	 **/
-	void setSpinVariation(float variation);
-
-	/**
-	 * Returns the amount of variation of the start spin of a particle.
-	 **/
-	float getSpinVariation() const;
-
-	/**
-	 * Sets the particles' offsets for rotation.
-	 * @param x The x offset.
-	 * @param y The y offset.
-	 **/
-	void setOffset(float x, float y);
-
-	/**
-	 * Returns of the particle offset.
-	 **/
-	love::Vector getOffset() const;
-
-	/**
-	 * Sets the color of the particles.
-	 * @param newColors Array of colors
-	 **/
-	void setColor(const std::vector<Colorf> &newColors);
-
-	/**
-	 * Returns the color of the particles.
-	 **/
-	std::vector<Colorf> getColor() const;
-
-	/**
-	 * Sets a list of Quads to use for particles over their lifetime.
-	 **/
-	void setQuads(const std::vector<Quad *> &newQuads);
-	void setQuads();
-
-	/**
-	 * Gets the Quads used when drawing the particles.
-	 **/
-	std::vector<Quad *> getQuads() const;
+	ParticleSystem *clone() override;
+	void setBufferSize(uint32 size) override;
+	void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override;
 
 
-	/**
-	 * sets whether particle angles & rotations are relative to their velocities.
-	 **/
-	void setRelativeRotation(bool enable);
-	bool hasRelativeRotation() const;
+private:
 
 
-	/**
-	 * Returns the amount of particles that are currently active in the system.
-	 **/
-	uint32 getCount() const;
-
-	/**
-	 * Starts/resumes the particle emitter.
-	 **/
-	void start();
-
-	/**
-	 * Stops the particle emitter and resets.
-	 **/
-	void stop();
-
-	/**
-	 * Pauses the particle emitter.
-	 **/
-	void pause();
-
-	/**
-	 * Resets the particle emitter.
-	 **/
-	void reset();
-
-	/**
-	 * Instantly emits a number of particles.
-	 * @param num The number of particles to emit.
-	 **/
-	void emit(uint32 num);
-
-	/**
-	 * Returns whether the particle emitter is active.
-	 **/
-	bool isActive() const;
-
-	/**
-	 * Returns whether the particle emitter is paused.
-	 **/
-	bool isPaused() const;
-
-	bool isStopped() const;
-
-	/**
-	 * Returns whether the particle system is empty of particles or not.
-	 **/
-	bool isEmpty() const;
-
-	/**
-	 * Returns whether the amount of particles has reached the buffer limit or not.
-	 **/
-	bool isFull() const;
-
-	/**
-	 * Draws the particle emitter at the specified position.
-	 * @param x The x-coordinate.
-	 * @param y The y-coordinate.
-	 **/
-	virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
-
-	/**
-	 * Updates the particle system.
-	 * @param dt Time since last update.
-	 **/
-	void update(float dt);
-
-	static bool getConstant(const char *in, AreaSpreadDistribution &out);
-	static bool getConstant(AreaSpreadDistribution in, const char *&out);
-
-	static bool getConstant(const char *in, InsertMode &out);
-	static bool getConstant(InsertMode in, const char *&out);
-
-protected:
-
-	// Represents a single particle.
-	struct Particle
-	{
-		Particle *prev;
-		Particle *next;
-
-		float lifetime;
-		float life;
-
-		love::Vector position;
-
-		// Particles gravitate towards this point.
-		love::Vector origin;
-
-		love::Vector velocity;
-		love::Vector linearAcceleration;
-		float radialAcceleration;
-		float tangentialAcceleration;
-
-		float linearDamping;
-
-		float size;
-		float sizeOffset;
-		float sizeIntervalSize;
-
-		float rotation; // Amount of rotation applied to the final angle.
-		float angle;
-		float spinStart;
-		float spinEnd;
-
-		Colorf color;
-
-		int quadIndex;
-	};
-
-	// Pointer to the beginning of the allocated memory.
-	Particle *pMem;
-
-	// Pointer to a free particle.
-	Particle *pFree;
-
-	// Pointer to the start of the linked list.
-	Particle *pHead;
-
-	// Pointer to the end of the linked list.
-	Particle *pTail;
+	void createVertices(size_t numparticles);
 
 
 	// array of transformed vertex data for all particles, for drawing
 	// array of transformed vertex data for all particles, for drawing
 	Vertex *particleVerts;
 	Vertex *particleVerts;
 
 
 	// Vertex index buffer.
 	// Vertex index buffer.
 	QuadIndices quadIndices;
 	QuadIndices quadIndices;
-
-	// The texture to be drawn.
-	StrongRef<Texture> texture;
-
-	// Whether the particle emitter is active.
-	bool active;
-
-	// Insert mode of new particles.
-	InsertMode insertMode;
-
-	// The maximum number of particles.
-	uint32 maxParticles;
-
-	// The number of active particles.
-	uint32 activeParticles;
-
-	// The emission rate (particles/sec).
-	float emissionRate;
-
-	// Used to determine when a particle should be emitted.
-	float emitCounter;
-
-	// The relative position of the particle emitter.
-	love::Vector position;
-	love::Vector prevPosition;
-
-	// Emission area spread.
-	AreaSpreadDistribution areaSpreadDistribution;
-	love::Vector areaSpread;
-
-	// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
-	float lifetime;
-	float life;
-
-	// The particle life.
-	float particleLifeMin;
-	float particleLifeMax;
-
-	// The direction (and spread) the particles will be emitted in. Measured in radians.
-	float direction;
-	float spread;
-
-	// The speed.
-	float speedMin;
-	float speedMax;
-
-	// Acceleration along the x and y axes.
-	love::Vector linearAccelerationMin;
-	love::Vector linearAccelerationMax;
-
-	// Acceleration towards the emitter's center
-	float radialAccelerationMin;
-	float radialAccelerationMax;
-
-	// Acceleration perpendicular to the particle's direction.
-	float tangentialAccelerationMin;
-	float tangentialAccelerationMax;
-
-	float linearDampingMin;
-	float linearDampingMax;
-
-	// Size.
-	std::vector<float> sizes;
-	float sizeVariation;
-
-	// Rotation
-	float rotationMin;
-	float rotationMax;
-
-	// Spin.
-	float spinStart;
-	float spinEnd;
-	float spinVariation;
-
-	// Offsets
-	love::Vector offset;
-
-	// Is the ParticleSystem using a default offset?
-	bool defaultOffset;
-
-	// Color.
-	std::vector<Colorf> colors;
-
-	// Quads.
-	std::vector<StrongRef<Quad>> quads;
-
-	bool relativeRotation;
-
-	void resetOffset();
-
-	void createBuffers(size_t size);
-	void deleteBuffers();
-
-	void addParticle(float t);
-	Particle *removeParticle(Particle *p);
-
-	// Called by addParticle.
-	void initParticle(Particle *p, float t);
-	void insertTop(Particle *p);
-	void insertBottom(Particle *p);
-	void insertRandom(Particle *p);
-
-	static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
-	static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
-
-	static StringMap<InsertMode, INSERT_MODE_MAX_ENUM>::Entry insertModesEntries[];
-	static StringMap<InsertMode, INSERT_MODE_MAX_ENUM> insertModes;
 };
 };
 
 
 } // opengl
 } // opengl