Browse Source

Merged default into minor

--HG--
branch : minor
Alex Szpakowski 11 years ago
parent
commit
aba33bf2e7

+ 11 - 1
changes.txt

@@ -8,6 +8,7 @@ LOVE 0.9.2 [Baby Inspector]
   * Added love.graphics.getCanvasFormats.
   * Added love.graphics.getCompressedImageFormats.
   * Added ParticleSystem:setQuads.
+  * Added ParticleSystem:setLinearDamping.
   * Added SpriteBatch:flush.
   * Added love.graphics.getStats.
   * Added optional duration argument to Joystick:setVibration.
@@ -18,8 +19,10 @@ LOVE 0.9.2 [Baby Inspector]
   * Added love.window.getDisplayName.
   * Added love.window.minimize.
   * Added love.window.showMessageBox.
-  * Added love.filesystem.isSymlink, love.filesystem.setSymlinksEnabled, and love.filesystem.areSymlinksEnabled.
   * Added 'refreshrate' field to the table returned by love.window.getMode.
+  * Added love.window.toPixels and love.window.fromPixels.
+  * Added love.window.setPosition and love.window.getPosition, and 'x' and 'y' fields to love.window.setMode and t.window in love.conf.
+  * Added love.filesystem.isSymlink, love.filesystem.setSymlinksEnabled, and love.filesystem.areSymlinksEnabled.
 
   * Deprecated SpriteBatch:bind and SpriteBatch:unbind.
   * Deprecated all uses of the name 'FSAA' in favor of 'MSAA'.
@@ -27,8 +30,11 @@ LOVE 0.9.2 [Baby Inspector]
   * Deprecated the 'dxt' and 'bc5' graphics feature enums in favor of getCompressedImageFormats.
 
   * Fixed love.filesystem.setIdentity breaking in some situations when called multiple times.
+  * Fixed the default love.filesystem identity when in Fused mode in Windows.
   * Fixed love.system.openURL sometimes blocking indefinitely on Linux.
+  * Fixed love.joystick.setGamepadMapping.
   * Fixed shader:getWarnings returning unnecessary information.
+  * Fixed some cases of noncompliant shader code not properly erroring on some nvidia drivers.
   * Fixed a potential crash when Shader objects are garbage collected.
   * Fixed love.graphics.newMesh(vertexcount, ...) causing the Mesh to do instanced rendering.
   * Fixed Mesh:getVertexMap.
@@ -38,6 +44,7 @@ LOVE 0.9.2 [Baby Inspector]
   * Fixed Texture memory leak when Meshes are garbage collected.
   * Fixed the default line join mode to be 'miter' instead of an undefined value.
   * Fixed the default error handler text size when highdpi mode is enabled on a Retina monitor.
+  * Fixed the default error handler background color when sRGB mode is enabled for the window.
   * Fixed love.window.setMode to fall back to the largest available mode if a width or height greater than the largest supported is specified and fullscreen=true.
   * Fixed the state of wireframe mode when love.window.setMode is called.
   * Fixed Canvas:getPixel to error if the coordinates are not within the Canvas' size.
@@ -47,6 +54,7 @@ LOVE 0.9.2 [Baby Inspector]
 
   * Updated the Lua wrapper code for modules to avoid crashes when the module's instance is created, deleted, and recreated.
   * Updated internal code for handling garbage collection of love objects to be more efficient.
+  * Updated love's initialization code to trigger a Lua error if love.conf has an error in it.
   * Updated the paths returned by love.filesystem.getSaveDirectory and friends to strip double-slashes from the string.
   * Updated the error message when love.filesystem.write or File:open fails because the directory doesn't exist.
   * Updated the error message when love.math.setRandomseed(0) is attempted.
@@ -55,6 +63,8 @@ LOVE 0.9.2 [Baby Inspector]
   * Updated SpriteBatch:unbind to use less VRAM if the SpriteBatch has the static usage hint.
   * Updated love.graphics.newImage, love.image.newImageData, etc. to leave less Lua-owned memory around.
   * Updated love.graphics.push to accept different stack types to push. Current types are "transform" and "all".
+  * Updated love shaders to accept GLSL ES precision qualifiers on variables, although they do nothing.
+  * Updated the error message for love.graphics.newShader to be less cryptic if an invalid filename is given.
 
 LOVE 0.9.1 [Baby Inspector]
 ---------------------------

+ 5 - 0
src/common/Matrix.cpp

@@ -37,6 +37,11 @@ Matrix::Matrix()
 	setIdentity();
 }
 
