Browse Source

Move backend-agnostic Mesh and SpriteBatch code out of the opengl implementation folder.

--HG--
branch : minor
Alex Szpakowski 8 years ago
parent
commit
c0e66eba83

+ 14 - 8
CMakeLists.txt

@@ -476,6 +476,10 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/Font.h
 	src/modules/graphics/Graphics.cpp
 	src/modules/graphics/Graphics.h
+	src/modules/graphics/Image.cpp
+	src/modules/graphics/Image.h
+	src/modules/graphics/Mesh.cpp
+	src/modules/graphics/Mesh.h
 	src/modules/graphics/ParticleSystem.cpp
 	src/modules/graphics/ParticleSystem.h
 	src/modules/graphics/Polyline.cpp
@@ -484,6 +488,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/Quad.h
 	src/modules/graphics/Shader.cpp
 	src/modules/graphics/Shader.h
+	src/modules/graphics/SpriteBatch.cpp
+	src/modules/graphics/SpriteBatch.h
 	src/modules/graphics/StreamBuffer.cpp
 	src/modules/graphics/StreamBuffer.h
 	src/modules/graphics/Text.cpp
@@ -500,10 +506,18 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/wrap_Canvas.h
 	src/modules/graphics/wrap_Font.cpp
 	src/modules/graphics/wrap_Font.h
+	src/modules/graphics/wrap_Image.cpp
+	src/modules/graphics/wrap_Image.h
+	src/modules/graphics/wrap_Mesh.cpp
+	src/modules/graphics/wrap_Mesh.h
+	src/modules/graphics/wrap_ParticleSystem.cpp
+	src/modules/graphics/wrap_ParticleSystem.h
 	src/modules/graphics/wrap_Quad.cpp
 	src/modules/graphics/wrap_Quad.h
 	src/modules/graphics/wrap_Shader.cpp
 	src/modules/graphics/wrap_Shader.h
+	src/modules/graphics/wrap_SpriteBatch.cpp
+	src/modules/graphics/wrap_SpriteBatch.h
 	src/modules/graphics/wrap_Texture.cpp
 	src/modules/graphics/wrap_Texture.h
 	src/modules/graphics/wrap_Text.cpp
@@ -543,14 +557,6 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
 	src/modules/graphics/opengl/Video.h
 	src/modules/graphics/opengl/wrap_Graphics.cpp
 	src/modules/graphics/opengl/wrap_Graphics.h
-	src/modules/graphics/opengl/wrap_Image.cpp
-	src/modules/graphics/opengl/wrap_Image.h
-	src/modules/graphics/opengl/wrap_Mesh.cpp
-	src/modules/graphics/opengl/wrap_Mesh.h
-	src/modules/graphics/opengl/wrap_ParticleSystem.cpp
-	src/modules/graphics/opengl/wrap_ParticleSystem.h
-	src/modules/graphics/opengl/wrap_SpriteBatch.cpp
-	src/modules/graphics/opengl/wrap_SpriteBatch.h
 )
 
 set(LOVE_SRC_MODULE_GRAPHICS

+ 50 - 30
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -459,15 +459,6 @@
 		FA0B7D5E1A95902C000E1D17 /* wrap_Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BAA1A95902C000E1D17 /* wrap_Graphics.cpp */; };
 		FA0B7D5F1A95902C000E1D17 /* wrap_Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BAA1A95902C000E1D17 /* wrap_Graphics.cpp */; };
 		FA0B7D601A95902C000E1D17 /* wrap_Graphics.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BAB1A95902C000E1D17 /* wrap_Graphics.h */; };
-		FA0B7D641A95902C000E1D17 /* wrap_Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BAE1A95902C000E1D17 /* wrap_Mesh.cpp */; };
-		FA0B7D651A95902C000E1D17 /* wrap_Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BAE1A95902C000E1D17 /* wrap_Mesh.cpp */; };
-		FA0B7D661A95902C000E1D17 /* wrap_Mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BAF1A95902C000E1D17 /* wrap_Mesh.h */; };
-		FA0B7D671A95902C000E1D17 /* wrap_ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BB01A95902C000E1D17 /* wrap_ParticleSystem.cpp */; };
-		FA0B7D681A95902C000E1D17 /* wrap_ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BB01A95902C000E1D17 /* wrap_ParticleSystem.cpp */; };
-		FA0B7D691A95902C000E1D17 /* wrap_ParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BB11A95902C000E1D17 /* wrap_ParticleSystem.h */; };
-		FA0B7D701A95902C000E1D17 /* wrap_SpriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BB61A95902C000E1D17 /* wrap_SpriteBatch.cpp */; };
-		FA0B7D711A95902C000E1D17 /* wrap_SpriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BB61A95902C000E1D17 /* wrap_SpriteBatch.cpp */; };
-		FA0B7D721A95902C000E1D17 /* wrap_SpriteBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BB71A95902C000E1D17 /* wrap_SpriteBatch.h */; };
 		FA0B7D791A95902C000E1D17 /* Quad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BBC1A95902C000E1D17 /* Quad.cpp */; };
 		FA0B7D7A1A95902C000E1D17 /* Quad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BBC1A95902C000E1D17 /* Quad.cpp */; };
 		FA0B7D7B1A95902C000E1D17 /* Quad.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BBD1A95902C000E1D17 /* Quad.h */; };
@@ -1024,6 +1015,21 @@
 		FADF541B1E3DA46C00012CC0 /* wrap_Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54191E3DA46C00012CC0 /* wrap_Image.cpp */; };
 		FADF541C1E3DA46C00012CC0 /* wrap_Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54191E3DA46C00012CC0 /* wrap_Image.cpp */; };
 		FADF541D1E3DA46C00012CC0 /* wrap_Image.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF541A1E3DA46C00012CC0 /* wrap_Image.h */; };
+		FADF54201E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */; };
+		FADF54211E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */; };
+		FADF54221E3DA52C00012CC0 /* wrap_ParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF541F1E3DA52C00012CC0 /* wrap_ParticleSystem.h */; };
+		FADF54251E3DA5BA00012CC0 /* Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54231E3DA5BA00012CC0 /* Mesh.cpp */; };
+		FADF54261E3DA5BA00012CC0 /* Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54231E3DA5BA00012CC0 /* Mesh.cpp */; };
+		FADF54271E3DA5BA00012CC0 /* Mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF54241E3DA5BA00012CC0 /* Mesh.h */; };
+		FADF542A1E3DAADA00012CC0 /* wrap_Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */; };
+		FADF542B1E3DAADA00012CC0 /* wrap_Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */; };
+		FADF542C1E3DAADA00012CC0 /* wrap_Mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF54291E3DAADA00012CC0 /* wrap_Mesh.h */; };
+		FADF542F1E3DABF600012CC0 /* SpriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF542D1E3DABF600012CC0 /* SpriteBatch.cpp */; };
+		FADF54301E3DABF600012CC0 /* SpriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF542D1E3DABF600012CC0 /* SpriteBatch.cpp */; };
+		FADF54311E3DABF600012CC0 /* SpriteBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF542E1E3DABF600012CC0 /* SpriteBatch.h */; };
+		FADF54341E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54321E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp */; };
+		FADF54351E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54321E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp */; };
+		FADF54361E3DAE6E00012CC0 /* wrap_SpriteBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF54331E3DAE6E00012CC0 /* wrap_SpriteBatch.h */; };
 		FAE272521C05A15B00A67640 /* ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAE272501C05A15B00A67640 /* ParticleSystem.cpp */; };
 		FAE272531C05A15B00A67640 /* ParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE272511C05A15B00A67640 /* ParticleSystem.h */; };
 		FAF140531E20934C00F898D2 /* CodeGen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF13FC21E20934C00F898D2 /* CodeGen.cpp */; };
@@ -1467,12 +1473,6 @@
 		FA0B7BA51A95902C000E1D17 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
 		FA0B7BAA1A95902C000E1D17 /* wrap_Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Graphics.cpp; sourceTree = "<group>"; };
 		FA0B7BAB1A95902C000E1D17 /* wrap_Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Graphics.h; sourceTree = "<group>"; };
-		FA0B7BAE1A95902C000E1D17 /* wrap_Mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Mesh.cpp; sourceTree = "<group>"; };
-		FA0B7BAF1A95902C000E1D17 /* wrap_Mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Mesh.h; sourceTree = "<group>"; };
-		FA0B7BB01A95902C000E1D17 /* wrap_ParticleSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_ParticleSystem.cpp; sourceTree = "<group>"; };
-		FA0B7BB11A95902C000E1D17 /* wrap_ParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_ParticleSystem.h; sourceTree = "<group>"; };
-		FA0B7BB61A95902C000E1D17 /* wrap_SpriteBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_SpriteBatch.cpp; sourceTree = "<group>"; };
-		FA0B7BB71A95902C000E1D17 /* wrap_SpriteBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_SpriteBatch.h; sourceTree = "<group>"; };
 		FA0B7BBC1A95902C000E1D17 /* Quad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Quad.cpp; sourceTree = "<group>"; };
 		FA0B7BBD1A95902C000E1D17 /* Quad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Quad.h; sourceTree = "<group>"; };
 		FA0B7BBE1A95902C000E1D17 /* Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Texture.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
@@ -1854,6 +1854,16 @@
 		FADF54151E3DA08E00012CC0 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; };
 		FADF54191E3DA46C00012CC0 /* wrap_Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Image.cpp; sourceTree = "<group>"; };
 		FADF541A1E3DA46C00012CC0 /* wrap_Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Image.h; sourceTree = "<group>"; };
+		FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_ParticleSystem.cpp; sourceTree = "<group>"; };
+		FADF541F1E3DA52C00012CC0 /* wrap_ParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_ParticleSystem.h; sourceTree = "<group>"; };
+		FADF54231E3DA5BA00012CC0 /* Mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Mesh.cpp; sourceTree = "<group>"; };
+		FADF54241E3DA5BA00012CC0 /* Mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mesh.h; sourceTree = "<group>"; };
+		FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Mesh.cpp; sourceTree = "<group>"; };
+		FADF54291E3DAADA00012CC0 /* wrap_Mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Mesh.h; sourceTree = "<group>"; };
+		FADF542D1E3DABF600012CC0 /* SpriteBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpriteBatch.cpp; sourceTree = "<group>"; };
+		FADF542E1E3DABF600012CC0 /* SpriteBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteBatch.h; sourceTree = "<group>"; };
+		FADF54321E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_SpriteBatch.cpp; sourceTree = "<group>"; };
+		FADF54331E3DAE6E00012CC0 /* wrap_SpriteBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_SpriteBatch.h; 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>"; };
 		FAF13FC21E20934C00F898D2 /* CodeGen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CodeGen.cpp; sourceTree = "<group>"; };
@@ -2649,6 +2659,8 @@
 				FA0B7B8B1A95902C000E1D17 /* Graphics.h */,
 				FADF54141E3DA08E00012CC0 /* Image.cpp */,
 				FADF54151E3DA08E00012CC0 /* Image.h */,
+				FADF54231E3DA5BA00012CC0 /* Mesh.cpp */,
+				FADF54241E3DA5BA00012CC0 /* Mesh.h */,
 				FA0B7B8C1A95902C000E1D17 /* opengl */,
 				FAE272501C05A15B00A67640 /* ParticleSystem.cpp */,
 				FAE272511C05A15B00A67640 /* ParticleSystem.h */,
@@ -2658,6 +2670,8 @@
 				FA0B7BBD1A95902C000E1D17 /* Quad.h */,
 				FA1BA0AF1E16FD0800AA2803 /* Shader.cpp */,
 				FA1BA0B01E16FD0800AA2803 /* Shader.h */,
+				FADF542D1E3DABF600012CC0 /* SpriteBatch.cpp */,
+				FADF542E1E3DABF600012CC0 /* SpriteBatch.h */,
 				FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */,
 				FA2AF6721DAD62710032B62C /* StreamBuffer.h */,
 				FADF53FB1E3D74F200012CC0 /* Text.cpp */,
@@ -2676,10 +2690,16 @@
 				FA1BA0A11E16D97500AA2803 /* wrap_Font.h */,
 				FADF54191E3DA46C00012CC0 /* wrap_Image.cpp */,
 				FADF541A1E3DA46C00012CC0 /* wrap_Image.h */,
+				FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */,
+				FADF54291E3DAADA00012CC0 /* wrap_Mesh.h */,
+				FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */,
+				FADF541F1E3DA52C00012CC0 /* wrap_ParticleSystem.h */,
 				FA620A2E1AA2F8DB005DB4C2 /* wrap_Quad.cpp */,
 				FA620A2F1AA2F8DB005DB4C2 /* wrap_Quad.h */,
 				FA1BA0B51E17043400AA2803 /* wrap_Shader.cpp */,
 				FA1BA0B61E17043400AA2803 /* wrap_Shader.h */,
+				FADF54321E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp */,
+				FADF54331E3DAE6E00012CC0 /* wrap_SpriteBatch.h */,
 				FADF54001E3D77B500012CC0 /* wrap_Text.cpp */,
 				FADF54011E3D77B500012CC0 /* wrap_Text.h */,
 				FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */,
@@ -2725,12 +2745,6 @@
 				FA0B7BAA1A95902C000E1D17 /* wrap_Graphics.cpp */,
 				FA0B7BAB1A95902C000E1D17 /* wrap_Graphics.h */,
 				FA6AE6041B3335EC00583D5C /* wrap_Graphics.lua */,
