1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-05-09 10:34:05 +00:00

More work on tile rendering:

- Use indexed triangle rendering, no more GL_QUADS for tiles
- Fix aquarian text on maps
- Tile tex repeat mode works again

Known broken:
- Editor
- Tile repeat with grid effects
This commit is contained in:
fgenesis 2023-08-09 02:41:04 +02:00
parent 395ff079e9
commit 368271c40e
25 changed files with 749 additions and 429 deletions

View file

@ -961,7 +961,7 @@ void AnimationEditor::editStripKey()
{
if(editingBone && editingBone->getGrid())
{
RenderGrid *grid = editingBone->getGrid();
DynamicRenderGrid *grid = editingBone->getGrid();
Animation *a = editSprite->getCurrentAnimation();
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
@ -975,8 +975,8 @@ void AnimationEditor::editStripKey()
assert(bk->controlpoints.size() == interp->bsp.ctrlX() * interp->bsp.ctrlY());
splinegrid = new SplineGrid;
RenderGrid *rgrid = splinegrid->resize(interp->bsp.ctrlX(), interp->bsp.ctrlY(), grid->width(), grid->height(), interp->bsp.degX(), interp->bsp.degY());
rgrid->drawOrder = grid->drawOrder;
DynamicRenderGrid *rgrid = splinegrid->resize(interp->bsp.ctrlX(), interp->bsp.ctrlY(), grid->width(), grid->height(), interp->bsp.degX(), interp->bsp.degY());
rgrid->setDrawOrder(grid->getDrawOrder());
splinegrid->setTexture(editingBone->texture->name);
splinegrid->setWidthHeight(editingBone->width, editingBone->height);
splinegrid->position = Vector(400, 300);
@ -1604,7 +1604,7 @@ void AnimationEditor::applyBoneToSplineGrid()
Animation *a = editSprite->getCurrentAnimation();
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
assert(bk->controlpoints.size() == splinegrid->getSpline().ctrlX() * splinegrid->getSpline().ctrlY());
assert(bk->grid.size() == editingBone->getDrawGrid().linearsize());
assert(bk->grid.size() == editingBone->getGrid()->linearsize());
splinegrid->importControlPoints(&bk->controlpoints[0]);
}
}
@ -1616,7 +1616,7 @@ void AnimationEditor::applySplineGridToBone()
Animation *a = editSprite->getCurrentAnimation();
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
assert(bk->controlpoints.size() == splinegrid->getSpline().ctrlX() * splinegrid->getSpline().ctrlY());
assert(bk->grid.size() == editingBone->getDrawGrid().linearsize());
assert(bk->grid.size() == editingBone->getGrid()->linearsize());
splinegrid->exportControlPoints(&bk->controlpoints[0]);
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
interp->updateGridAndBone(*bk, editingBone);

View file

@ -3522,14 +3522,14 @@ bool DSQ::loadTileset(std::string pack, const unsigned char *usedIdx, size_t use
y++;
}
et->tu1 = x*cell;
et->tv1 = y*cell;
et->tu2 = et->tu1 + cell;
et->tv2 = et->tv1 + cell;
et->tc.u1 = x*cell;
et->tc.v1 = y*cell;
et->tc.u2 = et->tc.u1 + cell;
et->tc.v2 = et->tc.v1 + cell;
et->tv2 = 1 - et->tv2;
et->tv1 = 1 - et->tv1;
std::swap(et->tv1,et->tv2);
/*et->tc.v2 = 1 - et->tc.v2;
et->tc.v1 = 1 - et->tc.v1;
std::swap(et->tc.v1,et->tc.v2);*/
et->w = 512*cell;
et->h = 512*cell;

View file

