diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index e199f8d..3a97dfd 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -201,7 +201,6 @@ DSQ::DSQ(const std::string& fileSystem, const std::string& extraDataDir) console = 0; cmDebug = 0; saveSlotMode = SSM_NONE; - afterEffectManagerLayer = LR_AFTER_EFFECTS; // LR_AFTER_EFFECTS renderObjectLayers.resize(LR_MAX); entities.resize(64, 0); @@ -585,7 +584,7 @@ static void loadBit(int index, float perc = 1) loading->setWidthHeight(loadingProgress*600, 23); - dsq->render(); + dsq->renderExternal(); dsq->showBuffer(); } @@ -688,6 +687,45 @@ static bool sdlVideoModeOK(int disp, const int w, const int h, const int bpp) #endif } +static bool preRenderDarkLayer(const RenderState& rs) +{ + if(core->darkLayer.isUsed()) + core->darkLayer.beginCapture(); + + return true; +} + +static void postRenderDarkLayer(const RenderState& rs) +{ + if(core->darkLayer.isUsed()) + { + core->darkLayer.endCapture(); + } +} + +static bool beginAfterEffectCapture(const RenderState& rs) +{ + if (core->afterEffectManager) + core->afterEffectManager->beginCapture(); + + return true; +} + +static bool preRenderAfterEffectLayer(const RenderState& rs) +{ + if(core->darkLayer.isUsed()) + core->darkLayer.render(rs); + + if(core->afterEffectManager) + { + unsigned page = core->frameBuffer.popCapture(); + core->afterEffectManager->render(rs, page); + } + + return true; +} + + void DSQ::init() { #define PSIZEOF(x) { std::ostringstream os_; os_ << ("sizeof(" #x ") = ") << sizeof(x); debugLog(os_.str()); } @@ -911,12 +949,6 @@ void DSQ::init() precacher.clear(); - - render(); - showBuffer(); - - - loadBit(LOAD_INITIAL); debugLog("Loading Particle Bank..."); @@ -968,7 +1000,6 @@ void DSQ::init() user.video.darkbuffersize = MAX(user.video.darkbuffersize,128); - darkLayer.setLayers(LR_ELEMENTS13, LR_AFTER_EFFECTS); debugLog("dark layer init"); darkLayer.init(user.video.darkbuffersize, user.video.darkfbuffer); debugLog("dark layer togle..."); @@ -1270,6 +1301,14 @@ void DSQ::init() renderObjectLayerOrder[LR_ENTITIES_MINUS3] = -1; renderObjectLayerOrder[LR_ENTITIES_MINUS2] = -1; + renderObjectLayers[LR_ZERO].preRender = beginAfterEffectCapture; + renderObjectLayers[LR_AFTER_EFFECTS].preRender = preRenderAfterEffectLayer; + + renderObjectLayers[LR_ELEMENTS13].preRender = preRenderDarkLayer; + renderObjectLayers[LR_ELEMENTS13].postRender = postRenderDarkLayer; + darkLayer.beginLayer = LR_ELEMENTS13; + darkLayer.lastLayer = LR_ELEMENTS13; + setMousePosition(center); @@ -2717,8 +2756,8 @@ void DSQ::doSaveSlotMenu(SaveSlotMode ssm, const Vector &position) glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, renderWidth, renderHeight); - clearBuffers(); - render(); + glClear(GL_COLOR_BUFFER_BIT); + renderExternal(); scrShotData = grabScreenshot(x, y, scrShotWidth, scrShotHeight); glPopAttrib(); showBuffer(); @@ -3315,6 +3354,11 @@ void DSQ::prepScreen(bool screenshot) } } +void DSQ::onPrepareRender() +{ + game->onPrepareRender(); +} + void DSQ::onRender() { if (cursor) @@ -3661,7 +3705,7 @@ void DSQ::onUpdate(float dt) pollEvents(sec); ActionMapper::onUpdate(sec); SDL_Delay(int(sec*1000)); - render(); + renderExternal(); showBuffer(); resetTimer(); @@ -4162,17 +4206,18 @@ void AquariaScreenTransition::capture() InterpolatedVector oldAlpha = dsq->cursor->alpha; dsq->cursor->alpha.x = 0; int width=0, height=0; - dsq->render(); + dsq->renderExternal(); width = dsq->getWindowWidth(); height = dsq->getWindowHeight(); glBindTexture(GL_TEXTURE_2D,screen_texture); + glReadBuffer(GL_BACK); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height); dsq->cursor->alpha = oldAlpha; - dsq->render(); + dsq->renderExternal(); dsq->showBuffer(); this->alpha = 1; diff --git a/Aquaria/DSQ.h b/Aquaria/DSQ.h index 9a0ad88..a2e1511 100644 --- a/Aquaria/DSQ.h +++ b/Aquaria/DSQ.h @@ -444,8 +444,9 @@ protected: float shakeCameraTimer; float shakeCameraMag; - void onUpdate(float dt); - void onRender(); + void onUpdate(float dt) OVERRIDE; + void onRender() OVERRIDE; + void onPrepareRender() OVERRIDE; void modifyDt(float &dt); diff --git a/Aquaria/Game.cpp b/Aquaria/Game.cpp index 911e8cc..3f19ce6 100644 --- a/Aquaria/Game.cpp +++ b/Aquaria/Game.cpp @@ -232,6 +232,7 @@ Game::Game() : StateObject() noSceneTransitionFadeout = false; fullTilesetReload = false; highestLoadedEntityID = 0; + waterSurfaceRender = NULL; } Game::~Game() @@ -2032,7 +2033,7 @@ bool Game::saveScene(std::string scene) saveFile.InsertEndChild(pathXml); } - for(size_t lr = 0; lr < MAX_TILE_LAYERS; ++lr) + for(unsigned lr = 0; lr < MAX_TILE_LAYERS; ++lr) { const TileStorage& ts = dsq->tilemgr.tilestore[lr]; std::ostringstream simpleElements; @@ -2084,7 +2085,7 @@ bool Game::saveScene(std::string scene) { XMLElement *simpleElementsXML = saveFile.NewElement("SE"); simpleElementsXML->SetAttribute("k", s.c_str()); - simpleElementsXML->SetAttribute("l", (unsigned)lr); + simpleElementsXML->SetAttribute("l", lr); std::string str = simpleElements_repeatScale.str(); if(!str.empty()) simpleElementsXML->SetAttribute("repeatScale", str.c_str()); @@ -2436,6 +2437,7 @@ void Game::applyState() inHelpScreen = false; helpBG = 0; helpBG2 = 0; + waterSurfaceRender = NULL; dsq->returnToScene = ""; @@ -4788,6 +4790,7 @@ void Game::removeState() gridRenderUser1 = 0; gridRenderUser2 = 0; worldMapRender = 0; + waterSurfaceRender = 0; clearObsRows(); @@ -4987,3 +4990,9 @@ void Game::onContinuityReset() { themenu->onContinuityReset(); } + +void Game::onPrepareRender() +{ + if(waterSurfaceRender) + waterSurfaceRender->prepareRender(); +} diff --git a/Aquaria/Game.h b/Aquaria/Game.h index 11d569e..d3296bf 100644 --- a/Aquaria/Game.h +++ b/Aquaria/Game.h @@ -398,6 +398,7 @@ public: bool isIgnoreAction(AquariaActions ac) const; void onContinuityReset(); + void onPrepareRender(); protected: unsigned highestLoadedEntityID; diff --git a/Aquaria/WaterSurfaceRender.cpp b/Aquaria/WaterSurfaceRender.cpp index 1b68c0c..eebb1d6 100644 --- a/Aquaria/WaterSurfaceRender.cpp +++ b/Aquaria/WaterSurfaceRender.cpp @@ -6,30 +6,26 @@ #include "RenderBase.h" - WaterSurfaceRender::WaterSurfaceRender() : Quad() { cull = false; this->texcoordOverride = true; - if (dsq->useFrameBuffer && dsq->frameBuffer.isInited()) + if (dsq->useFrameBuffer) { setSegs(4, 32, 0.5f, 0.5f, -0.005f, 0, 5, 1); } - + this->renderBorder = false; + this->renderCenter = false; + this->borderAlpha = 1; qLine = new Quad("water/water-line", Vector(0,0)); qLine->repeatTextureToFill(1); qLine->cull = false; game->addRenderObject(qLine, LR_WATERSURFACE2); - qLine2 = 0; - - - qSurface = new Quad("missingimage", Vector(0,0)); - qSurface->cull = false; qSurface->repeatTextureToFill(1); game->addRenderObject(qSurface, LR_WATERSURFACE2); @@ -38,16 +34,28 @@ WaterSurfaceRender::WaterSurfaceRender() : Quad() shareAlphaWithChildren = 0; } -void WaterSurfaceRender::onUpdate(float dt) +WaterSurfaceRender::~WaterSurfaceRender() { +} + +void WaterSurfaceRender::onEndOfLife() +{ + if(qLine) + qLine->safeKill(); + if(qSurface) + qSurface->safeKill(); + + qLine = NULL; + qSurface = NULL; +} + +void WaterSurfaceRender::prepareRender() +{ + bool fbEffectVisible = false; if (game->waterLevel.x > 0) { - Quad::onUpdate(dt); - qLine->alpha = qSurface->alpha = 1; - - position.x = core->screenCenter.x; position.y = game->waterLevel.x; @@ -88,28 +96,24 @@ void WaterSurfaceRender::onUpdate(float dt) if (dsq->useFrameBuffer && dsq->frameBuffer.isInited()) { - qSurface->alphaMod = 0.5; + qSurface->alphaMod = 0.5f; + fbEffectVisible = this->isOnScreen(); } } else { qLine->alpha = qSurface->alpha = 0; - - if (qLine2) - { - qLine2->alpha = 0; - } } - if (dsq->useFrameBuffer && dsq->frameBuffer.isInited()) + if (fbEffectVisible) { const float reflectSize = 97; const float reflectPos = (game->waterLevel.x - core->cameraPos.y) + (game->waterLevel.x - core->screenCenter.y) / 3; const float reflectOffset = -0.03f; - const float coordDiv = 768; - const float v0 = 1 + reflectOffset - (reflectPos * core->globalScale.x) / coordDiv; - const float v1 = v0 + (reflectSize * core->globalScale.x) / coordDiv; + const float coordMul = 1.0f / 768; + const float v0 = 1 + reflectOffset - (reflectPos * core->globalScale.x) * coordMul; + const float v1 = v0 + (reflectSize * core->globalScale.x) * coordMul; texcoords.u1 = 0; texcoords.u2 = core->frameBuffer.getWidthP(); @@ -127,27 +131,73 @@ void WaterSurfaceRender::onUpdate(float dt) color = Vector(0.4f, 0.7f, 0.8f); alpha = 0.2f; } + //debugLog(fbEffectVisible ? "ws on screen" : "ws not vis"); + this->fbEffectVisible = fbEffectVisible; + this->renderBorder = RenderObject::renderCollisionShape; + if(this->renderBorder) + { + this->renderBorderColor = fbEffectVisible ? Vector(1,0,1) : Vector(0,0.5f,1); + } grid->setTexCoords(texcoords); } +static void quadBlit(const RenderState& rs, unsigned tex) +{ + rs.gpu.setBlend(BLEND_DISABLED); + glBindTexture(GL_TEXTURE_2D, tex); + glColor4f(1,1,1,1); + + glPushMatrix(); + glLoadIdentity(); + glScalef(core->globalResolutionScale.x, core->globalResolutionScale.y, core->globalResolutionScale.z); + + int vw = core->getVirtualWidth(); + int vh = core->getVirtualHeight(); + int offx = -core->getVirtualOffX(); + int offy = -core->getVirtualOffY(); + + // verts are in 0..1, transform so that we cover the entire screen + glTranslatef(offx, offy, 0); + glScalef(vw, vh, 1); + + core->blitQuad.render(rs); + glPopMatrix(); +} + void WaterSurfaceRender::render(const RenderState& rs) const { if (game->waterLevel.x > 0) + { + if (fbEffectVisible) + { + // Everything up until now was rendered to the first page; flip it + const unsigned curpage = core->frameBuffer.getCurrentPage(); + const unsigned oldtex = core->frameBuffer.getTextureID(curpage); + const unsigned newtex = core->frameBuffer.getTextureID(curpage + 1); + + core->frameBuffer.replaceCapture(curpage + 1); + + if(glCopyImageSubDataEXT) + glCopyImageSubDataEXT( + oldtex, GL_TEXTURE_2D, 0, 0, 0, 0, + newtex, GL_TEXTURE_2D, 0, 0, 0, 0, + core->width, core->height, 1 + ); + else + quadBlit(rs, oldtex); + } + Quad::render(rs); + } } void WaterSurfaceRender::onRender(const RenderState& rs) const { - if (game->waterLevel == 0) return; - if (dsq->useFrameBuffer && dsq->frameBuffer.isInited()) - { - dsq->frameBuffer.bindTexture(); - } - else - { - glBindTexture(GL_TEXTURE_2D, 0); - } + if (!fbEffectVisible || game->waterLevel == 0) return; + + + core->frameBuffer.bindTexture(core->frameBuffer.getCurrentPage() - 1); Quad::onRender(rs); diff --git a/Aquaria/WaterSurfaceRender.h b/Aquaria/WaterSurfaceRender.h index 4f75fc7..2880049 100644 --- a/Aquaria/WaterSurfaceRender.h +++ b/Aquaria/WaterSurfaceRender.h @@ -27,11 +27,14 @@ class WaterSurfaceRender : public Quad { public: WaterSurfaceRender(); + virtual ~WaterSurfaceRender(); void render(const RenderState& rs) const OVERRIDE; + void prepareRender(); protected: - Quad *qSurface, *qLine, *qLine2; + virtual void onEndOfLife() OVERRIDE; + Quad *qSurface, *qLine; void onRender(const RenderState& rs) const OVERRIDE; - void onUpdate(float dt) OVERRIDE; + bool fbEffectVisible; }; #endif diff --git a/BBGE/AfterEffect.cpp b/BBGE/AfterEffect.cpp index 489ab65..7d6d562 100644 --- a/BBGE/AfterEffect.cpp +++ b/BBGE/AfterEffect.cpp @@ -72,6 +72,19 @@ void AfterEffectManager::deleteEffects() openSpots.clear(); } +void AfterEffectManager::beginCapture() +{ + assert(core->frameBuffer.isInited()); + core->frameBuffer.pushCapture(0); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); +} + +void AfterEffectManager::endCapture() +{ + core->frameBuffer.popCapture(); +} + void AfterEffectManager::deleteShaders() { for(size_t i = 0; i < shaderPipeline.size(); ++i) @@ -146,7 +159,7 @@ void AfterEffectManager::destroyEffect(int id) openSpots.push_back(id); } -void AfterEffectManager::render(const RenderState& rs) const +void AfterEffectManager::render(const RenderState& rs, unsigned fboPageWithImage) const { assert(core->frameBuffer.isInited()); @@ -154,48 +167,53 @@ void AfterEffectManager::render(const RenderState& rs) const rs.gpu.setBlend(BLEND_DISABLED); - core->frameBuffer.endCapture(); glTranslatef(core->cameraPos.x, core->cameraPos.y, 0); glScalef(core->invGlobalScale, core->invGlobalScale,0); glColor4f(1,1,1,1); - renderGrid(rs); + renderGrid(rs, fboPageWithImage); glPopMatrix(); } -void AfterEffectManager::renderGrid(const RenderState& rs) const +void AfterEffectManager::renderGrid(const RenderState& rs, unsigned fbPage) const { - int firstShader = -1; - int lastShader = -1; - Shader *activeShader = 0; + size_t firstShader, lastShader; + Shader *activeShader = NULL; for (size_t i = 0; i < shaderPipeline.size(); ++i) { if(shaderPipeline[i] && shaderPipeline[i]->isLoaded()) { - if(firstShader < 0) + if(!activeShader) { - firstShader = int(i); + firstShader = i; activeShader = shaderPipeline[i]; } - lastShader = int(i); + lastShader = i; } } + // Disable blending so we don't need to clear the framebuffers + rs.gpu.setBlend(BLEND_DISABLED); + int vw = core->getVirtualWidth(); int vh = core->getVirtualHeight(); int offx = -core->getVirtualOffX(); int offy = -core->getVirtualOffY(); - core->frameBuffer.bindTexture(); + const FrameBuffer * const fb = &core->frameBuffer; + + // STARTING POINT: game image was just rendered into fb(0), use that as starting point + fb->bindTexture(fbPage); if(activeShader) { - activeShader->bind(); activeShader->setInt("tex", 0); + activeShader->bind(); + // Unless this is the last pass, render to fb(1) if(firstShader != lastShader) - backupBuffer.startCapture(); + fb->pushCapture(1 - fbPage); } // verts are in 0..1, transform so that we cover the entire screen @@ -208,39 +226,44 @@ void AfterEffectManager::renderGrid(const RenderState& rs) const //renderGridPoints(rs); } else - blitQuad.render(rs); + core->blitQuad.render(rs); if (activeShader) + { activeShader->unbind(); - if(firstShader != lastShader) - { - // From here on: secondary shader passes. - // We just outputted to the backup buffer... - const FrameBuffer *fbIn = &core->frameBuffer; - const FrameBuffer *fbOut = &backupBuffer; - - for(int i = firstShader + 1; i <= lastShader; ++i) + if(firstShader != lastShader) { - activeShader = shaderPipeline[i]; - if(!(activeShader && activeShader->isLoaded())) - continue; + // From here on: secondary shader passes. + // We just outputted to the backup buffer... - // Swap and exchange framebuffers. The old output buffer serves as texture input for the other one - fbOut->endCapture(); - std::swap(fbIn, fbOut); - fbIn->bindTexture(); + unsigned pageOut = 1 - fbPage; - // If this is the last pass, do not render to a frame buffer again - if(i != lastShader) - fbOut->startCapture(); + for(size_t i = firstShader + 1; i <= lastShader; ++i) + { + unsigned pageIn = 1 - pageOut; + activeShader = shaderPipeline[i]; + if(!(activeShader && activeShader->isLoaded())) + continue; - activeShader->bind(); - activeShader->setInt("tex", 0); + // Swap and exchange framebuffers. The old output buffer serves as texture input for the other one + pageOut = pageIn; - blitQuad.render(rs); + // If this is the last pass, do not render to a frame buffer again + if(i == lastShader) + fb->popCapture(); + else + fb->replaceCapture(pageOut); - activeShader->unbind(); + fb->bindTexture(pageIn); + + activeShader->bind(); + activeShader->setInt("tex", 0); + + core->blitQuad.render(rs); + + activeShader->unbind(); + } } } @@ -255,9 +278,7 @@ void AfterEffectManager::renderGridPoints(const RenderState& rs) const void AfterEffectManager::unloadDevice() { - backupBuffer.unloadDevice(); grid.dropBuffers(); - blitQuad.dropBuffers(); unloadShaders(); } @@ -266,30 +287,12 @@ void AfterEffectManager::_updateScreenSize() screenWidth = core->getWindowWidth(); screenHeight = core->getWindowHeight(); - if (core->frameBuffer.isInited()) - { - textureWidth = core->frameBuffer.getTexWidth(); - textureHeight = core->frameBuffer.getTexHeight(); - } - else - { - textureWidth = screenWidth; - sizePowerOf2Texture(textureWidth); - textureHeight = screenHeight; - sizePowerOf2Texture(textureHeight); - } - - const float percentX = (float)screenWidth/(float)textureWidth; - const float percentY = (float)screenHeight/(float)textureHeight; - TexCoordBox tc = { 0, percentY, percentX, 0 }; // Y is upside down - grid.setTexCoords(tc); - blitQuad.setTexCoords(tc); + grid.setTexCoords(core->blitQuad.getTexCoords()); } void AfterEffectManager::updateDevice() { _updateScreenSize(); - backupBuffer.init(-1, -1, true); _initGrid(); } @@ -297,7 +300,6 @@ void AfterEffectManager::reloadDevice() { _updateScreenSize(); - backupBuffer.reloadDevice(); _initGrid(); for (size_t i = 0; i < loadedShaders.size(); ++i) @@ -442,10 +444,6 @@ void AfterEffectManager::_initGrid() grid.init(xDivs, yDivs); else grid.dropBuffers(); - - blitQuad.init(2, 2); - blitQuad.reset01(); - blitQuad.updateVBO(); // never changed afterwards } void AfterEffectManager::deleteShader(int handle) diff --git a/BBGE/AfterEffect.h b/BBGE/AfterEffect.h index 068cf60..ce80fbd 100644 --- a/BBGE/AfterEffect.h +++ b/BBGE/AfterEffect.h @@ -78,10 +78,13 @@ public: void clear(); void deleteEffects(); + void beginCapture(); + void endCapture(); + void resetGrid(); - void render(const RenderState& rs) const; - void renderGrid(const RenderState& rs) const; + void render(const RenderState& rs, unsigned fboPageWithImage) const; + void renderGrid(const RenderState& rs, unsigned fbPage) const; void renderGridPoints(const RenderState& rs) const; void loadShaders(); @@ -105,17 +108,15 @@ protected: int _insertShader(Shader *sh); void _initGrid(); - RenderGrid grid, blitQuad; + RenderGrid grid; bool active; int numEffects; int xDivs, yDivs; int screenWidth, screenHeight; - int textureWidth, textureHeight; std::vector effects; std::vector openSpots; std::vector shaderPipeline; // Shaders are applied in this order. Can contain the same pointer more than once. std::vector loadedShaders; - FrameBuffer backupBuffer; }; diff --git a/BBGE/Core.cpp b/BBGE/Core.cpp index f9d3da4..e40fc34 100644 --- a/BBGE/Core.cpp +++ b/BBGE/Core.cpp @@ -151,7 +151,7 @@ void Core::setup_opengl() setClearColor(clearColor); - frameBuffer.init(-1, -1, true); + frameBuffer.init(-1, -1, 2); if(afterEffectManager) afterEffectManager->updateDevice(); @@ -160,6 +160,32 @@ void Core::setup_opengl() defaultQuadGrid.init(2, 2, defaultTC); defautQuadBorder.initQuadVertices(defaultTC, GPUACCESS_DEFAULT); + + int screenWidth = getWindowWidth(); + int screenHeight = core->getWindowHeight(); + int textureWidth, textureHeight; + + if (core->frameBuffer.isInited()) + { + textureWidth = core->frameBuffer.getTexWidth(); + textureHeight = core->frameBuffer.getTexHeight(); + } + else + { + textureWidth = screenWidth; + sizePowerOf2Texture(textureWidth); + textureHeight = screenHeight; + sizePowerOf2Texture(textureHeight); + } + + const float percentX = (float)screenWidth/(float)textureWidth; + const float percentY = (float)screenHeight/(float)textureHeight; + TexCoordBox tc = { 0, percentY, percentX, 0 }; // Y is upside down + blitQuad.setTexCoords(tc); + blitQuad.init(2, 2); + blitQuad.reset01(); + blitQuad.updateVBO(); // never changed afterwards + } void Core::resizeWindow(int w, int h, int full, int bpp, int vsync, int display, int hz) @@ -378,7 +404,6 @@ Core::Core(const std::string &filesystem, const std::string& extraDataDir, int n baseCullRadius = 1; width = height = 0; _lastEnumeratedDisplayIndex = -1; - afterEffectManagerLayer = 0; renderObjectLayers.resize(1); invGlobalScale = 1.0; invGlobalScaleSqr = 1.0; @@ -740,7 +765,9 @@ void Core::initGraphicsLibrary(int width, int height, bool fullscreen, bool vsyn enumerateScreenModes(window->getDisplayIndex()); window->updateSize(); - cacheRender(); // Clears the window bg to black early; prevents flickering + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + showBuffer(); lib_graphics = true; } @@ -1182,15 +1209,14 @@ void Core::run(float runTime) if (settings.renderOn) { - if (darkLayer.isUsed()) + if(!minimized) { - darkLayer.preRender(); + prepareRender(); + renderInternal(-1, -1, true); + + showBuffer(); } - render(); - - showBuffer(); - if (nestedMains == 1) clearGarbage(); @@ -1221,11 +1247,6 @@ void Core::run(float runTime) nestedMains--; } -void Core::clearBuffers() -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer -} - void Core::setupRenderPositionAndScale() { glScalef(globalScale.x*globalResolutionScale.x, globalScale.y*globalResolutionScale.y, globalScale.z*globalResolutionScale.z); @@ -1766,7 +1787,7 @@ void Core::print(int x, int y, const char *str, float sz) void Core::cacheRender() { - render(); + renderExternal(); // what if the screen was full white? then you wouldn't want to clear buffers //clearBuffers(); showBuffer(); @@ -1780,7 +1801,13 @@ void Core::updateCullData() screenCenter = cullCenter = cameraPos + Vector(400.0f*invGlobalScale,300.0f*invGlobalScale); } -void Core::render(int startLayer, int endLayer, bool useFrameBufferIfAvail) +void Core::renderExternal() +{ + prepareRender(); + renderInternal(-1, -1, true); +} + +void Core::prepareRender() { renderObjectCount = 0; processedRenderObjectCount = 0; @@ -1788,13 +1815,10 @@ void Core::render(int startLayer, int endLayer, bool useFrameBufferIfAvail) globalScaleChanged(); - if (minimized) return; - onRender(); - - RenderObject::lastTextureApplied = 0; - updateCullData(); + onPrepareRender(); + // TODO: this could be done in parallel for (size_t i = 0; i < renderObjectLayers.size(); ++i) { @@ -1802,50 +1826,54 @@ void Core::render(int startLayer, int endLayer, bool useFrameBufferIfAvail) renderObjectLayers[i].prepareRender(); } + if (darkLayer.isUsed()) + { + darkLayer.preRender(); + } + + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, core->width, core->height); +} + + +// Make sure to call prepareRender() before calling this! +void Core::renderInternal(int startLayer, int endLayer, bool allowSkip) +{ + onRender(); + + RenderObject::lastTextureApplied = 0; + glBindTexture(GL_TEXTURE_2D, 0); glLoadIdentity(); // Reset The View - clearBuffers(); - - if (afterEffectManager && frameBuffer.isInited() && useFrameBufferIfAvail) - { - frameBuffer.startCapture(); - } setupRenderPositionAndScale(); CombinedRenderAndGPUState rs; - for (size_t c = 0; c < renderObjectLayerOrder.size(); c++) { int i = renderObjectLayerOrder[c]; if (i == -1) continue; if ((startLayer != -1 && endLayer != -1) && (i < startLayer || i > endLayer)) continue; - if (darkLayer.isUsed() ) - { - - if (i == darkLayer.getRenderLayer()) - { - darkLayer.render(rs); - } - - if (i == darkLayer.getLayer() && startLayer != i) - { - continue; - } - } - - if (afterEffectManager /*&& afterEffectManager->active*/ && i == afterEffectManagerLayer) - { - afterEffectManager->render(rs); - } + // don't render the layers that the dark layer takes as input + if(allowSkip && !darkLayer.shouldRenderLayer(i)) + continue; RenderObjectLayer *r = &renderObjectLayers[i]; if(!r->visible) continue; + if(r->preRender) + if(!r->preRender(rs)) + continue; + r->render(rs); + + if(r->postRender) + r->postRender(rs); } } @@ -1949,6 +1977,7 @@ void Core::shutdown() defaultQuadGrid.dropBuffers(); defautQuadBorder.dropBuffer(); + blitQuad.dropBuffers(); debugLog("Shutdown Graphics Library..."); shutdownGraphicsLibrary(); diff --git a/BBGE/Core.h b/BBGE/Core.h index 255f430..7d158b4 100644 --- a/BBGE/Core.h +++ b/BBGE/Core.h @@ -113,6 +113,9 @@ enum FollowCameraLock typedef std::vector RenderObjects; +typedef bool (*PreRenderFunc)(const RenderState& rs); +typedef void (*PostRenderFunc)(const RenderState& rs); + class RenderObjectLayer { public: @@ -170,6 +173,9 @@ public: bool update; + PreRenderFunc preRender; + PostRenderFunc postRender; + protected: RenderObjects renderObjects; std::vector toRender; @@ -206,8 +212,9 @@ public: void applyState(const std::string &state); - void clearBuffers(); - void render(int startLayer=-1, int endLayer=-1, bool useFrameBufferIfAvail=true); + void renderExternal(); + void prepareRender(); + void renderInternal(int startLayer, int endLayer, bool allowSkip); void showBuffer(); void quit(); bool isShuttingDown(); @@ -465,7 +472,6 @@ protected: virtual void onJoystickAdded(int deviceID); virtual void onJoystickRemoved(int instanceID); - int afterEffectManagerLayer; Vector cameraOffset; std::vector avgFPS; virtual void modifyDt(float &dt){} @@ -486,6 +492,7 @@ protected: virtual void onUpdate(float dt); virtual void onRender(){} + virtual void onPrepareRender(){} void setupFileAccess(); std::string _extraDataDir; @@ -498,6 +505,8 @@ protected: DynamicGPUBuffer defautQuadBorder; public: + RenderGrid blitQuad; + // inclusive! inline int getMaxActionStatusIndex() const { return int(actionStatus.size()) - 2; } // pass -1 for is a sentinel that captures all input diff --git a/BBGE/DarkLayer.cpp b/BBGE/DarkLayer.cpp index c2a138e..35255a0 100644 --- a/BBGE/DarkLayer.cpp +++ b/BBGE/DarkLayer.cpp @@ -26,12 +26,11 @@ DarkLayer::DarkLayer() { quality = 0; active = false; - layer = -1; - renderLayer = -1; texture = 0; - - format = GL_RGB; //FIXED?: used to be GL_LUMINANCE, that might have been causing problems - useFrameBuffer = true; //BUG?: will do this even if frame buffer is off in usersettings... + useFrameBuffer = false; + layer = -1; + beginLayer = -1; + lastLayer = -1; } void DarkLayer::unloadDevice() @@ -53,23 +52,16 @@ void DarkLayer::reloadDevice() texture = generateEmptyTexture(quality); } -int DarkLayer::getRenderLayer() +bool DarkLayer::isUsed() const { - return renderLayer; + return active; } -bool DarkLayer::isUsed() +bool DarkLayer::shouldRenderLayer(int lr) const { - //HACK: disabling dark layer for temporary testing build - // MAKE SURE TO RESTORE THIS CODE TO THE WAY IT WAS - return layer > -1 && active; - -} - -void DarkLayer::setLayers(int layer, int rl) -{ - this->layer = layer; - this->renderLayer = rl; + if(!active) + return true; + return useFrameBuffer || (lr < beginLayer || lr > lastLayer); } void DarkLayer::init(int quality, bool useFrameBufferParam) @@ -80,7 +72,7 @@ void DarkLayer::init(int quality, bool useFrameBufferParam) if (useFrameBuffer) { - if (!frameBuffer.init(quality, quality)) + if (!frameBuffer.init(quality, quality, 1)) useFrameBuffer = false; else debugLog("Dark Layer: using framebuffer"); @@ -92,11 +84,6 @@ void DarkLayer::init(int quality, bool useFrameBufferParam) } } -int DarkLayer::getLayer() -{ - return layer; -} - void DarkLayer::toggle(bool on) { this->active = on; @@ -104,77 +91,77 @@ void DarkLayer::toggle(bool on) void DarkLayer::preRender() { - if (layer != -1) + if(!useFrameBuffer) { - glViewport(0,0,quality,quality); - - if (useFrameBuffer) - frameBuffer.startCapture(); - + glViewport(0, 0, quality, quality); glClearColor(1,1,1,0); glClear(GL_COLOR_BUFFER_BIT); + core->renderInternal(beginLayer, lastLayer, false); + glBindTexture(GL_TEXTURE_2D, texture); + glReadBuffer(GL_BACK); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, quality, quality, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } +} - core->render(layer, layer, false); +void DarkLayer::beginCapture() +{ + if(useFrameBuffer) + { + frameBuffer.pushCapture(0); + glClearColor(1,1,1,0); + glClear(GL_COLOR_BUFFER_BIT); + } +} - if (useFrameBuffer) - frameBuffer.endCapture(); - else - { - glBindTexture(GL_TEXTURE_2D,texture); // Bind To The Blur Texture - // Copy Our ViewPort To The Blur Texture (From 0,0 To q,q... No Border) - glCopyTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, quality, quality, 0); - } - - glViewport(0, 0, core->width, core->height); - glClearColor(0,0,0,0); +void DarkLayer::endCapture() +{ + if(useFrameBuffer) + { + frameBuffer.popCapture(); } } void DarkLayer::render(const RenderState& rs) const { - if (renderLayer != -1) - { - glPushMatrix(); - glLoadIdentity(); + glPushMatrix(); + glLoadIdentity(); + glEnable(GL_TEXTURE_2D); + if (useFrameBuffer) + frameBuffer.bindTexture(0); + else + glBindTexture(GL_TEXTURE_2D,texture); - glEnable(GL_TEXTURE_2D); - if (useFrameBuffer) - frameBuffer.bindTexture(); - else - glBindTexture(GL_TEXTURE_2D,texture); + rs.gpu.setBlend(BLEND_MULT); - rs.gpu.setBlend(BLEND_MULT); + glColor4f(1,1,1,1); - glColor4f(1,1,1,1); + const float width = core->getWindowWidth(); + const float height = core->getWindowHeight(); + const float offX = -(core->getVirtualOffX() * width / core->getVirtualWidth()); + const float offY = -(core->getVirtualOffY() * height / core->getVirtualHeight()); + const float stretch = 4; - const float width = core->getWindowWidth(); - const float height = core->getWindowHeight(); - const float offX = -(core->getVirtualOffX() * width / core->getVirtualWidth()); - const float offY = -(core->getVirtualOffY() * height / core->getVirtualHeight()); - const float stretch = 4; + glBegin(GL_QUADS); - glBegin(GL_QUADS); + glTexCoord2f(0,1); + glVertex2f(offX-stretch, offY-stretch); - glTexCoord2f(0,1); - glVertex2f(offX-stretch, offY-stretch); + glTexCoord2f(0,0); + glVertex2f(offX-stretch, height+offY+stretch); - glTexCoord2f(0,0); - glVertex2f(offX-stretch, height+offY+stretch); + glTexCoord2f(1,0); + glVertex2f(width+offX+stretch, height+offY+stretch); - glTexCoord2f(1,0); - glVertex2f(width+offX+stretch, height+offY+stretch); + glTexCoord2f(1,1); + glVertex2f(width+offX+stretch, offY-stretch); - glTexCoord2f(1,1); - glVertex2f(width+offX+stretch, offY-stretch); + glEnd(); - glEnd(); + glPopMatrix(); - glPopMatrix(); - - RenderObject::lastTextureApplied = 0; - glBindTexture(GL_TEXTURE_2D, 0); - - } + RenderObject::lastTextureApplied = 0; + glBindTexture(GL_TEXTURE_2D, 0); } diff --git a/BBGE/DarkLayer.h b/BBGE/DarkLayer.h index c99d04d..78b3169 100644 --- a/BBGE/DarkLayer.h +++ b/BBGE/DarkLayer.h @@ -32,24 +32,28 @@ public: DarkLayer(); void init(int quality, bool useFrameBuffer=true); void toggle(bool on); - void setLayers(int layer, int renderLayer); - void preRender(); + void setSrcLayer(int layer); + void preRender(); // call before rendering anything else. this is for render-to-texture mode + void beginCapture(); // call this + endCapture() when rendering the layer where dark layer objects are located + void endCapture(); + void render(const RenderState& rs) const; - int getLayer(); - int getRenderLayer(); - bool isUsed(); + bool isUsed() const; + bool shouldRenderLayer(int lr) const; void unloadDevice(); void reloadDevice(); + int beginLayer; + int lastLayer; // inclusive + +protected: bool useFrameBuffer; FrameBuffer frameBuffer; -protected: int quality; + int layer; bool active; - int layer, renderLayer; unsigned texture; - unsigned format; }; #endif diff --git a/BBGE/FrameBuffer.cpp b/BBGE/FrameBuffer.cpp index 749f308..5d375da 100644 --- a/BBGE/FrameBuffer.cpp +++ b/BBGE/FrameBuffer.cpp @@ -24,15 +24,47 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //WARNING: FrameBuffer objects have to have reloadDevice/unloadDevice called manually! + +struct FBOStack +{ + const FrameBuffer *fbo; + unsigned page; +}; + +static FBOStack s_fbostack[4]; // first entry is always unused +static size_t s_stackpos = 0; + + +static bool isOnTop(const FrameBuffer *fbo) +{ + return s_fbostack[s_stackpos].fbo == fbo; +} + +static bool isInStack(const FrameBuffer *fbo, unsigned page) +{ + for(size_t i = 1; i <= s_stackpos; ++i) // first entry is always NULL + if(s_fbostack[i].fbo == fbo && s_fbostack[i].page == page) + return true; + return false; +} + + FrameBuffer::FrameBuffer() { inited = false; texw = 0; texh = 0; - g_frameBuffer = 0; - g_depthRenderBuffer = 0; - g_dynamicTextureID = 0; + viewportW = 0; + viewportH = 0; + _curpage = 0; _w = _h = 0; + _numpages = 0; + _numfbos = 0; + for(size_t i = 0; i < Countof(_texs); ++i) + { + _fbos[i] = 0; + _texs[i] = 0; + } } FrameBuffer::~FrameBuffer() @@ -50,10 +82,18 @@ float FrameBuffer::getHeightP() const return (float)core->getWindowHeight()/(float)texh; } -bool FrameBuffer::init(int width, int height, bool fitToScreen) +bool FrameBuffer::getCurrentPage() const { + assert(_curpage); + return _curpage - 1; +} + +bool FrameBuffer::init(int width, int height, unsigned pages) +{ + assert(pages && pages < Countof(_texs)); _w = width; _h = height; + _numpages = pages; if (width == -1) width = core->width; @@ -61,13 +101,12 @@ bool FrameBuffer::init(int width, int height, bool fitToScreen) if (height == -1) height = core->height; - if (fitToScreen) - { - sizePowerOf2Texture(width); - sizePowerOf2Texture(height); - } + viewportW = width; + viewportH = height; + + sizePowerOf2Texture(width); + sizePowerOf2Texture(height); - _fitToScreen = fitToScreen; if (width == 0 || height == 0) return false; @@ -78,12 +117,8 @@ bool FrameBuffer::init(int width, int height, bool fitToScreen) os << "Loading EXT_framebuffer_object (" << texw << ", " << texh << ")"; debugLog(os.str()); - if( !glIsRenderbufferEXT || !glBindRenderbufferEXT || !glDeleteRenderbuffersEXT || - !glGenRenderbuffersEXT || !glRenderbufferStorageEXT || !glGetRenderbufferParameterivEXT || - !glIsFramebufferEXT || !glBindFramebufferEXT || !glDeleteFramebuffersEXT || - !glGenFramebuffersEXT || !glCheckFramebufferStatusEXT || !glFramebufferTexture1DEXT || - !glFramebufferTexture2DEXT || !glFramebufferTexture3DEXT || !glFramebufferRenderbufferEXT|| - !glGetFramebufferAttachmentParameterivEXT) + if( !glIsFramebufferEXT || !glBindFramebufferEXT || !glDeleteFramebuffersEXT || + !glGenFramebuffersEXT || !glCheckFramebufferStatusEXT || !glFramebufferTexture2DEXT) { debugLog("One or more EXT_framebuffer_object functions were not found"); return false; @@ -91,58 +126,58 @@ bool FrameBuffer::init(int width, int height, bool fitToScreen) unloadDevice(); - // - // Create a frame-buffer object and a render-buffer object... - // + // If glDrawBuffersARB() is present, we can attach multiple textures per FBO + // and switch between them as render targets. More efficient than switching FBOs. + _numfbos = glDrawBuffersARB ? 1 : pages; - glGenFramebuffersEXT( 1, &g_frameBuffer ); - glGenRenderbuffersEXT( 1, &g_depthRenderBuffer ); + glGenFramebuffersEXT(_numfbos, &_fbos[0]); + for(unsigned i = 0; i < _numfbos; ++i) + if(!_fbos[i]) + return false; - // Initialize the render-buffer for usage as a depth buffer. - // We don't really need this to render things into the frame-buffer object, - // but without it the geometry will not be sorted properly. - glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, g_depthRenderBuffer ); - glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height ); + if(_numfbos == 1) + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[0] ); + glGenTextures( pages, &_texs[0] ); - glGenTextures( 1, &g_dynamicTextureID ); + for(unsigned i = 0; i < pages; ++i) + { + unsigned attach = GL_COLOR_ATTACHMENT0_EXT; + if(_numfbos > 1) + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[i] ); + else + attach += i; - glBindTexture( GL_TEXTURE_2D, g_dynamicTextureID ); - // GL_LINEAR - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glBindTexture(GL_TEXTURE_2D, _texs[i]); + // GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, - width, height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, + width, height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - - // Put together - - glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, g_frameBuffer ); - glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, g_dynamicTextureID, 0 ); - glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, g_depthRenderBuffer ); - - // - // Check for errors... - // - - GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attach, GL_TEXTURE_2D, _texs[i], 0); + } glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - switch( status ) + for(unsigned i = 0; i < _numfbos; ++i) { - case GL_FRAMEBUFFER_COMPLETE_EXT: - break; + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[i] ); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - debugLog("GL_FRAMEBUFFER_UNSUPPORTED_EXT!"); - default: - unloadDevice(); - return false; + switch( status ) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + break; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + debugLog("GL_FRAMEBUFFER_UNSUPPORTED_EXT!"); + default: + unloadDevice(); + return false; + } } debugLog("Done"); @@ -161,58 +196,107 @@ void FrameBuffer::unloadDevice() return; } - if (g_frameBuffer) - { - glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + for(size_t i = 0; i < Countof(_texs); ++i) + if (_fbos[i]) + { + debugLog("frameBuffer handle present, deleting"); + glDeleteFramebuffersEXT(1, &_fbos[i]); + _fbos[i] = 0; + } - debugLog("frameBuffer handle present, deleting"); - glDeleteFramebuffersEXT(1, &g_frameBuffer); - g_frameBuffer = 0; - } + for(size_t i = 0; i < Countof(_texs); ++i) + if (_texs[i]) + { + debugLog("delete framebuffer texture"); + glDeleteTextures(1, &_texs[i]); + _texs[i] = 0; + } - if (g_dynamicTextureID) - { - glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); - - debugLog("dynamic texture ID handle present, deleting"); - glDeleteTextures(1, &g_dynamicTextureID); - g_dynamicTextureID = 0; - } - - if (g_depthRenderBuffer) - { - glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); - - debugLog("depth render buffer handle present, deleting"); - glDeleteRenderbuffersEXT(1, &g_depthRenderBuffer); - g_depthRenderBuffer = 0; - } debugLog("done"); } void FrameBuffer::reloadDevice() { + if(!_numpages) + return; debugLog("frameBuffer::reloadDevice"); - init(_w, _h, _fitToScreen); + init(_w, _h, _numpages); } -void FrameBuffer::startCapture() const +void FrameBuffer::_bind(unsigned page) const +{ + assert(page < _numpages); + _curpage = page + 1; + if(glDrawBuffersARB) + { + assert(_numfbos == 1); + if(!isOnTop(this)) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbos[0]); + GLenum buf = GL_COLOR_ATTACHMENT0_EXT + page; + glDrawBuffersARB(1, &buf); + } + else + { + assert(_fbos[page]); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbos[page]); + } + + glViewport(0,0,viewportW,viewportH); +} + +void FrameBuffer::pushCapture(unsigned page) const { assert(inited); - glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, g_frameBuffer ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + _bind(page); + + size_t idx = ++s_stackpos; + assert(idx < Countof(s_fbostack)); + s_fbostack[idx].fbo = this; + s_fbostack[idx].page = page; } -void FrameBuffer::endCapture() const +unsigned FrameBuffer::popCapture() const { - assert(inited); - glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); + assert(inited && s_stackpos && isOnTop(this)); + const unsigned page = s_fbostack[s_stackpos].page; + + FBOStack prev = s_fbostack[--s_stackpos]; + if(prev.fbo) + prev.fbo->_bind(prev.page); + else + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + if(glDrawBuffersARB) + glDrawBuffer(GL_BACK); + glViewport(0, 0, core->width, core->height); + } + return page; } -void FrameBuffer::bindTexture() const +void FrameBuffer::replaceCapture(unsigned page) const { - assert(inited); - glBindTexture( GL_TEXTURE_2D, g_dynamicTextureID ); + assert(inited && s_stackpos); + + _bind(page); + s_fbostack[s_stackpos].fbo = this; + s_fbostack[s_stackpos].page = page; } +unsigned FrameBuffer::getTextureID(unsigned page) const +{ + assert(inited && page < _numpages); + return _texs[page]; +} + +void FrameBuffer::bindTexture(unsigned page) const +{ + // Technically it's enough that this texture isn't the one currently rendered to, + // but because we don't know when the topmost FBO is popped (and subsequently + // writing to the texture we're now about to read from) let's be extra safe + // and generalize this to: Textures part of the FBO stack are forbidden to bind. + assert(!isInStack(this, page)); + unsigned tex = getTextureID(page); + glBindTexture(GL_TEXTURE_2D, tex); +} diff --git a/BBGE/FrameBuffer.h b/BBGE/FrameBuffer.h index 55877ed..99aa7c4 100644 --- a/BBGE/FrameBuffer.h +++ b/BBGE/FrameBuffer.h @@ -23,32 +23,47 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Base.h" +class FrameBufferCapture; +struct RenderState; + class FrameBuffer { + friend class FrameBufferCapture; public: FrameBuffer(); ~FrameBuffer(); - bool init(int width, int height, bool fitToScreen=false); + bool init(int width, int height, unsigned pages); bool isInited() const { return inited; } - void startCapture() const; - void endCapture() const; - void bindTexture() const; + unsigned getTextureID(unsigned page) const; + void bindTexture(unsigned page) const; int getTexWidth() const { return texw; } int getTexHeight() const { return texh; } float getWidthP() const; float getHeightP() const; + bool getCurrentPage() const; void unloadDevice(); void reloadDevice(); + // push/pop capture stack + void pushCapture(unsigned page) const; + unsigned popCapture() const; // returns page + + // replace top of capture stack with this + void replaceCapture(unsigned page) const; + protected: + void _bind(unsigned page) const; + + unsigned _fbos[8]; + unsigned _texs[8]; + int _w, _h; - bool _fitToScreen; - unsigned g_frameBuffer; - unsigned g_depthRenderBuffer; - unsigned g_dynamicTextureID; + mutable unsigned _curpage; // 0 if not currently bound + unsigned _numpages, _numfbos; int texw, texh; + int viewportW, viewportH; bool inited; }; diff --git a/BBGE/GLLoad.cpp b/BBGE/GLLoad.cpp index 589268e..6400130 100644 --- a/BBGE/GLLoad.cpp +++ b/BBGE/GLLoad.cpp @@ -98,22 +98,13 @@ PFNGLUNIFORM3IVARBPROC glUniform3ivARB = NULL; PFNGLUNIFORM4IVARBPROC glUniform4ivARB = NULL; // GL_ARB_shader_objects and related -PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT = NULL; -PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT = NULL; -PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT = NULL; -PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT = NULL; -PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT = NULL; -PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT = NULL; PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT = NULL; PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = NULL; PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT = NULL; PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = NULL; PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = NULL; -PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT = NULL; PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT = NULL; -PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT = NULL; -PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT = NULL; -PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT = NULL; +PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB = NULL; // ARB_vertex_buffer_object PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; @@ -124,6 +115,8 @@ PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL; PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL; +PFNGLCOPYIMAGESUBDATAEXTPROC glCopyImageSubDataEXT = NULL; + // extern unsigned g_dbg_numRenderCalls = 0; bool g_has_GL_GENERATE_MIPMAP = false; @@ -182,22 +175,16 @@ bool lookup_all_glsyms() debugLog(os.str()); } // framebuffer - glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC)SDL_GL_GetProcAddress("glIsRenderbufferEXT"); - glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)SDL_GL_GetProcAddress("glBindRenderbufferEXT"); - glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)SDL_GL_GetProcAddress("glDeleteRenderbuffersEXT"); - glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)SDL_GL_GetProcAddress("glGenRenderbuffersEXT"); - glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)SDL_GL_GetProcAddress("glRenderbufferStorageEXT"); - glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)SDL_GL_GetProcAddress("glGetRenderbufferParameterivEXT"); glIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC)SDL_GL_GetProcAddress("glIsFramebufferEXT"); glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)SDL_GL_GetProcAddress("glBindFramebufferEXT"); glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)SDL_GL_GetProcAddress("glDeleteFramebuffersEXT"); glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)SDL_GL_GetProcAddress("glGenFramebuffersEXT"); glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT"); - glFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC)SDL_GL_GetProcAddress("glFramebufferTexture1DEXT"); glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)SDL_GL_GetProcAddress("glFramebufferTexture2DEXT"); - glFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC)SDL_GL_GetProcAddress("glFramebufferTexture3DEXT"); - glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)SDL_GL_GetProcAddress("glFramebufferRenderbufferEXT"); - glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameterivEXT"); + glDrawBuffersARB = NULL;//(PFNGLDRAWBUFFERSARBPROC)SDL_GL_GetProcAddress("glDrawBuffersARB"); + + // GL 4.3+, but maybe available as an extension + glCopyImageSubDataEXT = (PFNGLCOPYIMAGESUBDATAEXTPROC)SDL_GL_GetProcAddress("glCopyImageSubDataEXT"); // shaders glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)SDL_GL_GetProcAddress("glCreateProgramObjectARB"); @@ -265,22 +252,13 @@ void unload_all_glsyms() // set these back to NULL and reload them upon reinit, otherwise they // might point to a bogus address when the shared library is reloaded. - glIsRenderbufferEXT = NULL; - glBindRenderbufferEXT = NULL; - glDeleteRenderbuffersEXT = NULL; - glGenRenderbuffersEXT = NULL; - glRenderbufferStorageEXT = NULL; - glGetRenderbufferParameterivEXT = NULL; glIsFramebufferEXT = NULL; glBindFramebufferEXT = NULL; glDeleteFramebuffersEXT = NULL; glGenFramebuffersEXT = NULL; glCheckFramebufferStatusEXT = NULL; - glFramebufferTexture1DEXT = NULL; glFramebufferTexture2DEXT = NULL; - glFramebufferTexture3DEXT = NULL; - glFramebufferRenderbufferEXT = NULL; - glGetFramebufferAttachmentParameterivEXT = NULL; + glDrawBuffersARB = NULL; glCreateProgramObjectARB = NULL; glDeleteObjectARB = NULL; diff --git a/BBGE/OpenGLStubs.h b/BBGE/OpenGLStubs.h index 859291a..3d57d9d 100644 --- a/BBGE/OpenGLStubs.h +++ b/BBGE/OpenGLStubs.h @@ -87,6 +87,8 @@ 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,glReadBuffer,(GLenum mode),(mode),) +GL_FUNC(void,glDrawBuffer,(GLenum mode),(mode),) // stuff GLU needs... GL_FUNC(void,glGetIntegerv,(GLenum pname, GLint *params),(pname,params),) diff --git a/BBGE/RenderBase.h b/BBGE/RenderBase.h index 602b5de..b57c107 100644 --- a/BBGE/RenderBase.h +++ b/BBGE/RenderBase.h @@ -50,6 +50,10 @@ GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GL #endif #endif /* GL_ARB_debug_output */ +#if !defined(GL_ARB_copy_image) && !defined(GL_VERSION_4_3) +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAEXTPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +#endif + #ifdef _WINDOWS_ #error windows.h was included! euuugh! #endif @@ -86,22 +90,13 @@ extern PFNGLUNIFORM2IVARBPROC glUniform2ivARB; extern PFNGLUNIFORM3IVARBPROC glUniform3ivARB; extern PFNGLUNIFORM4IVARBPROC glUniform4ivARB; -extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT; -extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT; -extern PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT; -extern PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT; -extern PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT; -extern PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT; extern PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT; extern PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; extern PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; extern PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; -extern PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT; extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; -extern PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT; -extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT; -extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT; +extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB; extern PFNGLGENBUFFERSARBPROC glGenBuffersARB; extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; @@ -111,4 +106,6 @@ extern PFNGLBINDBUFFERARBPROC glBindBufferARB; extern PFNGLMAPBUFFERARBPROC glMapBufferARB; extern PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; +extern PFNGLCOPYIMAGESUBDATAEXTPROC glCopyImageSubDataEXT; + #endif diff --git a/BBGE/RenderObjectLayer.cpp b/BBGE/RenderObjectLayer.cpp index 9d4c691..3cdd6a2 100644 --- a/BBGE/RenderObjectLayer.cpp +++ b/BBGE/RenderObjectLayer.cpp @@ -37,6 +37,8 @@ RenderObjectLayer::RenderObjectLayer() renderObjects[i] = 0; objectCount = 0; firstFreeIdx = 0; + preRender = NULL; + postRender = NULL; } RenderObjectLayer::~RenderObjectLayer() @@ -251,8 +253,10 @@ void RenderObjectLayer::render(const RenderState& rs) const { assert(rs.pass == RenderObject::RENDER_ALL); const RenderObject * const * rlist = &toRender[0]; // known to have at least one element - while(const RenderObject *ro = *rlist++) + const RenderObject *ro = *rlist++; + do ro->render(rs); + while( (ro = *rlist++) ); proc += toRender.size() - 1; } else diff --git a/BBGE/ScreenTransition.cpp b/BBGE/ScreenTransition.cpp index 04bd213..9885776 100644 --- a/BBGE/ScreenTransition.cpp +++ b/BBGE/ScreenTransition.cpp @@ -81,11 +81,12 @@ void ScreenTransition::reloadDevice() void ScreenTransition::capture() { assert(screen_texture); - core->render(); + core->renderExternal(); if (screen_texture) { glBindTexture(GL_TEXTURE_2D,screen_texture); + glReadBuffer(GL_BACK); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, windowWidth, windowHeight); glBindTexture(GL_TEXTURE_2D, 0); }