-				FA0B7BAE1A95902C000E1D17 /* wrap_Mesh.cpp */,
-				FA0B7BAF1A95902C000E1D17 /* wrap_Mesh.h */,
-				FA0B7BB01A95902C000E1D17 /* wrap_ParticleSystem.cpp */,
-				FA0B7BB11A95902C000E1D17 /* wrap_ParticleSystem.h */,
-				FA0B7BB61A95902C000E1D17 /* wrap_SpriteBatch.cpp */,
-				FA0B7BB71A95902C000E1D17 /* wrap_SpriteBatch.h */,
 			);
 			path = opengl;
 			sourceTree = "<group>";
@@ -3491,6 +3505,7 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				FADF54221E3DA52C00012CC0 /* wrap_ParticleSystem.h in Headers */,
 				217DFC0A1D9F6D490055D849 /* unix.h in Headers */,
 				FA0B7D7E1A95902C000E1D17 /* Texture.h in Headers */,
 				FA0B7E561A95902C000E1D17 /* wrap_GearJoint.h in Headers */,
@@ -3520,6 +3535,7 @@
 				FA0B7DC61A95902C000E1D17 /* wrap_JoystickModule.h in Headers */,
 				FA0B7D201A95902C000E1D17 /* ImageRasterizer.h in Headers */,
 				FA0B7D241A95902C000E1D17 /* Vera.ttf.h in Headers */,
+				FADF54311E3DABF600012CC0 /* SpriteBatch.h in Headers */,
 				FA0B7E5F1A95902C000E1D17 /* wrap_MouseJoint.h in Headers */,
 				FAB17BED1ABFAF1800F9BA27 /* CompressedData.h in Headers */,
 				217DFC0C1D9F6D490055D849 /* unixtcp.h in Headers */,
@@ -3538,7 +3554,6 @@
 				FA0B7E901A95902C000E1D17 /* GmeDecoder.h in Headers */,
 				FA0B7D0E1A95902C000E1D17 /* wrap_Filesystem.h in Headers */,
 				FA0B7EE41A95902D000E1D17 /* Window.h in Headers */,
-				FA0B7D721A95902C000E1D17 /* wrap_SpriteBatch.h in Headers */,
 				FA0B7CFC1A95902C000E1D17 /* Filesystem.h in Headers */,
 				FA0B7AD81A958EA3000E1D17 /* lua-enet.h in Headers */,
 				FA0B7A3A1A958EA3000E1D17 /* b2DynamicTree.h in Headers */,
@@ -3546,6 +3561,7 @@
 				FA0B7EBA1A95902C000E1D17 /* Channel.h in Headers */,
 				FA0B7D3E1A95902C000E1D17 /* Image.h in Headers */,
 				FA0B7ECA1A95902C000E1D17 /* threads.h in Headers */,
+				FADF54361E3DAE6E00012CC0 /* wrap_SpriteBatch.h in Headers */,
 				FA0B7DB01A95902C000E1D17 /* wrap_CompressedImageData.h in Headers */,
 				FA0B7AC11A958EA3000E1D17 /* callbacks.h in Headers */,
 				FA0B7D8F1A95902C000E1D17 /* ddsHandler.h in Headers */,
@@ -3629,6 +3645,7 @@
 				FA0B7DCC1A95902C000E1D17 /* Keyboard.h in Headers */,
 				FA620A341AA2F8DB005DB4C2 /* wrap_Quad.h in Headers */,
 				FA0B7EA51A95902C000E1D17 /* SoundData.h in Headers */,
+				FADF54271E3DA5BA00012CC0 /* Mesh.h in Headers */,
 				FAF1405E1E20934C00F898D2 /* PoolAlloc.h in Headers */,
 				FA0B7A821A958EA3000E1D17 /* b2EdgeAndCircleContact.h in Headers */,
 				FA0B79341A958E3B000E1D17 /* Object.h in Headers */,
@@ -3673,7 +3690,6 @@
 				FA0B7A2B1A958EA3000E1D17 /* b2BroadPhase.h in Headers */,
 				217DFBFD1D9F6D490055D849 /* smtp.lua.h in Headers */,
 				FA27B39F1B498151008A9DCE /* Video.h in Headers */,
-				FA0B7D661A95902C000E1D17 /* wrap_Mesh.h in Headers */,
 				FA0B7AAC1A958EA3000E1D17 /* b2WeldJoint.h in Headers */,
 				217DFC041D9F6D490055D849 /* timeout.h in Headers */,
 				FA0B7E7A1A95902C000E1D17 /* wrap_WheelJoint.h in Headers */,
@@ -3808,7 +3824,6 @@
 				FA0B7B2E1A958EA3000E1D17 /* unchecked.h in Headers */,
 				FA0B7D081A95902C000E1D17 /* wrap_File.h in Headers */,
 				FA0B79451A958E3B000E1D17 /* Variant.h in Headers */,
-				FA0B7D691A95902C000E1D17 /* wrap_ParticleSystem.h in Headers */,
 				FA0B7E5C1A95902C000E1D17 /* wrap_MotorJoint.h in Headers */,
 				FA4F2BA91DE1E36400CA37D7 /* wrap_RecordingDevice.h in Headers */,
 				FA0B7A7C1A958EA3000E1D17 /* b2Contact.h in Headers */,
@@ -3818,6 +3833,7 @@
 				FA0B7DED1A95902C000E1D17 /* Cursor.h in Headers */,
 				FA0B7E501A95902C000E1D17 /* wrap_Fixture.h in Headers */,
 				FA28EBD71E352DB5003446F4 /* BufferSync.h in Headers */,
+				FADF542C1E3DAADA00012CC0 /* wrap_Mesh.h in Headers */,
 				FAA3A9B01B7D465A00CED060 /* android.h in Headers */,
 				217DFBDE1D9F6D490055D849 /* compat.h in Headers */,
 				FA0B7A491A958EA3000E1D17 /* b2PolygonShape.h in Headers */,
@@ -3978,6 +3994,7 @@
 				FADF54081E3D78F700012CC0 /* Video.cpp in Sources */,
 				FA9D8DD81DEF8411002CD881 /* Data.cpp in Sources */,
 				FA0B7E8F1A95902C000E1D17 /* GmeDecoder.cpp in Sources */,
+				FADF542B1E3DAADA00012CC0 /* wrap_Mesh.cpp in Sources */,
 				FA0B7CD71A95902C000E1D17 /* Audio.cpp in Sources */,
 				FA0B7AC01A958EA3000E1D17 /* host.c in Sources */,
 				FA0B7EB01A95902C000E1D17 /* System.cpp in Sources */,
@@ -4057,6 +4074,7 @@
 				FA0B7A3F1A958EA3000E1D17 /* b2ChainShape.cpp in Sources */,
 				FA0B7E921A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */,
 				FA0B7E521A95902C000E1D17 /* wrap_FrictionJoint.cpp in Sources */,
+				FADF54351E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp in Sources */,
 				FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */,
 				FA0B7A311A958EA3000E1D17 /* b2CollidePolygon.cpp in Sources */,
 				FA4F2C111DE936FE00CA37D7 /* unix.c in Sources */,
@@ -4079,6 +4097,7 @@
 				FA0B7A901A958EA3000E1D17 /* b2FrictionJoint.cpp in Sources */,
 				FA0B7DFB1A95902C000E1D17 /* Body.cpp in Sources */,
 				FA0B7ED21A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */,
+				FADF54261E3DA5BA00012CC0 /* Mesh.cpp in Sources */,
 				FA0B7EE01A95902D000E1D17 /* wrap_Touch.cpp in Sources */,
 				FA0B7A3C1A958EA3000E1D17 /* b2TimeOfImpact.cpp in Sources */,
 				FA4F2C081DE936DD00CA37D7 /* io.c in Sources */,
@@ -4095,11 +4114,9 @@
 				FA0B7DAF1A95902C000E1D17 /* wrap_CompressedImageData.cpp in Sources */,
 				FA0B7A481A958EA3000E1D17 /* b2PolygonShape.cpp in Sources */,
 				FA0B7A991A958EA3000E1D17 /* b2MotorJoint.cpp in Sources */,
-				FA0B7D681A95902C000E1D17 /* wrap_ParticleSystem.cpp in Sources */,
 				FA0B7AD51A958EA3000E1D17 /* unix.c in Sources */,
 				FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */,
 				FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */,
-				FA0B7D651A95902C000E1D17 /* wrap_Mesh.cpp in Sources */,
 				FA0B7A531A958EA3000E1D17 /* b2Math.cpp in Sources */,
 				FAF140A11E20934C00F898D2 /* RemoveTree.cpp in Sources */,
 				FA0B7CDA1A95902C000E1D17 /* Pool.cpp in Sources */,
@@ -4162,6 +4179,7 @@
 				FA0B7D071A95902C000E1D17 /* wrap_File.cpp in Sources */,
 				FA0B7AD01A958EA3000E1D17 /* peer.c in Sources */,
 				FA27B3C11B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */,
+				FADF54211E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */,
 				FA0B791C1A958E3B000E1D17 /* b64.cpp in Sources */,
 				FA1E88851DF363E100E808AA /* Filter.cpp in Sources */,
 				FA0B7D941A95902C000E1D17 /* Image.cpp in Sources */,
@@ -4187,6 +4205,7 @@
 				FA4F2C0B1DE936EA00CA37D7 /* options.c in Sources */,
 				FA4F2C0D1DE936F100CA37D7 /* serial.c in Sources */,
 				FA0B7E0A1A95902C000E1D17 /* EdgeShape.cpp in Sources */,
+				FADF54301E3DABF600012CC0 /* SpriteBatch.cpp in Sources */,
 				FAB17BF11ABFB37500F9BA27 /* wrap_CompressedData.cpp in Sources */,
 				FA0B7CF81A95902C000E1D17 /* FileData.cpp in Sources */,
 				FA0B7DA61A95902C000E1D17 /* PNGHandler.cpp in Sources */,
@@ -4211,7 +4230,6 @@
 				FA0B79411A958E3B000E1D17 /* utf8.cpp in Sources */,
 				FAB17BE11ABFAA2000F9BA27 /* Compressor.cpp in Sources */,
 				FA0B7ADF1A958EA3000E1D17 /* lodepng.cpp in Sources */,
-				FA0B7D711A95902C000E1D17 /* wrap_SpriteBatch.cpp in Sources */,
 				FA0B7D341A95902C000E1D17 /* Canvas.cpp in Sources */,
 				FAF140761E20934C00F898D2 /* IntermTraverse.cpp in Sources */,
 				FA0B7E8C1A95902C000E1D17 /* FLACDecoder.cpp in Sources */,
@@ -4335,6 +4353,7 @@
 				FADF54071E3D78F700012CC0 /* Video.cpp in Sources */,
 				217DFC031D9F6D490055D849 /* timeout.c in Sources */,
 				FA9D8DD71DEF8411002CD881 /* Data.cpp in Sources */,
+				FADF542A1E3DAADA00012CC0 /* wrap_Mesh.cpp in Sources */,
 				FA0B7D2B1A95902C000E1D17 /* wrap_Rasterizer.cpp in Sources */,
 				FA0B7E8E1A95902C000E1D17 /* GmeDecoder.cpp in Sources */,
 				FA0B7CD61A95902C000E1D17 /* Audio.cpp in Sources */,
@@ -4414,6 +4433,7 @@
 				FA0B7E911A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */,
 				FA0B7E511A95902C000E1D17 /* wrap_FrictionJoint.cpp in Sources */,
 				FA0B7AD61A958EA3000E1D17 /* win32.c in Sources */,
+				FADF54341E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp in Sources */,
 				FA0B7E0C1A95902C000E1D17 /* Fixture.cpp in Sources */,
 				FA0B7D181A95902C000E1D17 /* TrueTypeRasterizer.cpp in Sources */,
 				FA0B7CFA1A95902C000E1D17 /* Filesystem.cpp in Sources */,
@@ -4436,6 +4456,7 @@
 				FA0B7EDF1A95902D000E1D17 /* wrap_Touch.cpp in Sources */,
 				FA0B794A1A958E3B000E1D17 /* wrap_Data.cpp in Sources */,
 				217DFBFB1D9F6D490055D849 /* serial.c in Sources */,
+				FADF54251E3DA5BA00012CC0 /* Mesh.cpp in Sources */,
 				FA4F2BE51DE6650600CA37D7 /* wrap_Transform.cpp in Sources */,
 				217DFC0D1D9F6D490055D849 /* unixudp.c in Sources */,
 				FA0B7CDC1A95902C000E1D17 /* Source.cpp in Sources */,
@@ -4451,11 +4472,9 @@
 				FA0B7DAE1A95902C000E1D17 /* wrap_CompressedImageData.cpp in Sources */,
 				FA0B7A6E1A958EA3000E1D17 /* b2WorldCallbacks.cpp in Sources */,
 				FA0B7A831A958EA3000E1D17 /* b2EdgeAndPolygonContact.cpp in Sources */,
