1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-05-11 03:23:50 +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()) if(editingBone && editingBone->getGrid())
{ {
RenderGrid *grid = editingBone->getGrid(); DynamicRenderGrid *grid = editingBone->getGrid();
Animation *a = editSprite->getCurrentAnimation(); Animation *a = editSprite->getCurrentAnimation();
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx); BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
@ -975,8 +975,8 @@ void AnimationEditor::editStripKey()
assert(bk->controlpoints.size() == interp->bsp.ctrlX() * interp->bsp.ctrlY()); assert(bk->controlpoints.size() == interp->bsp.ctrlX() * interp->bsp.ctrlY());
splinegrid = new SplineGrid; splinegrid = new SplineGrid;
RenderGrid *rgrid = splinegrid->resize(interp->bsp.ctrlX(), interp->bsp.ctrlY(), grid->width(), grid->height(), interp->bsp.degX(), interp->bsp.degY()); DynamicRenderGrid *rgrid = splinegrid->resize(interp->bsp.ctrlX(), interp->bsp.ctrlY(), grid->width(), grid->height(), interp->bsp.degX(), interp->bsp.degY());
rgrid->drawOrder = grid->drawOrder; rgrid->setDrawOrder(grid->getDrawOrder());
splinegrid->setTexture(editingBone->texture->name); splinegrid->setTexture(editingBone->texture->name);
splinegrid->setWidthHeight(editingBone->width, editingBone->height); splinegrid->setWidthHeight(editingBone->width, editingBone->height);
splinegrid->position = Vector(400, 300); splinegrid->position = Vector(400, 300);
@ -1604,7 +1604,7 @@ void AnimationEditor::applyBoneToSplineGrid()
Animation *a = editSprite->getCurrentAnimation(); Animation *a = editSprite->getCurrentAnimation();
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx); BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
assert(bk->controlpoints.size() == splinegrid->getSpline().ctrlX() * splinegrid->getSpline().ctrlY()); 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]); splinegrid->importControlPoints(&bk->controlpoints[0]);
} }
} }
@ -1616,7 +1616,7 @@ void AnimationEditor::applySplineGridToBone()
Animation *a = editSprite->getCurrentAnimation(); Animation *a = editSprite->getCurrentAnimation();
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx); BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
assert(bk->controlpoints.size() == splinegrid->getSpline().ctrlX() * splinegrid->getSpline().ctrlY()); 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]); splinegrid->exportControlPoints(&bk->controlpoints[0]);
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx); BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
interp->updateGridAndBone(*bk, editingBone); interp->updateGridAndBone(*bk, editingBone);

View file

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

View file

@ -243,7 +243,7 @@ void Element::setElementEffectByIndex(int eidx)
eff->wavyMin = bity; eff->wavyMin = bity;
eff->wavyMax = bity*1.2f; 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 g->gridType = GRID_UNDEFINED; // by default it's GRID_WAVY, but that would reset during update
setGridFromWavy(); setGridFromWavy();

View file

@ -124,13 +124,15 @@ void TileMgr::createTiles(const TileDef* defs, size_t n)
t->flags |= TILEFLAG_FH; t->flags |= TILEFLAG_FH;
if(d.fv) if(d.fv)
t->flags |= TILEFLAG_FV; t->flags |= TILEFLAG_FV;
if(d.repeat)
t->setRepeatOn(d.rsx, d.rsy);
t->rotation = d.rot; t->rotation = d.rot;
t->tag = d.tag; t->tag = d.tag;
t->scalex = d.sx; t->scalex = d.sx;
t->scaley = d.sy; 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.beforeScaleOffsetY = 0;
t.flags = GetTileFlags(ef); t.flags = GetTileFlags(ef);
t.tag = 0; t.tag = 0;
t.rep = NULL;
t.et = tileset.getByIdx(tilesetID); t.et = tileset.getByIdx(tilesetID);
assert(t.et); assert(t.et);
/* t.eff = */ tileEffects.assignEffect(t, effidx); /* t.eff = */ tileEffects.assignEffect(t, effidx);

View file

@ -563,11 +563,11 @@ void WorldMapRender::setVis(WorldMapTile *tile)
if (visMethod == VIS_VERTEX) 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) if(g)
{ {
g->gridType = GRID_UNDEFINED; g->gridType = GRID_UNDEFINED;
g->drawOrder = GRID_DRAW_WORLDMAP; g->setDrawOrder(GRID_DRAW_WORLDMAP);
tileDataToVis(tile, g->array2d()); tileDataToVis(tile, g->array2d());
} }
} }

View file

@ -112,7 +112,7 @@ public:
int screenWidth, screenHeight; int screenWidth, screenHeight;
int textureWidth, textureHeight; 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 // returns handle > 0 on success
int loadShaderFile(const char *vert, const char *frag); int loadShaderFile(const char *vert, const char *frag);