+Matrix::Matrix(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
+{
+	setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+}
+
 Matrix::~Matrix()
 {
 }

+ 5 - 0
src/common/Matrix.h

@@ -41,6 +41,11 @@ public:
 	 **/
 	Matrix();
 
+	/**
+	 * Creates a new matrix set to a transformation.
+	 **/
+	Matrix(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
+
 	/**
 	 * Destructor.
 	 **/

+ 44 - 35
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -86,6 +86,8 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
 	, radialAccelerationMax(0)
 	, tangentialAccelerationMin(0)
 	, tangentialAccelerationMax(0)
+	, linearDampingMin(0.0f)
+	, linearDampingMax(0.0f)
 	, sizeVariation(0)
 	, rotationMin(0)
 	, rotationMax(0)
@@ -135,6 +137,8 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	, radialAccelerationMax(p.radialAccelerationMax)
 	, tangentialAccelerationMin(p.tangentialAccelerationMin)
 	, tangentialAccelerationMax(p.tangentialAccelerationMax)
+	, linearDampingMin(p.linearDampingMin)
+	, linearDampingMax(p.linearDampingMax)
 	, sizes(p.sizes)
 	, sizeVariation(p.sizeVariation)
 	, rotationMin(p.rotationMin)
@@ -284,6 +288,10 @@ void ParticleSystem::initParticle(Particle *p, float t)
 	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)];
@@ -460,12 +468,10 @@ void ParticleSystem::setParticleLifetime(float min, float max)
 		particleLifeMax = max;
 }
 
-void ParticleSystem::getParticleLifetime(float *min, float *max) const
+void ParticleSystem::getParticleLifetime(float &min, float &max) const
 {
-	if (min)
-		*min = particleLifeMin;
-	if (max)
-		*max = particleLifeMax;
+	min = particleLifeMin;
+	max = particleLifeMax;
 }
 
 void ParticleSystem::setPosition(float x, float y)
@@ -531,12 +537,10 @@ void ParticleSystem::setSpeed(float min, float max)
 	speedMax = max;
 }
 
-void ParticleSystem::getSpeed(float *min, float *max) const
+void ParticleSystem::getSpeed(float &min, float &max) const
 {
-	if (min)
-		*min = speedMin;
-	if (max)
-		*max = speedMax;
+	min = speedMin;
+	max = speedMax;
 }
 
 void ParticleSystem::setLinearAcceleration(float x, float y)
@@ -551,12 +555,10 @@ void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, f
 	linearAccelerationMax = love::Vector(xmax, ymax);
 }
 
-void ParticleSystem::getLinearAcceleration(love::Vector *min, love::Vector *max) const
+void ParticleSystem::getLinearAcceleration(love::Vector &min, love::Vector &max) const
 {
-	if (min)
-		*min = linearAccelerationMin;
-	if (max)
-		*max = linearAccelerationMax;
+	min = linearAccelerationMin;
+	max = linearAccelerationMax;
 }
 
 void ParticleSystem::setRadialAcceleration(float acceleration)
@@ -570,12 +572,10 @@ void ParticleSystem::setRadialAcceleration(float min, float max)
 	radialAccelerationMax = max;
 }
 
-void ParticleSystem::getRadialAcceleration(float *min, float *max) const
+void ParticleSystem::getRadialAcceleration(float &min, float &max) const
 {
-	if (min)
-		*min = radialAccelerationMin;
-	if (max)
-		*max = radialAccelerationMax;
+	min = radialAccelerationMin;
+	max = radialAccelerationMax;
 }
 
 void ParticleSystem::setTangentialAcceleration(float acceleration)
@@ -589,12 +589,22 @@ void ParticleSystem::setTangentialAcceleration(float min, float max)
 	tangentialAccelerationMax = max;
 }
 
-void ParticleSystem::getTangentialAcceleration(float *min, float *max) const
+void ParticleSystem::getTangentialAcceleration(float &min, float &max) const
+{
+	min = tangentialAccelerationMin;
+	max = tangentialAccelerationMax;
+}
+
+void ParticleSystem::setLinearDamping(float min, float max)
 {
-	if (min)
-		*min = tangentialAccelerationMin;
-	if (max)
-		*max = tangentialAccelerationMax;
+	linearDampingMin = min;
+	linearDampingMax = max;
+}
+
+void ParticleSystem::getLinearDamping(float &min, float &max) const
+{
+	min = linearDampingMin;
+	max = linearDampingMax;
 }
 
 void ParticleSystem::setSize(float size)
@@ -634,12 +644,10 @@ void ParticleSystem::setRotation(float min, float max)
 	rotationMax = max;
 }
 
-void ParticleSystem::getRotation(float *min, float *max) const
+void ParticleSystem::getRotation(float &min, float &max) const
 {
-	if (min)
-		*min = rotationMin;
-	if (max)
-		*max = rotationMax;
+	min = rotationMin;
+	max = rotationMax;
 }
 
 void ParticleSystem::setSpin(float spin)