-				FA0B7D671A95902C000E1D17 /* wrap_ParticleSystem.cpp in Sources */,
 				FA0B7AA11A958EA3000E1D17 /* b2PulleyJoint.cpp in Sources */,
 				FAB17BE61ABFAA9000F9BA27 /* lz4.c in Sources */,
 				FA0B7B211A958EA3000E1D17 /* luasocket.cpp in Sources */,
-				FA0B7D641A95902C000E1D17 /* wrap_Mesh.cpp in Sources */,
 				FA0B7A5B1A958EA3000E1D17 /* b2Timer.cpp in Sources */,
 				FA0B7CD91A95902C000E1D17 /* Pool.cpp in Sources */,
 				FAF140A01E20934C00F898D2 /* RemoveTree.cpp in Sources */,
@@ -4519,6 +4538,7 @@
 				FA0B7A4E1A958EA3000E1D17 /* b2Draw.cpp in Sources */,
 				FA27B3C01B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */,
 				FA0B7D931A95902C000E1D17 /* Image.cpp in Sources */,
+				FADF54201E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */,
 				FA0B7D9F1A95902C000E1D17 /* KTXHandler.cpp in Sources */,
 				FA1E88831DF363DB00E808AA /* Filter.cpp in Sources */,
 				FA0B7A2C1A958EA3000E1D17 /* b2CollideCircle.cpp in Sources */,
@@ -4544,6 +4564,7 @@
 				FA4F2B791DE0125B00CA37D7 /* xxhash.c in Sources */,
 				FA0B7E361A95902C000E1D17 /* WheelJoint.cpp in Sources */,
 				FA0B7A471A958EA3000E1D17 /* b2PolygonShape.cpp in Sources */,
+				FADF542F1E3DABF600012CC0 /* SpriteBatch.cpp in Sources */,
 				FA0B7D8D1A95902C000E1D17 /* ddsHandler.cpp in Sources */,
 				FA0B7DFD1A95902C000E1D17 /* ChainShape.cpp in Sources */,
 				FA0B79201A958E3B000E1D17 /* delay.cpp in Sources */,
@@ -4562,7 +4583,6 @@
 				FA0B7AD41A958EA3000E1D17 /* unix.c in Sources */,
 				FA0B7A771A958EA3000E1D17 /* b2CircleContact.cpp in Sources */,
 				FAB17BE01ABFAA2000F9BA27 /* Compressor.cpp in Sources */,
-				FA0B7D701A95902C000E1D17 /* wrap_SpriteBatch.cpp in Sources */,
 				FA0B7D331A95902C000E1D17 /* Canvas.cpp in Sources */,
 				FA0B7E941A95902C000E1D17 /* Mpg123Decoder.cpp in Sources */,
 				FA0B7E8B1A95902C000E1D17 /* FLACDecoder.cpp in Sources */,

+ 5 - 0
src/modules/graphics/Graphics.cpp

@@ -694,6 +694,11 @@ void Graphics::draw(Texture *texture, Quad *quad, const Matrix4 &m)
 	texture->drawq(this, quad, m);
 }
 
+void Graphics::drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount)
+{
+	mesh->drawInstanced(this, m, instancecount);
+}
+
 /**
  * Primitives (points, shapes, lines).
  **/

+ 14 - 0
src/modules/graphics/Graphics.h

@@ -34,6 +34,7 @@
 #include "Font.h"
 #include "Shader.h"
 #include "Quad.h"
+#include "Mesh.h"
 #include "math/Transform.h"
 #include "font/Rasterizer.h"
 #include "video/VideoStream.h"
@@ -50,6 +51,8 @@ class Reference;
 namespace graphics
 {
 
+class SpriteBatch;
+class ParticleSystem;
 class Text;
 class Video;
 class Buffer;
@@ -314,6 +317,10 @@ public:
 
 	Quad *newQuad(Quad::Viewport v, double sw, double sh);
 
+	virtual SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) = 0;
+
+	virtual ParticleSystem *newParticleSystem(Texture *texture, int size) = 0;
+
 	virtual Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::defaultFilter) = 0;
 
 	virtual Canvas *newCanvas(int width, int height, const Canvas::Settings &settings) = 0;
@@ -324,6 +331,12 @@ public:
 
 	virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
 
+	virtual Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
+	virtual Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
+
+	virtual Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
+	virtual Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
+
 	virtual Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {}) = 0;
 
 	bool validateShader(bool gles, const Shader::ShaderSource &source, std::string &err);
@@ -546,6 +559,7 @@ public:
 
 	void draw(Drawable *drawable, const Matrix4 &m);
 	void draw(Texture *texture, Quad *quad, const Matrix4 &m);
+	void drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount);
 
 	/**
 	 * Draws text at the specified coordinates

+ 642 - 0
src/modules/graphics/Mesh.cpp

@@ -0,0 +1,642 @@
+/**
+ * Copyright (c) 2006-2017 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 "Mesh.h"
+#include "common/Matrix.h"
+#include "common/Exception.h"
+#include "Shader.h"
+#include "Graphics.h"
+
+// C++
+#include <algorithm>
+#include <limits>
+
+namespace love
+{
+namespace graphics
+{
+
+static const char *getBuiltinAttribName(VertexAttribID attribid)
+{
+	const char *name = "";
+	vertex::getConstant(attribid, name);
+	return name;
+}
+
+static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct");
+static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
+static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
+
+std::vector<Mesh::AttribFormat> Mesh::getDefaultVertexFormat()
+{
+	// Corresponds to the love::Vertex struct.
+	std::vector<Mesh::AttribFormat> vertexformat = {
+		{getBuiltinAttribName(ATTRIB_POS),      Mesh::DATA_FLOAT, 2},
+		{getBuiltinAttribName(ATTRIB_TEXCOORD), Mesh::DATA_FLOAT, 2},
+		{getBuiltinAttribName(ATTRIB_COLOR),    Mesh::DATA_BYTE,  4},
+	};
+
+	return vertexformat;
+}
+
+love::Type Mesh::type("Mesh", &Drawable::type);
+
+Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage)
+	: vertexFormat(vertexformat)
+	, vbo(nullptr)
+	, vertexCount(0)
+	, vertexStride(0)
+	, ibo(nullptr)
+	, useIndexBuffer(false)
+	, elementCount(0)
+	, elementDataType(INDEX_UINT16)
+	, drawMode(drawmode)
+	, rangeStart(-1)
+	, rangeCount(-1)
+{
+	setupAttachedAttributes();
+	calculateAttributeSizes();
+
+	vertexCount = datasize / vertexStride;
+	elementDataType = vertex::getIndexDataTypeFromMax(vertexCount);
+
+	if (vertexCount == 0)
+		throw love::Exception("Data size is too small for specified vertex attribute formats.");
+
+	vbo = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
+
+	vertexScratchBuffer = new char[vertexStride];
+}
+
+Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage)
+	: vertexFormat(vertexformat)
+	, vbo(nullptr)
+	, vertexCount((size_t) vertexcount)
+	, vertexStride(0)
+	, ibo(nullptr)
+	, useIndexBuffer(false)
+	, elementCount(0)
+	, elementDataType(vertex::getIndexDataTypeFromMax(vertexcount))
+	, drawMode(drawmode)
+	, rangeStart(-1)
+	, rangeCount(-1)
+{
+	if (vertexcount <= 0)
+		throw love::Exception("Invalid number of vertices (%d).", vertexcount);
+
+	setupAttachedAttributes();
+	calculateAttributeSizes();
+
+	size_t buffersize = vertexCount * vertexStride;
+
+	vbo = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
+
+	// Initialize the buffer's contents to 0.
+	memset(vbo->map(), 0, buffersize);
+	vbo->setMappedRangeModified(0, vbo->getSize());
+	vbo->unmap();
+
+	vertexScratchBuffer = new char[vertexStride];
+}
+
+Mesh::~Mesh()
+{
+	delete vbo;
+	delete ibo;
+	delete vertexScratchBuffer;
+
+	for (const auto &attrib : attachedAttributes)
+	{
+		if (attrib.second.mesh != this)
+			attrib.second.mesh->release();
+	}
+}
+
+void Mesh::setupAttachedAttributes()
+{
+	for (size_t i = 0; i < vertexFormat.size(); i++)
+	{
+		const std::string &name = vertexFormat[i].name;
+
+		if (attachedAttributes.find(name) != attachedAttributes.end())
+			throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
+
+		attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true};
+	}
+}
+
+void Mesh::calculateAttributeSizes()
+{
+	size_t stride = 0;
+
+	for (const AttribFormat &format : vertexFormat)
+	{
+		// Hardware really doesn't like attributes that aren't 32 bit-aligned.
+		if (format.type == DATA_BYTE && format.components != 4)
+			throw love::Exception("byte vertex attributes must have 4 components.");
+
+		if (format.components <= 0 || format.components > 4)
+			throw love::Exception("Vertex attributes must have between 1 and 4 components.");
+
+		// Total size in bytes of each attribute in a single vertex.
+		attributeSizes.push_back(getAttribFormatSize(format));
+		stride += attributeSizes.back();
+	}
+
+	vertexStride = stride;
+}
+
+size_t Mesh::getAttributeOffset(size_t attribindex) const
+{
+	size_t offset = 0;
+
+	for (size_t i = 0; i < attribindex; i++)
+		offset += attributeSizes[i];
+
+	return offset;
+}
+
+void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
+{
+	if (vertindex >= vertexCount)
+		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
+
+	size_t offset = vertindex * vertexStride;
+	size_t size = std::min(datasize, vertexStride);
+
+	uint8 *bufferdata = (uint8 *) vbo->map();
+	memcpy(bufferdata + offset, data, size);
+
+	vbo->setMappedRangeModified(offset, size);
+}
+
+size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize)
+{
+	if (vertindex >= vertexCount)
+		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
+
+	size_t offset = vertindex * vertexStride;
+	size_t size = std::min(datasize, vertexStride);
+
+	// We're relying on vbo->map() returning read/write data... ew.
+	const uint8 *bufferdata = (const uint8 *) vbo->map();
+	memcpy(data, bufferdata + offset, size);
+
+	return size;
+}
+
+void *Mesh::getVertexScratchBuffer()
+{
+	return vertexScratchBuffer;
+}
+
+void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize)
+{
+	if (vertindex >= vertexCount)
+		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
+
+	if (attribindex >= (int) vertexFormat.size())
+		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
+
+	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
+	size_t size = std::min(datasize, attributeSizes[attribindex]);
+
+	uint8 *bufferdata = (uint8 *) vbo->map();
+	memcpy(bufferdata + offset, data, size);
+
+	vbo->setMappedRangeModified(offset, size);
+}
+
+size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize)
+{
+	if (vertindex >= vertexCount)
+		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
+
+	if (attribindex >= (int) vertexFormat.size())
+		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
+
+	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
+	size_t size = std::min(datasize, attributeSizes[attribindex]);
+
+	// We're relying on vbo->map() returning read/write data... ew.
+	const uint8 *bufferdata = (const uint8 *) vbo->map();
+	memcpy(data, bufferdata + offset, size);
+
+	return size;
+}
+
+size_t Mesh::getVertexCount() const
+{
+	return vertexCount;
+}
+
+size_t Mesh::getVertexStride() const
+{
+	return vertexStride;
+}
+
+const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
+{
+	return vertexFormat;
+}
+
+Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
+{
+	if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
+		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
+
+	DataType type = vertexFormat[attribindex].type;
+	components = vertexFormat[attribindex].components;
+
+	return type;
+}
+
+int Mesh::getAttributeIndex(const std::string &name) const
+{
+	for (int i = 0; i < (int) vertexFormat.size(); i++)
+	{
+		if (vertexFormat[i].name == name)
+			return i;
+	}
+
+	return -1;
+}
+
+void Mesh::setAttributeEnabled(const std::string &name, bool enable)
+{
+	auto it = attachedAttributes.find(name);
+
+	if (it == attachedAttributes.end())
+		throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
+
+	it->second.enabled = enable;
+}
+
+bool Mesh::isAttributeEnabled(const std::string &name) const
+{
+	const auto it = attachedAttributes.find(name);
+
+	if (it == attachedAttributes.end())
+		throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
+
+	return it->second.enabled;
+}
+
+void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step)
+{
+	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
+	if (step == STEP_PER_INSTANCE && !gfx->isSupported(Graphics::FEATURE_INSTANCING))
+		throw love::Exception("Vertex attribute instancing is not supported on this system.");
+
+	if (mesh != this)
+	{
+		for (const auto &it : mesh->attachedAttributes)
+		{
+			// If the supplied Mesh has attached attributes of its own, then we
+			// prevent it from being attached to avoid reference cycles.
+			if (it.second.mesh != mesh)
+				throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own.");
+		}
+	}
+
+	AttachedAttribute oldattrib = {};
+	AttachedAttribute newattrib = {};
+
+	auto it = attachedAttributes.find(name);
+	if (it != attachedAttributes.end())
+		oldattrib = it->second;
+
+	newattrib.mesh = mesh;
+	newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
+	newattrib.index = mesh->getAttributeIndex(attachname);
+	newattrib.step = step;
+
+	if (newattrib.index < 0)
+		throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
+
+	if (newattrib.mesh != this)
+		newattrib.mesh->retain();
+
+	attachedAttributes[name] = newattrib;
+
+	if (oldattrib.mesh && oldattrib.mesh != this)
+		oldattrib.mesh->release();
+}
+
+bool Mesh::detachAttribute(const std::string &name)
+{
+	auto it = attachedAttributes.find(name);
+
+	if (it != attachedAttributes.end() && it->second.mesh != this)
+	{
+		it->second.mesh->release();
+		attachedAttributes.erase(it);
+
+		if (getAttributeIndex(name) != -1)
+			attachAttribute(name, this, name);
+
+		return true;
+	}
+
+	return false;
+}
+
+void *Mesh::mapVertexData()
+{
+	return vbo->map();
+}
+
+void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
+{
+	vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
+	vbo->unmap();
+}
+
+void Mesh::flush()
+{
+	vbo->unmap();
+
+	if (ibo != nullptr)
+		ibo->unmap();
+}
+
+/**
+ * Copies index data from a vector to a mapped index buffer.
+ **/
+template <typename T>
+static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
+{
+	T *elems = (T *) buffermap.get();
+
+	for (size_t i = 0; i < indices.size(); i++)
+	{
+		if (indices[i] >= maxval)
+			throw love::Exception("Invalid vertex map value: %d", indices[i] + 1);
+
+		elems[i] = (T) indices[i];
+	}
+}
+
+void Mesh::setVertexMap(const std::vector<uint32> &map)
+{
+	size_t maxval = getVertexCount();
+
+	IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval);
+
+	// Calculate the size in bytes of the index buffer data.
+	size_t size = map.size() * vertex::getIndexDataSize(datatype);
+
+	if (ibo && size > ibo->getSize())
+	{
+		delete ibo;
+		ibo = nullptr;
+	}
+
+	if (!ibo && size > 0)
+	{
+		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
+		ibo = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
+	}
+
+	useIndexBuffer = true;
+	elementCount = map.size();
+
+	if (!ibo || elementCount == 0)
+		return;
+
+	Buffer::Mapper ibomap(*ibo);
+
+	// Fill the buffer with the index values from the vector.
+	switch (datatype)
+	{
+	case INDEX_UINT16:
+		copyToIndexBuffer<uint16>(map, ibomap, maxval);
+		break;
+	case INDEX_UINT32:
+	default:
+		copyToIndexBuffer<uint32>(map, ibomap, maxval);
+		break;
+	}
+
+	elementDataType = datatype;
+}
+
+void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize)
+{
+	if (ibo && datasize > ibo->getSize())
+	{
+		delete ibo;
+		ibo = nullptr;
+	}
+
+	if (!ibo && datasize > 0)
+	{
+		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
+		ibo = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
+	}
+
+	elementCount = datasize / vertex::getIndexDataSize(datatype);
+
+	if (!ibo || elementCount == 0)
+		return;
+
+	Buffer::Mapper ibomap(*ibo);
+	memcpy(ibomap.get(), data, datasize);
+
+	useIndexBuffer = true;
+	elementDataType = datatype;
+}
+
+void Mesh::setVertexMap()
+{
+	useIndexBuffer = false;
+}
+
+/**
+ * Copies index data from a mapped buffer to a vector.
+ **/
+template <typename T>
+static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32> &indices)
+{
+	T *elems = (T *) buffer;
+	for (size_t i = 0; i < count; i++)
+		indices.push_back((uint32) elems[i]);
+}
+
+bool Mesh::getVertexMap(std::vector<uint32> &map) const
+{
+	if (!useIndexBuffer)
+		return false;
+
+	map.clear();
+	map.reserve(elementCount);
+
+	if (!ibo || elementCount == 0)
+		return true;
+
+	// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
+	void *buffer = ibo->map();
+
+	// Fill the vector from the buffer.
+	switch (elementDataType)
+	{
+	case INDEX_UINT16:
+		copyFromIndexBuffer<uint16>(buffer, elementCount, map);
+		break;
+	case INDEX_UINT32:
+	default:
+		copyFromIndexBuffer<uint32>(buffer, elementCount, map);
+		break;
+	}
+
+	return true;
+}
+
+size_t Mesh::getVertexMapCount() const
+{
+	return elementCount;
+}
+
+void Mesh::setTexture(Texture *tex)
+{
+	texture.set(tex);
+}
+
+void Mesh::setTexture()
+{
+	texture.set(nullptr);
+}
+
+Texture *Mesh::getTexture() const
+{
+	return texture.get();
+}
+
+void Mesh::setDrawMode(DrawMode mode)
+{
+	drawMode = mode;
+}
+
+Mesh::DrawMode Mesh::getDrawMode() const
+{
+	return drawMode;
+}
+
+void Mesh::setDrawRange(int start, int count)
+{
+	if (start < 0 || count <= 0)
+		throw love::Exception("Invalid draw range.");
+
+	rangeStart = start;
+	rangeCount = count;
+}
+
+void Mesh::setDrawRange()
+{
+	rangeStart = rangeCount = -1;
+}
+
+bool Mesh::getDrawRange(int &start, int &count) const
+{
+	if (rangeStart < 0 || rangeCount <= 0)
+		return false;
+
+	start = rangeStart;
+	count = rangeCount;
+	return true;
+}
+
+void Mesh::draw(love::graphics::Graphics *gfx, const love::Matrix4 &m)
+{
+	drawInstanced(gfx, m, 1);
+}
+
+size_t Mesh::getAttribFormatSize(const AttribFormat &format)
+{
+	switch (format.type)
+	{
+	case DATA_BYTE:
+		return format.components * sizeof(uint8);
+	case DATA_FLOAT:
+		return format.components * sizeof(float);
+	default:
+		return 0;
+	}
+}
+
+bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
+{
+	return drawModes.find(in, out);
+}
+
+bool Mesh::getConstant(Mesh::DrawMode in, const char *&out)
+{
+	return drawModes.find(in, out);
+}
+
+bool Mesh::getConstant(const char *in, DataType &out)
+{
+	return dataTypes.find(in, out);
+}
+
+bool Mesh::getConstant(DataType in, const char *&out)
+{
+	return dataTypes.find(in, out);
+}
+
+bool Mesh::getConstant(const char *in, AttributeStep &out)
+{
+	return attributeSteps.find(in, out);
+}
+
+bool Mesh::getConstant(AttributeStep in, const char *&out)
+{
+	return attributeSteps.find(in, out);
+}
+
+StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
+{
+	{ "fan",       DRAWMODE_FAN       },
+	{ "strip",     DRAWMODE_STRIP     },
+	{ "triangles", DRAWMODE_TRIANGLES },
+	{ "points",    DRAWMODE_POINTS    },
+};
+
+StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
+
+StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM>::Entry Mesh::dataTypeEntries[] =
+{
+	{ "byte", DATA_BYTE   },
+	{ "float", DATA_FLOAT },
+};
+
+StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM> Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries));
+
+StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM>::Entry Mesh::attributeStepEntries[] =
+{
+	{ "pervertex",   STEP_PER_VERTEX   },
+	{ "perinstance", STEP_PER_INSTANCE },
+};
+
+StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM> Mesh::attributeSteps(Mesh::attributeStepEntries, sizeof(Mesh::attributeStepEntries));
+
+} // graphics
+} // love

+ 270 - 0
src/modules/graphics/Mesh.h

@@ -0,0 +1,270 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#pragma once
+
+// LOVE
+#include "common/config.h"
+#include "common/int.h"
+#include "common/math.h"
+#include "common/StringMap.h"
+#include "Drawable.h"
+#include "Texture.h"
+#include "vertex.h"
+#include "Buffer.h"
+
+// C++
+#include <vector>
+#include <unordered_map>
+
+namespace love
+{
+namespace graphics
+{
+
+class Graphics;
+
+/**
+ * Holds and draws arbitrary vertex geometry.
+ * Each vertex in the Mesh has a collection of vertex attributes specified on
+ * creation.
+ **/
+class Mesh : public Drawable
+{
+public:
+
+	static love::Type type;
+
+	// How the Mesh's vertices are used when drawing.
+	// http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
+	enum DrawMode
+	{
+		DRAWMODE_FAN,
+		DRAWMODE_STRIP,
+		DRAWMODE_TRIANGLES,
+		DRAWMODE_POINTS,
+		DRAWMODE_MAX_ENUM
+	};
+
+	// The type of data a vertex attribute can store.
+	enum DataType
+	{
+		DATA_BYTE,
+		DATA_FLOAT,
+		DATA_MAX_ENUM
+	};
+
+	enum AttributeStep
+	{
+		STEP_PER_VERTEX,
+		STEP_PER_INSTANCE,
+		STEP_MAX_ENUM
+	};
+
+	struct AttribFormat
+	{
+		std::string name;
+		DataType type;
+		int components; // max 4
+	};
+
+	Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage);
+	Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage);
+
+	virtual ~Mesh();
+
+	/**
+	 * Sets the values of all attributes at a specific vertex index in the Mesh.
+	 * The size of the data must be less than or equal to the total size of all
+	 * vertex attributes.
+	 **/
+	void setVertex(size_t vertindex, const void *data, size_t datasize);
+	size_t getVertex(size_t vertindex, void *data, size_t datasize);
+	void *getVertexScratchBuffer();
+
+	/**
+	 * Sets the values for a single attribute at a specific vertex index in the
+	 * Mesh. The size of the data must be less than or equal to the size of the
+	 * attribute.
+	 **/
+	void setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize);
+	size_t getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize);
+
+	/**
+	 * Gets the total number of vertices that can be used when drawing the Mesh.
+	 **/
+	size_t getVertexCount() const;
+
+	/**
+	 * Gets the size in bytes of the start of one vertex to the start of the
+	 * next, in the buffer.
+	 **/
+	size_t getVertexStride() const;
+
+	/**
+	 * Gets the format of each vertex attribute stored in the Mesh.
+	 **/
+	const std::vector<AttribFormat> &getVertexFormat() const;
+	DataType getAttributeInfo(int attribindex, int &components) const;
+	int getAttributeIndex(const std::string &name) const;
+
+	/**
+	 * Sets whether a specific vertex attribute is used when drawing the Mesh.
+	 **/
+	void setAttributeEnabled(const std::string &name, bool enable);
+	bool isAttributeEnabled(const std::string &name) const;
+
+	/**
+	 * Attaches a vertex attribute from another Mesh to this one. The attribute
+	 * will be used when drawing this Mesh.
+	 **/
+	void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
+	bool detachAttribute(const std::string &name);
+
+	void *mapVertexData();
+	void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
+
+	/**
+	 * Flushes all modified data to the GPU.
+	 **/
+	void flush();
+
+	/**
+	 * Sets the vertex map to use when drawing the Mesh. The vertex map
+	 * determines the order in which vertices are used by the draw mode.
+	 * A 0-element vector is equivalent to the default vertex map:
+	 * {0, 1, 2, 3, 4, ...}
+	 **/
+	void setVertexMap(const std::vector<uint32> &map);
+	void setVertexMap(IndexDataType datatype, const void *data, size_t datasize);
+	void setVertexMap();
+
+	/**
+	 * Fills the uint32 vector passed into the method with the previously set
+	 * vertex map (index buffer) values.
+	 **/
+	bool getVertexMap(std::vector<uint32> &map) const;
+
+	/**
+	 * Gets the total number of elements in the vertex map array.
+	 **/
+	size_t getVertexMapCount() const;
+
+	/**
+	 * Sets the texture used when drawing the Mesh.
+	 **/
+	void setTexture(Texture *texture);
+
+	/**
+	 * Disables any texture from being used when drawing the Mesh.
+	 **/
+	void setTexture();
+
+	/**
+	 * Gets the texture used when drawing the Mesh. May return null if no
+	 * texture is set.
+	 **/
+	Texture *getTexture() const;
+
+	/**
+	 * Sets the draw mode used when drawing the Mesh.
+	 **/
+	void setDrawMode(DrawMode mode);
+	DrawMode getDrawMode() const;
+
+	void setDrawRange(int start, int count);
+	void setDrawRange();
+	bool getDrawRange(int &start, int &count) const;
+
+	virtual int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) = 0;
+
+	virtual void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) = 0;
+
+	// Implements Drawable.
+	void draw(Graphics *gfx, const Matrix4 &m) override;
+
+	static bool getConstant(const char *in, DrawMode &out);
+	static bool getConstant(DrawMode in, const char *&out);
+
+	static bool getConstant(const char *in, DataType &out);
+	static bool getConstant(DataType in, const char *&out);
+
+	static bool getConstant(const char *in, AttributeStep &out);
+	static bool getConstant(AttributeStep in, const char *&out);
+
+protected:
+
+	struct AttachedAttribute
+	{
+		Mesh *mesh;
+		int index;
+		AttributeStep step;
+		bool enabled;
+	};
+
+	void setupAttachedAttributes();
+	void calculateAttributeSizes();
+	size_t getAttributeOffset(size_t attribindex) const;
+
+	static size_t getAttribFormatSize(const AttribFormat &format);
+	static std::vector<AttribFormat> getDefaultVertexFormat();
+
+	std::vector<AttribFormat> vertexFormat;
+	std::vector<size_t> attributeSizes;
+
+	std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
+
+	// Vertex buffer, for the vertex data.
+	Buffer *vbo;
+	size_t vertexCount;
+	size_t vertexStride;
+
+	// Block of memory whose size is at least as large as a single vertex. Helps
+	// avoid memory allocations when using Mesh::setVertex etc.
+	char *vertexScratchBuffer;
+
+	// Element (vertex index) buffer, for the vertex map.
+	Buffer *ibo;
+	bool useIndexBuffer;
+	size_t elementCount;
+	IndexDataType elementDataType;
+	
+	DrawMode drawMode;
+	
+	int rangeStart;
+	int rangeCount;
+	
+	StrongRef<Texture> texture;
+
+private:
+	
+	static StringMap<DrawMode, DRAWMODE_MAX_ENUM>::Entry drawModeEntries[];
+	static StringMap<DrawMode, DRAWMODE_MAX_ENUM> drawModes;
+	
+	static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[];
+	static StringMap<DataType, DATA_MAX_ENUM> dataTypes;
+	
+	static StringMap<AttributeStep, STEP_MAX_ENUM>::Entry attributeStepEntries[];
+	static StringMap<AttributeStep, STEP_MAX_ENUM> attributeSteps;
+	
+}; // Mesh
+
+} // graphics
+} // love

+ 258 - 0
src/modules/graphics/SpriteBatch.cpp

@@ -0,0 +1,258 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#include "common/config.h"
+#include "SpriteBatch.h"
+
+// LOVE
+#include "Texture.h"
+#include "Quad.h"
+#include "Graphics.h"
+#include "Buffer.h"
+
+// C++
+#include <algorithm>
+
+// C
+#include <stddef.h>
+
+namespace love
+{
+namespace graphics
+{
+
+love::Type SpriteBatch::type("SpriteBatch", &Drawable::type);
+
+SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage)
+	: texture(texture)
+	, size(size)
+	, next(0)
+	, color(0)
+	, array_buf(nullptr)
+	, quad_indices(gfx, size)
+	, range_start(-1)
+	, range_count(-1)
+{
+	if (size <= 0)
+		throw love::Exception("Invalid SpriteBatch size.");
+
+	size_t vertex_size = sizeof(Vertex) * 4 * size;
+
+	array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
+}
+
+SpriteBatch::~SpriteBatch()
+{
+	delete color;
+	delete array_buf;
+}
+
+int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
+{
+	if (index < -1 || index >= size)
+		throw love::Exception("Invalid sprite index: %d", index + 1);
+
+	if (index == -1 && next >= size)
+		setBufferSize(size * 2);
+
+	addv(texture->getVertices(), m, (index == -1) ? next : index);
+
+	// Increment counter.
+	if (index == -1)
+		return next++;
+
+	return index;
+}
+
+int SpriteBatch::addq(Quad *quad, const Matrix4 &m, int index /*= -1*/)
+{
+	if (index < -1 || index >= size)
+		throw love::Exception("Invalid sprite index: %d", index + 1);
+
+	if (index == -1 && next >= size)
+		setBufferSize(size * 2);
+
+	addv(quad->getVertices(), m, (index == -1) ? next : index);
+
+	// Increment counter.
+	if (index == -1)
+		return next++;
+
+	return index;
+}
+
+void SpriteBatch::clear()
+{
+	// Reset the position of the next index.
+	next = 0;
+}
+
+void SpriteBatch::flush()
+{
+	array_buf->unmap();
+}
+
+void SpriteBatch::setTexture(Texture *newtexture)
+{
+	texture.set(newtexture);
+}
+
+Texture *SpriteBatch::getTexture() const
+{
+	return texture.get();
+}
+
+void SpriteBatch::setColor(const Color &color)
+{
+	if (!this->color)
+		this->color = new Color(color);
+	else
+		*(this->color) = color;
+}
+
+void SpriteBatch::setColor()
+{
+	delete color;
+	color = nullptr;
+}
+
+const Color *SpriteBatch::getColor() const
+{
+	return color;
+}
+
+int SpriteBatch::getCount() const
+{
+	return next;
+}
+
+void SpriteBatch::setBufferSize(int newsize)
+{
+	if (newsize <= 0)
+		throw love::Exception("Invalid SpriteBatch size.");
+
+	if (newsize == size)
+		return;
+
+	size_t vertex_size = sizeof(Vertex) * 4 * newsize;
+	love::graphics::Buffer *new_array_buf = nullptr;
+
+	int new_next = std::min(next, newsize);
+
+	try
+	{
+		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
+		new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
+
+		// Copy as much of the old data into the new GLBuffer as can fit.
+		size_t copy_size = sizeof(Vertex) * 4 * new_next;
+		array_buf->copyTo(0, copy_size, new_array_buf, 0);
+
+		quad_indices = QuadIndices(gfx, newsize);
+	}
+	catch (love::Exception &)
+	{
+		delete new_array_buf;
+		throw;
+	}
+
+	// We don't need to unmap the old GLBuffer since we're deleting it.
+	delete array_buf;
+
+	array_buf = new_array_buf;
+	size = newsize;
+
+	next = new_next;
+}
+
+int SpriteBatch::getBufferSize() const
+{
+	return size;
+}
+
+void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
+{
+	AttachedAttribute oldattrib = {};
+	AttachedAttribute newattrib = {};
+
+	if (mesh->getVertexCount() < (size_t) next * 4)
+		throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
+
+	auto it = attached_attributes.find(name);
+	if (it != attached_attributes.end())
+		oldattrib = it->second;
+
+	newattrib.index = mesh->getAttributeIndex(name);
+
+	if (newattrib.index < 0)
+		throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
+
+	newattrib.mesh = mesh;
+
+	attached_attributes[name] = newattrib;
+}
+
+void SpriteBatch::setDrawRange(int start, int count)
+{
+	if (start < 0 || count <= 0)
+		throw love::Exception("Invalid draw range.");
+
+	range_start = start;
+	range_count = count;
+}
+
+void SpriteBatch::setDrawRange()
+{
+	range_start = range_count = -1;
+}
+
+bool SpriteBatch::getDrawRange(int &start, int &count) const
+{
+	if (range_start < 0 || range_count <= 0)
+		return false;
+
+	start = range_start;
+	count = range_count;
+	return true;
+}
+
+void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index)
+{
+	// 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 != nullptr)
+	{
+		for (int i = 0; i < 4; i++)
+			sprite[i].color = *color;
+	}
+	
+	// Always keep the VBO mapped when adding data for now (it'll be unmapped
+	// on draw.)
+	array_buf->map();
+	
+	array_buf->fill(index * sprite_size, sprite_size, sprite);
+}
+
+} // graphics
+} // love

+ 145 - 0
src/modules/graphics/SpriteBatch.h

@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#pragma once
+
+// C
+#include <cstring>
+
+// C++
+#include <unordered_map>
+
+// LOVE
+#include "common/math.h"
+#include "common/Matrix.h"
+#include "Drawable.h"
+#include "Color.h"
+#include "Mesh.h"
+
+namespace love
+{
+namespace graphics
+{
+
+// Forward declarations.
+class Graphics;
+class Texture;
+class Quad;
+class Buffer;
+
+class SpriteBatch : public Drawable
+{
+public:
+
+	static love::Type type;
+
+	SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
+	virtual ~SpriteBatch();
+
+	int add(const Matrix4 &m, int index = -1);
+	int addq(Quad *quad, const Matrix4 &m, int index = -1);
+	void clear();
+
+	void flush();
+
+	void setTexture(Texture *newtexture);
+	Texture *getTexture() const;
+
+	/**
+	 * Set the current color for this SpriteBatch. The sprites added
+	 * after this call will use this color. Note that global color
+	 * will not longer apply to the SpriteBatch if this is used.
+	 *
+	 * @param color The color to use for the following sprites.
+	 */
+	void setColor(const Color &color);
+
+	/**
+	 * Disable per-sprite colors for this SpriteBatch. The next call to
+	 * draw will use the global color for all sprites.
+	 */
+	void setColor();
+
+	/**
+	 * Get the current color for this SpriteBatch. Returns NULL if no color is
+	 * set.
+	 **/
+	const Color *getColor() const;
+
+	/**
+	 * Get the number of sprites currently in this SpriteBatch.
+	 **/
+	int getCount() const;
+
+	/**
+	 * Get the total number of sprites this SpriteBatch can currently hold.
+	 **/
+	int getBufferSize() const;
+
+	/**
+	 * Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
+	 * The vertex attribute will be used when drawing the SpriteBatch.
+	 **/
+	void attachAttribute(const std::string &name, Mesh *mesh);
+
+	void setDrawRange(int start, int count);
+	void setDrawRange();
+	bool getDrawRange(int &start, int &count) const;
+
+protected:
+
+	struct AttachedAttribute
+	{
+		StrongRef<Mesh> mesh;
+		int index;
+	};
+
+	/**
+	 * Sets the total number of sprites this SpriteBatch can hold.
+	 * Leaves existing sprite data intact when possible.
+	 **/
+	void setBufferSize(int newsize);
+
+	void addv(const Vertex *v, const Matrix4 &m, int index);
+
+	StrongRef<Texture> texture;
+
+	// Max number of sprites in the batch.
+	int size;
+
+	// The next free element.
+	int next;
+
+	// Current color. This color, if present, will be applied to the next
+	// added sprite.
+	Color *color;
+	
+	love::graphics::Buffer *array_buf;
+	QuadIndices quad_indices;
+	
+	std::unordered_map<std::string, AttachedAttribute> attached_attributes;
+	
+	int range_start;
+	int range_count;
+	
+}; // SpriteBatch
+
+} // graphics
+} // love

+ 6 - 11
src/modules/graphics/opengl/Graphics.cpp

@@ -118,12 +118,12 @@ graphics::Font *Graphics::newFont(love::font::Rasterizer *r, const Texture::Filt
 	return new Font(r, filter);
 }
 
-SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
+love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
 {
 	return new SpriteBatch(this, texture, size, usage);
 }
 
-ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
+love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
 {
 	return new ParticleSystem(this, texture, size);
 }
@@ -167,22 +167,22 @@ love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, Buffe
 	return new Buffer(size, data, type, usage, mapflags);
 }
 
-Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage)
+love::graphics::Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
 	return new Mesh(this, vertices, drawmode, usage);
 }
 
-Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
+love::graphics::Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
 	return new Mesh(this, vertexcount, drawmode, usage);
 }
 
-Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
+love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
 	return new Mesh(this, vertexformat, vertexcount, drawmode, usage);
 }
 
-Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage)
+love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
 	return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
 }
@@ -498,11 +498,6 @@ void Graphics::flushStreamDraws()
 	streamBufferState.indexCount = 0;
 }
 
-void Graphics::drawInstanced(Mesh *mesh, const love::Matrix4 &m, int instancecount)
-{
-	mesh->drawInstanced(this, m, instancecount);
-}
-
 static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
 {
 	// Human-readable strings for the debug info.

+ 6 - 8
src/modules/graphics/opengl/Graphics.h

@@ -66,9 +66,9 @@ public:
 
 	love::graphics::Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::defaultFilter) override;
 
-	SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage);
+	love::graphics::SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) override;
 
-	ParticleSystem *newParticleSystem(Texture *texture, int size);
+	love::graphics::ParticleSystem *newParticleSystem(Texture *texture, int size) override;
 
 	love::graphics::Canvas *newCanvas(int width, int height, const Canvas::Settings &settings) override;
 
@@ -76,11 +76,11 @@ public:
 
 	love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
 
-	Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage);
-	Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage);
+	love::graphics::Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage) override;
+	love::graphics::Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) override;
 
-	Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage);
-	Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage);
+	love::graphics::Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) override;
+	love::graphics::Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage) override;
 
 	love::graphics::Text *newText(love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {}) override;
 
@@ -94,8 +94,6 @@ public:
 
 	void flushStreamDraws() override;
 
-	void drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount);
-
 	void clear(Colorf color) override;
 	void clear(const std::vector<OptionalColorf> &colors) override;
 

+ 3 - 593
src/modules/graphics/opengl/Mesh.cpp

@@ -20,7 +20,6 @@
 
 // LOVE
 #include "Mesh.h"
-#include "common/Matrix.h"
 #include "common/Exception.h"
 #include "Shader.h"
 #include "graphics/Graphics.h"
@@ -36,87 +35,14 @@ namespace graphics
 namespace opengl
 {
 
-static const char *getBuiltinAttribName(VertexAttribID attribid)
-{
-	const char *name = "";
-	vertex::getConstant(attribid, name);
-	return name;
-}
-
-static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct");
-static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
-static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
-
-static std::vector<Mesh::AttribFormat> getDefaultVertexFormat()
-{
-	// Corresponds to the love::Vertex struct.
-	std::vector<Mesh::AttribFormat> vertexformat = {
-		{getBuiltinAttribName(ATTRIB_POS),      Mesh::DATA_FLOAT, 2},
-		{getBuiltinAttribName(ATTRIB_TEXCOORD), Mesh::DATA_FLOAT, 2},
-		{getBuiltinAttribName(ATTRIB_COLOR),    Mesh::DATA_BYTE,  4},
-	};
-
-	return vertexformat;
-}
-
-love::Type Mesh::type("Mesh", &Drawable::type);
-
 Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage)