View file

@ -155,7 +155,9 @@ void Core::setup_opengl()
if(afterEffectManager) if(afterEffectManager)
afterEffectManager->updateDevice(); 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) 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) 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; window = NULL;
sound = 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_RENDERER));
debugLog((const char*)glGetString(GL_VERSION)); debugLog((const char*)glGetString(GL_VERSION));
DynamicGPUBuffer::StaticInit();
enumerateScreenModes(window->getDisplayIndex()); enumerateScreenModes(window->getDisplayIndex());
window->updateSize(); window->updateSize();
@ -1920,7 +1924,7 @@ void Core::shutdown()
frameBuffer.unloadDevice(); frameBuffer.unloadDevice();
debugLog("OK"); debugLog("OK");
defaultQuadVertexBuf.dropBuffer(); defaultQuadGrid.dropBuffers();
debugLog("Shutdown Graphics Library..."); debugLog("Shutdown Graphics Library...");
shutdownGraphicsLibrary(); shutdownGraphicsLibrary();

View file

@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "TextureMgr.h" #include "TextureMgr.h"
#include "VertexBuffer.h" #include "VertexBuffer.h"
#include "DarkLayer.h" #include "DarkLayer.h"
#include "RenderGrid.h"
#include "GameKeys.h" #include "GameKeys.h"
class ParticleEffect; class ParticleEffect;
@ -413,7 +413,7 @@ public:
TextureMgr texmgr; TextureMgr texmgr;
inline const DynamicGPUBuffer *getDefaultQuadVertexBuffer() const { return &defaultQuadVertexBuf; } inline const RenderGrid *getDefaultQuadGrid() const { return &defaultQuadGrid; }
protected: protected:
@ -492,7 +492,7 @@ protected:
virtual void updateActionButtons(); virtual void updateActionButtons();
void clearActionButtons(); void clearActionButtons();
DynamicGPUBuffer defaultQuadVertexBuf; RenderGrid defaultQuadGrid;
public: public:
// inclusive! // 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 #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) if(g)
g->setSegs(dgox, dgoy, dgmx, dgmy, dgtm, dgo); g->setSegs(dgox, dgoy, dgmx, dgmy, dgtm, dgo);
return g; return g;
} }
RenderGrid *Quad::createGrid(int xd, int yd) DynamicRenderGrid *Quad::createGrid(int xd, int yd)
{ {
delete grid; delete grid;
return (grid = xd && yd grid = NULL;
? new RenderGrid(xd, yd) if(xd && yd)
: NULL); {
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) void Quad::setDrawGridAlpha(size_t x, size_t y, float alpha)
@ -208,7 +217,7 @@ void Quad::renderGrid(const RenderState& rs) const
glPushMatrix(); glPushMatrix();
glScalef(width, height, 1); glScalef(width, height, 1);
grid->render(rx, upperLeftTextureCoordinates, lowerRightTextureCoordinates); grid->render(rx);
// debug points // debug points
if (RenderObject::renderCollisionShape) if (RenderObject::renderCollisionShape)

View file

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

View file

@ -41,157 +41,87 @@ void RenderGrid::ResetWithAlpha(Vector* dst, size_t w, size_t h, float alpha)
} }
RenderGrid::RenderGrid()
RenderGrid::RenderGrid(size_t w, size_t h) : indexbuf(GPUBUF_STATIC | GPUBUF_INDEXBUF), vbo(GPUBUF_DYNAMIC | GPUBUF_VERTEXBUF), trisToDraw(0)
: gridTimer(0) , needVBOUpdate(false), drawOrder(GRID_DRAW_DEFAULT)
, drawGridOffsetX(0), drawGridOffsetY(0), drawGridModX(0), drawGridModY(0), drawGridTimeMultiplier(0)
, drawGridOut(false), gridType(GRID_WAVY), drawOrder(GRID_DRAW_DEFAULT)
{ {
init(w, h); tc.setStandard();
} }
RenderGrid::~RenderGrid() 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); assert(w > 1 && h > 1);
grid.init(w, h); grid.init(w, h);
setDrawOrder((GridDrawOrder)drawOrder, true);
this->tc = tc;
reset(); reset();
Vector *dg = grid.data(); Vector *dg = grid.data();
for(size_t i = 0; i < grid.linearsize(); ++i) for(size_t i = 0; i < grid.linearsize(); ++i)
dg[i].z = 1.0f; dg[i].z = 1.0f;
updateVBO();
} }
void RenderGrid::reset() void RenderGrid::reset()
{ {
ResetGrid(grid.data(), grid.width(), grid.height()); ResetGrid(grid.data(), grid.width(), grid.height());
needVBOUpdate = true;
} }
void RenderGrid::resetWithAlpha(float a) void RenderGrid::resetWithAlpha(float a)
{ {
ResetWithAlpha(grid.data(), grid.width(), grid.height(), a); ResetWithAlpha(grid.data(), grid.width(), grid.height(), a);
} needVBOUpdate = true;
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;
}
}
}
} }
void RenderGrid::setAlpha(size_t x, size_t y, float a) void RenderGrid::setAlpha(size_t x, size_t y, float a)
{ {
if (x < grid.width() && y < grid.height()) if (x < grid.width() && y < grid.height())
grid(x, y).z = a; 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; if(!force && drawOrder == ord)
drawGridOffsetY = dgoy; return;
drawGridModX = dgmx; drawOrder = ord;
drawGridModY = dgmy; trisToDraw = indexbuf.initGridIndices_Triangles(grid.width(), grid.height(), ord == GRID_DRAW_LRBT, GPUACCESS_HOSTCOPY);
drawGridTimeMultiplier = dgtm;
drawGridOut = dgo;
gridTimer = 0;
gridType = GRID_WAVY;
} }
void RenderGrid::setStripPoints(bool vert, const Vector* points, size_t n) void RenderGrid::setTexCoords(const TexCoordBox& tc)
{ {
reset(); this->tc = tc;
needVBOUpdate = true;
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;
}
}
}
} }
void RenderGrid::render(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const void RenderGrid::render(const RenderState& rs) const
{ {
switch(drawOrder) 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: case GRID_DRAW_WORLDMAP:
render_WithAlpha(rs, upperLeftTexCoords, lowerRightTexCoords); if(rs.alpha != 1 || rs.color != Vector(1,1,1))
break; {
render_WithAlpha(rs);
break;
}
// else fall through
default:
render_Indexed(rs);
} }
} }
@ -201,133 +131,74 @@ void RenderGrid::renderDebugPoints(const RenderState& rs) const
glPointSize(2); glPointSize(2);
glColor3f(1,0,0); glColor3f(1,0,0);
glBegin(GL_POINTS); vbo.apply();
const size_t W = grid.width(); glDrawArrays(GL_POINTS, 0, grid.linearsize());
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();
} }
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 percentX = tc.u2 - tc.u1;
const float percentY = lowerRightTexCoords.y - upperLeftTexCoords.y; const float percentY = tc.v2 - tc.v1;
const float baseX = upperLeftTexCoords.x; const float baseX = tc.u1;
const float baseY = upperLeftTexCoords.y; const float baseY = tc.v1;
const size_t NX = grid.width()-1; const size_t W = grid.width();
const size_t NY = grid.height()-1; const size_t H = grid.height();
// NOTE: These are used to avoid repeated expensive divide operations, // NOTE: These are used to avoid repeated expensive divide operations,
// but they may cause rounding error of around 1 part per million, // but they may cause rounding error of around 1 part per million,
// which could in theory cause minor graphical glitches with broken // which could in theory cause minor graphical glitches with broken
// OpenGL implementations. --achurch // OpenGL implementations. --achurch
const float incX = percentX / float(NX); const float incX = percentX / float(W-1);
const float incY = percentY / float(NY); const float incY = percentY / float(H-1);
glColor4f(rs.color.x, rs.color.y, rs.color.z, rs.alpha); do
glBegin(GL_QUADS);
float v0 = baseY;
float v1 = v0 + incY;
for (size_t y = 0; y < NY; y++, v0 = v1, v1 += incY)
{ {
float u0 = baseX; float *p = (float*)vbo.beginWrite(GPUBUFTYPE_VEC2_TC, W*H * (2*2) * sizeof(float), GPUACCESS_DEFAULT);
float u1 = u0 + incX;
const Vector *row0 = grid.row(y); float v = baseY;
const Vector *row1 = grid.row(y+1); for (size_t y = 0; y < H; y++, v += incY)
for (size_t x = 0; x < NX; x++, u0 = u1, u1 += incX) //for (size_t y = H; y --> 0; v += incY)
{ {
const Vector dg00 = row0[x]; float u = baseX;
const Vector dg01 = row1[x]; const Vector *row = grid.row(y);
const Vector dg10 = row0[x+1]; for (size_t x = 0; x < W; x++, u += incX)
const Vector dg11 = row1[x+1]; {
*p++ = row->x;
glTexCoord2f(u0, v0); *p++ = row->y;
glVertex2f(dg00.x, dg00.y); ++row;
*p++ = u;
glTexCoord2f(u0, v1); *p++ = v;
glVertex2f(dg01.x, dg01.y); }
glTexCoord2f(u1, v1);
glVertex2f(dg11.x, dg11.y);
glTexCoord2f(u1, v0);
glVertex2f(dg10.x, dg10.y);
} }
} }
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; (void)rs;
const float percentY = upperLeftTexCoords.y - lowerRightTexCoords.y; // 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
const float baseX = upperLeftTexCoords.x; assert(drawOrder != GRID_DRAW_WORLDMAP || (rs.color == Vector(1,1,1) && rs.alpha == 1));
const float baseY = lowerRightTexCoords.y; vbo.apply();
indexbuf.drawElements(GL_TRIANGLES, trisToDraw);
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 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 percentX = fabsf(tc.u2 - tc.u1);
const float percentY = fabsf(upperLeftTexCoords.y - lowerRightTexCoords.y); const float percentY = fabsf(tc.v1 - tc.v2);
const float baseX = const float baseX =
(lowerRightTexCoords.x < upperLeftTexCoords.x) (tc.u2 < tc.u1)
? lowerRightTexCoords.x : upperLeftTexCoords.x; ? tc.u2 : tc.u1;
const float baseY = const float baseY =
(lowerRightTexCoords.y < upperLeftTexCoords.y) (tc.v2 < tc.v1)
? lowerRightTexCoords.y : upperLeftTexCoords.y; ? tc.v2 : tc.v1;
const size_t NX = grid.width()-1; const size_t NX = grid.width()-1;
const size_t NY = grid.height()-1; const size_t NY = grid.height()-1;
@ -381,3 +252,120 @@ void RenderGrid::render_WithAlpha(const RenderState& rs, const Vector& upperLeft
glEnd(); 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 "Vector.h"
#include "DataStructures.h" #include "DataStructures.h"
#include "VertexBuffer.h"
#include "Texture.h" // TexCoordBox
struct RenderState; struct RenderState;
@ -23,23 +25,26 @@ enum GridType
GRID_INTERP = 3, // quad is in grid mode GRID_INTERP = 3, // quad is in grid mode
}; };
// simple render grid, must be manually uploaded to GPU if changed
class RenderGrid class RenderGrid
{ {
public: public:
RenderGrid(size_t w, size_t h); RenderGrid();
~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 reset();
void resetWithAlpha(float a); void resetWithAlpha(float a);
void update(float dt); void render(const RenderState& rs) const;
void render(const RenderState& rs, const Vector& upperLeftTexCoords, const Vector& lowerRightTexCoords) const;
void renderDebugPoints(const RenderState& rs) const; void renderDebugPoints(const RenderState& rs) const;
void setAlpha(size_t x, size_t y, float a); 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 setDrawOrder(GridDrawOrder ord, bool force = false);
void setStripPoints(bool vert, const Vector *points, size_t n); inline GridDrawOrder getDrawOrder() const { return GridDrawOrder(drawOrder); }
void setFromWavy(const float *wavy, size_t len, float width); 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 width() const { return grid.width(); }
size_t height() const { return grid.height(); } size_t height() const { return grid.height(); }
size_t linearsize() const { return grid.linearsize(); } size_t linearsize() const { return grid.linearsize(); }
@ -47,11 +52,39 @@ public:
Vector *data() { return grid.data(); } Vector *data() { return grid.data(); }
Array2d<Vector>& array2d() { return grid; } Array2d<Vector>& array2d() { return grid; }
const Array2d<Vector>& array2d() const { 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); static void ResetWithAlpha(Vector* dst, size_t w, size_t h, float alpha);
protected: protected:
DynamicGPUBuffer indexbuf, vbo;
size_t trisToDraw;
Array2d<Vector> grid; 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 gridTimer;
float drawGridOffsetX; float drawGridOffsetX;
float drawGridOffsetY; float drawGridOffsetY;
@ -61,11 +94,6 @@ protected:
bool drawGridOut; bool drawGridOut;
public: public:
unsigned char gridType; // unsigned char to save space 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 #endif

View file

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

View file

@ -33,7 +33,7 @@ public:
~SplineGrid(); ~SplineGrid();
// # of control points on each axis // # 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 recalc();
void exportControlPoints(Vector *controlpoints); void exportControlPoints(Vector *controlpoints);
void importControlPoints(const 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 "GLLoad.h"
#include "stb_image_resize.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() Texture::Texture()
{ {
@ -88,13 +101,13 @@ static const GLenum repeatLUT[] = { GL_CLAMP_TO_EDGE, GL_REPEAT };
void Texture::apply(bool repeat) const void Texture::apply(bool repeat) const
{ {
glBindTexture(GL_TEXTURE_2D, gltexid); glBindTexture(GL_TEXTURE_2D, gltexid);
if(repeat != _repeating) /*if(repeat != _repeating)
{ {
_repeating = repeat; _repeating = repeat;
GLenum rep = repeatLUT[repeat]; GLenum rep = repeatLUT[repeat];
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, rep); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, rep);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, rep); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, rep);
} }*/
} }
struct GlTexFormat struct GlTexFormat
@ -127,8 +140,8 @@ bool Texture::upload(const ImageData& img, bool mipmap)
if(!gltexid) if(!gltexid)
glGenTextures(1, &gltexid); glGenTextures(1, &gltexid);
glBindTexture(GL_TEXTURE_2D, 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_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT);
_repeating = false; _repeating = false;
const GlTexFormat& f = formatLUT[img.channels - 1]; 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 <string>
#include "Refcounted.h" #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 enum TextureLoadResult
{ {

View file

@ -146,12 +146,11 @@ static void dropEffect(TileData& t)
static void dropRepeat(TileData& t) static void dropRepeat(TileData& t)
{ {
if(t.flags & TILEFLAG_OWN_REPEAT) if(t.rep)
{ {
delete t.rep; delete t.rep;
t.flags &= ~TILEFLAG_OWN_REPEAT; t.rep = NULL;
} }
t.rep = NULL;
} }
static void dropAll(TileData& t) 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 for(size_t i = ret; i < N; ++i) // loop only over newly added tiles
{ {
TileData& t = tiles[i]; 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) if((t.flags & TILEFLAG_OWN_EFFDATA) && t.eff)
{ {
int efx = t.eff->efxidx; 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; t.flags &= TILEFLAG_OWN_EFFDATA;
effstore.assignEffect(t, efx); // recreate effect properly effstore.assignEffect(t, efx); // recreate effect properly
} }
if((t.flags & TILEFLAG_OWN_REPEAT) && t.rep)
{
t.rep = new TileRepeatData(*t.rep);
}
} }
refreshAll(); refreshAll();
@ -282,9 +281,11 @@ void TileStorage::clearSelection()
tiles[i].flags &= ~TILEFLAG_SELECTED; tiles[i].flags &= ~TILEFLAG_SELECTED;
} }
TileEffectData::TileEffectData(const TileEffectConfig& cfg) TileEffectData::TileEffectData(const TileEffectConfig& cfg, const TileData *t)
: efxtype(cfg.type), efxidx(cfg.index) : efxtype(cfg.type), efxidx(cfg.index)
, grid(NULL), alpha(1), blend(BLEND_DEFAULT) , grid(NULL), alpha(1), blend(BLEND_DEFAULT)
, ownGrid(false), shared(false)
{ {
switch(cfg.type) switch(cfg.type)
{ {
@ -294,14 +295,14 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
case EFX_WAVY: 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.wavy.resize(cfg.u.wavy.segsy, 0.0f);
wavy.flip = cfg.u.wavy.flip; wavy.flip = cfg.u.wavy.flip;
wavy.min = bity; wavy.min = bity;
wavy.max = bity*1.2f; wavy.max = bity*1.2f;
RenderGrid *g = new RenderGrid(2, cfg.u.wavy.segsy); DynamicRenderGrid *g = _ensureGrid(2, cfg.u.wavy.segsy, t);
grid = g;
g->gridType = GRID_UNDEFINED; // we do the grid update manually g->gridType = GRID_UNDEFINED; // we do the grid update manually
wavy.angleOffset = 0; wavy.angleOffset = 0;
@ -317,8 +318,7 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
case EFX_SEGS: case EFX_SEGS:
{ {
RenderGrid *g = new RenderGrid(cfg.u.segs.x, cfg.u.segs.y); DynamicRenderGrid *g = _ensureGrid(cfg.u.segs.x, cfg.u.segs.y, t);
grid = g;
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); 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; break;
@ -335,7 +335,43 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
TileEffectData::~TileEffectData() 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) void TileEffectData::Wavy::update(float dt)
@ -463,19 +499,28 @@ void TileEffectStorage::assignEffect(TileData& t, int index) const
return; return;
size_t idx = size_t(index); 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) if(configs[idx].type == EFX_NONE)
return; return;
t.eff = new TileEffectData(configs[idx]); t.eff = new TileEffectData(configs[idx], &t);
t.flags |= TILEFLAG_OWN_EFFDATA; t.flags |= TILEFLAG_OWN_EFFDATA;
} }
else if(idx < prepared.size() && prepared[idx])
{
t.eff = prepared[idx];
}
} }
void TileEffectStorage::update(float dt) void TileEffectStorage::update(float dt)
@ -513,7 +558,10 @@ void TileEffectStorage::finalize()
// segs and alpha are independent of the tile they are applied to, // segs and alpha are independent of the tile they are applied to,
// so we can create shared instances of the effect. // so we can create shared instances of the effect.
if(c.type == EFX_SEGS || c.type == EFX_ALPHA) 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() 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; float tw, th;
if(et.tex) if(et.tex)
{ {
@ -550,43 +607,118 @@ void TileRepeatData::refresh(const ElementTemplate& et, float scalex, float scal
th = et.h; th = et.h;
} }
const float tu1 = texOffX; TexCoordBox tc;
const float tv1 = texOffY; tc.u1 = texOffX;
const float tu2 = (et.w*scalex*texscaleX)/tw + texOffX; tc.v1 = texOffY;
const float tv2 = (et.h*scaley*texscaleY)/th + texOffY; tc.u2 = (et.w*t.scalex*texscaleX)/tw + texOffX;
tc.v2 = (et.h*t.scaley*texscaleY)/th + texOffY;
this->tu1 = tu1; // HACK: partially repeated textures have a weird Y axis. assuming a repeat factor of 0.5,
this->tv1 = tv1; // instead of texcoords from 0 -> 0.4 everything is biased towards the opposite end, ie. 0.6 -> 1.
this->tu2 = tu2; // This is especially true for partial repeats, we always need to bias towards the other end.
this->tv2 = tv2; // 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) TileRepeatData* TileData::setRepeatOn(float texscalex, float texscaley, float offx, float offy)
{ {
if(rep && !(flags & TILEFLAG_OWN_REPEAT)) flags |= TILEFLAG_REPEAT;
rep = NULL;
flags |= (TILEFLAG_OWN_REPEAT | TILEFLAG_REPEAT);
if(!rep) if(!rep)
rep = new TileRepeatData; rep = new TileRepeatData;
rep->texscaleX = texscalex; rep->texscaleX = texscalex;
rep->texscaleY = texscaley; rep->texscaleY = texscaley;
rep->texOffX = offx; rep->texOffX = offx;
rep->texOffY = offy; rep->texOffY = offy;
rep->refresh(*et, scalex, scaley); rep->refresh(*this);
// FIXME: if eff, link eff->grid to rep->grid
return rep; return rep;
} }
void TileData::setRepeatOff() void TileData::setRepeatOff()
{ {
flags &= ~TILEFLAG_REPEAT; 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() 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 "Vector.h"
#include "EngineEnums.h" #include "EngineEnums.h"
#include "VertexBuffer.h" #include "VertexBuffer.h"
#include "RenderGrid.h"
#include "Texture.h" // TexCoordBox
// A Tile is a very stripped down RenderObject that bypasses the default // A Tile is a very stripped down RenderObject that bypasses the default
// rendering pipeline for efficiency reasons. // rendering pipeline for efficiency reasons.
@ -33,14 +36,17 @@ Assumptions:
- Most tiles that exist are going to be rendered - Most tiles that exist are going to be rendered
- Only few tiles have an effect attached - Only few tiles have an effect attached
Gotaches: Gotchas:
- Keeping a pointer to a TileData is not safe. - 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 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 ElementTemplate;
class Texture; class Texture;
class RenderGrid; class DynamicRenderGrid;
class TileRender; class TileRender;
enum EFXType enum EFXType
@ -51,6 +57,8 @@ enum EFXType
EFX_WAVY EFX_WAVY
}; };
struct TileData;
// static configuration for one effect type. POD. // static configuration for one effect type. POD.
struct TileEffectConfig struct TileEffectConfig
{ {
@ -81,6 +89,9 @@ public:
BlendType blend; BlendType blend;
} alpha; } alpha;
} u; } u;
bool needsOwnInstanceForTile(const TileData& t) const;
}; };
enum TileFlags enum TileFlags
@ -96,24 +107,23 @@ enum TileFlags
TILEFLAG_HIDDEN = 0x80, // don't render tile TILEFLAG_HIDDEN = 0x80, // don't render tile
TILEFLAG_SELECTED = 0x100, // ephemeral: selected in editor 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_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 = 0x400, // flipped vertically
TILEFLAG_FV = 0x800, // flipped vertically
}; };
struct TileData;
struct TileEffectData 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(); ~TileEffectData();
void update(float dt, const TileData *t); // optional t needed for EFX_WAVY 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); void doInteraction(const TileData& t, const Vector& pos, const Vector& vel, float mult, float touchWidth);
const EFXType efxtype; const EFXType efxtype;
const unsigned efxidx; // index of TileEffect 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; InterpolatedVector alpha;
BlendType blend; 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 struct Wavy
{ {
@ -128,23 +138,29 @@ struct TileEffectData
Wavy wavy; Wavy wavy;
private: private:
DynamicRenderGrid *_ensureGrid(size_t w, size_t h, const TileData *t);
TileEffectData(const TileEffectData&); // no-copy TileEffectData(const TileEffectData&); // no-copy
}; };
struct TileRepeatData struct TileRepeatData
{ {
TileRepeatData(); TileRepeatData();
TileRepeatData(const TileRepeatData& o);
const TexCoordBox& getTexCoords() const { return grid.getTexCoords(); }
const DynamicRenderGrid& getGrid() const { return grid; }
// written via refresh() // written via refresh()
DynamicGPUBuffer vertexbuf; DynamicRenderGrid grid; // need this here because a repeating tile WITH a grid-based tile effect is a special and annoying case to handle
float tu1, tv1, tu2, tv2;
// set by user // set by user
float texscaleX, texscaleY; float texscaleX, texscaleY;
float texOffX, texOffY; float texOffX, texOffY;
// pass ET & scale of owning tile // pass owning tile
void refresh(const ElementTemplate& et, float scalex, float scaley); 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. // POD and as compact as possible. Intended for rendering as quickly as possible.
@ -157,9 +173,9 @@ struct TileData
float rotation; float rotation;
unsigned flags; // TileFlags unsigned flags; // TileFlags
unsigned tag; // FIXME: make this int 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... const ElementTemplate *et; // never NULL. texture, texcoords, etc is here.
TileEffectData *eff; // mostly NULL TileEffectData *eff; // mostly NULL. owned if flags & TILEFLAG_OWN_EFFDATA, otherwise shared
TileRepeatData *rep; TileRepeatData *rep; // NULL in most cases, set if repeating. Always owned.
// helpers for external access // helpers for external access
inline void setVisible(bool on) { if(on) flags &= ~TILEFLAG_HIDDEN; else flags |= TILEFLAG_HIDDEN; } 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); TileRepeatData *setRepeatOn(float texscalex = 1, float texscaley = 1, float offx = 0, float offy = 0);
void setRepeatOff(); void setRepeatOff();
void refreshRepeat(); void refreshRepeat();
bool hasStandardTexcoords() const;
const TexCoordBox& getTexcoords() const;
const RenderGrid *getGrid() const;
}; };
class TileEffectStorage class TileEffectStorage

