Tilemap: Rewrite to only process and render visible region
Previously, on creation, we would parse the entire map data, translating it into and uploading vertices once, then rendering the entire map on every draw (to keep the draw calls minimal). This worked great for smaller and medium sized maps, but starting with larger maps (200x200+) it doesn't scale as the GPUs vertex processing/culling is overwhelmed by the amount of data each frame. This rewrite instead changes the strategy to only processing and uploading a small subregion of the map (the currently visible part) and regenerating all buffers if this subregion changes. The amount of data transferred is small enough that it can be done every frame without causing lag. The changes also have the convenient side effect that we no longer require 32 bit indices in mkxp, easing the road to possible GLES2 support in the future.
This commit is contained in:
parent
300e61c64b
commit
295e0e5b15
419
src/tilemap.cpp
419
src/tilemap.cpp
|
@ -58,6 +58,12 @@ static const int atAreaH = autotileH * autotileCount;
|
||||||
|
|
||||||
static const int tsLaneW = tilesetW / 2;
|
static const int tsLaneW = tilesetW / 2;
|
||||||
|
|
||||||
|
/* Map viewport size */
|
||||||
|
static const int viewpW = 21;
|
||||||
|
static const int viewpH = 16;
|
||||||
|
|
||||||
|
static const size_t scanrowsMax = viewpH + 5;
|
||||||
|
|
||||||
/* Vocabulary:
|
/* Vocabulary:
|
||||||
*
|
*
|
||||||
* Atlas: A texture containing both the tileset and all
|
* Atlas: A texture containing both the tileset and all
|
||||||
|
@ -134,46 +140,27 @@ static const int tsLaneW = tilesetW / 2;
|
||||||
* (lowest z) to the bottom part (highest z).
|
* (lowest z) to the bottom part (highest z).
|
||||||
* Objects that would end up on the same scanrow are eg. trees.
|
* Objects that would end up on the same scanrow are eg. trees.
|
||||||
*
|
*
|
||||||
* Replica:
|
* Map viewport:
|
||||||
* A tilemap is not drawn as one rectangular object, but
|
* This rectangle describes the subregion of the map that is
|
||||||
* "tiled" if there is an area on the screen that would
|
* actually translated to vertices and stored on the GPU ready
|
||||||
* otherwise not be covered by it. RGSS does this so when the
|
* for rendering. Whenever, ox/oy are modified, its position is
|
||||||
* game orders a screen shake which draws the tilemap at slightly
|
* adjusted if necessary and the data is regenerated. Its size
|
||||||
* different x-offsets, black area isn't exposed (this would
|
* is fixed. This is NOT related to the RGSS Viewport class!
|
||||||
* always happen for 20x15 maps). 'Replicas' describes where the
|
|
||||||
* tilemap needs to be drawn again to achieve this tiled effect
|
|
||||||
* (above the tilemap, to the right, above and right etc.).
|
|
||||||
* 'Normal' means no replica needs to be drawn.
|
|
||||||
* Because the minimum map size in RMXP covers the entire screen,
|
|
||||||
* we don't have to worry about ever drawing more than one replica
|
|
||||||
* in each dimension.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Replica positions */
|
static int wrap(size_t value, size_t range)
|
||||||
enum Position
|
|
||||||
{
|
{
|
||||||
Normal = 1 << 0,
|
int res = value % range;
|
||||||
|
return res < 0 ? res + range : res;
|
||||||
|
}
|
||||||
|
|
||||||
Left = 1 << 1,
|
static int16_t tableGetWrapped(const Table *t, int x, int y, int z = 0)
|
||||||
Right = 1 << 2,
|
|
||||||
Top = 1 << 3,
|
|
||||||
Bottom = 1 << 4,
|
|
||||||
|
|
||||||
TopLeft = Top | Left,
|
|
||||||
TopRight = Top | Right,
|
|
||||||
BottomLeft = Bottom | Left,
|
|
||||||
BottomRight = Bottom | Right
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Position positions[] =
|
|
||||||
{
|
{
|
||||||
Normal,
|
return t->get(wrap(x, t->xSize()),
|
||||||
Left, Right, Top, Bottom,
|
wrap(y, t->ySize()),
|
||||||
TopLeft, TopRight, BottomLeft, BottomRight
|
z);
|
||||||
};
|
}
|
||||||
|
|
||||||
static elementsN(positions);
|
|
||||||
|
|
||||||
/* Autotile animation */
|
/* Autotile animation */
|
||||||
static const uint8_t atAnimation[16*4] =
|
static const uint8_t atAnimation[16*4] =
|
||||||
|
@ -206,6 +193,8 @@ struct GroundLayer : public ViewportElement
|
||||||
|
|
||||||
GroundLayer(TilemapPrivate *p, Viewport *viewport);
|
GroundLayer(TilemapPrivate *p, Viewport *viewport);
|
||||||
|
|
||||||
|
void updateVboCount();
|
||||||
|
|
||||||
void draw();
|
void draw();
|
||||||
void drawInt();
|
void drawInt();
|
||||||
void drawFlashInt();
|
void drawFlashInt();
|
||||||
|
@ -215,7 +204,7 @@ struct GroundLayer : public ViewportElement
|
||||||
|
|
||||||
struct ScanRow : public ViewportElement
|
struct ScanRow : public ViewportElement
|
||||||
{
|
{
|
||||||
const size_t index;
|
size_t index;
|
||||||
GLintptr vboOffset;
|
GLintptr vboOffset;
|
||||||
GLsizei vboCount;
|
GLsizei vboCount;
|
||||||
TilemapPrivate *p;
|
TilemapPrivate *p;
|
||||||
|
@ -228,11 +217,15 @@ struct ScanRow : public ViewportElement
|
||||||
* holds the element count of the entire batch */
|
* holds the element count of the entire batch */
|
||||||
GLsizei vboBatchCount;
|
GLsizei vboBatchCount;
|
||||||
|
|
||||||
ScanRow(TilemapPrivate *p, Viewport *viewport, size_t index);
|
ScanRow(TilemapPrivate *p, Viewport *viewport);
|
||||||
|
|
||||||
|
void setIndex(int value);
|
||||||
|
|
||||||
void draw();
|
void draw();
|
||||||
void drawInt();
|
void drawInt();
|
||||||
|
|
||||||
|
static int calculateZ(TilemapPrivate *p, int index);
|
||||||
|
|
||||||
void initUpdateZ();
|
void initUpdateZ();
|
||||||
void finiUpdateZ(ScanRow *prev);
|
void finiUpdateZ(ScanRow *prev);
|
||||||
};
|
};
|
||||||
|
@ -271,20 +264,18 @@ struct TilemapPrivate
|
||||||
std::vector<uint8_t> animatedATs;
|
std::vector<uint8_t> animatedATs;
|
||||||
} atlas;
|
} atlas;
|
||||||
|
|
||||||
/* Map size in tiles */
|
/* Map viewport position */
|
||||||
int mapWidth;
|
Vec2i viewpPos;
|
||||||
int mapHeight;
|
|
||||||
|
|
||||||
/* Ground layer vertices */
|
/* Ground layer vertices */
|
||||||
SVVector groundVert;
|
SVVector groundVert;
|
||||||
|
|
||||||
/* Scanrow vertices */
|
/* Scanrow vertices */
|
||||||
std::vector<SVVector> scanrowVert;
|
SVVector scanrowVert[scanrowsMax];
|
||||||
|
|
||||||
/* Base quad indices of each scanrow
|
/* Base quad indices of each scanrow
|
||||||
* in the shared buffer */
|
* in the shared buffer */
|
||||||
std::vector<int> scanrowBases;
|
size_t scanrowBases[scanrowsMax+1];
|
||||||
size_t scanrowCount;
|
|
||||||
|
|
||||||
/* Shared buffers for all tiles */
|
/* Shared buffers for all tiles */
|
||||||
struct
|
struct
|
||||||
|
@ -293,9 +284,6 @@ struct TilemapPrivate
|
||||||
VBO::ID vbo;
|
VBO::ID vbo;
|
||||||
bool animated;
|
bool animated;
|
||||||
|
|
||||||
/* Size of an IBO buffer frame, in bytes */
|
|
||||||
GLintptr bufferFrameSize;
|
|
||||||
|
|
||||||
/* Animation state */
|
/* Animation state */
|
||||||
uint8_t frameIdx;
|
uint8_t frameIdx;
|
||||||
uint8_t aniIdx;
|
uint8_t aniIdx;
|
||||||
|
@ -314,7 +302,9 @@ struct TilemapPrivate
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
GroundLayer *ground;
|
GroundLayer *ground;
|
||||||
std::vector<ScanRow*> scanrows;
|
ScanRow* scanrows[scanrowsMax];
|
||||||
|
/* Used rows out of 'scanrows' (rest is hidden) */
|
||||||
|
size_t activeRows;
|
||||||
Scene::Geometry sceneGeo;
|
Scene::Geometry sceneGeo;
|
||||||
Vec2i sceneOffset;
|
Vec2i sceneOffset;
|
||||||
|
|
||||||
|
@ -327,15 +317,14 @@ struct TilemapPrivate
|
||||||
unsigned int scanrowStamp;
|
unsigned int scanrowStamp;
|
||||||
} elem;
|
} elem;
|
||||||
|
|
||||||
/* Replica bitmask */
|
|
||||||
uint8_t replicas;
|
|
||||||
|
|
||||||
/* Affected by: autotiles, tileset */
|
/* Affected by: autotiles, tileset */
|
||||||
bool atlasSizeDirty;
|
bool atlasSizeDirty;
|
||||||
/* Affected by: autotiles(.changed), tileset(.changed), allocateAtlas */
|
/* Affected by: autotiles(.changed), tileset(.changed), allocateAtlas */
|
||||||
bool atlasDirty;
|
bool atlasDirty;
|
||||||
/* Affected by: mapData(.changed), priorities(.changed) */
|
/* Affected by: mapData(.changed), priorities(.changed) */
|
||||||
bool buffersDirty;
|
bool buffersDirty;
|
||||||
|
/* Affected by: ox, oy */
|
||||||
|
bool mapViewportDirty;
|
||||||
/* Affected by: oy */
|
/* Affected by: oy */
|
||||||
bool zOrderDirty;
|
bool zOrderDirty;
|
||||||
/* Affected by: flashData, buffersDirty */
|
/* Affected by: flashData, buffersDirty */
|
||||||
|
@ -364,12 +353,10 @@ struct TilemapPrivate
|
||||||
flashData(0),
|
flashData(0),
|
||||||
priorities(0),
|
priorities(0),
|
||||||
visible(true),
|
visible(true),
|
||||||
mapWidth(0),
|
|
||||||
mapHeight(0),
|
|
||||||
replicas(Normal),
|
|
||||||
atlasSizeDirty(false),
|
atlasSizeDirty(false),
|
||||||
atlasDirty(false),
|
atlasDirty(false),
|
||||||
buffersDirty(false),
|
buffersDirty(false),
|
||||||
|
mapViewportDirty(false),
|
||||||
zOrderDirty(false),
|
zOrderDirty(false),
|
||||||
flashDirty(false),
|
flashDirty(false),
|
||||||
tilemapReady(false)
|
tilemapReady(false)
|
||||||
|
@ -428,21 +415,33 @@ struct TilemapPrivate
|
||||||
elem.groundStamp = shState->genTimeStamp();
|
elem.groundStamp = shState->genTimeStamp();
|
||||||
elem.scanrowStamp = shState->genTimeStamp();
|
elem.scanrowStamp = shState->genTimeStamp();
|
||||||
|
|
||||||
|
elem.ground = new GroundLayer(this, viewport);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
|
elem.scanrows[i] = new ScanRow(this, viewport);
|
||||||
|
|
||||||
prepareCon = shState->prepareDraw.connect
|
prepareCon = shState->prepareDraw.connect
|
||||||
(sigc::mem_fun(this, &TilemapPrivate::prepare));
|
(sigc::mem_fun(this, &TilemapPrivate::prepare));
|
||||||
}
|
}
|
||||||
|
|
||||||
~TilemapPrivate()
|
~TilemapPrivate()
|
||||||
{
|
{
|
||||||
destroyElements();
|
/* Destroy elements */
|
||||||
|
delete elem.ground;
|
||||||
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
|
delete elem.scanrows[i];
|
||||||
|
|
||||||
shState->releaseAtlasTex(atlas.gl);
|
shState->releaseAtlasTex(atlas.gl);
|
||||||
|
|
||||||
|
/* Destroy tile buffers */
|
||||||
VAO::del(tiles.vao);
|
VAO::del(tiles.vao);
|
||||||
VBO::del(tiles.vbo);
|
VBO::del(tiles.vbo);
|
||||||
|
|
||||||
|
/* Destroy flash buffers */
|
||||||
VAO::del(flash.vao);
|
VAO::del(flash.vao);
|
||||||
VBO::del(flash.vbo);
|
VBO::del(flash.vbo);
|
||||||
|
|
||||||
|
/* Disconnect signal handlers */
|
||||||
tilesetCon.disconnect();
|
tilesetCon.disconnect();
|
||||||
for (size_t i = 0; i < autotileCount; ++i)
|
for (size_t i = 0; i < autotileCount; ++i)
|
||||||
{
|
{
|
||||||
|
@ -502,19 +501,6 @@ struct TilemapPrivate
|
||||||
tiles.animated = !animatedATs.empty();
|
tiles.animated = !animatedATs.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateMapDataInfo()
|
|
||||||
{
|
|
||||||
if (!mapData)
|
|
||||||
{
|
|
||||||
mapWidth = 0;
|
|
||||||
mapHeight = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapWidth = mapData->xSize();
|
|
||||||
mapHeight = mapData->ySize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSceneGeometry(const Scene::Geometry &geo)
|
void updateSceneGeometry(const Scene::Geometry &geo)
|
||||||
{
|
{
|
||||||
elem.sceneOffset.x = geo.rect.x - geo.xOrigin;
|
elem.sceneOffset.x = geo.rect.x - geo.xOrigin;
|
||||||
|
@ -524,35 +510,8 @@ struct TilemapPrivate
|
||||||
|
|
||||||
void updatePosition()
|
void updatePosition()
|
||||||
{
|
{
|
||||||
if (mapWidth == 0 || mapHeight == 0)
|
dispPos.x = -(offset.x - viewpPos.x * 32) + elem.sceneOffset.x;
|
||||||
return;
|
dispPos.y = -(offset.y - viewpPos.y * 32) + elem.sceneOffset.x;
|
||||||
|
|
||||||
dispPos.x = -offset.x + elem.sceneOffset.x;
|
|
||||||
dispPos.y = -offset.y + elem.sceneOffset.y;
|
|
||||||
|
|
||||||
dispPos.x %= mapWidth * 32;
|
|
||||||
dispPos.y %= mapHeight * 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute necessary replicas and store this
|
|
||||||
* information in a bitfield */
|
|
||||||
void updateReplicas()
|
|
||||||
{
|
|
||||||
replicas = Normal;
|
|
||||||
|
|
||||||
if (mapWidth == 0 || mapHeight == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const IntRect &sRect = elem.sceneGeo.rect;
|
|
||||||
|
|
||||||
if (dispPos.x > sRect.x)
|
|
||||||
replicas |= Left;
|
|
||||||
if (dispPos.y > sRect.y)
|
|
||||||
replicas |= Top;
|
|
||||||
if (dispPos.x+mapWidth*32 < sRect.x+sRect.w)
|
|
||||||
replicas |= Right;
|
|
||||||
if (dispPos.y+mapHeight*32 < sRect.y+sRect.h)
|
|
||||||
replicas |= Bottom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidateAtlasSize()
|
void invalidateAtlasSize()
|
||||||
|
@ -737,7 +696,8 @@ struct TilemapPrivate
|
||||||
|
|
||||||
void handleTile(int x, int y, int z)
|
void handleTile(int x, int y, int z)
|
||||||
{
|
{
|
||||||
int tileInd = mapData->at(x, y, z);
|
int tileInd =
|
||||||
|
tableGetWrapped(mapData, x + viewpPos.x, y + viewpPos.y, z);
|
||||||
|
|
||||||
/* Check for empty space */
|
/* Check for empty space */
|
||||||
if (tileInd < 48)
|
if (tileInd < 48)
|
||||||
|
@ -788,21 +748,17 @@ struct TilemapPrivate
|
||||||
{
|
{
|
||||||
groundVert.clear();
|
groundVert.clear();
|
||||||
|
|
||||||
scanrowVert.clear();
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
scanrowBases.clear();
|
scanrowVert[i].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void buildQuadArray()
|
void buildQuadArray()
|
||||||
{
|
{
|
||||||
clearQuadArrays();
|
clearQuadArrays();
|
||||||
|
|
||||||
int mapDepth = mapData->zSize();
|
for (int x = 0; x < viewpW; ++x)
|
||||||
|
for (int y = 0; y < viewpH; ++y)
|
||||||
scanrowVert.resize(mapHeight + 5);
|
for (int z = 0; z < mapData->zSize(); ++z)
|
||||||
|
|
||||||
for (int x = 0; x < mapWidth; ++x)
|
|
||||||
for (int y = 0; y < mapHeight; ++y)
|
|
||||||
for (int z = 0; z < mapDepth; ++z)
|
|
||||||
handleTile(x, y, z);
|
handleTile(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,29 +774,24 @@ struct TilemapPrivate
|
||||||
|
|
||||||
void uploadBuffers()
|
void uploadBuffers()
|
||||||
{
|
{
|
||||||
scanrowCount = scanrowVert.size();
|
|
||||||
scanrowBases.resize(scanrowCount + 1);
|
|
||||||
|
|
||||||
/* Calculate total quad count */
|
/* Calculate total quad count */
|
||||||
size_t groundQuadCount = groundVert.size() / 4;
|
size_t groundQuadCount = groundVert.size() / 4;
|
||||||
size_t quadCount = groundQuadCount;
|
size_t quadCount = groundQuadCount;
|
||||||
|
|
||||||
for (size_t i = 0; i < scanrowCount; ++i)
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
{
|
{
|
||||||
scanrowBases[i] = quadCount;
|
scanrowBases[i] = quadCount;
|
||||||
quadCount += scanrowVert[i].size() / 4;
|
quadCount += scanrowVert[i].size() / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
scanrowBases[scanrowCount] = quadCount;
|
scanrowBases[scanrowsMax] = quadCount;
|
||||||
|
|
||||||
tiles.bufferFrameSize = quadCount * 6 * sizeof(uint32_t);
|
|
||||||
|
|
||||||
VBO::bind(tiles.vbo);
|
VBO::bind(tiles.vbo);
|
||||||
VBO::allocEmpty(quadDataSize(quadCount));
|
VBO::allocEmpty(quadDataSize(quadCount));
|
||||||
|
|
||||||
VBO::uploadSubData(0, quadDataSize(groundQuadCount), &groundVert[0]);
|
VBO::uploadSubData(0, quadDataSize(groundQuadCount), &groundVert[0]);
|
||||||
|
|
||||||
for (size_t i = 0; i < scanrowCount; ++i)
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
{
|
{
|
||||||
if (scanrowVert[i].empty())
|
if (scanrowVert[i].empty())
|
||||||
continue;
|
continue;
|
||||||
|
@ -879,36 +830,9 @@ struct TilemapPrivate
|
||||||
shader.setTexSize(atlas.size);
|
shader.setTexSize(atlas.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2i getReplicaOffset(Position pos)
|
|
||||||
{
|
|
||||||
Vec2i offset;
|
|
||||||
|
|
||||||
if (pos & Left)
|
|
||||||
offset.x -= mapWidth*32;
|
|
||||||
if (pos & Right)
|
|
||||||
offset.x += mapWidth*32;
|
|
||||||
if (pos & Top)
|
|
||||||
offset.y -= mapHeight*32;
|
|
||||||
if (pos & Bottom)
|
|
||||||
offset.y += mapHeight*32;
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTranslation(Position replicaPos, ShaderBase &shader)
|
|
||||||
{
|
|
||||||
Vec2i repOff = getReplicaOffset(replicaPos);
|
|
||||||
repOff += dispPos;
|
|
||||||
|
|
||||||
shader.setTranslation(repOff);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sampleFlashColor(Vec4 &out, int x, int y)
|
bool sampleFlashColor(Vec4 &out, int x, int y)
|
||||||
{
|
{
|
||||||
const int _x = x % flashData->xSize();
|
int16_t packed = tableGetWrapped(flashData, x, y);
|
||||||
const int _y = y % flashData->ySize();
|
|
||||||
|
|
||||||
int16_t packed = flashData->at(_x, _y);
|
|
||||||
|
|
||||||
if (packed == 0)
|
if (packed == 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -926,13 +850,16 @@ struct TilemapPrivate
|
||||||
|
|
||||||
void updateFlash()
|
void updateFlash()
|
||||||
{
|
{
|
||||||
|
if (!flashData)
|
||||||
|
return;
|
||||||
|
|
||||||
std::vector<CVertex> vertices;
|
std::vector<CVertex> vertices;
|
||||||
|
|
||||||
for (int x = 0; x < mapWidth; ++x)
|
for (int x = 0; x < viewpW; ++x)
|
||||||
for (int y = 0; y < mapHeight; ++y)
|
for (int y = 0; y < viewpH; ++y)
|
||||||
{
|
{
|
||||||
Vec4 color;
|
Vec4 color;
|
||||||
if (!sampleFlashColor(color, x, y))
|
if (!sampleFlashColor(color, x+viewpPos.x, y+viewpPos.y))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FloatRect posRect(x*32, y*32, 32, 32);
|
FloatRect posRect(x*32, y*32, 32, 32);
|
||||||
|
@ -958,54 +885,60 @@ struct TilemapPrivate
|
||||||
shState->ensureQuadIBO(flash.quadCount);
|
shState->ensureQuadIBO(flash.quadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyElements()
|
void updateActiveElements(std::vector<int> &scanrowInd)
|
||||||
{
|
{
|
||||||
delete elem.ground;
|
elem.ground->updateVboCount();
|
||||||
elem.ground = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < elem.scanrows.size(); ++i)
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
delete elem.scanrows[i];
|
|
||||||
|
|
||||||
elem.scanrows.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void generateElements(std::vector<int> &scanrowInd)
|
|
||||||
{
|
|
||||||
elem.ground = new GroundLayer(this, viewport);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < scanrowInd.size(); ++i)
|
|
||||||
{
|
{
|
||||||
int index = scanrowInd[i];
|
if (i < scanrowInd.size())
|
||||||
elem.scanrows.push_back(new ScanRow(this, viewport, index));
|
{
|
||||||
|
int index = scanrowInd[i];
|
||||||
|
elem.scanrows[i]->setVisible(visible);
|
||||||
|
elem.scanrows[i]->setIndex(index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Hide unused rows */
|
||||||
|
elem.scanrows[i]->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateSceneElements()
|
void updateSceneElements()
|
||||||
{
|
{
|
||||||
destroyElements();
|
/* Only allocate elements for non-emtpy scanrows */
|
||||||
|
|
||||||
/* Only generate elements for non-emtpy scanrows */
|
|
||||||
std::vector<int> scanrowInd;
|
std::vector<int> scanrowInd;
|
||||||
|
|
||||||
for (size_t i = 0; i < scanrowCount; ++i)
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
if (scanrowVert[i].size() > 0)
|
if (scanrowVert[i].size() > 0)
|
||||||
scanrowInd.push_back(i);
|
scanrowInd.push_back(i);
|
||||||
|
|
||||||
generateElements(scanrowInd);
|
updateActiveElements(scanrowInd);
|
||||||
|
elem.activeRows = scanrowInd.size();
|
||||||
|
zOrderDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideElements()
|
||||||
|
{
|
||||||
|
elem.ground->setVisible(false);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < scanrowsMax; ++i)
|
||||||
|
elem.scanrows[i]->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateZOrder()
|
void updateZOrder()
|
||||||
{
|
{
|
||||||
if (elem.scanrows.empty())
|
if (elem.activeRows == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (size_t i = 0; i < elem.scanrows.size(); ++i)
|
for (size_t i = 0; i < elem.activeRows; ++i)
|
||||||
elem.scanrows[i]->initUpdateZ();
|
elem.scanrows[i]->initUpdateZ();
|
||||||
|
|
||||||
ScanRow *prev = elem.scanrows.front();
|
ScanRow *prev = elem.scanrows[0];
|
||||||
prev->finiUpdateZ(0);
|
prev->finiUpdateZ(0);
|
||||||
|
|
||||||
for (size_t i = 1; i < elem.scanrows.size(); ++i)
|
for (size_t i = 1; i < elem.activeRows; ++i)
|
||||||
{
|
{
|
||||||
ScanRow *row = elem.scanrows[i];
|
ScanRow *row = elem.scanrows[i];
|
||||||
row->finiUpdateZ(prev);
|
row->finiUpdateZ(prev);
|
||||||
|
@ -1024,9 +957,9 @@ struct TilemapPrivate
|
||||||
* single sized batches are possible. */
|
* single sized batches are possible. */
|
||||||
void prepareScanrowBatches()
|
void prepareScanrowBatches()
|
||||||
{
|
{
|
||||||
const std::vector<ScanRow*> &scanrows = elem.scanrows;
|
ScanRow *const *scanrows = elem.scanrows;
|
||||||
|
|
||||||
for (size_t i = 0; i < scanrows.size(); ++i)
|
for (size_t i = 0; i < elem.activeRows; ++i)
|
||||||
{
|
{
|
||||||
ScanRow *batchHead = scanrows[i];
|
ScanRow *batchHead = scanrows[i];
|
||||||
batchHead->batchedFlag = false;
|
batchHead->batchedFlag = false;
|
||||||
|
@ -1034,7 +967,7 @@ struct TilemapPrivate
|
||||||
GLsizei vboBatchCount = batchHead->vboCount;
|
GLsizei vboBatchCount = batchHead->vboCount;
|
||||||
IntruListLink<SceneElement> *iter = &batchHead->link;
|
IntruListLink<SceneElement> *iter = &batchHead->link;
|
||||||
|
|
||||||
for (i = i+1; i < scanrows.size(); ++i)
|
for (i = i+1; i < elem.activeRows; ++i)
|
||||||
{
|
{
|
||||||
iter = iter->next;
|
iter = iter->next;
|
||||||
ScanRow *row = scanrows[i];
|
ScanRow *row = scanrows[i];
|
||||||
|
@ -1054,13 +987,50 @@ struct TilemapPrivate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateMapViewport()
|
||||||
|
{
|
||||||
|
int tileOX, tileOY;
|
||||||
|
|
||||||
|
if (offset.x >= 0)
|
||||||
|
tileOX = offset.x / 32;
|
||||||
|
else
|
||||||
|
tileOX = -(-(offset.x-31) / 32);
|
||||||
|
|
||||||
|
if (offset.y >= 0)
|
||||||
|
tileOY = offset.y / 32;
|
||||||
|
else
|
||||||
|
tileOY = -(-(offset.y-31) / 32);
|
||||||
|
|
||||||
|
bool dirty = false;
|
||||||
|
|
||||||
|
if (tileOX < viewpPos.x || tileOX + 21 > viewpPos.x + viewpW)
|
||||||
|
{
|
||||||
|
viewpPos.x = tileOX;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tileOY < viewpPos.y || tileOY + 16 > viewpPos.y + viewpH)
|
||||||
|
{
|
||||||
|
viewpPos.y = tileOY;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty)
|
||||||
|
{
|
||||||
|
buffersDirty = true;
|
||||||
|
flashDirty = true;
|
||||||
|
updatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void prepare()
|
void prepare()
|
||||||
{
|
{
|
||||||
if (!verifyResources())
|
if (!verifyResources())
|
||||||
{
|
{
|
||||||
if (elem.ground)
|
if (tilemapReady)
|
||||||
destroyElements();
|
hideElements();
|
||||||
tilemapReady = false;
|
tilemapReady = false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1076,11 +1046,17 @@ struct TilemapPrivate
|
||||||
atlasDirty = false;
|
atlasDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mapViewportDirty)
|
||||||
|
{
|
||||||
|
updateMapViewport();
|
||||||
|
mapViewportDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (buffersDirty)
|
if (buffersDirty)
|
||||||
{
|
{
|
||||||
buildQuadArray();
|
buildQuadArray();
|
||||||
uploadBuffers();
|
uploadBuffers();
|
||||||
generateSceneElements();
|
updateSceneElements();
|
||||||
buffersDirty = false;
|
buffersDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1104,13 +1080,17 @@ struct TilemapPrivate
|
||||||
|
|
||||||
GroundLayer::GroundLayer(TilemapPrivate *p, Viewport *viewport)
|
GroundLayer::GroundLayer(TilemapPrivate *p, Viewport *viewport)
|
||||||
: ViewportElement(viewport, 0, p->elem.groundStamp),
|
: ViewportElement(viewport, 0, p->elem.groundStamp),
|
||||||
|
vboCount(0),
|
||||||
p(p)
|
p(p)
|
||||||
{
|
{
|
||||||
vboCount = p->scanrowBases[0] * 6;
|
|
||||||
|
|
||||||
onGeometryChange(scene->getGeometry());
|
onGeometryChange(scene->getGeometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GroundLayer::updateVboCount()
|
||||||
|
{
|
||||||
|
vboCount = p->scanrowBases[0] * 6;
|
||||||
|
}
|
||||||
|
|
||||||
void GroundLayer::draw()
|
void GroundLayer::draw()
|
||||||
{
|
{
|
||||||
ShaderBase *shader;
|
ShaderBase *shader;
|
||||||
|
@ -1120,19 +1100,8 @@ void GroundLayer::draw()
|
||||||
|
|
||||||
VAO::bind(p->tiles.vao);
|
VAO::bind(p->tiles.vao);
|
||||||
|
|
||||||
p->setTranslation(Normal, *shader);
|
shader->setTranslation(p->dispPos);
|
||||||
|
drawInt();
|
||||||
for (size_t i = 0; i < positionsN; ++i)
|
|
||||||
{
|
|
||||||
const Position pos = positions[i];
|
|
||||||
|
|
||||||
if (!(p->replicas & pos))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
p->setTranslation(pos, *shader);
|
|
||||||
|
|
||||||
drawInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p->flash.quadCount > 0)
|
if (p->flash.quadCount > 0)
|
||||||
{
|
{
|
||||||
|
@ -1143,18 +1112,9 @@ void GroundLayer::draw()
|
||||||
shader.bind();
|
shader.bind();
|
||||||
shader.applyViewportProj();
|
shader.applyViewportProj();
|
||||||
shader.setAlpha(flashAlpha[p->flash.alphaIdx] / 255.f);
|
shader.setAlpha(flashAlpha[p->flash.alphaIdx] / 255.f);
|
||||||
|
shader.setTranslation(p->dispPos);
|
||||||
|
|
||||||
for (size_t i = 0; i < positionsN; ++i)
|
drawFlashInt();
|
||||||
{
|
|
||||||
const Position pos = positions[i];
|
|
||||||
|
|
||||||
if (!(p->replicas & pos))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
p->setTranslation(pos, shader);
|
|
||||||
|
|
||||||
drawFlashInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
glState.blendMode.pop();
|
glState.blendMode.pop();
|
||||||
}
|
}
|
||||||
|
@ -1164,8 +1124,7 @@ void GroundLayer::draw()
|
||||||
|
|
||||||
void GroundLayer::drawInt()
|
void GroundLayer::drawInt()
|
||||||
{
|
{
|
||||||
gl.DrawElements(GL_TRIANGLES, vboCount,
|
gl.DrawElements(GL_TRIANGLES, vboCount, GL_UNSIGNED_INT, (GLvoid*) 0);
|
||||||
GL_UNSIGNED_INT, (GLvoid*) 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroundLayer::drawFlashInt()
|
void GroundLayer::drawFlashInt()
|
||||||
|
@ -1177,15 +1136,24 @@ void GroundLayer::onGeometryChange(const Scene::Geometry &geo)
|
||||||
{
|
{
|
||||||
p->updateSceneGeometry(geo);
|
p->updateSceneGeometry(geo);
|
||||||
p->updatePosition();
|
p->updatePosition();
|
||||||
p->updateReplicas();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScanRow::ScanRow(TilemapPrivate *p, Viewport *viewport, size_t index)
|
ScanRow::ScanRow(TilemapPrivate *p, Viewport *viewport)
|
||||||
: ViewportElement(viewport, 32 + index*32, p->elem.scanrowStamp),
|
: ViewportElement(viewport, 0, p->elem.scanrowStamp),
|
||||||
index(index),
|
index(0),
|
||||||
|
vboOffset(0),
|
||||||
|
vboCount(0),
|
||||||
p(p),
|
p(p),
|
||||||
vboBatchCount(0)
|
vboBatchCount(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ScanRow::setIndex(int value)
|
||||||
{
|
{
|
||||||
|
index = value;
|
||||||
|
|
||||||
|
z = calculateZ(p, index);
|
||||||
|
scene->reinsert(*this);
|
||||||
|
|
||||||
vboOffset = p->scanrowBases[index] * sizeof(uint32_t) * 6;
|
vboOffset = p->scanrowBases[index] * sizeof(uint32_t) * 6;
|
||||||
vboCount = p->scanrowSize(index) * 6;
|
vboCount = p->scanrowSize(index) * 6;
|
||||||
}
|
}
|
||||||
|
@ -1202,27 +1170,20 @@ void ScanRow::draw()
|
||||||
|
|
||||||
VAO::bind(p->tiles.vao);
|
VAO::bind(p->tiles.vao);
|
||||||
|
|
||||||
p->setTranslation(Normal, *shader);
|
shader->setTranslation(p->dispPos);
|
||||||
|
drawInt();
|
||||||
for (size_t i = 0; i < positionsN; ++i)
|
|
||||||
{
|
|
||||||
const Position pos = positions[i];
|
|
||||||
|
|
||||||
if (!(p->replicas & pos))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
p->setTranslation(pos, *shader);
|
|
||||||
|
|
||||||
drawInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
VAO::unbind();
|
VAO::unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScanRow::drawInt()
|
void ScanRow::drawInt()
|
||||||
{
|
{
|
||||||
gl.DrawElements(GL_TRIANGLES, vboBatchCount,
|
gl.DrawElements(GL_TRIANGLES, vboBatchCount, GL_UNSIGNED_INT, (GLvoid*) vboOffset);
|
||||||
GL_UNSIGNED_INT, (GLvoid*) vboOffset);
|
}
|
||||||
|
|
||||||
|
int ScanRow::calculateZ(TilemapPrivate *p, int index)
|
||||||
|
{
|
||||||
|
return 32 * (index + p->viewpPos.y + 1) - p->offset.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScanRow::initUpdateZ()
|
void ScanRow::initUpdateZ()
|
||||||
|
@ -1232,7 +1193,7 @@ void ScanRow::initUpdateZ()
|
||||||
|
|
||||||
void ScanRow::finiUpdateZ(ScanRow *prev)
|
void ScanRow::finiUpdateZ(ScanRow *prev)
|
||||||
{
|
{
|
||||||
z = 32 * (index+1) - p->offset.y;
|
z = calculateZ(p, index);
|
||||||
|
|
||||||
if (prev)
|
if (prev)
|
||||||
scene->insertAfter(*this, *prev);
|
scene->insertAfter(*this, *prev);
|
||||||
|
@ -1369,9 +1330,6 @@ void Tilemap::setMapData(Table *value)
|
||||||
p->mapDataCon.disconnect();
|
p->mapDataCon.disconnect();
|
||||||
p->mapDataCon = value->modified.connect
|
p->mapDataCon = value->modified.connect
|
||||||
(sigc::mem_fun(p, &TilemapPrivate::invalidateBuffers));
|
(sigc::mem_fun(p, &TilemapPrivate::invalidateBuffers));
|
||||||
|
|
||||||
|
|
||||||
p->updateMapDataInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tilemap::setFlashData(Table *value)
|
void Tilemap::setFlashData(Table *value)
|
||||||
|
@ -1417,7 +1375,7 @@ void Tilemap::setVisible(bool value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
p->elem.ground->setVisible(value);
|
p->elem.ground->setVisible(value);
|
||||||
for (size_t i = 0; i < p->elem.scanrows.size(); ++i)
|
for (size_t i = 0; i < p->elem.activeRows; ++i)
|
||||||
p->elem.scanrows[i]->setVisible(value);
|
p->elem.scanrows[i]->setVisible(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1430,7 +1388,7 @@ void Tilemap::setOX(int value)
|
||||||
|
|
||||||
p->offset.x = value;
|
p->offset.x = value;
|
||||||
p->updatePosition();
|
p->updatePosition();
|
||||||
p->updateReplicas();
|
p->mapViewportDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tilemap::setOY(int value)
|
void Tilemap::setOY(int value)
|
||||||
|
@ -1442,9 +1400,8 @@ void Tilemap::setOY(int value)
|
||||||
|
|
||||||
p->offset.y = value;
|
p->offset.y = value;
|
||||||
p->updatePosition();
|
p->updatePosition();
|
||||||
p->updateReplicas();
|
|
||||||
|
|
||||||
p->zOrderDirty = true;
|
p->zOrderDirty = true;
|
||||||
|
p->mapViewportDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue