From 577f606dac48856dd9ad41a978664ee12c947f3d Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sun, 26 Oct 2014 20:00:56 +0100
Subject: [PATCH] Tilemap: Factor out flash tile code

This will be reused later in TilemapVX.
---
 CMakeLists.txt  |   1 +
 mkxp.pro        |   1 +
 src/flashmap.h  | 219 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/tilemap.cpp | 166 ++++--------------------------------
 4 files changed, 239 insertions(+), 148 deletions(-)
 create mode 100644 src/flashmap.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f3457d..8cf8e74 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -124,6 +124,7 @@ set(MAIN_HEADERS
 	src/glstate.h
 	src/quad.h
 	src/tilemap.h
+	src/flashmap.h
 	src/graphics.h
 	src/debuglogger.h
 	src/global-ibo.h
diff --git a/mkxp.pro b/mkxp.pro
index 5d17087..0ee0547 100644
--- a/mkxp.pro
+++ b/mkxp.pro
@@ -103,6 +103,7 @@ HEADERS += \
 	src/glstate.h \
 	src/quad.h \
 	src/tilemap.h \
+	src/flashmap.h \
 	src/graphics.h \
 	src/debuglogger.h \
 	src/global-ibo.h \
diff --git a/src/flashmap.h b/src/flashmap.h
new file mode 100644
index 0000000..2e916ed
--- /dev/null
+++ b/src/flashmap.h
@@ -0,0 +1,219 @@
+/*
+** flashmap.h
+**
+** This file is part of mkxp.
+**
+** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
+**
+** mkxp is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 2 of the License, or
+** (at your option) any later version.
+**
+** mkxp is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FLASHMAP_H
+#define FLASHMAP_H
+
+#include "table.h"
+#include "gl-util.h"
+#include "gl-meta.h"
+#include "sharedstate.h"
+#include "global-ibo.h"
+#include "glstate.h"
+#include "shader.h"
+#include "vertex.h"
+
+#include <stdint.h>
+#include <vector>
+
+#include <sigc++/connection.h>
+
+static inline int
+wrap(int value, int range)
+{
+	int res = value % range;
+	return res < 0 ? res + range : res;
+}
+
+static inline int16_t
+tableGetWrapped(const Table *t, int x, int y, int z = 0)
+{
+	return t->get(wrap(x, t->xSize()),
+	              wrap(y, t->ySize()),
+	              z);
+}
+
+struct FlashMap
+{
+	FlashMap()
+		: dirty(false),
+	      data(0),
+	      allocQuads(0)
+	{
+		vao.vbo = VBO::gen();
+		vao.ibo = shState->globalIBO().ibo;
+		GLMeta::vaoFillInVertexData<CVertex>(vao);
+
+		GLMeta::vaoInit(vao);
+	}
+
+	~FlashMap()
+	{
+		GLMeta::vaoFini(vao);
+		VBO::del(vao.vbo);
+		dataCon.disconnect();
+	}
+
+	Table *getData() const
+	{
+		return data;
+	}
+
+	void setData(Table *value)
+	{
+		if (data == value)
+			return;
+
+		data = value;
+		dataCon.disconnect();
+		dirty = true;
+
+		if (!data)
+			return;
+
+		dataCon = data->modified.connect
+			(sigc::mem_fun(this, &FlashMap::setDirty));
+	}
+
+	void setViewport(const IntRect &value)
+	{
+		viewp = value;
+		dirty = true;
+	}
+
+	void prepare()
+	{
+		if (!dirty)
+			return;
+
+		rebuildBuffer();
+		dirty = false;
+	}
+
+	void draw(float alpha, const Vec2i &trans)
+	{
+		const size_t count = quadCount();
+
+		if (count == 0)
+			return;
+
+		GLMeta::vaoBind(vao);
+		glState.blendMode.pushSet(BlendAddition);
+
+		FlashMapShader &shader = shState->shaders().flashMap;
+		shader.bind();
+		shader.applyViewportProj();
+		shader.setAlpha(alpha);
+		shader.setTranslation(trans);
+
+		gl.DrawElements(GL_TRIANGLES, count * 6, _GL_INDEX_TYPE, 0);
+
+		glState.blendMode.pop();
+
+		GLMeta::vaoUnbind(vao);
+	}
+
+private:
+	void setDirty()
+	{
+		dirty = true;
+	}
+
+	size_t quadCount() const
+	{
+		return vertices.size() / 4;
+	}
+
+	bool sampleFlashColor(Vec4 &out, int x, int y) const
+	{
+		int16_t packed = tableGetWrapped(data, x, y);
+
+		if (packed == 0)
+			return false;
+
+		const float max = 0xF;
+
+		float b = ((packed & 0x000F) >> 0) / max;
+		float g = ((packed & 0x00F0) >> 4) / max;
+		float r = ((packed & 0x0F00) >> 8) / max;
+
+		out = Vec4(r, g, b, 1);
+
+		return true;
+	}
+
+	void rebuildBuffer()
+	{
+		vertices.clear();
+
+		if (!data)
+			return;
+
+		for (int x = 0; x < viewp.w; ++x)
+			for (int y = 0; y < viewp.h; ++y)
+			{
+				Vec4 color;
+
+				if (!sampleFlashColor(color, x+viewp.x, y+viewp.y))
+					continue;
+
+				FloatRect posRect(x*32, y*32, 32, 32);
+
+				CVertex v[4];
+				Quad::setPosRect(v, posRect);
+				Quad::setColor(v, color);
+
+				for (size_t i = 0; i < 4; ++i)
+					vertices.push_back(v[i]);
+			}
+
+		if (vertices.size() == 0)
+			return;
+
+		VBO::bind(vao.vbo);
+
+		if (quadCount() > allocQuads)
+		{
+			allocQuads = quadCount();
+			VBO::allocEmpty(sizeof(CVertex) * vertices.size());
+		}
+
+		VBO::uploadSubData(0, sizeof(CVertex) * vertices.size(), dataPtr(vertices));
+
+		VBO::unbind();
+
+		/* Ensure global IBO size */
+		shState->ensureQuadIBO(quadCount());
+	}
+
+	bool dirty;
+
+	Table *data;
+	sigc::connection dataCon;
+
+	IntRect viewp;
+
+	GLMeta::VAO vao;
+	size_t allocQuads;
+	std::vector<CVertex> vertices;
+};
+
+#endif // FLASHMAP_H
diff --git a/src/tilemap.cpp b/src/tilemap.cpp
index ac50c70..2acdb49 100644
--- a/src/tilemap.cpp
+++ b/src/tilemap.cpp
@@ -36,6 +36,7 @@
 #include "quad.h"
 #include "vertex.h"
 #include "tileatlas.h"
+#include "flashmap.h"
 
 #include <sigc++/connection.h>
 
@@ -152,19 +153,6 @@ static const size_t zlayersMax = viewpH + 5;
  *
  */
 
-static int wrap(int value, int range)
-{
-	int res = value % range;
-	return res < 0 ? res + range : res;
-}
-
-static int16_t tableGetWrapped(const Table *t, int x, int y, int z = 0)
-{
-	return t->get(wrap(x, t->xSize()),
-	              wrap(y, t->ySize()),
-	              z);
-}
-
 /* Autotile animation */
 static const uint8_t atAnimation[16*4] =
 {
@@ -200,7 +188,6 @@ struct GroundLayer : public ViewportElement
 
 	void draw();
 	void drawInt();
-	void drawFlashInt();
 
 	void onGeometryChange(const Scene::Geometry &geo);
 
@@ -246,7 +233,6 @@ struct TilemapPrivate
 	Bitmap *tileset;
 
 	Table *mapData;
-	Table *flashData;
 	Table *priorities;
 	bool visible;
 	Vec2i offset;
@@ -296,14 +282,8 @@ struct TilemapPrivate
 		uint8_t aniIdx;
 	} tiles;
 
-	/* Flash buffers */
-	struct
-	{
-		GLMeta::VAO vao;
-		VBO::ID vbo;
-		size_t quadCount;
-		uint8_t alphaIdx;
-	} flash;
+	FlashMap flashMap;
+	uint8_t flashAlphaIdx;
 
 	/* Scene elements */
 	struct
@@ -326,8 +306,6 @@ struct TilemapPrivate
 	bool mapViewportDirty;
 	/* Affected by: oy */
 	bool zOrderDirty;
-	/* Affected by: flashData, buffersDirty */
-	bool flashDirty;
 
 	/* Resources are sufficient and tilemap is ready to be drawn */
 	bool tilemapReady;
@@ -337,7 +315,6 @@ struct TilemapPrivate
 	sigc::connection autotilesCon[autotileCount];
 	sigc::connection mapDataCon;
 	sigc::connection prioritiesCon;