@@ -654,12 +662,10 @@ void ParticleSystem::setSpin(float start, float end)
 	spinEnd = end;
 }
 
-void ParticleSystem::getSpin(float *start, float *end) const
+void ParticleSystem::getSpin(float &start, float &end) const
 {
-	if (start)
-		*start = spinStart;
-	if (end)
-		*end = spinEnd;
+	start = spinStart;
+	end = spinEnd;
 }
 
 void ParticleSystem::setSpinVariation(float variation)
@@ -931,6 +937,9 @@ void ParticleSystem::update(float dt)
 			// 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;
 

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

@@ -162,7 +162,7 @@ public:
 	 * @param[out] min The minimum life.
 	 * @param[out] max The maximum life.
 	 **/
-	void getParticleLifetime(float *min, float *max) const;
+	void getParticleLifetime(float &min, float &max) const;
 
 	/**
 	 * Sets the position of the center of the emitter.
@@ -249,7 +249,7 @@ public:
 	 * @param[out] min The minimum speed.
 	 * @param[out] max The maximum speed.
 	 **/
-	void getSpeed(float *min, float *max) const;
+	void getSpeed(float &min, float &max) const;
 
 	/**
 	 * Sets the linear acceleration (the acceleration along the x and y axes).
@@ -272,7 +272,7 @@ public:
 	 * @param[out] min The minimum acceleration.
 	 * @param[out] max The maximum acceleration.
 	 **/
-	void getLinearAcceleration(love::Vector *min, love::Vector *max) const;
+	void getLinearAcceleration(love::Vector &min, love::Vector &max) const;
 
 	/**
 	 * Sets the radial acceleration (the acceleration towards the particle emitter).
@@ -292,7 +292,7 @@ public:
 	 * @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;
+	void getRadialAcceleration(float &min, float &max) const;
 
 	/**
 	 * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
@@ -312,7 +312,18 @@ public:
 	 * @param[out] min The minimum tangential acceleration.
 	 * @param[out] max The maximum tangential acceleration.
 	 **/
-	void getTangentialAcceleration(float *min, float *max) const;
+	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).
@@ -360,7 +371,7 @@ public:
 	 * @param[out] min The minimum initial rotation.
 	 * @param[out] max The maximum initial rotation.
 	 **/
-	void getRotation(float *min, float *max) const;
+	void getRotation(float &min, float &max) const;
 
 	/**
 	 * Sets the spin of the sprite.
@@ -380,7 +391,7 @@ public:
 	 * @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;
+	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).
@@ -533,6 +544,8 @@ protected:
 		float radialAcceleration;
 		float tangentialAcceleration;
 
+		float linearDamping;
+
 		float size;
 		float sizeOffset;
 		float sizeIntervalSize;
@@ -619,6 +632,9 @@ protected:
 	float tangentialAccelerationMin;
 	float tangentialAccelerationMax;
 
+	float linearDampingMin;
+	float linearDampingMax;
+
 	// Size.
 	std::vector<float> sizes;
 	float sizeVariation;

+ 26 - 53
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -47,7 +47,7 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
 	, next(0)
 	, color(0)
 	, array_buf(nullptr)
-	, element_buf(nullptr)
+	, element_buf(size)
 	, buffer_used_offset(0)
 	, buffer_used_size(0)
 {
@@ -74,18 +74,15 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
 	try
 	{
 		array_buf = VertexBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
-		element_buf = new VertexIndex(size);
 	}
 	catch (love::Exception &)
 	{
 		delete array_buf;
-		delete element_buf;
 		throw;
 	}
 	catch (std::bad_alloc &)
 	{
 		delete array_buf;
-		delete element_buf;
 		throw love::Exception("Out of memory.");
 	}
 }
@@ -94,7 +91,6 @@ SpriteBatch::~SpriteBatch()
 {
 	delete color;
 	delete array_buf;
-	delete element_buf;
 }
 
 int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index /*= -1*/)
@@ -103,20 +99,9 @@ int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, fl
 	if ((index == -1 && next >= size) || index < -1 || index >= size)
 		return -1;
 
-	Vertex sprite[4];
+	Matrix t(x, y, a, sx, sy, ox, oy, kx, ky);
 
-	// Needed for colors.
-	memcpy(sprite, texture->getVertices(), sizeof(Vertex) * 4);
-
-	// Transform.
-	Matrix t;
-	t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
-	t.transform(sprite, sprite, 4);
-
-	if (color)
-		setColorv(sprite, *color);
-
-	addv(sprite, (index == -1) ? next : index);
+	addv(texture->getVertices(), t, (index == -1) ? next : index);
 
 	// Increment counter.
 	if (index == -1)