@ -243,7 +243,7 @@ void Element::setElementEffectByIndex(int eidx)
eff->wavyMin = bity;
eff->wavyMax = bity*1.2f;
if(RenderGrid *g = createGrid(2, e.segsy))
if(DynamicRenderGrid *g = createGrid(2, e.segsy))
{
g->gridType = GRID_UNDEFINED; // by default it's GRID_WAVY, but that would reset during update
setGridFromWavy();

View file

@ -124,13 +124,15 @@ void TileMgr::createTiles(const TileDef* defs, size_t n)
t->flags |= TILEFLAG_FH;
if(d.fv)
t->flags |= TILEFLAG_FV;
if(d.repeat)
t->setRepeatOn(d.rsx, d.rsy);
t->rotation = d.rot;
t->tag = d.tag;
t->scalex = d.sx;
t->scaley = d.sy;
// must be done last
if(d.repeat)
t->setRepeatOn(d.rsx, d.rsy);
}
}
@ -188,6 +190,7 @@ TileData* TileMgr::_createTile(unsigned tilesetID, unsigned layer, float x, flo
//t.beforeScaleOffsetY = 0;
t.flags = GetTileFlags(ef);
t.tag = 0;
t.rep = NULL;
t.et = tileset.getByIdx(tilesetID);
assert(t.et);
/* t.eff = */ tileEffects.assignEffect(t, effidx);

View file

@ -563,11 +563,11 @@ void WorldMapRender::setVis(WorldMapTile *tile)
if (visMethod == VIS_VERTEX)
{
RenderGrid *g = tile->q->setSegs(MAPVIS_SUBDIV, MAPVIS_SUBDIV, 0, 0, 0, 0, 2.0, 1);
DynamicRenderGrid *g = tile->q->setSegs(MAPVIS_SUBDIV, MAPVIS_SUBDIV, 0, 0, 0, 0, 2.0, 1);
if(g)
{
g->gridType = GRID_UNDEFINED;
g->drawOrder = GRID_DRAW_WORLDMAP;
g->setDrawOrder(GRID_DRAW_WORLDMAP);
tileDataToVis(tile, g->array2d());
}
}

View file

@ -112,7 +112,7 @@ public:
int screenWidth, screenHeight;
int textureWidth, textureHeight;
Vector ** drawGrid; // TODO: make this + related code use RenderGrid
Vector ** drawGrid; // TODO: make this + related code use DynamicRenderGrid
// returns handle > 0 on success
int loadShaderFile(const char *vert, const char *frag);

View file

@ -155,7 +155,9 @@ void Core::setup_opengl()
if(afterEffectManager)
afterEffectManager->updateDevice();
defaultQuadVertexBuf.initQuadVertices(0, 0, 1, 1);
TexCoordBox defaultTC;
defaultTC.setStandard();
defaultQuadGrid.init(2, 2, defaultTC);
}
void Core::resizeWindow(int w, int h, int full, int bpp, int vsync, int display, int hz)
@ -279,7 +281,7 @@ static bool checkWritable(const std::string& path, bool warn, bool critical)
Core::Core(const std::string &filesystem, const std::string& extraDataDir, int numRenderLayers, const std::string &appName, int particleSize, std::string userDataSubFolder)
: ActionMapper(), StateManager(), appName(appName), defaultQuadVertexBuf(GPUBUF_STATIC | GPUBUF_VERTEXBUF)
: ActionMapper(), StateManager(), appName(appName)
{
window = NULL;
sound = NULL;
@ -731,6 +733,8 @@ void Core::initGraphicsLibrary(int width, int height, bool fullscreen, bool vsyn
debugLog((const char*)glGetString(GL_RENDERER));
debugLog((const char*)glGetString(GL_VERSION));
DynamicGPUBuffer::StaticInit();
enumerateScreenModes(window->getDisplayIndex());
window->updateSize();
@ -1920,7 +1924,7 @@ void Core::shutdown()
frameBuffer.unloadDevice();
debugLog("OK");
defaultQuadVertexBuf.dropBuffer();
defaultQuadGrid.dropBuffers();
debugLog("Shutdown Graphics Library...");
shutdownGraphicsLibrary();

View file

@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "TextureMgr.h"
#include "VertexBuffer.h"
#include "DarkLayer.h"
#include "RenderGrid.h"
#include "GameKeys.h"
class ParticleEffect;
@ -413,7 +413,7 @@ public:
TextureMgr texmgr;
inline const DynamicGPUBuffer *getDefaultQuadVertexBuffer() const { return &defaultQuadVertexBuf; }
inline const RenderGrid *getDefaultQuadGrid() const { return &defaultQuadGrid; }
protected:
@ -492,7 +492,7 @@ protected:
virtual void updateActionButtons();
void clearActionButtons();
DynamicGPUBuffer defaultQuadVertexBuf;
RenderGrid defaultQuadGrid;
public:
// inclusive!

View file

@ -84,4 +84,35 @@ public:
};
template<typename T>
class Accessor2d
{
public:
Accessor2d(T *p, size_t w, size_t h) : _p(p), _w(w), _h(h) {}
size_t width() const {return _w;}
size_t height() const {return _h;}
size_t linearsize() const {return _w * _h;}
inline T& operator()(size_t x, size_t y)
{
return _p[y * _w + x];
}
inline const T& operator()(size_t x, size_t y) const
{
return _p[y * _w + x];
}
const T *data() const { return _p; }
T *data() { return _p; }
const T *row(size_t y) const { return &_p[y * _w]; }
T *row(size_t y) { return &_p[y * _w]; }
private:
const T *_p;
const size_t _w, _h;
};
#endif

View file

@ -75,20 +75,29 @@ void Quad::destroy()
RenderGrid *Quad::setSegs(int x, int y, float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo)
DynamicRenderGrid *Quad::setSegs(int x, int y, float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo)
{
RenderGrid *g = createGrid(x, y);
DynamicRenderGrid *g = createGrid(x, y);
if(g)
g->setSegs(dgox, dgoy, dgmx, dgmy, dgtm, dgo);
return g;
}
RenderGrid *Quad::createGrid(int xd, int yd)
DynamicRenderGrid *Quad::createGrid(int xd, int yd)
{
delete grid;
return (grid = xd && yd
? new RenderGrid(xd, yd)
: NULL);
grid = NULL;
if(xd && yd)
{
TexCoordBox tc;
tc.u1 = upperLeftTextureCoordinates.x;
tc.v1 = upperLeftTextureCoordinates.y;
tc.u2 = lowerRightTextureCoordinates.x;
tc.v2 = lowerRightTextureCoordinates.y;
grid = new DynamicRenderGrid();
grid->init(xd, yd, tc);
}
return grid;
}
void Quad::setDrawGridAlpha(size_t x, size_t y, float alpha)
@ -208,7 +217,7 @@ void Quad::renderGrid(const RenderState& rs) const
glPushMatrix();
glScalef(width, height, 1);
grid->render(rx, upperLeftTextureCoordinates, lowerRightTextureCoordinates);
grid->render(rx);
// debug points
if (RenderObject::renderCollisionShape)

View file

@ -39,7 +39,7 @@ protected:
void onRender(const RenderState& rs) const OVERRIDE;
};
class RenderGrid;
class DynamicRenderGrid;
class Quad : public RenderObject
{
@ -47,7 +47,7 @@ public:
Quad(const std::string &tex, const Vector &pos);
Quad();
virtual ~Quad();
RenderGrid *createGrid(int x, int y);
DynamicRenderGrid *createGrid(int x, int y);
void destroy() OVERRIDE;
bool isCoordinateInside(Vector coord, int minSize=0) const;
bool isCoordinateInsideWorld(const Vector &coord, int minSize=0) const;
@ -61,14 +61,14 @@ public:
float getWidth() const {return width;}
float getHeight() const {return height;}
RenderGrid *setSegs(int x, int y, float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo);
DynamicRenderGrid *setSegs(int x, int y, float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo);
void setDrawGridAlpha(size_t x, size_t y, float alpha);
void repeatTextureToFill(bool on);
void refreshRepeatTextureToFill();
bool isRepeatingTextureToFill() const { return repeatTexture; }
void setStripPoints(bool vert, const Vector *points, size_t n);
RenderGrid *getGrid() { return grid; }
const RenderGrid *getGrid() const { return grid; }
DynamicRenderGrid *getGrid() { return grid; }
const DynamicRenderGrid *getGrid() const { return grid; }
void reloadDevice() OVERRIDE;
@ -88,7 +88,7 @@ public:
protected:
RenderGrid *grid;
DynamicRenderGrid *grid;
void resetGrid();

View file

@ -41,157 +41,87 @@ void RenderGrid::ResetWithAlpha(Vector* dst, size_t w, size_t h, float alpha)
}
RenderGrid::RenderGrid(size_t w, size_t h)
: gridTimer(0)
, drawGridOffsetX(0), drawGridOffsetY(0), drawGridModX(0), drawGridModY(0), drawGridTimeMultiplier(0)
, drawGridOut(false), gridType(GRID_WAVY), drawOrder(GRID_DRAW_DEFAULT)
RenderGrid::RenderGrid()
: indexbuf(GPUBUF_STATIC | GPUBUF_INDEXBUF), vbo(GPUBUF_DYNAMIC | GPUBUF_VERTEXBUF), trisToDraw(0)
, needVBOUpdate(false), drawOrder(GRID_DRAW_DEFAULT)
{
init(w, h);
tc.setStandard();
}
RenderGrid::~RenderGrid()
{
}
void RenderGrid::init(size_t w, size_t h)
void RenderGrid::dropBuffers()
{
vbo.dropBuffer();
indexbuf.dropBuffer();
}
void RenderGrid::init(size_t w, size_t h, const TexCoordBox& tc)
{
assert(w > 1 && h > 1);
grid.init(w, h);
setDrawOrder((GridDrawOrder)drawOrder, true);
this->tc = tc;
reset();
Vector *dg = grid.data();
for(size_t i = 0; i < grid.linearsize(); ++i)
dg[i].z = 1.0f;
updateVBO();
}
void RenderGrid::reset()
{
ResetGrid(grid.data(), grid.width(), grid.height());
needVBOUpdate = true;
}
void RenderGrid::resetWithAlpha(float a)
{
ResetWithAlpha(grid.data(), grid.width(), grid.height(), a);
}
void RenderGrid::update(float dt)
{
if (gridType == GRID_WAVY)
{
gridTimer += dt * drawGridTimeMultiplier;
reset();
const size_t w = grid.width();
const size_t h = grid.height();
size_t nx = w;
if(drawGridOut)
nx /= 2;
for (size_t y = 0; y < h; y++)
{
Vector * const row = grid.row(y);
const float xoffset = y * drawGridOffsetX;
const float addx = sinf(gridTimer+xoffset)*drawGridModX;
size_t x;
for (x = 0; x < nx; x++)
row[x].x -= addx;
for (; x < w; x++)
row[x].x += addx;
if(const float dgmy = drawGridModY)
for (x = 0; x < w; x++)
{
float yoffset = x * drawGridOffsetY;
row[x].y += cosf(gridTimer+yoffset)*dgmy;
}
}
}
needVBOUpdate = true;
}
void RenderGrid::setAlpha(size_t x, size_t y, float a)
{
if (x < grid.width() && y < grid.height())
grid(x, y).z = a;
needVBOUpdate = true;
}
void RenderGrid::setSegs(float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo)
void RenderGrid::setDrawOrder(GridDrawOrder ord, bool force)
{
drawGridOffsetX = dgox;
drawGridOffsetY = dgoy;
drawGridModX = dgmx;
drawGridModY = dgmy;
drawGridTimeMultiplier = dgtm;
drawGridOut = dgo;
gridTimer = 0;
gridType = GRID_WAVY;
if(!force && drawOrder == ord)
return;
drawOrder = ord;
trisToDraw = indexbuf.initGridIndices_Triangles(grid.width(), grid.height(), ord == GRID_DRAW_LRBT, GPUACCESS_HOSTCOPY);
}
void RenderGrid::setStripPoints(bool vert, const Vector* points, size_t n)
void RenderGrid::setTexCoords(const TexCoordBox& tc)
{
reset();
const float mul = float(n);
if (!vert) // horz
{
const size_t xmax = std::min(grid.width(), n);
for (size_t y = 0; y < grid.height(); y++)
{
Vector *row = grid.row(y);
for (size_t x = 0; x < xmax; x++)
row[x] += points[x] * mul;
}
}
else
{
const size_t ymax = std::min(grid.height(), n);
for (size_t x = 0; x < grid.width(); x++)
for (size_t y = 0; y < ymax; y++)
grid(x, y) += points[y] * mul;
}
}
void RenderGrid::setFromWavy(const float* wavy, size_t len, float width)
{
const size_t NX = grid.width() - 1;
const size_t H = grid.height();
const float iw = 1.0f / width;
for (size_t y = 0; y < H; y++)
{
const size_t wavy_y = (H - y)-1;
if (wavy_y < len)
{
const float tmp = wavy[wavy_y] * iw;
Vector * const row = grid.row(y);
for (size_t x = 0; x < NX; x++)
{
row[x].x = tmp - 0.5f;
row[x+1].x = tmp + 0.5f;
}
}
}
this->tc = tc;
needVBOUpdate = true;
}
void RenderGrid::render(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const
void RenderGrid::render(const RenderState& rs) const
{
switch(drawOrder)
{
case GRID_DRAW_LRTB:
render_LRTB(rs, upperLeftTexCoords, lowerRightTexCoords);
break;
case GRID_DRAW_LRBT:
render_LRBT(rs, upperLeftTexCoords, lowerRightTexCoords);
break;
case GRID_DRAW_WORLDMAP:
render_WithAlpha(rs, upperLeftTexCoords, lowerRightTexCoords);
break;
if(rs.alpha != 1 || rs.color != Vector(1,1,1))
{
render_WithAlpha(rs);
break;
}
// else fall through
default:
render_Indexed(rs);
}
}
@ -201,133 +131,74 @@ void RenderGrid::renderDebugPoints(const RenderState& rs) const
glPointSize(2);
glColor3f(1,0,0);
glBegin(GL_POINTS);
const size_t W = grid.width();
const size_t H = grid.height();
for (size_t y = 0; y < H; y++)
{
const Vector * const row = grid.row(y);
for (size_t x = 0; x < W; x++)
glVertex2f(row[x].x, row[x].y);
}
glEnd();
vbo.apply();
glDrawArrays(GL_POINTS, 0, grid.linearsize());
}
void RenderGrid::render_LRTB(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const
void RenderGrid::updateVBO()
{
const float percentX = lowerRightTexCoords.x - upperLeftTexCoords.x;
const float percentY = lowerRightTexCoords.y - upperLeftTexCoords.y;
const float percentX = tc.u2 - tc.u1;
const float percentY = tc.v2 - tc.v1;
const float baseX = upperLeftTexCoords.x;
const float baseY = upperLeftTexCoords.y;
const float baseX = tc.u1;
const float baseY = tc.v1;
const size_t NX = grid.width()-1;
const size_t NY = grid.height()-1;
const size_t W = grid.width();
const size_t H = grid.height();
// NOTE: These are used to avoid repeated expensive divide operations,
// but they may cause rounding error of around 1 part per million,
// which could in theory cause minor graphical glitches with broken
// OpenGL implementations. --achurch
const float incX = percentX / float(NX);
const float incY = percentY / float(NY);
const float incX = percentX / float(W-1);
const float incY = percentY / float(H-1);
glColor4f(rs.color.x, rs.color.y, rs.color.z, rs.alpha);
glBegin(GL_QUADS);
float v0 = baseY;
float v1 = v0 + incY;
for (size_t y = 0; y < NY; y++, v0 = v1, v1 += incY)
do
{
float u0 = baseX;
float u1 = u0 + incX;
const Vector *row0 = grid.row(y);
const Vector *row1 = grid.row(y+1);
for (size_t x = 0; x < NX; x++, u0 = u1, u1 += incX)
float *p = (float*)vbo.beginWrite(GPUBUFTYPE_VEC2_TC, W*H * (2*2) * sizeof(float), GPUACCESS_DEFAULT);
float v = baseY;
for (size_t y = 0; y < H; y++, v += incY)
//for (size_t y = H; y --> 0; v += incY)
{
const Vector dg00 = row0[x];
const Vector dg01 = row1[x];
const Vector dg10 = row0[x+1];
const Vector dg11 = row1[x+1];
glTexCoord2f(u0, v0);
glVertex2f(dg00.x, dg00.y);
glTexCoord2f(u0, v1);
glVertex2f(dg01.x, dg01.y);
glTexCoord2f(u1, v1);
glVertex2f(dg11.x, dg11.y);
glTexCoord2f(u1, v0);
glVertex2f(dg10.x, dg10.y);
float u = baseX;
const Vector *row = grid.row(y);
for (size_t x = 0; x < W; x++, u += incX)
{
*p++ = row->x;
*p++ = row->y;
++row;
*p++ = u;
*p++ = v;
}
}
}
glEnd();
while(!vbo.commitWrite());
needVBOUpdate = false;
}
void RenderGrid::render_LRBT(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const
void RenderGrid::render_Indexed(const RenderState& rs) const
{
const float percentX = lowerRightTexCoords.x - upperLeftTexCoords.x;
const float percentY = upperLeftTexCoords.y - lowerRightTexCoords.y;
const float baseX = upperLeftTexCoords.x;
const float baseY = lowerRightTexCoords.y;
const size_t NX = grid.width()-1;
const size_t NY = grid.height()-1;
// NOTE: These are used to avoid repeated expensive divide operations,
// but they may cause rounding error of around 1 part per million,
// which could in theory cause minor graphical glitches with broken
// OpenGL implementations. --achurch
const float incX = percentX / float(NX);
const float incY = percentY / float(NY);
glColor4f(rs.color.x, rs.color.y, rs.color.z, rs.alpha);
glBegin(GL_QUADS);
float v0 = baseY;
float v1 = v0 + incY;
for (size_t y = NY; y --> 0; v0 = v1, v1 += incY)
{
float u0 = baseX;
float u1 = u0 + incX;
const Vector *row0 = grid.row(y+1);
const Vector *row1 = grid.row(y);
for (size_t x = 0; x < NX; x++, u0 = u1, u1 += incX)
{
const Vector dg00 = row0[x];
const Vector dg01 = row1[x];
const Vector dg10 = row0[x+1];
const Vector dg11 = row1[x+1];
glTexCoord2f(u0, v0);
glVertex2f(dg00.x, dg00.y);
glTexCoord2f(u0, v1);
glVertex2f(dg01.x, dg01.y);
glTexCoord2f(u1, v1);
glVertex2f(dg11.x, dg11.y);
glTexCoord2f(u1, v0);
glVertex2f(dg10.x, dg10.y);
}
}
glEnd();
(void)rs;
// can't render this here when color/alpha is modulated AND we have colors as part of the vertex data;
// old opengl simply doesn't support this
assert(drawOrder != GRID_DRAW_WORLDMAP || (rs.color == Vector(1,1,1) && rs.alpha == 1));
vbo.apply();
indexbuf.drawElements(GL_TRIANGLES, trisToDraw);
}
void RenderGrid::render_WithAlpha(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const
void RenderGrid::render_WithAlpha(const RenderState& rs) const
{
const float percentX = fabsf(lowerRightTexCoords.x - upperLeftTexCoords.x);
const float percentY = fabsf(upperLeftTexCoords.y - lowerRightTexCoords.y);
const float percentX = fabsf(tc.u2 - tc.u1);
const float percentY = fabsf(tc.v1 - tc.v2);
const float baseX =
(lowerRightTexCoords.x < upperLeftTexCoords.x)
? lowerRightTexCoords.x : upperLeftTexCoords.x;
(tc.u2 < tc.u1)
? tc.u2 : tc.u1;
const float baseY =
(lowerRightTexCoords.y < upperLeftTexCoords.y)
? lowerRightTexCoords.y : upperLeftTexCoords.y;
(tc.v2 < tc.v1)
? tc.v2 : tc.v1;
const size_t NX = grid.width()-1;
const size_t NY = grid.height()-1;
@ -381,3 +252,120 @@ void RenderGrid::render_WithAlpha(const RenderState& rs, const Vector& upperLeft
glEnd();
}
// -------------------------------------
DynamicRenderGrid::DynamicRenderGrid()
: RenderGrid()
, gridTimer(0)
, drawGridOffsetX(0), drawGridOffsetY(0), drawGridModX(0), drawGridModY(0), drawGridTimeMultiplier(0)
, drawGridOut(false), gridType(GRID_WAVY)
{
}
DynamicRenderGrid::~DynamicRenderGrid()
{
}
void DynamicRenderGrid::update(float dt)
{
if (gridType == GRID_WAVY)
{
gridTimer += dt * drawGridTimeMultiplier;
reset();
const size_t w = grid.width();
const size_t h = grid.height();
size_t nx = w;
if(drawGridOut)
nx /= 2;
for (size_t y = 0; y < h; y++)
{
Vector * const row = grid.row(y);
const float xoffset = y * drawGridOffsetX;
const float addx = sinf(gridTimer+xoffset)*drawGridModX;
size_t x;
for (x = 0; x < nx; x++)
row[x].x -= addx;
for (; x < w; x++)
row[x].x += addx;
if(const float dgmy = drawGridModY)
for (x = 0; x < w; x++)
{
float yoffset = x * drawGridOffsetY;
row[x].y += cosf(gridTimer+yoffset)*dgmy;
}
}
// always update vbo now
}
else if(!needVBOUpdate)
return;
updateVBO();
}
void DynamicRenderGrid::setSegs(float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo)
{
drawGridOffsetX = dgox;
drawGridOffsetY = dgoy;
drawGridModX = dgmx;
drawGridModY = dgmy;
drawGridTimeMultiplier = dgtm;
drawGridOut = dgo;
gridTimer = 0;
gridType = GRID_WAVY;
}
void DynamicRenderGrid::setStripPoints(bool vert, const Vector* points, size_t n)
{
reset();
const float mul = float(n);
if (!vert) // horz
{
const size_t xmax = std::min(grid.width(), n);
for (size_t y = 0; y < grid.height(); y++)
{
Vector *row = grid.row(y);
for (size_t x = 0; x < xmax; x++)
row[x] += points[x] * mul;
}
}
else
{
const size_t ymax = std::min(grid.height(), n);
for (size_t x = 0; x < grid.width(); x++)
for (size_t y = 0; y < ymax; y++)
grid(x, y) += points[y] * mul;
}
needVBOUpdate = true;
}
void DynamicRenderGrid::setFromWavy(const float* wavy, size_t len, float width)
{
const size_t NX = grid.width() - 1;
const size_t H = grid.height();
const float iw = 1.0f / width;
for (size_t y = 0; y < H; y++)
{
const size_t wavy_y = (H - y)-1;
if (wavy_y < len)
{
const float tmp = wavy[wavy_y] * iw;
Vector * const row = grid.row(y);
for (size_t x = 0; x < NX; x++)
{
row[x].x = tmp - 0.5f;
row[x+1].x = tmp + 0.5f;
}
}
}
needVBOUpdate = true;
}

View file

@ -3,6 +3,8 @@
#include "Vector.h"
#include "DataStructures.h"
#include "VertexBuffer.h"
#include "Texture.h" // TexCoordBox
struct RenderState;
@ -23,23 +25,26 @@ enum GridType
GRID_INTERP = 3, // quad is in grid mode
};
// simple render grid, must be manually uploaded to GPU if changed
class RenderGrid
{
public:
RenderGrid(size_t w, size_t h);
RenderGrid();
~RenderGrid();
void dropBuffers();
void init(size_t w, size_t h);
void init(size_t w, size_t h, const TexCoordBox& tc);
void reset();
void resetWithAlpha(float a);
void update(float dt);
void render(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const;
void render(const RenderState& rs) const;
void renderDebugPoints(const RenderState& rs) const;
void setAlpha(size_t x, size_t y, float a);
void setSegs(float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo);
void setStripPoints(bool vert, const Vector *points, size_t n);
void setFromWavy(const float *wavy, size_t len, float width);
void setDrawOrder(GridDrawOrder ord, bool force = false);
inline GridDrawOrder getDrawOrder() const { return GridDrawOrder(drawOrder); }
void setTexCoords(const TexCoordBox& tc);
const TexCoordBox& getTexCoords() const { return tc; }
bool empty() const { return !(width() | height()); }
size_t width() const { return grid.width(); }
size_t height() const { return grid.height(); }
size_t linearsize() const { return grid.linearsize(); }
@ -47,11 +52,39 @@ public:
Vector *data() { return grid.data(); }
Array2d<Vector>& array2d() { return grid; }
const Array2d<Vector>& array2d() const { return grid; }
const DynamicGPUBuffer& getVBO() const { return vbo; }
void updateVBO();
static void ResetWithAlpha(Vector* dst, size_t w, size_t h, float alpha);
protected:
DynamicGPUBuffer indexbuf, vbo;
size_t trisToDraw;
Array2d<Vector> grid;
TexCoordBox tc;
bool needVBOUpdate;
void render_Indexed(const RenderState& rs) const;
void render_WithAlpha(const RenderState& rs) const;
public:
GridDrawOrder drawOrder;
};
// supports animation and automatic upload
class DynamicRenderGrid : public RenderGrid
{
public:
DynamicRenderGrid();
~DynamicRenderGrid();
void update(float dt);
void setSegs(float dgox, float dgoy, float dgmx, float dgmy, float dgtm, bool dgo);
void setStripPoints(bool vert, const Vector *points, size_t n);
void setFromWavy(const float *wavy, size_t len, float width);
protected:
float gridTimer;
float drawGridOffsetX;
float drawGridOffsetY;
@ -61,11 +94,6 @@ protected:
bool drawGridOut;
public:
unsigned char gridType; // unsigned char to save space
unsigned char drawOrder;
void render_LRTB(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const;
void render_LRBT(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const;
void render_WithAlpha(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const;
};
#endif

View file

@ -130,7 +130,7 @@ void Bone::addSegment(Bone *b)
void Bone::createStrip(bool vert, int num)
{
RenderGrid *grid = vert ? createGrid(2, num) : createGrid(num, 2);
DynamicRenderGrid *grid = vert ? createGrid(2, num) : createGrid(num, 2);
stripVert = vert;
grid->gridType = GRID_STRIP;
changeStrip.resize(num);
@ -974,7 +974,7 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
XMLElement *bones = xml->NewElement("Bones");
for (i = 0; i < this->bones.size(); i++)
{
const RenderGrid * const grid = this->bones[i]->getGrid();
const DynamicRenderGrid * const grid = this->bones[i]->getGrid();
XMLElement *bone = xml->NewElement("Bone");
bone->SetAttribute("idx", (unsigned int) this->bones[i]->boneIdx);
bone->SetAttribute("gfx", this->bones[i]->gfx.c_str());
@ -1033,9 +1033,9 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
bone->SetAttribute("sz", os.str().c_str());
}
if(grid && grid->drawOrder != GRID_DRAW_DEFAULT)
if(grid && grid->getDrawOrder() != GRID_DRAW_DEFAULT)
{
bone->SetAttribute("gridDrawOrder", (int)grid->drawOrder);
bone->SetAttribute("gridDrawOrder", (int)grid->getDrawOrder());
}
@ -1073,7 +1073,8 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
const BoneGridInterpolator& bgip = a->interpolators[j];
XMLElement *interp = xml->NewElement("Interpolator");
Bone *bone = this->getBoneByIdx(bgip.idx);
assert(bone->gridType == Quad::GRID_INTERP);
DynamicRenderGrid *grid = bone->getGrid();
assert(grid && grid->gridType == GRID_INTERP);
if(bgip.storeBoneByIdx)
interp->SetAttribute("bone", (int)bone->boneIdx);
else
@ -1128,7 +1129,7 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
Bone *bone = this->getBoneByIdx(b->idx);
if(bone)
{
const RenderGrid * const bgrid = bone->getGrid();
const DynamicRenderGrid * const bgrid = bone->getGrid();
os << b->idx << " " << b->x << " " << b->y << " " << b->rot << " ";
// don't want to store grid points if they can be regenerated automatically
size_t usedGridSize = (!bgrid || bgrid->gridType == GRID_INTERP) ? 0 : b->grid.size();
@ -1601,7 +1602,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
}
if (bone->Attribute("grid"))
{
RenderGrid *grid = newb->getGrid();
DynamicRenderGrid *grid = newb->getGrid();
if(!grid)
{
SimpleIStringStream is(bone->Attribute("grid"), SimpleIStringStream::REUSE);
@ -1618,7 +1619,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if(const char *gdo = bone->Attribute("gridDrawOrder"))
{
int ord = atoi(gdo);
grid->drawOrder = (GridDrawOrder)ord;
grid->setDrawOrder((GridDrawOrder)ord);
}
}
bone = bone->NextSiblingElement("Bone");
@ -1842,7 +1843,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
debugLog(os.str());
continue;
}
RenderGrid *grid = bi->getGrid();
DynamicRenderGrid *grid = bi->getGrid();
if(!grid)
{
std::ostringstream os;
@ -2060,7 +2061,7 @@ void AnimationLayer::updateBones()
b->scale.x = lerp(bkey1->sx, bkey2->sx, dt, lerpType);
b->scale.y = lerp(bkey1->sy, bkey2->sy, dt, lerpType);
}
RenderGrid *grid = b->getGrid();
DynamicRenderGrid *grid = b->getGrid();
if (grid && b->animated==Bone::ANIM_ALL && !b->changeStrip.empty() && grid->gridType == GRID_STRIP)
{
if (bkey2->grid.size() < b->changeStrip.size())
@ -2079,12 +2080,12 @@ void AnimationLayer::updateBones()
if(bkey1->grid.size() < N)
{
bkey1->grid.resize(N);
RenderGrid::ResetWithAlpha(&bkey1->grid[0], grid->width(), grid->height(), 1.0f);
DynamicRenderGrid::ResetWithAlpha(&bkey1->grid[0], grid->width(), grid->height(), 1.0f);
}
if(bkey2->grid.size() < N)
{
bkey2->grid.resize(N);
RenderGrid::ResetWithAlpha(&bkey2->grid[0], grid->width(), grid->height(), 1.0f);
DynamicRenderGrid::ResetWithAlpha(&bkey2->grid[0], grid->width(), grid->height(), 1.0f);
}
Vector *dst = grid->data();
@ -2217,7 +2218,7 @@ void SkeletalSprite::selectNextBone()
void BoneGridInterpolator::updateGridOnly(BoneKeyframe& bk, const Bone *bone)
{
const RenderGrid *grid = bone->getGrid();
const DynamicRenderGrid *grid = bone->getGrid();
assert(bone->boneIdx == bk.idx);
assert(bk.grid.size() == grid->linearsize());
bsp.recalc(&bk.grid[0], grid->width(), grid->height(), &bk.controlpoints[0]);

View file

@ -81,12 +81,12 @@ SplineGrid::~SplineGrid()
{
}
RenderGrid *SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yres, unsigned degx, unsigned degy)
DynamicRenderGrid *SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yres, unsigned degx, unsigned degy)
{
size_t oldcpx = bsp.ctrlX();
size_t oldcpy = bsp.ctrlY();
RenderGrid *ret = this->createGrid(xres, yres);
DynamicRenderGrid *ret = this->createGrid(xres, yres);
std::vector<SplineGridCtrlPoint*> oldp;
ctrlp.swap(oldp);

View file

@ -33,7 +33,7 @@ public:
~SplineGrid();
// # of control points on each axis
RenderGrid *resize(size_t w, size_t h, size_t xres, size_t yres, unsigned degx, unsigned degy);
DynamicRenderGrid *resize(size_t w, size_t h, size_t xres, size_t yres, unsigned degx, unsigned degy);
void recalc();
void exportControlPoints(Vector *controlpoints);
void importControlPoints(const Vector *controlpoints);

View file

@ -30,6 +30,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "GLLoad.h"
#include "stb_image_resize.h"
bool TexCoordBox::isStandard() const
{
return u1 == 0 && v1 == 0 && u2 == 1 && v2 == 1;
}
void TexCoordBox::setStandard()
{
u1 = 0;
v1 = 0;
u2 = 1;
v2 = 1;
}
Texture::Texture()
{
@ -88,13 +101,13 @@ static const GLenum repeatLUT[] = { GL_CLAMP_TO_EDGE, GL_REPEAT };
void Texture::apply(bool repeat) const
{
glBindTexture(GL_TEXTURE_2D, gltexid);
if(repeat != _repeating)
/*if(repeat != _repeating)
{
_repeating = repeat;
GLenum rep = repeatLUT[repeat];
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, rep);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, rep);
}
}*/
}
struct GlTexFormat
@ -127,8 +140,8 @@ bool Texture::upload(const ImageData& img, bool mipmap)
if(!gltexid)
glGenTextures(1, &gltexid);
glBindTexture(GL_TEXTURE_2D, gltexid);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT);
_repeating = false;
const GlTexFormat& f = formatLUT[img.channels - 1];

View file

@ -24,6 +24,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <string>
#include "Refcounted.h"
struct TexCoordBox
{
float u1, v1; // upper left (x,y)
float u2, v2; // lower right (x,y)
bool isStandard() const;
void setStandard();
};
enum TextureLoadResult
{

View file

@ -146,12 +146,11 @@ static void dropEffect(TileData& t)
static void dropRepeat(TileData& t)
{
if(t.flags & TILEFLAG_OWN_REPEAT)
if(t.rep)
{
delete t.rep;
t.flags &= ~TILEFLAG_OWN_REPEAT;
t.rep = NULL;
}
t.rep = NULL;
}
static void dropAll(TileData& t)
@ -232,6 +231,10 @@ size_t TileStorage::cloneSome(const TileEffectStorage& effstore, const size_t* i
for(size_t i = ret; i < N; ++i) // loop only over newly added tiles
{
TileData& t = tiles[i];
if(t.rep)
{
t.rep = new TileRepeatData(*t.rep); // must be done BEFORE assigning eff
}
if((t.flags & TILEFLAG_OWN_EFFDATA) && t.eff)
{
int efx = t.eff->efxidx;
@ -239,10 +242,6 @@ size_t TileStorage::cloneSome(const TileEffectStorage& effstore, const size_t* i
t.flags &= TILEFLAG_OWN_EFFDATA;
effstore.assignEffect(t, efx); // recreate effect properly
}
if((t.flags & TILEFLAG_OWN_REPEAT) && t.rep)
{
t.rep = new TileRepeatData(*t.rep);
}
}
refreshAll();
@ -282,9 +281,11 @@ void TileStorage::clearSelection()
tiles[i].flags &= ~TILEFLAG_SELECTED;
}
TileEffectData::TileEffectData(const TileEffectConfig& cfg)
TileEffectData::TileEffectData(const TileEffectConfig& cfg, const TileData *t)
: efxtype(cfg.type), efxidx(cfg.index)
, grid(NULL), alpha(1), blend(BLEND_DEFAULT)
, ownGrid(false), shared(false)
{
switch(cfg.type)
{
@ -294,14 +295,14 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
case EFX_WAVY:
{
float bity = 20; // FIXME
assert(t);
float bity = t->et->h/float(cfg.u.wavy.segsy);
wavy.wavy.resize(cfg.u.wavy.segsy, 0.0f);
wavy.flip = cfg.u.wavy.flip;
wavy.min = bity;
wavy.max = bity*1.2f;
RenderGrid *g = new RenderGrid(2, cfg.u.wavy.segsy);
grid = g;
DynamicRenderGrid *g = _ensureGrid(2, cfg.u.wavy.segsy, t);
g->gridType = GRID_UNDEFINED; // we do the grid update manually
wavy.angleOffset = 0;
@ -317,8 +318,7 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
case EFX_SEGS:
{
RenderGrid *g = new RenderGrid(cfg.u.segs.x, cfg.u.segs.y);
grid = g;
DynamicRenderGrid *g = _ensureGrid(cfg.u.segs.x, cfg.u.segs.y, t);
g->setSegs(cfg.u.segs.dgox, cfg.u.segs.dgoy, cfg.u.segs.dgmx, cfg.u.segs.dgmy, cfg.u.segs.dgtm, cfg.u.segs.dgo);
}
break;
@ -335,7 +335,43 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
TileEffectData::~TileEffectData()
{
delete grid;
if(ownGrid)
delete grid;
}
DynamicRenderGrid *TileEffectData::_ensureGrid(size_t w, size_t h, const TileData *t)
{
DynamicRenderGrid *g = grid;
if(ownGrid)
{
assert(g);
return g;
}
if(t && t->rep)
{
assert(!shared); // a shared instance MUST have its own grid and MUST NOT refer to the grid of any tile
if(ownGrid)
delete g;
g = &t->rep->grid;
ownGrid = false;
}
if(!g)
{
g = new DynamicRenderGrid;
ownGrid = true;
}
grid = g;
TexCoordBox tc;
if(t)
tc = t->getTexcoords();
else
tc.setStandard();
g->init(w, h, tc);
if(t && t->rep)
t->rep->refresh(*t);
return g;
}
void TileEffectData::Wavy::update(float dt)
@ -463,19 +499,28 @@ void TileEffectStorage::assignEffect(TileData& t, int index) const
return;
size_t idx = size_t(index);
if(idx >= configs.size())
return;
if(idx < prepared.size() && prepared[idx])
bool needinstance = false;
if(idx < configs.size())
{
t.eff = prepared[idx];
needinstance = configs[idx].needsOwnInstanceForTile(t);
}
else if(idx < configs.size())
if(needinstance)
{
if(configs[idx].type == EFX_NONE)
return;
t.eff = new TileEffectData(configs[idx]);
t.eff = new TileEffectData(configs[idx], &t);
t.flags |= TILEFLAG_OWN_EFFDATA;
}
else if(idx < prepared.size() && prepared[idx])
{
t.eff = prepared[idx];
}
}
void TileEffectStorage::update(float dt)
@ -513,7 +558,10 @@ void TileEffectStorage::finalize()
// segs and alpha are independent of the tile they are applied to,
// so we can create shared instances of the effect.
if(c.type == EFX_SEGS || c.type == EFX_ALPHA)
prepared[i] = new TileEffectData(c);
{
prepared[i] = new TileEffectData(c, NULL);
prepared[i]->shared = true;
}
}
}
@ -532,12 +580,21 @@ bool TileData::isCoordinateInside(float cx, float cy, float minsize) const
}
TileRepeatData::TileRepeatData()
: vertexbuf(GPUBUF_STATIC | GPUBUF_VERTEXBUF)
: texscaleX(1), texscaleY(1)
, texOffX(0), texOffY(0)
{
}
void TileRepeatData::refresh(const ElementTemplate& et, float scalex, float scaley)
TileRepeatData::TileRepeatData(const TileRepeatData& o)
: texscaleX(o.texscaleX), texscaleY(o.texscaleY)
, texOffX(o.texOffX), texOffY(o.texOffY)
{
}
TexCoordBox TileRepeatData::calcTexCoords(const TileData& t) const
{
const ElementTemplate& et = *t.et;
float tw, th;
if(et.tex)
{
@ -550,43 +607,118 @@ void TileRepeatData::refresh(const ElementTemplate& et, float scalex, float scal
th = et.h;
}
const float tu1 = texOffX;
const float tv1 = texOffY;
const float tu2 = (et.w*scalex*texscaleX)/tw + texOffX;
const float tv2 = (et.h*scaley*texscaleY)/th + texOffY;
TexCoordBox tc;
tc.u1 = texOffX;
tc.v1 = texOffY;
tc.u2 = (et.w*t.scalex*texscaleX)/tw + texOffX;
tc.v2 = (et.h*t.scaley*texscaleY)/th + texOffY;
this->tu1 = tu1;
this->tv1 = tv1;
this->tu2 = tu2;
this->tv2 = tv2;
// HACK: partially repeated textures have a weird Y axis. assuming a repeat factor of 0.5,
// instead of texcoords from 0 -> 0.4 everything is biased towards the opposite end, ie. 0.6 -> 1.
// This is especially true for partial repeats, we always need to bias towards the other end.
// I have no idea why this has to be like this for tiles, but this is NOT the case for fonts.
// And NOTE: without this, maps may look deceivingly correct, but they really are not.
const float percentY = tc.v2 - tc.v1;
const float remainder = 1.0f - fmodf(percentY, 1.0f);
tc.v1 += remainder; // bias towards next int
tc.v2 += remainder;
vertexbuf.initQuadVertices(tu1, tv1, tu2, tv2);
return tc;
}
void TileRepeatData::refresh(const TileData& t)
{
TexCoordBox tc = calcTexCoords(t);
/*if(t.eff)
if(const DynamicRenderGrid *g = t.eff->grid)
{
grid.init(g->width(), g->height(), grid.getTexCoords());
grid.gridType = g->gridType;
}*/
if(grid.empty())
grid.init(2, 2, tc);
else
{
grid.setTexCoords(tc);
grid.reset();
grid.updateVBO();
}
}
TileRepeatData* TileData::setRepeatOn(float texscalex, float texscaley, float offx, float offy)
{
if(rep && !(flags & TILEFLAG_OWN_REPEAT))
rep = NULL;
flags |= (TILEFLAG_OWN_REPEAT | TILEFLAG_REPEAT);
flags |= TILEFLAG_REPEAT;
if(!rep)
rep = new TileRepeatData;
rep->texscaleX = texscalex;
rep->texscaleY = texscaley;
rep->texOffX = offx;
rep->texOffY = offy;
rep->refresh(*et, scalex, scaley);
rep->refresh(*this);
// FIXME: if eff, link eff->grid to rep->grid
return rep;
}
void TileData::setRepeatOff()
{
flags &= ~TILEFLAG_REPEAT;
// don't delete this->rep; if we're in editor mode we don't want to lose the repeat data just yet
// also, a TileEffectData may point to rep->grid
}
void TileData::refreshRepeat()
{
if((flags & TILEFLAG_OWN_REPEAT) && rep)
if(rep)
{
rep->refresh(*et, scalex, scaley);
rep->refresh(*this);
}
}
bool TileData::hasStandardTexcoords() const
{
// repeat applies per-tile texcoords, so if that's set it's non-standard
return !rep && et->tc.isStandard();
}
const TexCoordBox& TileData::getTexcoords() const
{
return !(flags & TILEFLAG_REPEAT)
? et->tc
: rep->grid.getTexCoords();
}
const RenderGrid *TileData::getGrid() const
{
if(flags & TILEFLAG_REPEAT)
return &rep->getGrid();
if(eff && eff->grid)
return eff->grid;
return et->grid;
}
bool TileEffectConfig::needsOwnInstanceForTile(const TileData& t) const
{
const bool rep = !!(t.flags & TILEFLAG_REPEAT);
switch(type)
{
case EFX_NONE:
case EFX_ALPHA:
return false;
case EFX_WAVY:
return true;
case EFX_SEGS:
return rep || !t.hasStandardTexcoords();
}
assert(false);
return true; // uhhhh
}

View file

@ -5,6 +5,9 @@
#include "Vector.h"
#include "EngineEnums.h"
#include "VertexBuffer.h"
#include "RenderGrid.h"
#include "Texture.h" // TexCoordBox
// A Tile is a very stripped down RenderObject that bypasses the default
// rendering pipeline for efficiency reasons.
@ -33,14 +36,17 @@ Assumptions:
- Most tiles that exist are going to be rendered
- Only few tiles have an effect attached
Gotaches:
Gotchas:
- Keeping a pointer to a TileData is not safe.
- Tile indexes are not stable. Moving a tile changes the index it can be addressed with
- Tile repeat causes a tile to have non-standard texcoords.
Grid effect texcoords need to be synced with repeat tc, if repeat is toggled.
Also mind non-standard texcoords in ElementTemplate (eg. aquarian glyphs)
*/
class ElementTemplate;
class Texture;
class RenderGrid;
class DynamicRenderGrid;
class TileRender;
enum EFXType
@ -51,6 +57,8 @@ enum EFXType
EFX_WAVY
};
struct TileData;
// static configuration for one effect type. POD.
struct TileEffectConfig
{
@ -81,6 +89,9 @@ public:
BlendType blend;
} alpha;
} u;
bool needsOwnInstanceForTile(const TileData& t) const;
};
enum TileFlags
@ -96,24 +107,23 @@ enum TileFlags
TILEFLAG_HIDDEN = 0x80, // don't render tile
TILEFLAG_SELECTED = 0x100, // ephemeral: selected in editor
TILEFLAG_EDITOR_HIDDEN = 0x200, // tile is hidden for editor reasons. temporarily set when multi-selecting and moving. doesn't count as hidden externally and is only for rendering.
TILEFLAG_OWN_REPEAT = 0x400, // owns TileRepeatData, may update, must delete
TILEFLAG_FV = 0x800, // flipped vertically
TILEFLAG_FV = 0x400, // flipped vertically
};
struct TileData;
struct TileEffectData
{
TileEffectData(const TileEffectConfig& cfg);
TileEffectData(const TileEffectConfig& cfg, const TileData *t); // NULL is passed in during global prepare, when we don't have a tile
~TileEffectData();
void update(float dt, const TileData *t); // optional t needed for EFX_WAVY
void doInteraction(const TileData& t, const Vector& pos, const Vector& vel, float mult, float touchWidth);
const EFXType efxtype;
const unsigned efxidx; // index of TileEffect
RenderGrid *grid;
DynamicRenderGrid *grid; // may or may not own this. This possibly points to a tile's TileRepeatData::grid
InterpolatedVector alpha;
BlendType blend;
bool ownGrid; // true if we own grid
bool shared; // only used for assertions. set if this tile effect instance is pre-made and shared across many tiles
struct Wavy
{
@ -128,23 +138,29 @@ struct TileEffectData
Wavy wavy;
private:
DynamicRenderGrid *_ensureGrid(size_t w, size_t h, const TileData *t);
TileEffectData(const TileEffectData&); // no-copy
};
struct TileRepeatData
{
TileRepeatData();
TileRepeatData(const TileRepeatData& o);
const TexCoordBox& getTexCoords() const { return grid.getTexCoords(); }
const DynamicRenderGrid& getGrid() const { return grid; }
// written via refresh()
DynamicGPUBuffer vertexbuf;
float tu1, tv1, tu2, tv2;
DynamicRenderGrid grid; // need this here because a repeating tile WITH a grid-based tile effect is a special and annoying case to handle
// set by user
float texscaleX, texscaleY;
float texOffX, texOffY;
// pass ET & scale of owning tile
void refresh(const ElementTemplate& et, float scalex, float scaley);
// pass owning tile
void refresh(const TileData& t);
private:
TexCoordBox calcTexCoords(const TileData& t) const;
};
// POD and as compact as possible. Intended for rendering as quickly as possible.
@ -157,9 +173,9 @@ struct TileData
float rotation;
unsigned flags; // TileFlags
unsigned tag; // FIXME: make this int
const ElementTemplate *et; // never NULL. texture, texcoords, etc is here. // TODO: maybe replace with unsigned tilesetID? but that's an extra indirection or two during rendering...
TileEffectData *eff; // mostly NULL
TileRepeatData *rep;
const ElementTemplate *et; // never NULL. texture, texcoords, etc is here.
TileEffectData *eff; // mostly NULL. owned if flags & TILEFLAG_OWN_EFFDATA, otherwise shared
TileRepeatData *rep; // NULL in most cases, set if repeating. Always owned.
// helpers for external access
inline void setVisible(bool on) { if(on) flags &= ~TILEFLAG_HIDDEN; else flags |= TILEFLAG_HIDDEN; }
@ -168,6 +184,11 @@ struct TileData
TileRepeatData *setRepeatOn(float texscalex = 1, float texscaley = 1, float offx = 0, float offy = 0);
void setRepeatOff();
void refreshRepeat();
bool hasStandardTexcoords() const;
const TexCoordBox& getTexcoords() const;
const RenderGrid *getGrid() const;
};
class TileEffectStorage

View file

@ -64,8 +64,6 @@ void TileRender::onRender(const RenderState& rs) const
const bool renderExtras = renderBorders || RenderObject::renderCollisionShape;
const TileEffectData *prevEff = ((TileEffectData*)NULL)+1; // initial value is different from anything else
const RenderGrid *grid = NULL;
const DynamicGPUBuffer *lastVertexBuf = NULL;
for(size_t i = 0; i < storage.tiles.size(); ++i)
{
@ -114,12 +112,9 @@ void TileRender::onRender(const RenderState& rs) const
{
prevEff = eff;
BlendType blend = BLEND_DEFAULT;
alpha = rs.alpha;
grid = NULL;
if(eff)
{
grid = eff->grid;
alpha *= eff->alpha.x;
blend = eff->blend;
}
@ -136,7 +131,7 @@ void TileRender::onRender(const RenderState& rs) const
// Maps were designed with the bug present so we need to replicate it,
// otherwise things won't look correct.
unsigned effflag = tile.flags;
if(grid)
if(eff && eff->grid)
effflag &= ~TILEFLAG_FV;
float effrot = tile.rotation;
@ -166,37 +161,12 @@ void TileRender::onRender(const RenderState& rs) const
glScalef(sw, sh, 1);
const RenderGrid *grid = tile.getGrid();
if(!grid)
{
const DynamicGPUBuffer *vb = !(tile.flags & TILEFLAG_REPEAT)
? tile.et->vertexbuf
: &tile.rep->vertexbuf;
assert(vb);
if(vb != lastVertexBuf)
{
lastVertexBuf = vb;
vb->apply();
}
vb->DrawArrays(GL_TRIANGLE_FAN, 4);
}
else
{
rx.alpha = alpha;
grid = core->getDefaultQuadGrid();
rx.alpha = alpha;
grid->render(rx);
Vector upperLeftTextureCoordinates, lowerRightTextureCoordinates;
if(tile.flags & TILEFLAG_REPEAT)
{
upperLeftTextureCoordinates = Vector(tile.rep->tu1, tile.rep->tv1);
lowerRightTextureCoordinates = Vector(tile.rep->tu2, tile.rep->tv2);
}
else
{
upperLeftTextureCoordinates = Vector(et->tu1, et->tv1);
lowerRightTextureCoordinates = Vector(et->tu2, et->tv2);
}
grid->render(rx, upperLeftTextureCoordinates, lowerRightTextureCoordinates);
}
if(renderExtras)
{
@ -221,6 +191,7 @@ void TileRender::onRender(const RenderState& rs) const
glVertex2f(0,0);
glEnd();
// TODO: move this to the IBO
glLineWidth(2);
glBegin(GL_LINE_STRIP);
glVertex2f(0.5f, 0.5f);
@ -235,7 +206,8 @@ void TileRender::onRender(const RenderState& rs) const
glPopMatrix();
}
glBindBufferARB(GL_ARRAY_BUFFER, 0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
RenderObject::lastTextureApplied = lastTexId;
RenderObject::lastTextureRepeat = !!lastTexRepeat;

View file

@ -172,8 +172,7 @@ const ElementTemplate* Tileset::getAdjacent(size_t idx, int direction, bool wrap
ElementTemplate::~ElementTemplate()
{
if(ownsVertexbuf)
delete const_cast<DynamicGPUBuffer*>(vertexbuf);
delete grid;
}
void ElementTemplate::finalize()
@ -195,20 +194,16 @@ void ElementTemplate::finalize()
h = 64;
}
if(tu1 == 0 && tv1 == 0 && tu2 == 1 && tv2 == 1)
if(tc.isStandard())
{
// this avoids buffer switches later on
vertexbuf = core->getDefaultQuadVertexBuffer();
ownsVertexbuf = false;
delete grid;
grid = NULL;
}
else
{
DynamicGPUBuffer *vb = ownsVertexbuf
? const_cast<DynamicGPUBuffer*>(vertexbuf)
: new DynamicGPUBuffer(GPUBUF_STATIC | GPUBUF_VERTEXBUF);
vb->initQuadVertices(tu1, tv1, tu2, tv2);
vertexbuf = vb;
ownsVertexbuf = true;
if(!grid)
grid = new RenderGrid;
grid->init(2, 2, tc);
}
}

View file

@ -4,12 +4,14 @@
#include "Vector.h"
#include <vector>
#include "Texture.h"
#include "VertexBuffer.h"
#include "RenderGrid.h"
class DynamicRenderGrid;
class ElementTemplate
{
public:
ElementTemplate() { w=0; h=0; idx=-1; tu1=tv1=0; tu2=tv2=1; vertexbuf = NULL; ownsVertexbuf = false; }
ElementTemplate() { w=0; h=0; idx=-1; tc.setStandard(); grid = NULL; }
~ElementTemplate();
inline bool operator<(const ElementTemplate& o) const { return idx < o.idx; }
@ -18,11 +20,10 @@ public:
// lazily assigned when tex is loaded
CountedPtr<Texture> tex; // NULL if failed to load or not yet loaded
float w,h; // custom size if used, otherwise texture size
const DynamicGPUBuffer * vertexbuf; // never NULL
bool ownsVertexbuf;
RenderGrid *grid; // NULL if default, otherwise we own this
// fixed
float tu1, tu2, tv1, tv2; // texcoords
TexCoordBox tc;
size_t idx;
std::string gfx;

View file

@ -2,6 +2,7 @@
#include "RenderBase.h"
#include "Base.h"
#include <assert.h>
#include "Texture.h" // TexCoordBox
bool DynamicGPUBuffer::_HasARB = false;
@ -34,8 +35,9 @@ DynamicGPUBuffer::DynamicGPUBuffer(unsigned usage)
: _bufid(0)
, _binding((usage & GPUBUF_INDEXBUF) ? GL_ELEMENT_ARRAY_BUFFER_ARB : GL_ARRAY_BUFFER_ARB)
, _size(0)
, _cap(0)
, _h_cap(0)
, _h_data(NULL)
, _d_cap(0)
, _d_map(NULL)
, _usage(toGlUsage(usage))
, _datatype(BufDataType(-1))
@ -55,7 +57,7 @@ void* DynamicGPUBuffer::_allocBytes(size_t bytes)
void *p = realloc(_h_data, bytes);
if(p)
{
_cap = bytes;
_h_cap = bytes;
_h_data = p;
}
return p;
@ -63,10 +65,10 @@ void* DynamicGPUBuffer::_allocBytes(size_t bytes)
void* DynamicGPUBuffer::_ensureBytes(size_t bytes)
{
if(bytes < _cap)
if(bytes < _h_cap)
return _h_data;
size_t newsize = 2 * _size;
size_t newsize = 2 * _h_cap;
if(newsize < bytes)
newsize += bytes;
@ -81,9 +83,10 @@ void* DynamicGPUBuffer::beginWrite(BufDataType type, size_t newsize, unsigned ac
if(_HasARB)
{
glBindBufferARB(_binding, _ensureDBuf());
glBufferDataARB(_binding, newsize, NULL, _usage); // orphan buffer
if(!(access & GPUACCESS_HOSTCOPY))
{
_d_cap = newsize;
glBufferDataARB(_binding, newsize, NULL, _usage); // orphan buffer
void *p = glMapBufferARB(_binding, GL_WRITE_ONLY_ARB);
_d_map = p;
if(p)
@ -95,21 +98,41 @@ void* DynamicGPUBuffer::beginWrite(BufDataType type, size_t newsize, unsigned ac
}
bool DynamicGPUBuffer::commitWrite()
{
return _commitWrite(_size);
}
bool DynamicGPUBuffer::commitWrite(size_t used)
{
_size = used;
return _commitWrite(used);
}
bool DynamicGPUBuffer::_commitWrite(size_t used)
{
if(_HasARB)
{
if(_d_map)
{
assert(used <= _d_cap);
_d_map = NULL;
return glUnmapBufferARB(_binding); // can fail
}
// otherwise, the prev. call to glMapBufferARB failed (or GPUACCESS_NOMAP was set).
// otherwise, the prev. call to glMapBufferARB failed (or GPUACCESS_HOSTCOPY was set).
// -> didn't map, but wrote to host memory. upload it.
assert(_h_data);
glBufferSubDataARB(_binding, 0, _size, _h_data);
assert(used <= _h_cap);
if(used <= _d_cap)
glBufferSubDataARB(_binding, 0, used, _h_data); // update existing buffer
else
{
_d_cap = used;
glBufferDataARB(_binding, used, _h_data, _usage); // alloc new buffer
}
}
// else nothing to do
assert(used <= _h_cap);
return true;
}
@ -144,33 +167,40 @@ void DynamicGPUBuffer::apply(BufDataType usetype) const
if(!usetype)
usetype = _datatype;
const unsigned bufid = this->_bufid;
void *p;
if(_HasARB)
if(bufid)
{
unsigned bufid = this->_bufid;
assert(bufid != s_lastVertexBuffer); // check that it's no redundant state change
if(bufid == s_lastVertexBuffer && usetype == s_lastDataType)
return;
p = NULL;
s_lastVertexBuffer = bufid;
glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid);
//if(bufid != s_lastVertexBuffer)
// glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid);
}
else
{
p = (void*)this->_h_data;
assert(p != s_lastHostPtr); // check that it's no redundant state change
if(p == s_lastHostPtr && usetype == s_lastDataType) // don't need to check for datatype since that's const for the buffer with that ptr
return;
//assert(p != s_lastHostPtr); // check that it's no redundant state change
//if(p == s_lastHostPtr && usetype == s_lastDataType) // don't need to check for datatype since that's const for the buffer with that ptr
// return;
}
s_lastDataType = usetype;
assert(bufid || p);
assert((ty & 0xf) < Countof(s_gltype));
const unsigned gltype = s_gltype[usetype & 0xf];
const unsigned scalars = (usetype >> 4) & 0xf;
const unsigned stride = (usetype >> 8) & 0xff;
const unsigned tcoffset = (usetype >> 16) & 0xff;
const unsigned coloroffset = usetype >> 24;
glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid);
//if(bufid == s_lastVertexBuffer && usetype == s_lastDataType && p == s_lastHostPtr)
// return;
s_lastDataType = usetype;
s_lastVertexBuffer = bufid;
unsigned u = usetype; // always want unsigned shifts
assert((u & 0xf) < Countof(s_gltype));
const unsigned gltype = s_gltype[u & 0xf];
const unsigned scalars = (u >> 4u) & 0xf;
const unsigned stride = (u >> 8u) & 0xff;
const unsigned tcoffset = (u >> 16u) & 0xff;
const unsigned coloroffset = u >> 24u;
// vertex and texcoords are always enabled
glVertexPointer(scalars, gltype, stride, p);
@ -225,38 +255,107 @@ void DynamicGPUBuffer::dropBuffer()
_size = 0;
}
void DynamicGPUBuffer::DrawArrays(unsigned glmode, size_t n, size_t first)
static unsigned getBoundBuffer(unsigned target)
{
glDrawArrays(glmode, first, n);
int id = 0;
glGetIntegerv(target, &id);
return id;
}
void DynamicGPUBuffer::drawElements(unsigned glmode, size_t n, size_t first)
void DynamicGPUBuffer::drawElements(unsigned glmode, size_t n, size_t first) const
{
assert(_binding == GL_ELEMENT_ARRAY_BUFFER_ARB);
assert(s_gltype[_datatype & 0xf] == GL_SHORT);
assert(getBoundBuffer(GL_ARRAY_BUFFER_BINDING)); // FIXME: this assert is wrong if indices are on the host
if(s_lastIndexBuffer != _bufid)
{
s_lastIndexBuffer = _bufid;
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, _bufid);
}
unsigned id = _bufid;
glDrawElements(glmode, n, GL_SHORT, NULL);
//if(s_lastIndexBuffer != id)
//{
// s_lastIndexBuffer = id;
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, id);
//}
void *p = id ? NULL : _h_data;
assert(p || id);
glDrawElements(glmode, n, GL_UNSIGNED_SHORT, p);
}
void DynamicGPUBuffer::initQuadVertices(float tu1, float tv1, float tu2, float tv2)
void DynamicGPUBuffer::initQuadVertices(const TexCoordBox& tc, unsigned access)
{
do
{
float *p = (float*)beginWrite(GPUBUFTYPE_VEC2_TC, (4*4) * sizeof(float), GPUACCESS_DEFAULT);
float *p = (float*)beginWrite(GPUBUFTYPE_VEC2_TC, (4*4) * sizeof(float), access);
*p++ = -0.5f; *p++ = +0.5f; // xy
*p++ = tu1; *p++ = 1.0f-tv1; // uv
*p++ = tc.u1; *p++ = tc.v1; // uv
*p++ = +0.5f; *p++ = +0.5f; // xy
*p++ = tu2; *p++ = 1.0f-tv1; // uv
*p++ = tc.u2; *p++ = tc.v1; // uv
*p++ = +0.5f; *p++ = -0.5f; // xy
*p++ = tu2; *p++ = 1.0f-tv2; // uv
*p++ = tc.u2; *p++ = tc.v2; // uv
*p++ = -0.5f; *p++ = -0.5f; // xy
*p++ = tu1; *p++ = 1.0f-tv2; // uv
*p++ = tc.u1; *p++ = tc.v2; // uv
}
while(!commitWrite());
}
// 0---1---2---3
// | | | |
// 4---5---6---7
// | | | |
// 8---9---10--11
// This is a 4x3 grid
// Which is 3*2 = 6 quads
// That's 12 triangles
// Each triangle is 3 indices, so we get 36 indices in total
size_t DynamicGPUBuffer::initGridIndices_Triangles(size_t w, size_t h, bool invert, unsigned access)
{
assert(w * h < 0xffff);
const size_t quadsx = w - 1;
const size_t quadsy = h - 1;
const size_t quads = quadsx * quadsy;
do
{
unsigned short *p = (unsigned short*)beginWrite(GPUBUFTYPE_U16, 6*quads * sizeof(short), access);
if(!invert)
{
// top to bottom
for(size_t y = 0; y < quadsy; ++y)
{
size_t i = y * w;
for(size_t x = 0; x < quadsx; ++x)
{
*p++ = (unsigned short)(i); // 0
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w); // 4
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w + 1); // 5
*p++ = (unsigned short)(i + w); // 4
}
}
}
else
{
// bottom to top
for(size_t y = quadsy; y --> 0; )
{
size_t i = y * w;
for(size_t x = 0; x < quadsx; ++x)
{
*p++ = (unsigned short)(i); // 0
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w); // 4
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w + 1); // 5
*p++ = (unsigned short)(i + w); // 4
}
}
}
}
while(!commitWrite());
return quads * 6; // each quad is 2 triangles x 3 verts
}

View file

@ -3,6 +3,8 @@
#include <stdlib.h> // size_t
struct TexCoordBox;
enum BufUsage
{
// usage
@ -43,9 +45,8 @@ enum AccessFlags
class DynamicGPUBuffer
{
friend class BufMapW;
public:
void StaticInit();
static void StaticInit();
DynamicGPUBuffer(unsigned usage);
~DynamicGPUBuffer();
void dropBuffer();
@ -54,37 +55,51 @@ public:
// beginWrite(), then write exactly newsize bytes, then commit
void *beginWrite(BufDataType type, size_t newsize, unsigned access); // AccessFlags
bool commitWrite();
bool commitWrite(); // used same size as passed to beginWrite()
bool commitWrite(size_t used); // explicitly specify used size (may be less than initially requested)
void upload(BufDataType type, const void *data, size_t size);
static void DrawArrays(unsigned glmode, size_t n, size_t first = 0); // uses last applied buffer for drawing
// uses own data for indexing and prev. applied buffer for the data to draw
void drawElements(unsigned glmode, size_t n, size_t first = 0);
void drawElements(unsigned glmode, size_t n, size_t first = 0) const;
void apply(BufDataType usetype = GPUBUFTYPE_NONE) const;
// Inteded for use with DrawArrays(4) and GL_TRIANGLE_FAN or GL_QUADS (both work)
void initQuadVertices(float tu1, float tu2, float tv1, float tv2);
void initQuadVertices(const TexCoordBox& tc, unsigned access);
// Init indices for drawing a grid, like this 4x3 grid:
// 0---1---2---3
// | | | |
// 4---5---6---7
// | | | |
// 8---9---10--11
// Returns the number of triangles to use with GL_TRIANGLES.
// Pass invert==true to draw from bottom to top.
size_t initGridIndices_Triangles(size_t w, size_t h, bool invert, unsigned access);
private:
void* _allocBytes(size_t bytes);
void* _ensureBytes(size_t bytes);
unsigned _ensureDBuf();
bool _commitWrite(size_t used);
unsigned _bufid;
unsigned _binding;
size_t _size;
size_t _cap;
size_t _h_cap;
void *_h_data;
size_t _d_cap;
void *_d_map;
const unsigned _usage;
BufDataType _datatype;
static bool _HasARB;
DynamicGPUBuffer(const DynamicGPUBuffer&); // no copy
DynamicGPUBuffer& operator=(const DynamicGPUBuffer&); // no assign
};