-	sigc::connection flashDataCon;
 
 	/* Dispose watches */
 	sigc::connection autotilesDispCon[autotileCount];
@@ -349,15 +326,14 @@ struct TilemapPrivate
 	    : viewport(viewport),
 	      tileset(0),
 	      mapData(0),
-	      flashData(0),
 	      priorities(0),
 	      visible(true),
+	      flashAlphaIdx(0),
 	      atlasSizeDirty(false),
 	      atlasDirty(false),
 	      buffersDirty(false),
 	      mapViewportDirty(false),
 	      zOrderDirty(false),
-	      flashDirty(false),
 	      tilemapReady(false)
 	{
 		memset(autotiles, 0, sizeof(autotiles));
@@ -378,18 +354,6 @@ struct TilemapPrivate
 
 		GLMeta::vaoInit(tiles.vao);
 
-		/* Init flash buffers */
-		flash.vbo = VBO::gen();
-
-		GLMeta::vaoFillInVertexData<CVertex>(flash.vao);
-		flash.vao.vbo = flash.vbo;
-		flash.vao.ibo = shState->globalIBO().ibo;
-
-		GLMeta::vaoInit(flash.vao);
-
-		flash.quadCount = 0;
-		flash.alphaIdx = 0;
-
 		elem.ground = new GroundLayer(this, viewport);
 
 		for (size_t i = 0; i < zlayersMax; ++i)
@@ -397,6 +361,8 @@ struct TilemapPrivate
 
 		prepareCon = shState->prepareDraw.connect
 		        (sigc::mem_fun(this, &TilemapPrivate::prepare));
+
+		updateFlashMapViewport();
 	}
 
 	~TilemapPrivate()
@@ -412,10 +378,6 @@ struct TilemapPrivate
 		GLMeta::vaoFini(tiles.vao);
 		VBO::del(tiles.vbo);
 
-		/* Destroy flash buffers */
-		GLMeta::vaoFini(flash.vao);
-		VBO::del(flash.vbo);
-
 		/* Disconnect signal handlers */
 		tilesetCon.disconnect();
 		for (int i = 0; i < autotileCount; ++i)
@@ -425,11 +387,15 @@ struct TilemapPrivate
 		}
 		mapDataCon.disconnect();
 		prioritiesCon.disconnect();
-		flashDataCon.disconnect();
 
 		prepareCon.disconnect();
 	}
 
+	void updateFlashMapViewport()
+	{
+		flashMap.setViewport(IntRect(viewpPos.x, viewpPos.y, viewpW, viewpH));
+	}
+
 	void updateAtlasInfo()
 	{
 		if (nullOrDisposed(tileset))
@@ -501,11 +467,6 @@ struct TilemapPrivate
 		buffersDirty = true;
 	}
 
-	void invalidateFlash()
-	{
-		flashDirty = true;
-	}
-
 	/* Checks for the minimum amount of data needed to display */
 	bool verifyResources()
 	{
@@ -807,61 +768,6 @@ struct TilemapPrivate
 		shader.setTexSize(atlas.size);
 	}
 
-	bool sampleFlashColor(Vec4 &out, int x, int y)
-	{
-		int16_t packed = tableGetWrapped(flashData, x, y);
-
-		if (packed == 0)
-			return false;
-
-		const float max = 0xF;
-
-		float b = ((packed & 0x000F) >> 0) / max;
-		float g = ((packed & 0x00F0) >> 4) / max;
-		float r = ((packed & 0x0F00) >> 8) / max;
-
-		out = Vec4(r, g, b, 1);
-
-		return true;
-	}
-
-	void updateFlash()
-	{
-		if (!flashData)
-			return;
-
-		std::vector<CVertex> vertices;
-
-		for (int x = 0; x < viewpW; ++x)
-			for (int y = 0; y < viewpH; ++y)
-			{
-				Vec4 color;
-				if (!sampleFlashColor(color, x+viewpPos.x, y+viewpPos.y))
-					continue;
-
-				FloatRect posRect(x*32, y*32, 32, 32);
-
-				CVertex v[4];
-				Quad::setPosRect(v, posRect);
-				Quad::setColor(v, color);
-
-				for (size_t i = 0; i < 4; ++i)
-					vertices.push_back(v[i]);
-			}
-
-		flash.quadCount = vertices.size() / 4;
-
-		if (flash.quadCount == 0)
-			return;
-
-		VBO::bind(flash.vbo);
-		VBO::uploadData(sizeof(CVertex) * vertices.size(), dataPtr(vertices));
-		VBO::unbind();
-
-		/* Ensure global IBO size */
-		shState->ensureQuadIBO(flash.quadCount);
-	}
-
 	void updateActiveElements(std::vector<int> &zlayerInd)
 	{
 		elem.ground->updateVboCount();
@@ -995,7 +901,7 @@ struct TilemapPrivate
 		if (dirty)
 		{
 			buffersDirty = true;
-			flashDirty = true;
+			updateFlashMapViewport();
 			updatePosition();
 		}
 	}