@@ -131,19 +116,9 @@ int SpriteBatch::addq(Quad *quad, float x, float y, float a, float sx, float sy,
 	if ((index == -1 && next >= size) || index < -1 || index >= next)
 		return -1;
 
-	Vertex sprite[4];
-
-	// Needed for colors.
-	memcpy(sprite, quad->getVertices(), sizeof(Vertex) * 4);
-
-	Matrix t;
-	t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
-	t.transform(sprite, sprite, 4);
+	Matrix t(x, y, a, sx, sy, ox, oy, kx, ky);
 
-	if (color)
-		setColorv(sprite, *color);
-
-	addv(sprite, (index == -1) ? next : index);
+	addv(quad->getVertices(), t, (index == -1) ? next : index);
 
 	// Increment counter.
 	if (index == -1)
@@ -171,7 +146,7 @@ void SpriteBatch::setTexture(Texture *newtexture)
 	texture.set(newtexture);
 }
 
-Texture *SpriteBatch::getTexture()
+Texture *SpriteBatch::getTexture() const
 {
 	return texture.get();
 }
@@ -216,40 +191,32 @@ void SpriteBatch::setBufferSize(int newsize)
 	}
 
 	size_t vertex_size = sizeof(Vertex) * 4 * newsize;
-
 	VertexBuffer *new_array_buf = nullptr;
-	VertexIndex *new_element_buf = nullptr;
 
 	try
 	{
 		new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
-		new_element_buf = new VertexIndex(newsize);
+
+		// Copy as much of the old data into the new VertexBuffer as can fit.
+		VertexBuffer::Bind bind(*new_array_buf);
+		void *new_data = new_array_buf->map();
+		memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size));
+
+		element_buf = VertexIndex(newsize);
 	}
 	catch (love::Exception &)
 	{
 		delete new_array_buf;
-		delete new_element_buf;
 		throw;
 	}
 
-	// Copy as much of the old data into the new VertexBuffer as can fit.
-	{
-		VertexBuffer::Bind bind(*new_array_buf);
-		new_array_buf->fill(0, sizeof(Vertex) * 4 * std::min(newsize, size), old_data);
-	}
-
 	// We don't need to unmap the old VertexBuffer since we're deleting it.
 	delete array_buf;
-	delete element_buf;
 
 	array_buf = new_array_buf;
-	element_buf = new_element_buf;
 	size = newsize;
 
 	next = std::min(next, newsize);
-
-	// The new VertexBuffer isn't mapped, so we should reset these variables.
-	buffer_used_offset = buffer_used_size = 0;
 }
 
 int SpriteBatch::getBufferSize() const
@@ -266,8 +233,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	if (next == 0)
 		return;
 
-	static Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(x, y, angle, sx, sy, ox, oy, kx, ky);
 
 	OpenGL::TempTransform transform(gl);
 	transform.get() *= t;
@@ -275,7 +241,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	texture->predraw();
 
 	VertexBuffer::Bind array_bind(*array_buf);
-	VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
+	VertexBuffer::Bind element_bind(*element_buf.getVertexBuffer());
 
 	// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
 	array_buf->unmap(buffer_used_offset, buffer_used_size);
@@ -297,7 +263,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
 
 	gl.prepareDraw();
-	gl.drawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
+	gl.drawElements(GL_TRIANGLES, element_buf.getIndexCount(next), element_buf.getType(), element_buf.getPointer(0));
 
 	glDisableVertexAttribArray(ATTRIB_TEXCOORD);
 	glDisableVertexAttribArray(ATTRIB_POS);
@@ -311,9 +277,16 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	texture->postdraw();
 }
 