-	: vertexFormat(vertexformat)
-	, vbo(nullptr)
-	, vertexCount(0)
-	, vertexStride(0)
-	, ibo(nullptr)
-	, useIndexBuffer(false)
-	, elementCount(0)
-	, elementDataType(INDEX_UINT16)
-	, drawMode(drawmode)
-	, rangeStart(-1)
-	, rangeCount(-1)
+	: love::graphics::Mesh(gfx, vertexformat, data, datasize, drawmode, usage)
 {
-	setupAttachedAttributes();
-	calculateAttributeSizes();
-
-	vertexCount = datasize / vertexStride;
-	elementDataType = vertex::getIndexDataTypeFromMax(vertexCount);
-
-	if (vertexCount == 0)
-		throw love::Exception("Data size is too small for specified vertex attribute formats.");
-
-	vbo = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
-
-	vertexScratchBuffer = new char[vertexStride];
 }
 
 Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage)
-	: vertexFormat(vertexformat)
-	, vbo(nullptr)
-	, vertexCount((size_t) vertexcount)
-	, vertexStride(0)
-	, ibo(nullptr)
-	, useIndexBuffer(false)
-	, elementCount(0)
-	, elementDataType(vertex::getIndexDataTypeFromMax(vertexcount))
-	, drawMode(drawmode)
-	, rangeStart(-1)
-	, rangeCount(-1)
+	: love::graphics::Mesh(gfx, vertexformat, vertexcount, drawmode, usage)
 {
-	if (vertexcount <= 0)
-		throw love::Exception("Invalid number of vertices (%d).", vertexcount);
-
-	setupAttachedAttributes();
-	calculateAttributeSizes();
-
-	size_t buffersize = vertexCount * vertexStride;
-
-	vbo = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
-
-	// Initialize the buffer's contents to 0.
-	memset(vbo->map(), 0, buffersize);
-	vbo->setMappedRangeModified(0, vbo->getSize());
-	vbo->unmap();
-
-	vertexScratchBuffer = new char[vertexStride];
 }
 
 Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Vertex> &vertices, DrawMode drawmode, vertex::Usage usage)
@@ -131,448 +57,6 @@ Mesh::Mesh(graphics::Graphics *gfx, int vertexcount, DrawMode drawmode, vertex::
 
 Mesh::~Mesh()
 {
-	delete vbo;
-	delete ibo;
-	delete vertexScratchBuffer;
-
-	for (const auto &attrib : attachedAttributes)
-	{
-		if (attrib.second.mesh != this)
-			attrib.second.mesh->release();
-	}
-}
-
-void Mesh::setupAttachedAttributes()
-{
-	for (size_t i = 0; i < vertexFormat.size(); i++)
-	{
-		const std::string &name = vertexFormat[i].name;
-
-		if (attachedAttributes.find(name) != attachedAttributes.end())
-			throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
-
-		attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true};
-	}
-}
-
-void Mesh::calculateAttributeSizes()
-{
-	size_t stride = 0;
-
-	for (const AttribFormat &format : vertexFormat)
-	{
-		// Hardware really doesn't like attributes that aren't 32 bit-aligned.
-		if (format.type == DATA_BYTE && format.components != 4)
-			throw love::Exception("byte vertex attributes must have 4 components.");
-
-		if (format.components <= 0 || format.components > 4)
-			throw love::Exception("Vertex attributes must have between 1 and 4 components.");
-
-		// Total size in bytes of each attribute in a single vertex.
-		attributeSizes.push_back(getAttribFormatSize(format));
-		stride += attributeSizes.back();
-	}
-
-	vertexStride = stride;
-}
-
-size_t Mesh::getAttributeOffset(size_t attribindex) const
-{
-	size_t offset = 0;
-
-	for (size_t i = 0; i < attribindex; i++)
-		offset += attributeSizes[i];
-
-	return offset;
-}
-
-void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
-{
-	if (vertindex >= vertexCount)
-		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
-
-	size_t offset = vertindex * vertexStride;
-	size_t size = std::min(datasize, vertexStride);
-
-	uint8 *bufferdata = (uint8 *) vbo->map();
-	memcpy(bufferdata + offset, data, size);
-
-	vbo->setMappedRangeModified(offset, size);
-}
-
-size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize)
-{
-	if (vertindex >= vertexCount)
-		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
-
-	size_t offset = vertindex * vertexStride;
-	size_t size = std::min(datasize, vertexStride);
-
-	// We're relying on vbo->map() returning read/write data... ew.
-	const uint8 *bufferdata = (const uint8 *) vbo->map();
-	memcpy(data, bufferdata + offset, size);
-
-	return size;
-}
-
-void *Mesh::getVertexScratchBuffer()
-{
-	return vertexScratchBuffer;
-}
-
-void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize)
-{
-	if (vertindex >= vertexCount)
-		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
-
-	if (attribindex >= (int) vertexFormat.size())
-		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
-
-	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
-	size_t size = std::min(datasize, attributeSizes[attribindex]);
-
-	uint8 *bufferdata = (uint8 *) vbo->map();
-	memcpy(bufferdata + offset, data, size);
-
-	vbo->setMappedRangeModified(offset, size);
-}
-
-size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize)
-{
-	if (vertindex >= vertexCount)
-		throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
-
-	if (attribindex >= (int) vertexFormat.size())
-		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
-
-	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
-	size_t size = std::min(datasize, attributeSizes[attribindex]);
-
-	// We're relying on vbo->map() returning read/write data... ew.
-	const uint8 *bufferdata = (const uint8 *) vbo->map();
-	memcpy(data, bufferdata + offset, size);
-
-	return size;
-}
-
-size_t Mesh::getVertexCount() const
-{
-	return vertexCount;
-}
-
-size_t Mesh::getVertexStride() const
-{
-	return vertexStride;
-}
-
-const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
-{
-	return vertexFormat;
-}
-
-Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
-{
-	if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
-		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
-
-	DataType type = vertexFormat[attribindex].type;
-	components = vertexFormat[attribindex].components;
-
-	return type;
-}
-
-int Mesh::getAttributeIndex(const std::string &name) const
-{
-	for (int i = 0; i < (int) vertexFormat.size(); i++)
-	{
-		if (vertexFormat[i].name == name)
-			return i;
-	}
-
-	return -1;
-}
-
-void Mesh::setAttributeEnabled(const std::string &name, bool enable)
-{
-	auto it = attachedAttributes.find(name);
-
-	if (it == attachedAttributes.end())
-		throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
-
-	it->second.enabled = enable;
-}
-
-bool Mesh::isAttributeEnabled(const std::string &name) const
-{
-	const auto it = attachedAttributes.find(name);
-
-	if (it == attachedAttributes.end())
-		throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
-
-	return it->second.enabled;
-}
-
-void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step)
-{
-	if (step == STEP_PER_INSTANCE && !gl.isInstancingSupported())
-		throw love::Exception("Vertex attribute instancing is not supported on this system.");
-
-	if (mesh != this)
-	{
-		for (const auto &it : mesh->attachedAttributes)
-		{
-			// If the supplied Mesh has attached attributes of its own, then we
-			// prevent it from being attached to avoid reference cycles.
-			if (it.second.mesh != mesh)
-				throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own.");
-		}
-	}
-
-	AttachedAttribute oldattrib = {};
-	AttachedAttribute newattrib = {};
-
-	auto it = attachedAttributes.find(name);
-	if (it != attachedAttributes.end())
-		oldattrib = it->second;
-
-	newattrib.mesh = mesh;
-	newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
-	newattrib.index = mesh->getAttributeIndex(attachname);
-	newattrib.step = step;
-
-	if (newattrib.index < 0)
-		throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
-
-	if (newattrib.mesh != this)
-		newattrib.mesh->retain();
-
-	attachedAttributes[name] = newattrib;
-
-	if (oldattrib.mesh && oldattrib.mesh != this)
-		oldattrib.mesh->release();
-}
-
-bool Mesh::detachAttribute(const std::string &name)
-{
-	auto it = attachedAttributes.find(name);
-
-	if (it != attachedAttributes.end() && it->second.mesh != this)
-	{
-		it->second.mesh->release();
-		attachedAttributes.erase(it);
-
-		if (getAttributeIndex(name) != -1)
-			attachAttribute(name, this, name);
-
-		return true;
-	}
-
-	return false;
-}
-
-void *Mesh::mapVertexData()
-{
-	return vbo->map();
-}
-
-void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
-{
-	vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
-	vbo->unmap();
-}
-
-void Mesh::flush()
-{
-	vbo->unmap();
-
-	if (ibo != nullptr)
-		ibo->unmap();
-}
-
-/**
- * Copies index data from a vector to a mapped index buffer.
- **/
-template <typename T>
-static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
-{
-	T *elems = (T *) buffermap.get();
-
-	for (size_t i = 0; i < indices.size(); i++)
-	{
-		if (indices[i] >= maxval)
-			throw love::Exception("Invalid vertex map value: %d", indices[i] + 1);
-
-		elems[i] = (T) indices[i];
-	}
-}
-
-void Mesh::setVertexMap(const std::vector<uint32> &map)
-{
-	size_t maxval = getVertexCount();
-
-	IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval);
-
-	// Calculate the size in bytes of the index buffer data.
-	size_t size = map.size() * vertex::getIndexDataSize(datatype);
-
-	if (ibo && size > ibo->getSize())
-	{
-		delete ibo;
-		ibo = nullptr;
-	}
-
-	if (!ibo && size > 0)
-	{
-		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		ibo = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
-	}
-
-	useIndexBuffer = true;
-	elementCount = map.size();
-
-	if (!ibo || elementCount == 0)
-		return;
-
-	Buffer::Mapper ibomap(*ibo);
-
-	// Fill the buffer with the index values from the vector.
-	switch (datatype)
-	{
-	case INDEX_UINT16:
-		copyToIndexBuffer<uint16>(map, ibomap, maxval);
-		break;
-	case INDEX_UINT32:
-	default:
-		copyToIndexBuffer<uint32>(map, ibomap, maxval);
-		break;
-	}
-
-	elementDataType = datatype;
-}
-
-void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize)
-{
-	if (ibo && datasize > ibo->getSize())
-	{
-		delete ibo;
-		ibo = nullptr;
-	}
-
-	if (!ibo && datasize > 0)
-	{
-		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		ibo = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
-	}
-
-	elementCount = datasize / vertex::getIndexDataSize(datatype);
-
-	if (!ibo || elementCount == 0)
-		return;
-
-	Buffer::Mapper ibomap(*ibo);
-	memcpy(ibomap.get(), data, datasize);
-
-	useIndexBuffer = true;
-	elementDataType = datatype;
-}
-
-void Mesh::setVertexMap()
-{
-	useIndexBuffer = false;
-}
-
-/**
- * Copies index data from a mapped buffer to a vector.
- **/
-template <typename T>
-static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32> &indices)
-{
-	T *elems = (T *) buffer;
-	for (size_t i = 0; i < count; i++)
-		indices.push_back((uint32) elems[i]);
-}
-
-bool Mesh::getVertexMap(std::vector<uint32> &map) const
-{
-	if (!useIndexBuffer)
-		return false;
-
-	map.clear();
-	map.reserve(elementCount);
-
-	if (!ibo || elementCount == 0)
-		return true;
-
-	// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
-	void *buffer = ibo->map();
-
-	// Fill the vector from the buffer.
-	switch (elementDataType)
-	{
-	case INDEX_UINT16:
-		copyFromIndexBuffer<uint16>(buffer, elementCount, map);
-		break;
-	case INDEX_UINT32:
-	default:
-		copyFromIndexBuffer<uint32>(buffer, elementCount, map);
-		break;
-	}
-
-	return true;
-}
-
-size_t Mesh::getVertexMapCount() const
-{
-	return elementCount;
-}
-
-void Mesh::setTexture(Texture *tex)
-{
-	texture.set(tex);
-}
-
-void Mesh::setTexture()
-{
-	texture.set(nullptr);
-}
-
-Texture *Mesh::getTexture() const
-{
-	return texture.get();
-}
-
-void Mesh::setDrawMode(DrawMode mode)
-{
-	drawMode = mode;
-}
-
-Mesh::DrawMode Mesh::getDrawMode() const
-{
-	return drawMode;
-}
-
-void Mesh::setDrawRange(int start, int count)
-{
-	if (start < 0 || count <= 0)
-		throw love::Exception("Invalid draw range.");
-
-	rangeStart = start;
-	rangeCount = count;
-}
-
-void Mesh::setDrawRange()
-{
-	rangeStart = rangeCount = -1;
-}
-
-bool Mesh::getDrawRange(int &start, int &count) const
-{
-	if (rangeStart < 0 || rangeCount <= 0)
-		return false;
-
-	start = rangeStart;
-	count = rangeCount;
-	return true;
 }
 
 int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname)
@@ -627,7 +111,7 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
 		if (!attrib.second.enabled)
 			continue;
 
-		Mesh *mesh = attrib.second.mesh;
+		love::graphics::Mesh *mesh = attrib.second.mesh;
 		int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
 
 		if (location >= 0)
@@ -692,24 +176,6 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
 	}
 }
 
-void Mesh::draw(love::graphics::Graphics *gfx, const love::Matrix4 &m)
-{
-	drawInstanced(gfx, m, 1);
-}
-
-size_t Mesh::getAttribFormatSize(const AttribFormat &format)
-{
-	switch (format.type)
-	{
-	case DATA_BYTE:
-		return format.components * sizeof(uint8);
-	case DATA_FLOAT:
-		return format.components * sizeof(float);
-	default:
-		return 0;
-	}
-}
-
 GLenum Mesh::getGLDrawMode(DrawMode mode)
 {
 	switch (mode)
@@ -739,62 +205,6 @@ GLenum Mesh::getGLDataType(DataType type)
 	}
 }
 
-bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
-{
-	return drawModes.find(in, out);
-}
-
-bool Mesh::getConstant(Mesh::DrawMode in, const char *&out)
-{
-	return drawModes.find(in, out);
-}
-
-bool Mesh::getConstant(const char *in, DataType &out)
-{
-	return dataTypes.find(in, out);
-}
-
-bool Mesh::getConstant(DataType in, const char *&out)
-{
-	return dataTypes.find(in, out);
-}
-
-bool Mesh::getConstant(const char *in, AttributeStep &out)
-{
-	return attributeSteps.find(in, out);
-}
-
-bool Mesh::getConstant(AttributeStep in, const char *&out)
-{
-	return attributeSteps.find(in, out);
-}
-
-StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
-{
-	{ "fan",       DRAWMODE_FAN       },
-	{ "strip",     DRAWMODE_STRIP     },
-	{ "triangles", DRAWMODE_TRIANGLES },
-	{ "points",    DRAWMODE_POINTS    },
-};
-
-StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
-
-StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM>::Entry Mesh::dataTypeEntries[] =
-{
-	{ "byte", DATA_BYTE   },
-	{ "float", DATA_FLOAT },
-};
-
-StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM> Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries));
-
-StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM>::Entry Mesh::attributeStepEntries[] =
-{
-	{ "pervertex",   STEP_PER_VERTEX   },
-	{ "perinstance", STEP_PER_INSTANCE },
-};
-
-StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM> Mesh::attributeSteps(Mesh::attributeStepEntries, sizeof(Mesh::attributeStepEntries));
-
 } // opengl
 } // graphics
 } // love

+ 5 - 228
src/modules/graphics/opengl/Mesh.h

@@ -18,78 +18,23 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#ifndef LOVE_GRAPHICS_OPENGL_MESH_H
-#define LOVE_GRAPHICS_OPENGL_MESH_H
+#pragma once
 
 // LOVE
-#include "common/config.h"
-#include "common/int.h"
-#include "common/math.h"
-#include "common/StringMap.h"
-#include "graphics/Drawable.h"
-#include "graphics/Texture.h"
-#include "graphics/vertex.h"
-#include "graphics/Buffer.h"
+#include "graphics/Mesh.h"
 #include "OpenGL.h"
 
-// C++
-#include <vector>
-#include <unordered_map>
-
 namespace love
 {
 namespace graphics
 {
-
-class Graphics;
-
 namespace opengl
 {
 
-/**
- * Holds and draws arbitrary vertex geometry.
- * Each vertex in the Mesh has a collection of vertex attributes specified on
- * creation.
- **/
-class Mesh : public Drawable
+class Mesh : public love::graphics::Mesh
 {
 public:
 
-	static love::Type type;
-
-	// How the Mesh's vertices are used when drawing.
-	// http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
-	enum DrawMode
-	{
-		DRAWMODE_FAN,
-		DRAWMODE_STRIP,
-		DRAWMODE_TRIANGLES,
-		DRAWMODE_POINTS,
-		DRAWMODE_MAX_ENUM
-	};
-
-	// The type of data a vertex attribute can store.
-	enum DataType
-	{
-		DATA_BYTE,
-		DATA_FLOAT,
-		DATA_MAX_ENUM
-	};
-
-	enum AttributeStep
-	{
-		STEP_PER_VERTEX,
-		STEP_PER_INSTANCE,
-		STEP_MAX_ENUM
-	};
-
-	struct AttribFormat
-	{
-		std::string name;
-		DataType type;
-		int components; // max 4
-	};
-
 	Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage);
 	Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage);
 
@@ -98,184 +43,16 @@ public:
 
 	virtual ~Mesh();
 
-	/**
-	 * Sets the values of all attributes at a specific vertex index in the Mesh.
-	 * The size of the data must be less than or equal to the total size of all
-	 * vertex attributes.
-	 **/
-	void setVertex(size_t vertindex, const void *data, size_t datasize);
-	size_t getVertex(size_t vertindex, void *data, size_t datasize);
-	void *getVertexScratchBuffer();
-
-	/**
-	 * Sets the values for a single attribute at a specific vertex index in the
-	 * Mesh. The size of the data must be less than or equal to the size of the
-	 * attribute.
-	 **/
-	void setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize);
-	size_t getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize);
-
-	/**
-	 * Gets the total number of vertices that can be used when drawing the Mesh.
-	 **/
-	size_t getVertexCount() const;
-
-	/**
-	 * Gets the size in bytes of the start of one vertex to the start of the
-	 * next, in the buffer.
-	 **/
-	size_t getVertexStride() const;
-
-	/**
-	 * Gets the format of each vertex attribute stored in the Mesh.
-	 **/
-	const std::vector<AttribFormat> &getVertexFormat() const;
-	DataType getAttributeInfo(int attribindex, int &components) const;
-	int getAttributeIndex(const std::string &name) const;
-
-	/**
-	 * Sets whether a specific vertex attribute is used when drawing the Mesh.
-	 **/
-	void setAttributeEnabled(const std::string &name, bool enable);
-	bool isAttributeEnabled(const std::string &name) const;
-
-	/**
-	 * Attaches a vertex attribute from another Mesh to this one. The attribute
-	 * will be used when drawing this Mesh.
-	 **/
-	void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
-	bool detachAttribute(const std::string &name);
-
-	void *mapVertexData();
-	void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
-
-	/**
-	 * Flushes all modified data to the GPU.
-	 **/
-	void flush();
-
-	/**
-	 * Sets the vertex map to use when drawing the Mesh. The vertex map
-	 * determines the order in which vertices are used by the draw mode.
-	 * A 0-element vector is equivalent to the default vertex map:
-	 * {0, 1, 2, 3, 4, ...}
-	 **/
-	void setVertexMap(const std::vector<uint32> &map);
-	void setVertexMap(IndexDataType datatype, const void *data, size_t datasize);
-	void setVertexMap();
-
-	/**
-	 * Fills the uint32 vector passed into the method with the previously set
-	 * vertex map (index buffer) values.
-	 **/
-	bool getVertexMap(std::vector<uint32> &map) const;
-
-	/**
-	 * Gets the total number of elements in the vertex map array.
-	 **/
-	size_t getVertexMapCount() const;
-
-	/**
-	 * Sets the texture used when drawing the Mesh.
-	 **/
-	void setTexture(Texture *texture);
-
-	/**
-	 * Disables any texture from being used when drawing the Mesh.
-	 **/
-	void setTexture();
-
-	/**
-	 * Gets the texture used when drawing the Mesh. May return null if no
-	 * texture is set.
-	 **/
-	Texture *getTexture() const;
-
-	/**
-	 * Sets the draw mode used when drawing the Mesh.
-	 **/
-	void setDrawMode(DrawMode mode);
-	DrawMode getDrawMode() const;
-
-	void setDrawRange(int start, int count);
-	void setDrawRange();
-	bool getDrawRange(int &start, int &count) const;
-
-	int bindAttributeToShaderInput(int attributeindex, const std::string &inputname);
-
-	void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
-
-	// Implements Drawable.
-	void draw(Graphics *gfx, const Matrix4 &m) override;
-
-	static bool getConstant(const char *in, DrawMode &out);
-	static bool getConstant(DrawMode in, const char *&out);
-
-	static bool getConstant(const char *in, DataType &out);
-	static bool getConstant(DataType in, const char *&out);
-
-	static bool getConstant(const char *in, AttributeStep &out);
-	static bool getConstant(AttributeStep in, const char *&out);
+	int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) override;
+	void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) override;
 
 private:
 
-	struct AttachedAttribute
-	{
-		Mesh *mesh;
-		int index;
-		AttributeStep step;
-		bool enabled;
-	};
-
-	void setupAttachedAttributes();
-	void calculateAttributeSizes();
-	size_t getAttributeOffset(size_t attribindex) const;
-
-	static size_t getAttribFormatSize(const AttribFormat &format);
-
 	static GLenum getGLDrawMode(DrawMode mode);
 	static GLenum getGLDataType(DataType type);
 
-	std::vector<AttribFormat> vertexFormat;
-	std::vector<size_t> attributeSizes;
-
-	std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
-
-	// Vertex buffer, for the vertex data.
-	Buffer *vbo;
-	size_t vertexCount;
-	size_t vertexStride;
-
-	// Block of memory whose size is at least as large as a single vertex. Helps
-	// avoid memory allocations when using Mesh::setVertex etc.
-	char *vertexScratchBuffer;
-
-	// Element (vertex index) buffer, for the vertex map.
-	Buffer *ibo;
-	bool useIndexBuffer;
-	size_t elementCount;
-	IndexDataType elementDataType;
-
-	DrawMode drawMode;
-
-	int rangeStart;
-	int rangeCount;
-
-	StrongRef<Texture> texture;
-
-	static StringMap<DrawMode, DRAWMODE_MAX_ENUM>::Entry drawModeEntries[];
-	static StringMap<DrawMode, DRAWMODE_MAX_ENUM> drawModes;
-
-	static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[];
-	static StringMap<DataType, DATA_MAX_ENUM> dataTypes;
-
-	static StringMap<AttributeStep, STEP_MAX_ENUM>::Entry attributeStepEntries[];
-	static StringMap<AttributeStep, STEP_MAX_ENUM> attributeSteps;
-
 }; // Mesh
 
 } // opengl
 } // graphics
 } // love
-
-#endif // LOVE_GRAPHICS_OPENGL_MESH_H

+ 2 - 208
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -25,6 +25,7 @@
 #include "OpenGL.h"
 
 // LOVE
+#include "graphics/Buffer.h"
 #include "graphics/Texture.h"
 #include "graphics/Graphics.h"
 
@@ -41,199 +42,13 @@ namespace graphics
 namespace opengl
 {
 
-love::Type SpriteBatch::type("SpriteBatch", &Drawable::type);
-
 SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage)
-	: texture(texture)
-	, size(size)
-	, next(0)
-	, color(0)
-	, array_buf(nullptr)
-	, quad_indices(gfx, size)
-	, range_start(-1)
-	, range_count(-1)
+	: love::graphics::SpriteBatch(gfx, texture, size, usage)
 {
-	if (size <= 0)
-		throw love::Exception("Invalid SpriteBatch size.");
-
-	size_t vertex_size = sizeof(Vertex) * 4 * size;
-
-	array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
 }
 
 SpriteBatch::~SpriteBatch()
 {
-	delete color;
-	delete array_buf;
-}
-
-int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
-{
-	if (index < -1 || index >= size)
-		throw love::Exception("Invalid sprite index: %d", index + 1);
-
-	if (index == -1 && next >= size)
-		setBufferSize(size * 2);
-
-	addv(texture->getVertices(), m, (index == -1) ? next : index);
-
-	// Increment counter.
-	if (index == -1)
-		return next++;
-
-	return index;
-}
-
-int SpriteBatch::addq(Quad *quad, const Matrix4 &m, int index /*= -1*/)
-{
-	if (index < -1 || index >= size)
-		throw love::Exception("Invalid sprite index: %d", index + 1);
-
-	if (index == -1 && next >= size)
-		setBufferSize(size * 2);
-
-	addv(quad->getVertices(), m, (index == -1) ? next : index);
-
-	// Increment counter.
-	if (index == -1)
-		return next++;
-
-	return index;
-}
-
-void SpriteBatch::clear()
-{
-	// Reset the position of the next index.
-	next = 0;
-}
-
-void SpriteBatch::flush()
-{
-	array_buf->unmap();
-}
-
-void SpriteBatch::setTexture(Texture *newtexture)
-{
-	texture.set(newtexture);
-}
-
-Texture *SpriteBatch::getTexture() const
-{
-	return texture.get();
-}
-
-void SpriteBatch::setColor(const Color &color)
-{
-	if (!this->color)
-		this->color = new Color(color);
-	else
-		*(this->color) = color;
-}
-
-void SpriteBatch::setColor()
-{
-	delete color;
-	color = nullptr;
-}
-
-const Color *SpriteBatch::getColor() const
-{
-	return color;
-}
-
-int SpriteBatch::getCount() const
-{
-	return next;
-}
-
-void SpriteBatch::setBufferSize(int newsize)
-{
-	if (newsize <= 0)
-		throw love::Exception("Invalid SpriteBatch size.");
-
-	if (newsize == size)
-		return;
-
-	size_t vertex_size = sizeof(Vertex) * 4 * newsize;
-	love::graphics::Buffer *new_array_buf = nullptr;
-
-	int new_next = std::min(next, newsize);
-
-	try
-	{
-		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
-
-		// Copy as much of the old data into the new GLBuffer as can fit.
-		size_t copy_size = sizeof(Vertex) * 4 * new_next;
-		array_buf->copyTo(0, copy_size, new_array_buf, 0);
-
-		quad_indices = QuadIndices(gfx, newsize);
-	}
-	catch (love::Exception &)
-	{
-		delete new_array_buf;
-		throw;
-	}
-
-	// We don't need to unmap the old GLBuffer since we're deleting it.
-	delete array_buf;
-
-	array_buf = new_array_buf;
-	size = newsize;
-
-	next = new_next;
-}
-
-int SpriteBatch::getBufferSize() const
-{
-	return size;
-}
-
-void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
-{
-	AttachedAttribute oldattrib = {};
-	AttachedAttribute newattrib = {};
-
-	if (mesh->getVertexCount() < (size_t) next * 4)
-		throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
-
-	auto it = attached_attributes.find(name);
-	if (it != attached_attributes.end())
-		oldattrib = it->second;
-
-	newattrib.index = mesh->getAttributeIndex(name);
-
-	if (newattrib.index < 0)
-		throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
-
-	newattrib.mesh = mesh;
-
-	attached_attributes[name] = newattrib;
-}
-
-void SpriteBatch::setDrawRange(int start, int count)
-{
-	if (start < 0 || count <= 0)
-		throw love::Exception("Invalid draw range.");
-
-	range_start = start;
-	range_count = count;
-}
-
-void SpriteBatch::setDrawRange()
-{
-	range_start = range_count = -1;
-}
-
-bool SpriteBatch::getDrawRange(int &start, int &count) const
-{
-	if (range_start < 0 || range_count <= 0)
-		return false;
-
-	start = range_start;
-	count = range_count;
-	return true;
 }
 
 void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
