mkxp-freebird/src/tilemap-common.h

279 lines
4.9 KiB
C++

/*
** tilemap-common.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 TILEMAPCOMMON_H
#define TILEMAPCOMMON_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 "quad.h"
#include "etc-internal.h"
#include <stdint.h>
#include <assert.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 Vec2i
wrap(const Vec2i &value, int range)
{
return Vec2i(wrap(value.x, range),
wrap(value.y, range));
}
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);
}
/* Calculate the tile x/y on which this pixel x/y lies */
static inline Vec2i
getTilePos(const Vec2i &pixelPos)
{
/* Round the pixel position down to the nearest top left
* tile boundary, by masking off the lower 5 bits (2^5 = 32).
* Then divide by 32 to convert into tile units. */
return (pixelPos & ~(32-1)) / 32;
}
enum AtSubPos
{
TopLeft = 0,
TopRight = 1,
BottomLeft = 2,
BottomRight = 3,
BottomLeftTable = 4,
BottomRightTable = 5
};
static inline void
atSelectSubPos(FloatRect &pos, int i)
{
switch (i)
{
case TopLeft:
return;
case TopRight:
pos.x += 16;
return;
case BottomLeft:
pos.y += 16;
return;
case BottomRight:
pos.x += 16;
pos.y += 16;
return;
case BottomLeftTable:
pos.y += 24;
return;
case BottomRightTable:
pos.x += 16;
pos.y += 24;
return;
default:
assert(!"Unreachable");
}
}
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 // TILEMAPCOMMON_H