View file

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

View file

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

View file

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

View file

@ -2,6 +2,7 @@
#include "RenderBase.h" #include "RenderBase.h"
#include "Base.h" #include "Base.h"
#include <assert.h> #include <assert.h>
#include "Texture.h" // TexCoordBox
bool DynamicGPUBuffer::_HasARB = false; bool DynamicGPUBuffer::_HasARB = false;
@ -34,8 +35,9 @@ DynamicGPUBuffer::DynamicGPUBuffer(unsigned usage)
: _bufid(0) : _bufid(0)
, _binding((usage & GPUBUF_INDEXBUF) ? GL_ELEMENT_ARRAY_BUFFER_ARB : GL_ARRAY_BUFFER_ARB) , _binding((usage & GPUBUF_INDEXBUF) ? GL_ELEMENT_ARRAY_BUFFER_ARB : GL_ARRAY_BUFFER_ARB)
, _size(0) , _size(0)
, _cap(0) , _h_cap(0)
, _h_data(NULL) , _h_data(NULL)
, _d_cap(0)
, _d_map(NULL) , _d_map(NULL)
, _usage(toGlUsage(usage)) , _usage(toGlUsage(usage))
, _datatype(BufDataType(-1)) , _datatype(BufDataType(-1))
@ -55,7 +57,7 @@ void* DynamicGPUBuffer::_allocBytes(size_t bytes)
void *p = realloc(_h_data, bytes); void *p = realloc(_h_data, bytes);
if(p) if(p)
{ {
_cap = bytes; _h_cap = bytes;
_h_data = p; _h_data = p;
} }
return p; return p;
@ -63,10 +65,10 @@ void* DynamicGPUBuffer::_allocBytes(size_t bytes)
void* DynamicGPUBuffer::_ensureBytes(size_t bytes) void* DynamicGPUBuffer::_ensureBytes(size_t bytes)
{ {
if(bytes < _cap) if(bytes < _h_cap)
return _h_data; return _h_data;
size_t newsize = 2 * _size; size_t newsize = 2 * _h_cap;
if(newsize < bytes) if(newsize < bytes)
newsize += bytes; newsize += bytes;
@ -81,9 +83,10 @@ void* DynamicGPUBuffer::beginWrite(BufDataType type, size_t newsize, unsigned ac
if(_HasARB) if(_HasARB)
{ {
glBindBufferARB(_binding, _ensureDBuf()); glBindBufferARB(_binding, _ensureDBuf());
glBufferDataARB(_binding, newsize, NULL, _usage); // orphan buffer
if(!(access & GPUACCESS_HOSTCOPY)) if(!(access & GPUACCESS_HOSTCOPY))
{ {
_d_cap = newsize;
glBufferDataARB(_binding, newsize, NULL, _usage); // orphan buffer
void *p = glMapBufferARB(_binding, GL_WRITE_ONLY_ARB); void *p = glMapBufferARB(_binding, GL_WRITE_ONLY_ARB);
_d_map = p; _d_map = p;
if(p) if(p)
@ -95,21 +98,41 @@ void* DynamicGPUBuffer::beginWrite(BufDataType type, size_t newsize, unsigned ac
} }
bool DynamicGPUBuffer::commitWrite() 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(_HasARB)
{ {
if(_d_map) if(_d_map)
{ {
assert(used <= _d_cap);
_d_map = NULL; _d_map = NULL;
return glUnmapBufferARB(_binding); // can fail 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. // -> didn't map, but wrote to host memory. upload it.
assert(_h_data); 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 // else nothing to do
assert(used <= _h_cap);
return true; return true;
} }
@ -144,33 +167,40 @@ void DynamicGPUBuffer::apply(BufDataType usetype) const
if(!usetype) if(!usetype)
usetype = _datatype; usetype = _datatype;
const unsigned bufid = this->_bufid;
void *p; 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; p = NULL;
s_lastVertexBuffer = bufid; //if(bufid != s_lastVertexBuffer)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid); // glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid);
} }
else else
{ {
p = (void*)this->_h_data; p = (void*)this->_h_data;
assert(p != s_lastHostPtr); // check that it's no redundant state change //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 //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; // return;
} }
s_lastDataType = usetype; assert(bufid || p);
assert((ty & 0xf) < Countof(s_gltype)); glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid);
const unsigned gltype = s_gltype[usetype & 0xf];
const unsigned scalars = (usetype >> 4) & 0xf; //if(bufid == s_lastVertexBuffer && usetype == s_lastDataType && p == s_lastHostPtr)
const unsigned stride = (usetype >> 8) & 0xff; // return;
const unsigned tcoffset = (usetype >> 16) & 0xff;
const unsigned coloroffset = usetype >> 24; 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 // vertex and texcoords are always enabled
glVertexPointer(scalars, gltype, stride, p); glVertexPointer(scalars, gltype, stride, p);
@ -225,38 +255,107 @@ void DynamicGPUBuffer::dropBuffer()
_size = 0; _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(_binding == GL_ELEMENT_ARRAY_BUFFER_ARB);
assert(s_gltype[_datatype & 0xf] == GL_SHORT); 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) unsigned id = _bufid;
{
s_lastIndexBuffer = _bufid;
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, _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 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++ = -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++ = +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++ = +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++ = -0.5f; *p++ = -0.5f; // xy
*p++ = tu1; *p++ = 1.0f-tv2; // uv *p++ = tc.u1; *p++ = tc.v2; // uv
} }
while(!commitWrite()); 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 #include <stdlib.h> // size_t
struct TexCoordBox;
enum BufUsage enum BufUsage
{ {
// usage // usage
@ -43,9 +45,8 @@ enum AccessFlags
class DynamicGPUBuffer class DynamicGPUBuffer
{ {
friend class BufMapW;
public: public:
void StaticInit(); static void StaticInit();
DynamicGPUBuffer(unsigned usage); DynamicGPUBuffer(unsigned usage);
~DynamicGPUBuffer(); ~DynamicGPUBuffer();
void dropBuffer(); void dropBuffer();
@ -54,37 +55,51 @@ public:
// beginWrite(), then write exactly newsize bytes, then commit // beginWrite(), then write exactly newsize bytes, then commit
void *beginWrite(BufDataType type, size_t newsize, unsigned access); // AccessFlags 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); 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 // 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; void apply(BufDataType usetype = GPUBUFTYPE_NONE) const;
// Inteded for use with DrawArrays(4) and GL_TRIANGLE_FAN or GL_QUADS (both work) // 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: private:
void* _allocBytes(size_t bytes); void* _allocBytes(size_t bytes);
void* _ensureBytes(size_t bytes); void* _ensureBytes(size_t bytes);
unsigned _ensureDBuf(); unsigned _ensureDBuf();
bool _commitWrite(size_t used);
unsigned _bufid; unsigned _bufid;
unsigned _binding; unsigned _binding;
size_t _size; size_t _size;
size_t _cap; size_t _h_cap;
void *_h_data; void *_h_data;
size_t _d_cap;
void *_d_map; void *_d_map;
const unsigned _usage; const unsigned _usage;
BufDataType _datatype; BufDataType _datatype;
static bool _HasARB; static bool _HasARB;
DynamicGPUBuffer(const DynamicGPUBuffer&); // no copy
DynamicGPUBuffer& operator=(const DynamicGPUBuffer&); // no assign
}; };