@@ -308,27 +123,6 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	}
 }
 
-void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index)
-{
-	// 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 != nullptr)
-	{
-		for (int i = 0; i < 4; i++)
-			sprite[i].color = *color;
-	}
-
-	// Always keep the VBO mapped when adding data for now (it'll be unmapped
-	// on draw.)
-	array_buf->map();
-
-	array_buf->fill(index * sprite_size, sprite_size, sprite);
-}
-
 } // opengl
 } // graphics
 } // love

+ 3 - 112
src/modules/graphics/opengl/SpriteBatch.h

@@ -18,139 +18,30 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#ifndef LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
-#define LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
-
-// C
-#include <cstring>
-
-// C++
-#include <unordered_map>
+#pragma once
 
 // LOVE
-#include "common/math.h"
-#include "common/Matrix.h"
-#include "graphics/Drawable.h"
-#include "graphics/Volatile.h"
-#include "graphics/Color.h"
-#include "graphics/Quad.h"
-#include "graphics/Buffer.h"
-#include "Mesh.h"
+#include "graphics/SpriteBatch.h"
 
 namespace love
 {
 namespace graphics
 {
-
-// Forward declarations.
-class Texture;
-class Graphics;
-
 namespace opengl
 {
 
-class SpriteBatch : public Drawable
+class SpriteBatch : public love::graphics::SpriteBatch
 {
 public:
 
-	static love::Type type;
-
 	SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
 	virtual ~SpriteBatch();
 
-	int add(const Matrix4 &m, int index = -1);
-	int addq(Quad *quad, const Matrix4 &m, int index = -1);
-	void clear();
-
-	void flush();
-
-	void setTexture(Texture *newtexture);
-	Texture *getTexture() const;
-
-	/**
-	 * Set the current color for this SpriteBatch. The sprites added
-	 * after this call will use this color. Note that global color
-	 * will not longer apply to the SpriteBatch if this is used.
-	 *
-	 * @param color The color to use for the following sprites.
-	 */
-	void setColor(const Color &color);
-
-	/**
-	 * Disable per-sprite colors for this SpriteBatch. The next call to
-	 * draw will use the global color for all sprites.
-	 */
-	void setColor();
-
-	/**
-	 * Get the current color for this SpriteBatch. Returns NULL if no color is
-	 * set.
-	 **/
-	const Color *getColor() const;
-
-	/**
-	 * Get the number of sprites currently in this SpriteBatch.
-	 **/
-	int getCount() const;
-
-	/**
-	 * Get the total number of sprites this SpriteBatch can currently hold.
-	 **/
-	int getBufferSize() const;
-
-	/**
-	 * Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
-	 * The vertex attribute will be used when drawing the SpriteBatch.
-	 **/
-	void attachAttribute(const std::string &name, Mesh *mesh);
-
-	void setDrawRange(int start, int count);
-	void setDrawRange();
-	bool getDrawRange(int &start, int &count) const;
-
 	// Implements Drawable.
 	void draw(Graphics *gfx, const Matrix4 &m) override;
 
-private:
-
-	struct AttachedAttribute
-	{
-		StrongRef<Mesh> mesh;
-		int index;
-	};
-
-	/**
-	 * Sets the total number of sprites this SpriteBatch can hold.
-	 * Leaves existing sprite data intact when possible.
-	 **/
-	void setBufferSize(int newsize);
-
-	void addv(const Vertex *v, const Matrix4 &m, int index);
-
-	StrongRef<Texture> texture;
-
-	// Max number of sprites in the batch.
-	int size;
-
-	// The next free element.
-	int next;
-
-	// Current color. This color, if present, will be applied to the next
-	// added sprite.
-	Color *color;
-
-	love::graphics::Buffer *array_buf;
-	QuadIndices quad_indices;
-
-	std::unordered_map<std::string, AttachedAttribute> attached_attributes;
-
-	int range_start;
-	int range_count;
-
 }; // SpriteBatch
 
 } // opengl
 } // graphics
 } // love
-
-#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H

+ 8 - 8
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -651,7 +651,7 @@ int w_newSpriteBatch(lua_State *L)
 			return luaL_error(L, "Invalid usage hint: %s", usagestr);
 	}
 
-	SpriteBatch *t = nullptr;
+	love::graphics::SpriteBatch *t = nullptr;
 	luax_catchexcept(L,
 		[&](){ t = instance()->newSpriteBatch(texture, size, usage); }
 	);
@@ -667,7 +667,7 @@ int w_newParticleSystem(lua_State *L)
 
 	Texture *texture = luax_checktexture(L, 1);
 	lua_Number size = luaL_optnumber(L, 2, 1000);
-	ParticleSystem *t = 0;
+	love::graphics::ParticleSystem *t = nullptr;
 	if (size < 1.0 || size > ParticleSystem::MAX_PARTICLES)
 		return luaL_error(L, "Invalid ParticleSystem size");
 
@@ -882,9 +882,9 @@ static Mesh::DrawMode luax_optmeshdrawmode(lua_State *L, int idx, Mesh::DrawMode
 	return def;
 }
 
-static Mesh *newStandardMesh(lua_State *L)
+static love::graphics::Mesh *newStandardMesh(lua_State *L)
 {
-	Mesh *t = nullptr;
+	love::graphics::Mesh *t = nullptr;
 
 	Mesh::DrawMode drawmode = luax_optmeshdrawmode(L, 2, Mesh::DRAWMODE_FAN);
 	vertex::Usage usage = luax_optmeshusage(L, 3, vertex::USAGE_DYNAMIC);
@@ -938,9 +938,9 @@ static Mesh *newStandardMesh(lua_State *L)
 	return t;
 }
 
-static Mesh *newCustomMesh(lua_State *L)
+static love::graphics::Mesh *newCustomMesh(lua_State *L)
 {
-	Mesh *t = nullptr;
+	love::graphics::Mesh *t = nullptr;
 
 	// First argument is the vertex format, second is a table of vertices or
 	// the number of vertices.
@@ -1067,7 +1067,7 @@ int w_newMesh(lua_State *L)
 	if (arg1type != LUA_TTABLE && arg1type != LUA_TNUMBER)
 		luaL_argerror(L, 1, "table or number expected");
 
-	Mesh *t = nullptr;
+	love::graphics::Mesh *t = nullptr;
 
 	int arg2type = lua_type(L, 2);
 	if (arg1type == LUA_TTABLE && (arg2type == LUA_TTABLE || arg2type == LUA_TNUMBER || arg2type == LUA_TUSERDATA))
@@ -1671,7 +1671,7 @@ int w_draw(lua_State *L)
 
 int w_drawInstanced(lua_State *L)
 {
-	Mesh *t = luax_checkmesh(L, 1);
+	love::graphics::Mesh *t = luax_checkmesh(L, 1);
 	int instancecount = (int) luaL_checkinteger(L, 2);
 
 	if (luax_istype(L, 3, math::Transform::type))

+ 3 - 3
src/modules/graphics/opengl/wrap_Graphics.h

@@ -26,11 +26,11 @@
 #include "graphics/wrap_Font.h"
 #include "graphics/wrap_Image.h"
 #include "graphics/wrap_Quad.h"
-#include "wrap_SpriteBatch.h"
-#include "wrap_ParticleSystem.h"
+#include "graphics/wrap_SpriteBatch.h"
+#include "graphics/wrap_ParticleSystem.h"
 #include "graphics/wrap_Canvas.h"
 #include "graphics/wrap_Shader.h"
-#include "wrap_Mesh.h"
+#include "graphics/wrap_Mesh.h"
 #include "graphics/wrap_Text.h"
 #include "graphics/wrap_Video.h"
 #include "Graphics.h"

+ 1 - 5
src/modules/graphics/opengl/wrap_Mesh.cpp → src/modules/graphics/wrap_Mesh.cpp

@@ -22,18 +22,15 @@
 #include "wrap_Mesh.h"
 #include "Image.h"
 #include "Canvas.h"
-#include "graphics/wrap_Texture.h"
+#include "wrap_Texture.h"
 
 // C++
-#include <typeinfo>
 #include <algorithm>
 
 namespace love
 {
 namespace graphics
 {
-namespace opengl
-{
 
 Mesh *luax_checkmesh(lua_State *L, int idx)
 {
@@ -570,6 +567,5 @@ extern "C" int luaopen_mesh(lua_State *L)
 	return luax_register_type(L, &Mesh::type, w_Mesh_functions, nullptr);
 }
 
-} // opengl
 } // graphics
 } // love

+ 1 - 7
src/modules/graphics/opengl/wrap_Mesh.h → src/modules/graphics/wrap_Mesh.h

@@ -18,8 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#ifndef LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
-#define LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
+#pragma once
 
 // LOVE
 #include "common/config.h"
@@ -30,8 +29,6 @@ namespace love
 {
 namespace graphics
 {
-namespace opengl
-{
 
 char *luax_writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, int components, char *data);
 const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data);
@@ -39,8 +36,5 @@ const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int compon
 Mesh *luax_checkmesh(lua_State *L, int idx);
 extern "C" int luaopen_mesh(lua_State *L);
 
-} // opengl
 } // graphics
 } // love
-
-#endif // LOVE_GRAPHICS_OPENGL_WRAP_MESH_H

+ 1 - 4
src/modules/graphics/opengl/wrap_ParticleSystem.cpp → src/modules/graphics/wrap_ParticleSystem.cpp

@@ -24,7 +24,7 @@
 
 #include "Image.h"
 #include "Canvas.h"
-#include "graphics/wrap_Texture.h"
+#include "wrap_Texture.h"
 
 // C
 #include <cstring>
@@ -33,8 +33,6 @@ namespace love
 {
 namespace graphics
 {
-namespace opengl
-{
 
 ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx)
 {
@@ -772,6 +770,5 @@ extern "C" int luaopen_particlesystem(lua_State *L)
 	return luax_register_type(L, &ParticleSystem::type, w_ParticleSystem_functions, nullptr);
 }
 
-} // opengl
 } // graphics
 } // love

+ 1 - 7
src/modules/graphics/opengl/wrap_ParticleSystem.h → src/modules/graphics/wrap_ParticleSystem.h

@@ -18,8 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#ifndef LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
-#define LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
+#pragma once
 
 // LOVE
 #include "common/runtime.h"
@@ -29,14 +28,9 @@ namespace love
 {
 namespace graphics
 {
-namespace opengl
-{
 
 ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
 extern "C" int luaopen_particlesystem(lua_State *L);
 
-} // opengl
 } // graphics
 } // love
-
-#endif // LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H

+ 1 - 7
src/modules/graphics/opengl/wrap_SpriteBatch.cpp → src/modules/graphics/wrap_SpriteBatch.cpp

@@ -22,18 +22,13 @@
 #include "wrap_SpriteBatch.h"
 #include "Image.h"
 #include "Canvas.h"
-#include "graphics/wrap_Texture.h"
+#include "wrap_Texture.h"
 #include "math/wrap_Transform.h"
 
-// C++
-#include <typeinfo>
-
 namespace love
 {
 namespace graphics
 {
-namespace opengl
-{
 
 SpriteBatch *luax_checkspritebatch(lua_State *L, int idx)
 {
@@ -274,6 +269,5 @@ extern "C" int luaopen_spritebatch(lua_State *L)
 	return luax_register_type(L, &SpriteBatch::type, w_SpriteBatch_functions, nullptr);
 }
 
-} // opengl
 } // graphics
 } // love

+ 1 - 7
src/modules/graphics/opengl/wrap_SpriteBatch.h → src/modules/graphics/wrap_SpriteBatch.h

@@ -18,8 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#ifndef LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H
-#define LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H
+#pragma once
 
 #include "common/runtime.h"
 #include "SpriteBatch.h"
@@ -28,14 +27,9 @@ namespace love
 {
 namespace graphics
 {
-namespace opengl
-{
 
 SpriteBatch *luax_checkspritebatch(lua_State *L, int idx);
 extern "C" int luaopen_spritebatch(lua_State *L);
 
-} // opengl
 } // graphics
 } // love
-
-#endif // LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H