@@ -1037,11 +943,7 @@ struct TilemapPrivate
 			buffersDirty = false;
 		}
 
-		if (flashDirty)
-		{
-			updateFlash();
-			flashDirty = false;
-		}
+		flashMap.prepare();
 
 		if (zOrderDirty)
 		{
@@ -1082,23 +984,7 @@ void GroundLayer::draw()
 
 	GLMeta::vaoUnbind(p->tiles.vao);
 
-	if (p->flash.quadCount > 0)
-	{
-		GLMeta::vaoBind(p->flash.vao);
-		glState.blendMode.pushSet(BlendAddition);
-
-		FlashMapShader &shader = shState->shaders().flashMap;
-		shader.bind();
-		shader.applyViewportProj();
-		shader.setAlpha(flashAlpha[p->flash.alphaIdx] / 255.f);
-		shader.setTranslation(p->dispPos);
-
-		drawFlashInt();
-
-		glState.blendMode.pop();
-
-		GLMeta::vaoUnbind(p->flash.vao);
-	}
+	p->flashMap.draw(flashAlpha[p->flashAlphaIdx] / 255.f, p->dispPos);
 }
 
 void GroundLayer::drawInt()
@@ -1106,11 +992,6 @@ void GroundLayer::drawInt()
 	gl.DrawElements(GL_TRIANGLES, vboCount, _GL_INDEX_TYPE, (GLvoid*) 0);
 }
 
-void GroundLayer::drawFlashInt()
-{
-	gl.DrawElements(GL_TRIANGLES, p->flash.quadCount * 6, _GL_INDEX_TYPE, 0);
-}
-
 void GroundLayer::onGeometryChange(const Scene::Geometry &geo)
 {
 	p->updateSceneGeometry(geo);
@@ -1236,8 +1117,8 @@ void Tilemap::update()
 		return;
 
 	/* Animate flash */
-	if (++p->flash.alphaIdx >= flashAlphaN)
-		p->flash.alphaIdx = 0;
+	if (++p->flashAlphaIdx >= flashAlphaN)
+		p->flashAlphaIdx = 0;
 
 	/* Animate autotiles */
 	if (!p->tiles.animated)
@@ -1259,7 +1140,7 @@ Tilemap::Autotiles &Tilemap::getAutotiles()
 DEF_ATTR_RD_SIMPLE(Tilemap, Viewport, Viewport*, p->viewport)
 DEF_ATTR_RD_SIMPLE(Tilemap, Tileset, Bitmap*, p->tileset)
 DEF_ATTR_RD_SIMPLE(Tilemap, MapData, Table*, p->mapData)
-DEF_ATTR_RD_SIMPLE(Tilemap, FlashData, Table*, p->flashData)
+DEF_ATTR_RD_SIMPLE(Tilemap, FlashData, Table*, p->flashMap.getData())
 DEF_ATTR_RD_SIMPLE(Tilemap, Priorities, Table*, p->priorities)
 DEF_ATTR_RD_SIMPLE(Tilemap, Visible, bool, p->visible)
 DEF_ATTR_RD_SIMPLE(Tilemap, OX, int, p->offset.x)
@@ -1307,18 +1188,7 @@ void Tilemap::setFlashData(Table *value)
 {
 	guardDisposed();
 
-	if (p->flashData == value)
-		return;
-
-	p->flashData = value;
-
-	if (!value)
-		return;
-
-	p->invalidateFlash();
-	p->flashDataCon.disconnect();
-	p->flashDataCon = value->modified.connect
-	        (sigc::mem_fun(p, &TilemapPrivate::invalidateFlash));
+	p->flashMap.setData(value);
 }
 
 void Tilemap::setPriorities(Table *value)