From 6851c5e01120348fa99e8f5df020a0ed8a1aab0e Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Sat, 28 Mar 2015 13:42:29 +0000 Subject: [PATCH] Improve graphics performance + Add Framebuffer rendering to store data + Re-implement water using projected grid aproach --- rwengine/include/render/GameRenderer.hpp | 11 +- rwengine/include/render/GameShaders.hpp | 21 ++- rwengine/include/render/GeometryBuffer.hpp | 2 + rwengine/include/render/WaterRenderer.hpp | 48 ++++++ rwengine/src/render/GameRenderer.cpp | 192 ++++++++------------- rwengine/src/render/GameShaders.cpp | 113 ++++++++++-- rwengine/src/render/WaterRenderer.cpp | 159 +++++++++++++++++ rwgame/RWGame.cpp | 5 +- 8 files changed, 406 insertions(+), 145 deletions(-) create mode 100644 rwengine/include/render/WaterRenderer.hpp create mode 100644 rwengine/src/render/WaterRenderer.cpp diff --git a/rwengine/include/render/GameRenderer.hpp b/rwengine/include/render/GameRenderer.hpp index 4dfb3085..cdc4001d 100644 --- a/rwengine/include/render/GameRenderer.hpp +++ b/rwengine/include/render/GameRenderer.hpp @@ -11,6 +11,7 @@ #include #include "MapRenderer.hpp" #include "TextRenderer.hpp" +#include "WaterRenderer.hpp" class Model; class ModelFrame; @@ -77,10 +78,16 @@ class GameRenderer /** Camera values passed to renderWorld() */ ViewCamera _camera; + + GLuint framebufferName; + GLuint fbTextures[2]; + GLuint fbRenderBuffers[1]; + Renderer::ShaderProgram* postProg; public: GameRenderer(GameWorld*); + ~GameRenderer(); /** Number of issued draw calls */ size_t rendered; @@ -92,7 +99,6 @@ public: /** @todo Clean up all these shader program and location variables */ Renderer::ShaderProgram* worldProg; Renderer::ShaderProgram* skyProg; - Renderer::ShaderProgram* waterProg; Renderer::ShaderProgram* particleProg; GLuint ssRectProgram; @@ -181,7 +187,10 @@ public: return renderer; } + void setViewport(int w, int h); + MapRenderer map; + WaterRenderer water; TextRenderer text; }; diff --git a/rwengine/include/render/GameShaders.hpp b/rwengine/include/render/GameShaders.hpp index c93d3fda..d27f9acd 100644 --- a/rwengine/include/render/GameShaders.hpp +++ b/rwengine/include/render/GameShaders.hpp @@ -2,15 +2,26 @@ #ifndef _GAMESHADERS_HPP_ #define _GAMESHADERS_HPP_ +#define SHADER_VF(Name) \ +struct Name {\ + static const char* VertexShader;\ + static const char* FragmentShader;\ +}; + /** * @brief collection of shaders to make managing them a little easier. */ namespace GameShaders { -struct WaterHQ { - static const char* VertexShader; - static const char* FragmentShader; -}; +/** + * High Quality Projected-Grid water shader + */ +SHADER_VF(WaterHQ) + +/** + * Simple 3D masking shader + */ +SHADER_VF(Mask3D) struct Sky { static const char* VertexShader; @@ -37,6 +48,8 @@ struct ScreenSpaceRect { static const char* FragmentShader; }; +SHADER_VF(DefaultPostProcess); + } #endif diff --git a/rwengine/include/render/GeometryBuffer.hpp b/rwengine/include/render/GeometryBuffer.hpp index 7ec67ae2..1a8bb08f 100644 --- a/rwengine/include/render/GeometryBuffer.hpp +++ b/rwengine/include/render/GeometryBuffer.hpp @@ -79,6 +79,8 @@ public: const AttributeList& getDataAttributes() const { return attributes; } + AttributeList& getDataAttributes() + { return attributes; } }; #endif diff --git a/rwengine/include/render/WaterRenderer.hpp b/rwengine/include/render/WaterRenderer.hpp new file mode 100644 index 00000000..976a47db --- /dev/null +++ b/rwengine/include/render/WaterRenderer.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +class GameRenderer; +class GameWorld; + +/** + * Implements the rendering routines for drawing the sea water. + */ +class WaterRenderer +{ +public: + WaterRenderer(GameRenderer* renderer); + ~WaterRenderer(); + + /** + * Creates the required data for rendering the water. Accepts + * two arrays. waterHeights stores the real world heights which + * are indexed into by the array tiles for each water tile. + * + * This data is used to create the internal stencil mask for clipping + * the water rendering. + */ + void setWaterTable(float* waterHeights, unsigned int nHeights, uint8_t* tiles, unsigned int nTiles); + + void setDataTexture(GLuint fbBinding, GLuint dataTexture); + + /** + * Render the water using the currently active render state + */ + void render(GameRenderer* renderer, GameWorld* world); +private: + Renderer::ShaderProgram* waterProg; + Renderer::ShaderProgram* maskProg; + + DrawBuffer maskDraw; + GeometryBuffer maskGeom; + + std::vector maskSizes; + + DrawBuffer gridDraw; + GeometryBuffer gridGeom; + + GLuint fbOutput; + GLuint dataTexture; +}; diff --git a/rwengine/src/render/GameRenderer.cpp b/rwengine/src/render/GameRenderer.cpp index 2c9156ce..37acfc48 100644 --- a/rwengine/src/render/GameRenderer.cpp +++ b/rwengine/src/render/GameRenderer.cpp @@ -37,20 +37,6 @@ struct WaterVertex { float x, y; }; -std::vector waterLQVerts = { - {1.0f, 1.0f}, - {0.0f, 1.0f}, - {1.0f,-0.0f}, - {0.0f,-0.0f} -}; - -std::vector waterHQVerts; - -GeometryBuffer waterLQBuffer; -DrawBuffer waterLQDraw; -GeometryBuffer waterHQBuffer; -DrawBuffer waterHQDraw; - /// @todo collapse all of these into "VertPNC" etc. struct ParticleVert { static const AttributeList vertex_attributes() { @@ -81,7 +67,7 @@ DrawBuffer ssRectDraw; GameRenderer::GameRenderer(GameWorld* engine) : engine(engine), renderer(new OpenGLRenderer), _renderAlpha(0.f), - map(engine, renderer), text(engine, this) + map(engine, renderer), water(this), text(engine, this) { engine->logger.info("Renderer", renderer->getIDString()); @@ -102,49 +88,44 @@ GameRenderer::GameRenderer(GameWorld* engine) renderer->setProgramBlockBinding(particleProg, "ObjectData", 2); skyProg = renderer->createShader( - GameShaders::Sky::VertexShader, - GameShaders::Sky::FragmentShader); + GameShaders::Sky::VertexShader, + GameShaders::Sky::FragmentShader); renderer->setProgramBlockBinding(skyProg, "SceneData", 1); - waterProg = renderer->createShader( - GameShaders::WaterHQ::VertexShader, - GameShaders::WaterHQ::FragmentShader); - - renderer->setUniformTexture(waterProg, "texture", 0); - - renderer->setProgramBlockBinding(waterProg, "SceneData", 1); - renderer->setProgramBlockBinding(waterProg, "ObjectData", 2); + postProg = renderer->createShader( + GameShaders::DefaultPostProcess::VertexShader, + GameShaders::DefaultPostProcess::FragmentShader); glGenVertexArrays( 1, &vao ); + + glGenFramebuffers(1, &framebufferName); + glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + glGenTextures(2, fbTextures); + + glBindTexture(GL_TEXTURE_2D, fbTextures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, fbTextures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, 128, 128, 0, GL_RED, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // Upload water plane - waterLQBuffer.uploadVertices(waterLQVerts); - waterLQDraw.addGeometry(&waterLQBuffer); - waterLQDraw.setFaceType(GL_TRIANGLE_STRIP); - - // Generate HQ water geometry - int waterverts = 5; - float vertStep = 1.f/waterverts; - for(int x = 0; x < waterverts; ++x) { - float xB = vertStep * x; - for(int y = 0; y < waterverts; ++y) { - float yB = vertStep * y; - waterHQVerts.push_back({xB + vertStep, yB + vertStep}); - waterHQVerts.push_back({xB, yB + vertStep}); - waterHQVerts.push_back({xB + vertStep, yB }); - - waterHQVerts.push_back({xB + vertStep, yB }); - waterHQVerts.push_back({xB, yB + vertStep}); - waterHQVerts.push_back({xB, yB }); - } - } - - waterHQBuffer.uploadVertices(waterHQVerts); - waterHQDraw.addGeometry(&waterHQBuffer); - waterHQDraw.setFaceType(GL_TRIANGLES); - - + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbTextures[0], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbTextures[1], 0); + + // Give water renderer the data texture + water.setDataTexture(1, fbTextures[1]); + + glGenRenderbuffers(1, fbRenderBuffers); + glBindRenderbuffer(GL_RENDERBUFFER, fbRenderBuffers[0]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 128, 128); + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbRenderBuffers[0] + ); + // Create the skydome size_t segments = skydomeSegments, rows = skydomeRows; @@ -199,6 +180,7 @@ GameRenderer::GameRenderer(GameWorld* engine) ssRectGeom.uploadVertices(sspaceRect); ssRectDraw.addGeometry(&ssRectGeom); + ssRectDraw.setFaceType(GL_TRIANGLE_STRIP); ssRectProgram = compileProgram(GameShaders::ScreenSpaceRect::VertexShader, GameShaders::ScreenSpaceRect::FragmentShader); @@ -233,6 +215,11 @@ GameRenderer::GameRenderer(GameWorld* engine) cylinderBuffer.setFaceType(GL_TRIANGLES); } +GameRenderer::~GameRenderer() +{ + glDeleteFramebuffers(1, &framebufferName); +} + float mix(uint8_t a, uint8_t b, float num) { return a+(b-a)*num; @@ -248,6 +235,8 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha) // Set the viewport const glm::ivec2& vp = getRenderer()->getViewport(); glViewport(0, 0, vp.x, vp.y); + glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + glClear(GL_DEPTH_BUFFER_BIT); glBindVertexArray( vao ); @@ -394,76 +383,7 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha) } glDepthMask(GL_TRUE); - // Draw the water. - renderer->useProgram( waterProg ); - - float blockLQSize = WATER_WORLD_SIZE/WATER_LQ_DATA_SIZE; - float blockHQSize = WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE; - - glm::vec2 waterOffset { -WATER_WORLD_SIZE/2.f, -WATER_WORLD_SIZE/2.f }; - auto waterTex = engine->gameData.findTexture("water_old"); - glActiveTexture(GL_TEXTURE0); - - auto camposFlat = glm::vec2(camera.position); - - Renderer::DrawParameters wdp; - wdp.start = 0; - wdp.count = waterHQVerts.size(); - wdp.texture = waterTex->getName(); - - renderer->useProgram(waterProg); - renderer->setSceneParameters(sceneParams); - - // Draw High detail water - renderer->setUniform(waterProg, "size", blockHQSize); - renderer->setUniform(waterProg, "time", engine->gameTime); - renderer->setUniform(waterProg, "waveParams", glm::vec2(WATER_SCALE, WATER_HEIGHT)); - - for( int x = 0; x < WATER_HQ_DATA_SIZE; x++ ) { - for( int y = 0; y < WATER_HQ_DATA_SIZE; y++ ) { - auto waterWS = waterOffset + glm::vec2(blockHQSize) * glm::vec2(x, y); - auto cullWS = waterWS + (blockHQSize / 2.f); - - // Check that this is the right time to draw the HQ water - if( glm::distance(camposFlat, cullWS) - blockHQSize >= WATER_HQ_DISTANCE ) continue; - - int i = (x*WATER_HQ_DATA_SIZE) + y; - int hI = engine->gameData.realWater[i]; - if( hI >= NO_WATER_INDEX ) continue; - float h = engine->gameData.waterHeights[hI]; - - glm::mat4 m; - m = glm::translate(m, glm::vec3(waterWS, h)); - - renderer->drawArrays(m, &waterHQDraw, wdp); - } - } - - - wdp.count = waterLQVerts.size(); - renderer->setUniform(waterProg, "size", blockLQSize); - renderer->setUniform(waterProg, "waveParams", glm::vec2(0.f)); - - for( int x = 0; x < WATER_LQ_DATA_SIZE; x++ ) { - for( int y = 0; y < WATER_LQ_DATA_SIZE; y++ ) { - auto waterWS = waterOffset + glm::vec2(blockLQSize) * glm::vec2(x, y); - auto cullWS = waterWS + (blockLQSize / 2.f); - - // Check that this is the right time to draw the LQ - if( glm::distance(camposFlat, cullWS) - blockHQSize/4.f < WATER_HQ_DISTANCE ) continue; - if( glm::distance(camposFlat, cullWS) - blockLQSize/2.f > camera.frustum.far ) continue; - - int i = (x*WATER_LQ_DATA_SIZE) + y; - int hI = engine->gameData.visibleWater[i]; - if( hI >= NO_WATER_INDEX ) continue; - float h = engine->gameData.waterHeights[hI]; - - glm::mat4 m; - m = glm::translate(m, glm::vec3(waterWS, h)); - - renderer->drawArrays(m, &waterLQDraw, wdp); - } - } + water.render(this, engine); glBindVertexArray( vao ); @@ -531,6 +451,18 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha) if( (engine->state.isCinematic || engine->state.currentCutscene ) && splashTexName == 0 ) { renderLetterbox(); } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + renderer->useProgram(postProg); + + Renderer::DrawParameters wdp; + wdp.start = 0; + wdp.count = ssRectGeom.getCount(); + wdp.texture = fbTextures[0]; + + renderer->drawArrays(glm::mat4(), &ssRectDraw, wdp); glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -1205,3 +1137,19 @@ void GameRenderer::renderLetterbox() glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } +void GameRenderer::setViewport(int w, int h) +{ + auto& lastViewport = renderer->getViewport(); + if( lastViewport.x != w || lastViewport.y != h) + { + renderer->setViewport({w, h}); + + glBindTexture(GL_TEXTURE_2D, fbTextures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, fbTextures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, w, h, 0, GL_RED, GL_FLOAT, NULL); + + glBindRenderbuffer(GL_RENDERBUFFER, fbRenderBuffers[0]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); + } +} diff --git a/rwengine/src/render/GameShaders.cpp b/rwengine/src/render/GameShaders.cpp index 62648d1d..f5d27eee 100644 --- a/rwengine/src/render/GameShaders.cpp +++ b/rwengine/src/render/GameShaders.cpp @@ -21,26 +21,43 @@ layout(std140) uniform SceneData { float fogEnd; }; -layout(std140) uniform ObjectData { - mat4 model; - vec4 colour; - float diffusefac; - float ambientfac; - float visibility; -}; - -uniform float size; - uniform float time; uniform vec2 waveParams; +uniform sampler2D data; + +vec3 waterNormal = vec3(0.0, 0.0, 1.0); + +vec3 planeIntercept( vec3 start, vec3 dir, float height ) +{ + float dist = (height - dot(waterNormal, start)) / dot(dir, waterNormal); + if( dist < 0.0 ) + { + return start + dir * dist; + } + else + { + // uh oh + return vec3(0.0); + } +} void main() { - mat4 MVP = projection * view; - vec4 vp = model * vec4(position * size, 0.0, 1.0); - vp.z = (1.0+sin(time + (vp.x + vp.y) * waveParams.x)) * waveParams.y; - TexCoords = position * 2.0; - gl_Position = MVP * vp; + TexCoords = position * vec2(0.5,0.5) + vec2(0.5); + + mat4 vp = projection * view; + mat4 projector = inverse(vp); + + mat3 rot = mat3(view); + vec3 ray = vec3(-position.x, -position.y, projection[0][0] ) * rot; + + float plane = texture2D( data, TexCoords ).r; + + vec3 ws = planeIntercept( campos.xyz, ray, plane ); + + ws.z = ws.z + (-1.0+(sin(time + (ws.x + ws.y) * waveParams.x)) * waveParams.y); + TexCoords = ws.xy / 5.0; + gl_Position = vp * vec4(ws, 1.0); })"; const char* WaterHQ::FragmentShader = R"( @@ -49,11 +66,46 @@ in vec3 Normal; in vec2 TexCoords; uniform sampler2D texture; out vec4 outColour; +in vec3 test; void main() { vec4 c = texture2D(texture, TexCoords); outColour = c; })"; +const char* Mask3D::VertexShader = R"( +#version 130 +#extension GL_ARB_explicit_attrib_location : enable +#extension GL_ARB_uniform_buffer_object : enable + +layout(location = 0) in vec3 position; + +layout(std140) uniform SceneData { + mat4 projection; + mat4 view; + vec4 ambient; + vec4 dynamic; + vec4 fogColor; + vec4 campos; + float fogStart; + float fogEnd; +}; + +out vec3 pp; + +void main() +{ + pp = position; + gl_Position = projection * view * vec4(position, 1.0); +})"; + +const char* Mask3D::FragmentShader = R"( +#version 130 +in vec3 pp; +out vec4 outColour; +void main() { + outColour = vec4(pp.z, 0.0, 0.0, 1.0); +})"; + const char* Sky::VertexShader = R"( #version 130 #extension GL_ARB_explicit_attrib_location : enable @@ -260,4 +312,33 @@ void main() // Set colour to 0, 0, 0, 1 for textured mode. outColour = vec4(colour.rgb + c.rgb, colour.a); })"; -} + +const char* DefaultPostProcess::VertexShader = R"( +#version 130 +#extension GL_ARB_explicit_attrib_location : enable +#extension GL_ARB_uniform_buffer_object : enable + +layout(location = 0) in vec2 position; +out vec2 TexCoords; +void main() +{ + TexCoords = position * vec2(0.5,0.5) + vec2(0.5); + gl_Position = vec4(position, 0.0, 1.0); +})"; + +const char* DefaultPostProcess::FragmentShader = R"( +#version 130 +in vec2 TexCoords; + +uniform sampler2D colour; +uniform sampler2D data; + +out vec4 outColour; + +void main() +{ + vec4 c = texture2D(colour, TexCoords); + outColour = c; +})"; + +} \ No newline at end of file diff --git a/rwengine/src/render/WaterRenderer.cpp b/rwengine/src/render/WaterRenderer.cpp new file mode 100644 index 00000000..e379cf1f --- /dev/null +++ b/rwengine/src/render/WaterRenderer.cpp @@ -0,0 +1,159 @@ +#include +#include +#include +#include + +#include + +WaterRenderer::WaterRenderer(GameRenderer* renderer) +: waterProg(nullptr) +{ + maskDraw.setFaceType(GL_TRIANGLES); + gridDraw.setFaceType(GL_TRIANGLES); + + waterProg = renderer->getRenderer()->createShader( + GameShaders::WaterHQ::VertexShader, GameShaders::WaterHQ::FragmentShader + ); + maskProg = renderer->getRenderer()->createShader( + GameShaders::Mask3D::VertexShader, GameShaders::Mask3D::FragmentShader + ); + + renderer->getRenderer()->setProgramBlockBinding(waterProg, "SceneData", 1); + renderer->getRenderer()->setProgramBlockBinding(maskProg, "SceneData", 1); + + renderer->getRenderer()->setUniformTexture(waterProg, "data", 1); + + // Generate grid mesh + int gridres = 60; + std::vector grid; + float gridresinv = 1.f / (gridres*0.5f); + glm::vec2 b(-1.f,-1.f); + for( int x = 0; x < gridres; x++ ) + { + for( int y = 0; y < gridres; y++ ) + { + glm::vec2 tMin( b + glm::vec2(x,y) * gridresinv ); + glm::vec2 tMax( b + glm::vec2(x+1,y+1) * gridresinv ); + + // Build geometry + grid.push_back(glm::vec2(tMax.x, tMax.y)); + grid.push_back(glm::vec2(tMax.x, tMin.y)); + grid.push_back(glm::vec2(tMin.x, tMin.y)); + + grid.push_back(glm::vec2(tMin.x, tMin.y)); + grid.push_back(glm::vec2(tMin.x, tMax.y)); + grid.push_back(glm::vec2(tMax.x, tMax.y)); + } + } + + gridGeom.uploadVertices(grid.size(), sizeof(glm::vec2)*grid.size(), grid.data()); + gridGeom.getDataAttributes().push_back( + {ATRS_Position, 2, 0, 0, GL_FLOAT} + ); + gridDraw.addGeometry(&gridGeom); +} + +WaterRenderer::~WaterRenderer() +{ + +} + +void WaterRenderer::setWaterTable(float* waterHeights, unsigned int nHeights, uint8_t* tiles, unsigned int nTiles) +{ + // Determine the dimensions of the input tiles + int edgeNum = sqrt(nTiles); + float tileSize = WATER_WORLD_SIZE/edgeNum; + glm::vec2 wO { -WATER_WORLD_SIZE/2.f, -WATER_WORLD_SIZE/2.f }; + + std::vector vertexData; + + for( int x = 0; x < edgeNum; x++ ) + { + int xi = x * WATER_HQ_DATA_SIZE; + for( int y = 0; y < edgeNum; y++ ) + { + // Tiles with the magic value contain no water. + if( tiles[xi + y] >= NO_WATER_INDEX ) continue; + float h = waterHeights[tiles[xi + y]]; + float hMax = h + WATER_HEIGHT; + glm::vec2 tMin( wO + glm::vec2(x,y) * tileSize ); + glm::vec2 tMax( wO + glm::vec2(x+1,y+1) * tileSize ); + + // Build geometry + vertexData.push_back(glm::vec3(tMax.x, tMax.y, hMax)); + vertexData.push_back(glm::vec3(tMax.x, tMin.y, hMax)); + vertexData.push_back(glm::vec3(tMin.x, tMin.y, hMax)); + + vertexData.push_back(glm::vec3(tMin.x, tMin.y, hMax)); + vertexData.push_back(glm::vec3(tMin.x, tMax.y, hMax)); + vertexData.push_back(glm::vec3(tMax.x, tMax.y, hMax)); + } + } + + maskGeom.uploadVertices(vertexData.size(), sizeof(glm::vec3)*vertexData.size(), vertexData.data()); + maskGeom.getDataAttributes().push_back( + {ATRS_Position, 3, 0, 0, GL_FLOAT} + ); + maskDraw.addGeometry(&maskGeom); +} + +void WaterRenderer::setDataTexture(GLuint fbBinding, GLuint dataTex) +{ + fbOutput = fbBinding; + dataTexture = dataTex; +} + +void WaterRenderer::render(GameRenderer* renderer, GameWorld* world) +{ + auto r = renderer->getRenderer(); + + auto waterTex = world->gameData.findTexture("water_old"); + + Renderer::DrawParameters wdp; + wdp.start = 0; + wdp.count = maskGeom.getCount(); + wdp.texture = 0; + glm::mat4 m(1.0); + + glEnable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilMask(0xFF); + + GLenum buffers[] = {GL_COLOR_ATTACHMENT1}; + glDrawBuffers(1, buffers); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + r->useProgram( maskProg ); + + + r->drawArrays(m, &maskDraw, wdp); + + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilMask(0x00); + glEnable(GL_DEPTH_TEST); + + r->useProgram( waterProg ); + + buffers[0] = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, buffers); + + r->setUniform(waterProg, "time", world->gameTime); + r->setUniform(waterProg, "waveParams", glm::vec2(WATER_SCALE, WATER_HEIGHT)); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, dataTexture); + glActiveTexture(GL_TEXTURE0); + wdp.count = gridGeom.getCount(); + wdp.texture = waterTex->getName(); + + r->drawArrays(m, &gridDraw, wdp); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + glDisable(GL_STENCIL_TEST); +} diff --git a/rwgame/RWGame.cpp b/rwgame/RWGame.cpp index e61b485c..920d4307 100644 --- a/rwgame/RWGame.cpp +++ b/rwgame/RWGame.cpp @@ -92,6 +92,8 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[]) /// @TODO language choices. engine->gameData.loadGXT("english.gxt"); + getRenderer()->water.setWaterTable(engine->gameData.waterHeights, 48, engine->gameData.realWater, 128*128); + for(int m = 0; m < MAP_BLOCK_SIZE; ++m) { std::string num = (m < 10 ? "0" : ""); @@ -109,7 +111,6 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[]) loading->setNextState(new MenuState(this)); } - StateManager::get().enter(loading); engine->logger.info("Game", "Started"); @@ -265,7 +266,7 @@ void RWGame::tick(float dt) void RWGame::render(float alpha, float time) { auto size = getWindow().getSize(); - renderer->getRenderer()->setViewport({size.x, size.y}); + renderer->setViewport(size.x, size.y); ViewCamera viewCam; if( engine->state.currentCutscene != nullptr && engine->state.cutsceneStartTime >= 0.f )