diff --git a/Aquaria/Game.cpp b/Aquaria/Game.cpp index f3254b1..952bd0f 100644 --- a/Aquaria/Game.cpp +++ b/Aquaria/Game.cpp @@ -18,6 +18,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "Game.h" +#include #include "../BBGE/Gradient.h" #include "../BBGE/AfterEffect.h" #include "../BBGE/MathFunctions.h" @@ -31,7 +34,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "ReadXML.h" #include "RenderBase.h" -#include "Game.h" #include "GridRender.h" #include "WaterSurfaceRender.h" #include "ScriptedEntity.h" @@ -328,6 +330,8 @@ void Game::addObsRow(unsigned tx, unsigned ty, unsigned len) void Game::clearObsRows() { obsRows.clear(); + mapGridW = 0; + mapGridH = 0; } void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim) @@ -539,6 +543,8 @@ void Game::reconstructEntityGrid() Entity *e = *i; e->fillGrid(); } + + updateGridRender(OT_INVISIBLEENT); } void Game::reconstructGrid(bool force) @@ -574,6 +580,9 @@ void Game::reconstructGrid(bool force) } trimGrid(); + + // This does intentionally not update black. + updateGridRender(OT_MASK_NOTBLACK); } void Game::trimGrid() @@ -1887,25 +1896,36 @@ void Game::setMusicToPlay(const std::string &m) stringToLower(musicToPlay); } +TileVector Game::computeMapSizeFromObs() const +{ + TileVector ret; + const size_t N = obsRows.size(); + for (size_t i = 0; i < N; i++) + { + const ObsRow *r = &obsRows[i]; + ret.x = std::max(ret.x, r->tx + r->len); + ret.y = std::max(ret.y, r->ty); + } + return ret; +} + void Game::findMaxCameraValues() { cameraMin.x = 20; cameraMin.y = 20; - cameraMax.x = -1; - cameraMax.y = -1; - for (size_t i = 0; i < obsRows.size(); i++) + if(obsRows.size()) { - ObsRow *r = &obsRows[i]; - TileVector t(r->tx + r->len, r->ty); - Vector v = t.worldVector(); - if (v.x > cameraMax.x) - { - cameraMax.x = v.x; - } - if (v.y > cameraMax.y) - { - cameraMax.y = v.y; - } + TileVector wh = computeMapSizeFromObs(); + mapGridW = wh.x; + mapGridH = wh.y; + cameraMax = wh.worldVector(); + } + else + { + mapGridW = 0; + mapGridH = 0; + cameraMax.x = -1; + cameraMax.y = -1; } } @@ -2679,6 +2699,16 @@ void Game::applyState() bindInput(); + // loadScene() calls reconstructGrid(), which requires these to exist + blackRender = new GridRender(OT_BLACK); + gridRender = new GridRender(OT_INVISIBLE); + gridRender2 = new GridRender(OT_HURT); + gridRender3 = new GridRender(OT_INVISIBLEIN); + edgeRender = new GridRender(OT_BLACKINVIS); + gridRenderEnt = new GridRender(OT_INVISIBLEENT); + gridRenderUser1 = new GridRender(OT_USER1); + gridRenderUser2 = new GridRender(OT_USER2); + if (verbose) debugLog("Loading Scene"); if(!loadScene(sceneToLoad)) { @@ -2686,6 +2716,8 @@ void Game::applyState() } if (verbose) debugLog("...Done"); + // ----------------- SCENE IS LOADED BELOW HERE ------------------- + dsq->continuity.worldMap.revealMap(sceneName); if (verbose) debugLog("Adding Avatar"); @@ -2703,37 +2735,31 @@ void Game::applyState() songLineRender = new SongLineRender(); addRenderObject(songLineRender, LR_HUD); - gridRender = new GridRender(OT_INVISIBLE); + gridRender->color = Vector(1, 0, 0); addRenderObject(gridRender, LR_DEBUG_TEXT); gridRender->alpha = 0; - gridRender2 = new GridRender(OT_HURT); gridRender2->color = Vector(1, 1, 0); addRenderObject(gridRender2, LR_DEBUG_TEXT); gridRender2->alpha = 0; - gridRender3 = new GridRender(OT_INVISIBLEIN); gridRender3->color = Vector(1, 0.5f, 0); addRenderObject(gridRender3, LR_DEBUG_TEXT); gridRender3->alpha = 0; - edgeRender = new GridRender(OT_BLACKINVIS); edgeRender->color = Vector(0.3f, 0, 0.6f); addRenderObject(edgeRender, LR_DEBUG_TEXT); edgeRender->alpha = 0; - gridRenderEnt = new GridRender(OT_INVISIBLEENT); gridRenderEnt->color = Vector(0, 1, 0.5); addRenderObject(gridRenderEnt, LR_DEBUG_TEXT); gridRenderEnt->alpha = 0; - gridRenderUser1 = new GridRender(OT_USER1); addRenderObject(gridRenderUser1, LR_DEBUG_TEXT); gridRenderUser1->color = Vector(1, 0, 1); gridRenderUser1->alpha = 0; - gridRenderUser2 = new GridRender(OT_USER2); addRenderObject(gridRenderUser2, LR_DEBUG_TEXT); gridRenderUser2->color = Vector(1, 1, 1); gridRenderUser2->alpha = 0; @@ -2742,11 +2768,11 @@ void Game::applyState() //waterSurfaceRender->setRenderPass(-1); addRenderObject(waterSurfaceRender, LR_WATERSURFACE); - GridRender *blackRender = new GridRender(OT_BLACK); blackRender->color = Vector(0, 0, 0); //blackRender->alpha = 0; blackRender->setBlendType(BLEND_DISABLED); addRenderObject(blackRender, LR_ELEMENTS4); + blackRender->rebuildBuffers(this->obsRows); miniMapRender = new MiniMapRender; // position is set in minimaprender::onupdate @@ -3986,7 +4012,16 @@ void Game::toggleGridRender() float t = 0; float a = 0; if (gridRender->alpha == 0) + { a = 0.5f; + gridRender->rebuildBuffersIfNecessary(); + gridRender2->rebuildBuffersIfNecessary(); + gridRender3->rebuildBuffersIfNecessary(); + edgeRender->rebuildBuffersIfNecessary(); + gridRenderEnt->rebuildBuffersIfNecessary(); + gridRenderUser1->rebuildBuffersIfNecessary(); + gridRenderUser2->rebuildBuffersIfNecessary(); + } gridRender->alpha.interpolateTo(a, t); gridRender2->alpha.interpolateTo(a, t); @@ -3997,6 +4032,30 @@ void Game::toggleGridRender() gridRenderUser2->alpha.interpolateTo(a, t); } +static void checkgridrender(GridRender *gr, ObsType obs) +{ + if(gr->getObs() & obs) + gr->markForRebuild(); +} + +void Game::updateGridRender(ObsType obs) +{ + // These are usually not visible. Delay rebuild until they are actually shown. + checkgridrender(gridRender, obs); + checkgridrender(gridRender2, obs); + checkgridrender(gridRender3, obs); + checkgridrender(edgeRender, obs); + checkgridrender(gridRenderEnt, obs); + checkgridrender(gridRenderUser1, obs); + checkgridrender(gridRenderUser2, obs); + + // This is normally not necessary, because black is only changed by the editor. + // Keeping it here possibly for future mod compat. + // It's also always shown, so we can immediately rebuild it + if(obs & OT_BLACK) + blackRender->rebuildBuffers(this->obsRows); +} + Vector Game::getCameraPositionFor(const Vector &pos) { return Vector(pos.x - 400 * core->invGlobalScale, pos.y - 300 * core->invGlobalScale, 0); @@ -4721,6 +4780,7 @@ void Game::removeState() controlHint_text = 0; miniMapRender = 0; + blackRender = 0; gridRender = 0; gridRender2 = 0; gridRender3 = 0; diff --git a/Aquaria/Game.h b/Aquaria/Game.h index 8f2548f..29245ba 100644 --- a/Aquaria/Game.h +++ b/Aquaria/Game.h @@ -85,22 +85,6 @@ struct MinimapIcon typedef std::list Ingredients; -class ObsRow -{ -public: - inline ObsRow(unsigned tx, unsigned ty, unsigned len) - : tx(tx), ty(ty), len(len) {} - inline ObsRow(const ObsRow& o) - : tx(o.tx), ty(o.ty), len(o.len) {} - const unsigned tx, ty, len; -}; - -enum FlagCheckType -{ - NO_TYPE =-1, - AND =0, - OR =1 -}; class EntityClass { @@ -136,6 +120,7 @@ public: Avatar *avatar; Entity *li; + TileVector getGridSize() const; // available after calling findMaxCameraValues() ObsType getGrid(const TileVector &tile) const; ObsType getGridRaw(const TileVector &tile) const; unsigned char *getGridColumn(int tileX); @@ -381,8 +366,9 @@ public: void createGradient(); std::string saveMusic; - GridRender *gridRender, *gridRender2, *gridRender3, *edgeRender, *gridRenderEnt, *gridRenderUser1, *gridRenderUser2; + GridRender *blackRender, *gridRender, *gridRender2, *gridRender3, *edgeRender, *gridRenderEnt, *gridRenderUser1, *gridRenderUser2; void toggleGridRender(); + void updateGridRender(ObsType obs); bool invinciblity; @@ -457,7 +443,9 @@ protected: void createLi(); void createPets(); void findMaxCameraValues(); + TileVector computeMapSizeFromObs() const; std::vector obsRows; + size_t mapGridW, mapGridH; std::string musicToPlay; @@ -503,6 +491,11 @@ ObsType Game::getGridRaw(const TileVector &tile) const : OT_OUTOFBOUNDS; } +inline TileVector Game::getGridSize() const +{ + return TileVector(int(this->mapGridW), int(this->mapGridH)); +} + inline ObsType Game::getGrid(const TileVector &tile) const { diff --git a/Aquaria/GameEnums.h b/Aquaria/GameEnums.h index c7d3555..c129096 100644 --- a/Aquaria/GameEnums.h +++ b/Aquaria/GameEnums.h @@ -14,6 +14,7 @@ enum ObsType OT_BLACK = 0x01, OT_BLACKINVIS = 0x02, // same as OT_BLACK, but not drawn OT_MASK_BLACK = OT_BLACK | OT_BLACKINVIS, + OT_MASK_NOTBLACK = ~OT_MASK_BLACK, // set by tiles OT_INVISIBLE = 0x04, @@ -31,7 +32,7 @@ enum ObsType OT_USER2 = 0x80, OT_USER_MASK = OT_USER1 | OT_USER2, - OT_OUTOFBOUNDS = 0xff + OT_OUTOFBOUNDS = 0xff, // all bits set }; diff --git a/Aquaria/GameStructs.h b/Aquaria/GameStructs.h index 12ce6a3..de9c736 100644 --- a/Aquaria/GameStructs.h +++ b/Aquaria/GameStructs.h @@ -186,5 +186,15 @@ struct UnderWaterResult Path *waterbubble; }; +class ObsRow +{ +public: + inline ObsRow(unsigned tx, unsigned ty, unsigned len) + : tx(tx), ty(ty), len(len) {} + inline ObsRow(const ObsRow& o) + : tx(o.tx), ty(o.ty), len(o.len) {} + const unsigned tx, ty, len; +}; + #endif diff --git a/Aquaria/GridRender.cpp b/Aquaria/GridRender.cpp index 2409850..5dc3963 100644 --- a/Aquaria/GridRender.cpp +++ b/Aquaria/GridRender.cpp @@ -22,39 +22,142 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Game.h" #include "RenderBase.h" -GridRender::GridRender(ObsType obsType) : RenderObject() + +static void collectRows(std::vector& rows, ObsType obs) +{ + const TileVector gs = game->getGridSize(); + const size_t endX = std::min(gs.x, MAX_GRID); + const size_t endY = std::min(gs.y, MAX_GRID); + + for(size_t y = 0; y < endY; ++y) + { + bool on = game->getGridRaw(TileVector(0, y)) == obs; + size_t startx = 0; + for(size_t x = 1; x < endX; ++x) + { + const ObsType ot = game->getGridRaw(TileVector(x, y)); + if(ot == obs) + { + if(!on) + { + startx = x; + on = true; + } + } + else + { + if(on) + { + // previous tile is the last one, so -1 + rows.push_back(ObsRow(startx, y, x - startx)); + on = false; + } + } + } + if(on) + rows.push_back(ObsRow(startx, y, endX - startx)); + } +} + +GridRender::GridRender(ObsType obsType) + : RenderObject() + , vbo(GPUBUF_VERTEXBUF | GPUBUF_STATIC) + , primsToDraw(0) + , obsType(obsType) + //, ibo(GPUBUF_INDEXBUF | GPUBUF_STATIC) + , markedForRebuild(true) { color = Vector(1, 0, 0); position.z = 5; cull = false; alpha = 0.5f; - this->obsType = obsType; + this->scale.x = TILE_SIZE; + this->scale.y = TILE_SIZE; } -void GridRender::onUpdate(float dt) +void GridRender::rebuildBuffers() { - RenderObject::onUpdate(dt); + std::vector rows; + collectRows(rows, obsType); + rebuildBuffers(rows); } -inline static void doRenderGrid(int x, int startCol, int endCol) +void GridRender::rebuildBuffers(const std::vector& rows) { - const int drawx1 = x*TILE_SIZE; - const int drawx2 = (x+1)*TILE_SIZE; - const int drawy1 = startCol*TILE_SIZE; - const int drawy2 = (endCol+1)*TILE_SIZE; + markedForRebuild = false; - glBegin(GL_QUADS); - glVertex3i(drawx1, drawy2, 0.0f); - glVertex3i(drawx2, drawy2, 0.0f); - glVertex3i(drawx2, drawy1, 0.0f); - glVertex3i(drawx1, drawy1, 0.0f); - glEnd(); + const size_t N = rows.size(); + primsToDraw = N * 6; + if(!N) + return; + // 2 tris = 6 verts per ObsRow, each vertex is 2x uint16, makes 24b per quad. + // We could use indexed rendering and use 2 verts less (16b), + // but the 6 indices would cost another 12b so it's definitely cheaper to send + // triangle soup to the gpu in this case, since each vertex is only 4 bytes. + const size_t szv = N * 6 * 2 * sizeof(unsigned short); + + do + { + unsigned short *pxy = (unsigned short*)vbo.beginWrite(GPUBUFTYPE_UVEC2, szv, GPUACCESS_DEFAULT); + + for(size_t i = 0; i < N; ++i) + { + const ObsRow& row = rows[i]; + + // Don't bother to transform to float. The GPU can do that better. + // The scale factor of a GridRender is set to TILE_SIZE, that pre-bakes the + // required scaling multiplication into the object scale so we can get away + // with using raw, unscaled values here + + const unsigned short x0 = row.tx; + const unsigned short x1 = row.tx + row.len; + const unsigned short y0 = row.ty; + const unsigned short y1 = row.ty + 1; + + // top left triangle + *pxy++ = x0; + *pxy++ = y0; + + *pxy++ = x1; + *pxy++ = y0; + + *pxy++ = x0; + *pxy++ = y1; + + // bottom right triangle + *pxy++ = x1; + *pxy++ = y0; + + *pxy++ = x1; + *pxy++ = y1; + + *pxy++ = x0; + *pxy++ = y1; + } + } + while(!vbo.commitWrite()); +} + +void GridRender::rebuildBuffersIfNecessary() +{ + if(markedForRebuild) + rebuildBuffers(); +} + +void GridRender::rebuildBuffersIfNecessary(const std::vector& rows) +{ + if(markedForRebuild) + rebuildBuffers(rows); } void GridRender::onRender(const RenderState& rs) const { + if(!primsToDraw) + return; + + /* const signed char obsType = this->obsType; Vector camPos = core->cameraPos; camPos.x -= core->getVirtualOffX() * (core->invGlobalScale); @@ -75,41 +178,13 @@ void GridRender::onRender(const RenderState& rs) const endY = MAX_GRID-1; if (startY > endY) return; - for (int x = startX; x <= endX; ++x) - { - const unsigned char *gridColumn = game->getGridColumn(x); - int startCol = -1, y; + */ - // fast-forward to next drawable byte - if(const unsigned char *next = (const unsigned char*)memchr(gridColumn + startY, obsType, endY - startY + 1)) // find next byte with correct obs type - { - y = next - gridColumn; // will get incremented right away, which is okay, because we alrady set startCol - startCol = y; - } - else - continue; // nothing do draw in this column + // TODO: keep track of prim index at which a row starts + // then horizontally span only as much prims as are necessary? - for ( ; y < endY; ++y) - { - if (gridColumn[y] != obsType) - { - doRenderGrid(x, startCol, y - 1); - - // fast-forward to next drawable byte - if(const unsigned char *next = (const unsigned char*)memchr(gridColumn + y, obsType, endY - y)) // find next byte with correct obs type - { - y = next - gridColumn; // will get incremented right away, which is okay, because we alrady set startCol - startCol = y; - } - else - break; - } - } - if (y == endY) - { - doRenderGrid(x, startCol, y); - } - } + vbo.apply(); + glDrawArrays(GL_TRIANGLES, 0, primsToDraw); } SongLineRender::SongLineRender() diff --git a/Aquaria/GridRender.h b/Aquaria/GridRender.h index d5175d4..2995b9d 100644 --- a/Aquaria/GridRender.h +++ b/Aquaria/GridRender.h @@ -22,8 +22,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define GRIDRENDER_H #include "GameEnums.h" +#include "GameStructs.h" #include "../BBGE/Quad.h" #include "ActionMapper.h" +#include "VertexBuffer.h" class GemMover; struct MinimapIcon; @@ -36,9 +38,18 @@ class GridRender : public RenderObject { public: GridRender(ObsType obsType); + void rebuildBuffers(); + void rebuildBuffers(const std::vector& rows); + void rebuildBuffersIfNecessary(); + void rebuildBuffersIfNecessary(const std::vector& rows); + void markForRebuild() { markedForRebuild = true; } + ObsType getObs() const { return obsType; } protected: - ObsType obsType; - void onUpdate(float dt) OVERRIDE; + DynamicGPUBuffer vbo; + size_t primsToDraw; + const ObsType obsType; + bool markedForRebuild; + void onRender(const RenderState& rs) const OVERRIDE; }; diff --git a/Aquaria/SceneEditor.cpp b/Aquaria/SceneEditor.cpp index 1e6b148..433c582 100644 --- a/Aquaria/SceneEditor.cpp +++ b/Aquaria/SceneEditor.cpp @@ -1622,6 +1622,7 @@ void SceneEditor::generateLevel() } game->reconstructGrid(true); + game->updateGridRender(OT_MASK_BLACK); maxX--; maxY--; diff --git a/BBGE/OpenGLStubs.h b/BBGE/OpenGLStubs.h index 48b013b..859291a 100644 --- a/BBGE/OpenGLStubs.h +++ b/BBGE/OpenGLStubs.h @@ -87,7 +87,6 @@ GL_FUNC(void,glTexCoord2f,(GLfloat s, GLfloat t),(s,t),) //GL_FUNC(void,glTexCoord2d,(GLdouble s, GLdouble t),(s,t),) GL_FUNC(void,glVertex2f,(GLfloat x, GLfloat y),(x,y),) GL_FUNC(void,glVertex3f,(GLfloat x, GLfloat y, GLfloat z),(x,y,z),) -GL_FUNC(void,glVertex3i,(GLint x, GLint y, GLint z),(x,y,z),) // stuff GLU needs... GL_FUNC(void,glGetIntegerv,(GLenum pname, GLint *params),(pname,params),) diff --git a/BBGE/VertexBuffer.h b/BBGE/VertexBuffer.h index ed39d32..eca145f 100644 --- a/BBGE/VertexBuffer.h +++ b/BBGE/VertexBuffer.h @@ -29,7 +29,8 @@ enum BufDataType GPUBUFTYPE_VEC2_TC = 0x00081021, // xyuv xyuv xyuv GPUBUFTYPE_VEC2_TC_RGBA = 0x10082021, // xyuvrgba xyuvrgba xyuvrgba // ccoossnt - GPUBUFTYPE_VEC2_TC_RGBA_BUT_NO_COLOR = GPUBUFTYPE_VEC2_TC_RGBA & 0xffffff + GPUBUFTYPE_VEC2_TC_RGBA_BUT_NO_COLOR = GPUBUFTYPE_VEC2_TC_RGBA & 0xffffff, + GPUBUFTYPE_UVEC2 = 0x00000420 }; enum AccessFlags