mirror of
https://github.com/rwengine/openrw.git
synced 2024-09-18 16:32:32 +02:00
Improve graphics performance
+ Add Framebuffer rendering to store data + Re-implement water using projected grid aproach
This commit is contained in:
parent
2985a70354
commit
30e059a0b6
@ -11,6 +11,7 @@
|
||||
#include <render/OpenGLRenderer.hpp>
|
||||
#include "MapRenderer.hpp"
|
||||
#include "TextRenderer.hpp"
|
||||
#include "WaterRenderer.hpp"
|
||||
|
||||
class Model;
|
||||
class ModelFrame;
|
||||
@ -78,9 +79,15 @@ 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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -79,6 +79,8 @@ public:
|
||||
|
||||
const AttributeList& getDataAttributes() const
|
||||
{ return attributes; }
|
||||
AttributeList& getDataAttributes()
|
||||
{ return attributes; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
48
rwengine/include/render/WaterRenderer.hpp
Normal file
48
rwengine/include/render/WaterRenderer.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <engine/RWTypes.hpp>
|
||||
#include <render/OpenGLRenderer.hpp>
|
||||
|
||||
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<int> maskSizes;
|
||||
|
||||
DrawBuffer gridDraw;
|
||||
GeometryBuffer gridGeom;
|
||||
|
||||
GLuint fbOutput;
|
||||
GLuint dataTexture;
|
||||
};
|
@ -37,20 +37,6 @@ struct WaterVertex {
|
||||
float x, y;
|
||||
};
|
||||
|
||||
std::vector<WaterVertex> waterLQVerts = {
|
||||
{1.0f, 1.0f},
|
||||
{0.0f, 1.0f},
|
||||
{1.0f,-0.0f},
|
||||
{0.0f,-0.0f}
|
||||
};
|
||||
|
||||
std::vector<WaterVertex> 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());
|
||||
|
||||
@ -107,43 +93,38 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
||||
|
||||
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 );
|
||||
|
||||
// Upload water plane
|
||||
waterLQBuffer.uploadVertices(waterLQVerts);
|
||||
waterLQDraw.addGeometry(&waterLQBuffer);
|
||||
waterLQDraw.setFaceType(GL_TRIANGLE_STRIP);
|
||||
glGenFramebuffers(1, &framebufferName);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
|
||||
glGenTextures(2, fbTextures);
|
||||
|
||||
// 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 });
|
||||
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);
|
||||
|
||||
waterHQVerts.push_back({xB + vertStep, yB });
|
||||
waterHQVerts.push_back({xB, yB + vertStep});
|
||||
waterHQVerts.push_back({xB, yB });
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -532,6 +452,18 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
|
||||
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);
|
||||
glBindBuffer(GL_ELEMENT_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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
})";
|
||||
|
||||
}
|
159
rwengine/src/render/WaterRenderer.cpp
Normal file
159
rwengine/src/render/WaterRenderer.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
#include <render/WaterRenderer.hpp>
|
||||
#include <render/GameRenderer.hpp>
|
||||
#include <render/GameShaders.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
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<glm::vec2> 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<glm::vec3> 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);
|
||||
}
|
@ -94,6 +94,8 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
|
||||
|
||||
StateManager::get().enter(new LoadingState(this));
|
||||
|
||||
getRenderer()->water.setWaterTable(engine->gameData.waterHeights, 48, engine->gameData.realWater, 128*128);
|
||||
|
||||
engine->logger.info("Game", "Started");
|
||||
}
|
||||
|
||||
@ -247,7 +249,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 )
|
||||
|
Loading…
Reference in New Issue
Block a user