-void SpriteBatch::addv(const Vertex *v, int index)
+void SpriteBatch::addv(const Vertex *v, const Matrix &m, int index)
 {
-	static const size_t sprite_size = 4 * sizeof(Vertex); // bytecount
+	// Needed for colors.
+	Vertex sprite[4] = {v[0], v[1], v[2], v[3]};
+	const size_t sprite_size = 4 * sizeof(Vertex); // bytecount
+
+	m.transform(sprite, sprite, 4);
+
+	if (color)
+		setColorv(sprite, *color);
 
 	VertexBuffer::Bind bind(*array_buf);
 
@@ -321,7 +294,7 @@ void SpriteBatch::addv(const Vertex *v, int index)
 	// on draw.)
 	array_buf->map();
 
-	array_buf->fill(index * sprite_size, sprite_size, v);
+	array_buf->fill(index * sprite_size, sprite_size, sprite);
 
 	buffer_used_offset = std::min(buffer_used_offset, index * sprite_size);
 	buffer_used_size = std::max(buffer_used_size, (index + 1) * sprite_size - buffer_used_offset);

+ 4 - 6
src/modules/graphics/opengl/SpriteBatch.h

@@ -26,13 +26,13 @@
 
 // LOVE
 #include "common/math.h"
-#include "common/Object.h"
 #include "common/Matrix.h"
 #include "common/StringMap.h"
 #include "graphics/Drawable.h"
 #include "graphics/Volatile.h"
 #include "graphics/Color.h"
 #include "graphics/Quad.h"
+#include "VertexBuffer.h"
 
 namespace love
 {
@@ -43,8 +43,6 @@ namespace opengl
 
 // Forward declarations.
 class Texture;
-class VertexBuffer;
-class VertexIndex;
 
 class SpriteBatch : public Drawable
 {
@@ -68,7 +66,7 @@ public:
 	void flush();
 
 	void setTexture(Texture *newtexture);
-	Texture *getTexture();
+	Texture *getTexture() const;
 
 	/**
 	 * Set the current color for this SpriteBatch. The sprites added
@@ -115,7 +113,7 @@ public:
 
 private:
 
-	void addv(const Vertex *v, int index);
+	void addv(const Vertex *v, const Matrix &m, int index);
 
 	/**
 	 * Set the color for vertices.
@@ -139,7 +137,7 @@ private:
 	Color *color;
 
 	VertexBuffer *array_buf;
-	VertexIndex *element_buf;
+	VertexIndex element_buf;
 
 	// The portion of the vertex buffer that's been modified while mapped.
 	size_t buffer_used_offset;

+ 15 - 1
src/modules/graphics/opengl/VertexBuffer.cpp

@@ -50,7 +50,7 @@ VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacki
 	, backing(backing)
 	, vbo(0)
 	, memory_map(nullptr)
-	, is_dirty(true)
+	, is_dirty(false)
 {
 	if (getMemoryBacking() == BACKING_FULL)
 		memory_map = (char *) malloc(getSize());
@@ -249,6 +249,20 @@ VertexIndex::VertexIndex(size_t size)
 	addSize(size);
 }
 
+VertexIndex::VertexIndex(const VertexIndex &other)
+	: size(other.size)
+{
+	addSize(size);
+}
+
+VertexIndex &VertexIndex::operator = (const VertexIndex &other)
+{
+	addSize(other.size);
+	removeSize(size);
+	size = other.size;
+	return *this;
+}
+
 VertexIndex::~VertexIndex()
 {
 	removeSize(size);

+ 3 - 0
src/modules/graphics/opengl/VertexBuffer.h

@@ -344,6 +344,9 @@ public:
 	 */
 	VertexIndex(size_t size);
 
+	VertexIndex(const VertexIndex &other);
+	VertexIndex &operator = (const VertexIndex &other);
+
 	/**
 	 * Removes an entry from the list of sizes and resizes the VertexBuffer
 	 * if needed.

+ 14 - 0
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -28,6 +28,7 @@
 
 #include "scripts/graphics.lua.h"
 #include <cassert>
+#include <cstring>
 
 namespace love
 {
@@ -412,6 +413,19 @@ int w_newShader(lua_State *L)
 			lua_call(L, 1, 1);
 			lua_replace(L, i);
 		}
+		else
+		{
+			// Check if the argument looks like a filepath - we want a nicer
+			// error for misspelled filepath arguments.
+			size_t slen = 0;
+			const char *str = lua_tolstring(L, i, &slen);
+			if (slen > 0 && slen < 256 && !strchr(str, '\n'))
+			{
+				const char *ext = strchr(str, '.');
+				if (ext != nullptr && !strchr(ext, ';') && !strchr(ext, ' '))
+					return luaL_error(L, "Could not open file %s. Does not exist.", str);
+			}
+		}
 	}
 
 	bool has_arg1 = lua_isstring(L, 1);

+ 28 - 7
src/modules/graphics/opengl/wrap_ParticleSystem.cpp

@@ -164,7 +164,7 @@ int w_ParticleSystem_getParticleLifetime(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float min, max;
-	t->getParticleLifetime(&min, &max);
+	t->getParticleLifetime(min, max);
 	lua_pushnumber(L, min);
 	lua_pushnumber(L, max);
 	return 2;
@@ -278,7 +278,7 @@ int w_ParticleSystem_getSpeed(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float min, max;
-	t->getSpeed(&min, &max);
+	t->getSpeed(min, max);
 	lua_pushnumber(L, min);
 	lua_pushnumber(L, max);
 	return 2;
@@ -299,7 +299,7 @@ int w_ParticleSystem_getLinearAcceleration(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	love::Vector min, max;
-	t->getLinearAcceleration(&min, &max);
+	t->getLinearAcceleration(min, max);
 	lua_pushnumber(L, min.x);
 	lua_pushnumber(L, min.y);
 	lua_pushnumber(L, max.x);
@@ -320,7 +320,7 @@ int w_ParticleSystem_getRadialAcceleration(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float min, max;
-	t->getRadialAcceleration(&min, &max);
+	t->getRadialAcceleration(min, max);
 	lua_pushnumber(L, min);
 	lua_pushnumber(L, max);
 	return 2;
@@ -339,7 +339,26 @@ int w_ParticleSystem_getTangentialAcceleration(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float min, max;
-	t->getTangentialAcceleration(&min, &max);
+	t->getTangentialAcceleration(min, max);
+	lua_pushnumber(L, min);
+	lua_pushnumber(L, max);
+	return 2;
+}
+
+int w_ParticleSystem_setLinearDamping(lua_State *L)
+{
+	ParticleSystem *t = luax_checkparticlesystem(L, 1);
+	float arg1 = (float)luaL_checknumber(L, 2);
+	float arg2 = (float)luaL_optnumber(L, 3, arg1);
+	t->setLinearDamping(arg1, arg2);
+	return 0;
+}
+
+int w_ParticleSystem_getLinearDamping(lua_State *L)
+{
+	ParticleSystem *t = luax_checkparticlesystem(L, 1);
+	float min, max;
+	t->getLinearDamping(min, max);
 	lua_pushnumber(L, min);
 	lua_pushnumber(L, max);
 	return 2;
@@ -411,7 +430,7 @@ int w_ParticleSystem_getRotation(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float min, max;
-	t->getRotation(&min, &max);
+	t->getRotation(min, max);
 	lua_pushnumber(L, min);
 	lua_pushnumber(L, max);
 	return 2;
@@ -430,7 +449,7 @@ int w_ParticleSystem_getSpin(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float start, end;
-	t->getSpin(&start, &end);
+	t->getSpin(start, end);
 	lua_pushnumber(L, start);
 	lua_pushnumber(L, end);
 	return 2;
@@ -730,6 +749,8 @@ static const luaL_Reg functions[] =
 	{ "getRadialAcceleration", w_ParticleSystem_getRadialAcceleration },
 	{ "setTangentialAcceleration", w_ParticleSystem_setTangentialAcceleration },
 	{ "getTangentialAcceleration", w_ParticleSystem_getTangentialAcceleration },
+	{ "setLinearDamping", w_ParticleSystem_setLinearDamping },
+	{ "getLinearDamping", w_ParticleSystem_getLinearDamping },
 	{ "setSizes", w_ParticleSystem_setSizes },
 	{ "getSizes", w_ParticleSystem_getSizes },
 	{ "setSizeVariation", w_ParticleSystem_setSizeVariation },

+ 2 - 0
src/modules/graphics/opengl/wrap_ParticleSystem.h

@@ -63,6 +63,8 @@ int w_ParticleSystem_setRadialAcceleration(lua_State *L);
 int w_ParticleSystem_getRadialAcceleration(lua_State *L);
 int w_ParticleSystem_setTangentialAcceleration(lua_State *L);
 int w_ParticleSystem_getTangentialAcceleration(lua_State *L);
+int w_ParticleSystem_setLinearDamping(lua_State *L);
+int w_ParticleSystem_getLinearDamping(lua_State *L);
 int w_ParticleSystem_setSizes(lua_State *L);
 int w_ParticleSystem_getSizes(lua_State *L);
 int w_ParticleSystem_setSizeVariation(lua_State *L);

+ 2 - 0
src/modules/window/Window.cpp

@@ -83,6 +83,8 @@ StringMap<Window::Setting, Window::SETTING_MAX_ENUM>::Entry Window::settingEntri
 	{"highdpi", SETTING_HIGHDPI},
 	{"srgb", SETTING_SRGB},
 	{"refreshrate", SETTING_REFRESHRATE},
+	{"x", SETTING_X},
+	{"y", SETTING_Y},
 };
 
 StringMap<Window::Setting, Window::SETTING_MAX_ENUM> Window::settings(Window::settingEntries, sizeof(Window::settingEntries));

+ 8 - 0
src/modules/window/Window.h

@@ -59,6 +59,8 @@ public:
 		SETTING_HIGHDPI,
 		SETTING_SRGB,
 		SETTING_REFRESHRATE,
+		SETTING_X,
+		SETTING_Y,
 		SETTING_MAX_ENUM
 	};
 
@@ -118,6 +120,9 @@ public:
 
 	virtual void getDesktopDimensions(int displayindex, int &width, int &height) const = 0;
 
+	virtual void setPosition(int x, int y, int displayindex) = 0;
+	virtual void getPosition(int &x, int &y, int &displayindex) = 0;
+
 	virtual bool isCreated() const = 0;
 
 	virtual void setWindowTitle(const std::string &title) = 0;
@@ -199,6 +204,9 @@ struct WindowSettings
 	bool highdpi = false;
 	bool sRGB = false;
 	double refreshrate = 0.0;
+	bool useposition = false;
+	int x = 0;
+	int y = 0;
 };
 
 } // window

+ 69 - 9
src/modules/window/sdl/Window.cpp

@@ -116,6 +116,25 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
 #endif
 
+	int x = f.x;
+	int y = f.y;
+
+	if (f.useposition && !f.fullscreen)
+	{
+		// The position needs to be in the global coordinate space.
+		SDL_Rect displaybounds = {};
+		SDL_GetDisplayBounds(f.display, &displaybounds);
+		x += displaybounds.x;
+		y += displaybounds.y;
+	}
+	else
+	{
+		if (f.centered)
+			x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(f.display);
+		else
+			x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display);
+	}
+
 	graphics::Graphics *gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 	if (gfx != nullptr)
 		gfx->unSetMode();
@@ -148,9 +167,6 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		}
 	}
 
-	int centeredpos = SDL_WINDOWPOS_CENTERED_DISPLAY(f.display);
-	int uncenteredpos = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display);
-
 	if (!window)
 	{
 		created = false;
@@ -159,9 +175,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		setWindowGLAttributes(f.msaa, f.sRGB);
 
 		const char *title = windowTitle.c_str();
-		int pos = f.centered ? centeredpos : uncenteredpos;
 
-		window = SDL_CreateWindow(title, pos, pos, width, height, sdlflags);
+		window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
 
 		if (!window && f.msaa > 0)
 		{
@@ -169,7 +184,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
 			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
 
-			window = SDL_CreateWindow(title, pos, pos, width, height, sdlflags);
+			window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
 			f.msaa = 0;
 		}
 
@@ -187,8 +202,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 	// Enforce minimum window dimensions.
 	SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
 
-	if (f.centered && !f.fullscreen)
-		SDL_SetWindowPosition(window, centeredpos, centeredpos);
+	if ((f.useposition || f.centered) && !f.fullscreen)
+		SDL_SetWindowPosition(window, x, y);
 
 	SDL_RaiseWindow(window);
 
@@ -366,7 +381,8 @@ void Window::updateSettings(const WindowSettings &newsettings)
 	curMode.settings.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
 	curMode.settings.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
 	curMode.settings.centered = newsettings.centered;
-	curMode.settings.display = std::max(SDL_GetWindowDisplayIndex(window), 0);
+
+	getPosition(curMode.settings.x, curMode.settings.y, curMode.settings.display);
 
 #if SDL_VERSION_ATLEAST(2,0,1)
 	curMode.settings.highdpi = (wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
@@ -525,6 +541,50 @@ void Window::getDesktopDimensions(int displayindex, int &width, int &height) con
 	}
 }
 
+void Window::setPosition(int x, int y, int displayindex)
+{
+	if (!window)
+		return;
+
+	displayindex = std::min(std::max(displayindex, 0), getDisplayCount() - 1);
+
+	SDL_Rect displaybounds = {};
+	SDL_GetDisplayBounds(displayindex, &displaybounds);
+
+	// The position needs to be in the global coordinate space.
+	x += displaybounds.x;
+	y += displaybounds.y;
+
+	SDL_SetWindowPosition(window, x, y);
+
+	curMode.settings.useposition = true;
+}
+
+void Window::getPosition(int &x, int &y, int &displayindex)
+{
+	if (!window)
+	{
+		x = y =  0;
+		displayindex = 0;
+		return;
+	}
+
+	displayindex = std::max(SDL_GetWindowDisplayIndex(window), 0);
+
+	SDL_GetWindowPosition(window, &x, &y);
+
+	// SDL always reports 0, 0 for fullscreen windows.
+	if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN))
+	{
+		SDL_Rect displaybounds = {};
+		SDL_GetDisplayBounds(displayindex, &displaybounds);
+
+		// The position needs to be in the monitor's coordinate space.
+		x -= displaybounds.x;
+		y -= displaybounds.y;
+	}
+}
+
 bool Window::isCreated() const
 {
 	return created;

+ 3 - 0
src/modules/window/sdl/Window.h

@@ -57,6 +57,9 @@ public:
 
 	void getDesktopDimensions(int displayindex, int &width, int &height) const;
 
+	void setPosition(int x, int y, int displayindex);
+	void getPosition(int &x, int &y, int &displayindex);
+
 	bool isCreated() const;
 
 	void setWindowTitle(const std::string &title);

+ 39 - 0
src/modules/window/wrap_Window.cpp

@@ -109,6 +109,16 @@ int w_setMode(lua_State *L)
 	settings.highdpi = luax_boolflag(L, 3, settingName(Window::SETTING_HIGHDPI), false);
 	settings.sRGB = luax_boolflag(L, 3, settingName(Window::SETTING_SRGB), false);
 
+	lua_getfield(L, 3, settingName(Window::SETTING_X));
+	lua_getfield(L, 3, settingName(Window::SETTING_Y));
+	settings.useposition = !(lua_isnoneornil(L, -2) && lua_isnoneornil(L, -1));
+	if (settings.useposition)
+	{
+		settings.x = luaL_optint(L, -2, 0);
+		settings.y = luaL_optint(L, -1, 0);
+	}
+	lua_pop(L, 2);
+
 	// We don't explicitly set the refresh rate, it's "read-only".
 
 	luax_catchexcept(L,
@@ -171,6 +181,12 @@ int w_getMode(lua_State *L)
 	lua_pushnumber(L, settings.refreshrate);
 	lua_setfield(L, -2, settingName(Window::SETTING_REFRESHRATE));
 
+	lua_pushinteger(L, settings.x);
+	lua_setfield(L, -2, settingName(Window::SETTING_X));
+
+	lua_pushinteger(L, settings.y);
+	lua_setfield(L, -2, settingName(Window::SETTING_Y));
+
 	return 3;
 }
 
@@ -253,6 +269,27 @@ int w_getDesktopDimensions(lua_State *L)
 	return 2;
 }
 
+int w_setPosition(lua_State *L)
+{
+	int x = luaL_checkint(L, 1);
+	int y = luaL_checkint(L, 2);
+	int displayindex = luaL_optint(L, 3, 1) - 1;
+	instance()->setPosition(x, y, displayindex);
+	return 0;
+}
+
+int w_getPosition(lua_State *L)
+{
+	int x = 0;
+	int y = 0;
+	int displayindex = 0;
+	instance()->getPosition(x, y, displayindex);
+	lua_pushinteger(L, x);
+	lua_pushinteger(L, y);
+	lua_pushinteger(L, displayindex + 1);
+	return 3;
+}
+
 int w_setIcon(lua_State *L)
 {
 	image::ImageData *i = luax_checktype<image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
@@ -426,6 +463,8 @@ static const luaL_Reg functions[] =
 	{ "getFullscreen", w_getFullscreen },
 	{ "isCreated", w_isCreated },
 	{ "getDesktopDimensions", w_getDesktopDimensions },
+	{ "setPosition", w_setPosition },
+	{ "getPosition", w_getPosition },
 	{ "setIcon", w_setIcon },
 	{ "getIcon", w_getIcon },
 	{ "setTitle", w_setTitle },

+ 2 - 0
src/modules/window/wrap_Window.h

@@ -38,6 +38,8 @@ int w_setFullscreen(lua_State *L);
 int w_getFullscreen(lua_State *L);
 int w_isCreated(lua_State *L);
 int w_getDesktopDimensions(lua_State *L);
+int w_setPosition(lua_State *L);
+int w_getPosition(lua_State *L);
 int w_setIcon(lua_State *L);
 int w_getIcon(lua_State *L);
 int w_setTitle(lua_State *L);

+ 4 - 0
src/scripts/boot.lua

@@ -303,6 +303,8 @@ function love.init()
 		window = {
 			width = 800,
 			height = 600,
+			x = nil,
+			y = nil,
 			minwidth = 1,
 			minheight = 1,
 			fullscreen = false,
@@ -408,6 +410,8 @@ function love.init()
 			display = c.window.display,
 			highdpi = c.window.highdpi,
 			srgb = c.window.srgb,
+			x = c.window.x,
+			y = c.window.y,
 		}), "Could not set window mode")
 		love.window.setTitle(c.window.title or c.title)
 		if c.window.icon then

+ 4 - 0
src/scripts/boot.lua.h

@@ -553,6 +553,8 @@ const unsigned char boot_lua[] =
 	0x09, 0x09, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x7b, 0x0a,
 	0x09, 0x09, 0x09, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x38, 0x30, 0x30, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x36, 0x30, 0x30, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x78, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x79, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x6d, 0x69, 0x6e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x31, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x6d, 0x69, 0x6e, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x61, 
@@ -716,6 +718,8 @@ const unsigned char boot_lua[] =
 	0x64, 0x6f, 0x77, 0x2e, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x73, 0x72, 0x67, 0x62, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 
 	0x2e, 0x73, 0x72, 0x67, 0x62, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x78, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x78, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x79, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x79, 0x2c, 0x0a,
 	0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 
 	0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a,
 	0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54,