openspades/Sources/Draw/GLWaterRenderer.cpp
2016-11-22 23:08:35 +02:00

1081 lines
30 KiB
C++

/*
Copyright (c) 2013 yvt
This file is part of OpenSpades.
OpenSpades is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GLWaterRenderer.h"
#include "GLRenderer.h"
#include "IGLDevice.h"
#include "GLFramebufferManager.h"
#include <vector>
#include "GLProgramUniform.h"
#include "GLProgramAttribute.h"
#include "GLImage.h"
#include "GLProgram.h"
#include "GLShadowShader.h"
#include "../Core/Debug.h"
#include "../Client/GameMap.h"
#include "../Core/ConcurrentDispatch.h"
#include <stdlib.h>
#include "../kiss_fft130/kiss_fft.h"
#include "GLProfiler.h"
#include "../Core/Settings.h"
namespace spades {
namespace draw {
#pragma mark - Wave Tank Simulation
class GLWaterRenderer::IWaveTank: public ConcurrentDispatch {
protected:
float dt;
int size, samples;
private:
uint32_t *bitmap;
int Encode8bit(float v){
v = (v + 1.f) * .5f * 255.f;
v = floorf(v + .5f);
int i = (int)v;
if(i < 0) i = 0;
if(i > 255) i = 255;
return i;
}
uint32_t MakeBitmapPixel(float dx, float dy, float h){
float x = dx, y = dy, z = 0.04f;
float scale = 200.f;
x *= scale; y *= scale; z *= scale;
uint32_t out;
out = Encode8bit(z);
out |= Encode8bit(y) << 8;
out |= Encode8bit(x) << 16;
out |= Encode8bit(h * -10.f) << 24;
return out;
}
void MakeBitmapRow(float *h1, float *h2, float *h3,
uint32_t *out) {
out[0] = MakeBitmapPixel(h2[1] - h2[size-1],
h3[0]-h1[0],
h2[0]);
out[size-1] = MakeBitmapPixel(h2[0] - h2[size-2],
h3[size-1]-h1[size-1],
h2[size-1]);
for(int x = 1; x < size - 1; x++) {
out[x] = MakeBitmapPixel(h2[x+1]-h2[x-1], h3[x]-h1[x],
h2[x]);
}
}
public:
IWaveTank(int size):size(size) {
bitmap = new uint32_t[size*size];
samples = size * size;
}
virtual ~IWaveTank(){
delete[] bitmap;
}
void SetTimeStep(float dt){
this->dt = dt;
}
int GetSize() const {
return size;
}
uint32_t *GetBitmap() const {
return bitmap;
}
void MakeBitmap(float *height){
MakeBitmapRow(height + (size-1) * size,
height,
height + size,
bitmap);
MakeBitmapRow(height + (size-2) * size,
height + (size-1) * size,
height,
bitmap + (size-1)*size);
for(int y = 1; y < size - 1; y++){
MakeBitmapRow(height + (y - 1) * size,
height + y * size,
height + (y + 1) * size,
bitmap + y*size);
}
}
};
#pragma mark - FFT Wave Solver
struct SinCosTable {
float sinCoarse[256];
float cosCoarse[256];
float sinFine[256];
float cosFine[256];
public:
SinCosTable() {
for(int i = 0; i < 256; i++){
float ang = (float)i / 256.f * (float)M_PI * 2.f;
sinCoarse[i] = sinf(ang);
cosCoarse[i] = cosf(ang);
ang = (float)i / 65536.f * (float)M_PI * 2.f;
sinFine[i] = sinf(ang);
cosFine[i] = cosf(ang);
}
}
void Compute(unsigned int step, float& outSin, float &outCos) {
step &= 0xffff;
if(step == 0){
outSin = 0;
outCos = 1.f;
return;
}
int fine = step & 0xff;
int coarse = step >> 8;
outSin = sinCoarse[coarse];
outCos = cosCoarse[coarse];
if(fine != 0){
float c = cosFine[fine];
float s = sinFine[fine];
float c2 = outCos * c - outSin * s;
float s2 = outCos * s + outSin * c;
outCos = c2;
outSin = s2;
}
}
};
static SinCosTable sinCosTable;
template <int SizeBits>
class GLWaterRenderer::FFTWaveTank: public IWaveTank {
enum {
Size = 1 << SizeBits,
SizeHalf = Size / 2
};
kiss_fft_cfg fft;
typedef kiss_fft_cpx Complex;
struct Cell {
float magnitude;
uint32_t phase;
float phasePerSecond;
float m00, m01;
float m10, m11;
};
Cell cells[SizeHalf+1][Size];
Complex spectrum[SizeHalf+1][Size];
Complex temp1[Size];
Complex temp2[Size];
Complex temp3[Size][Size];
float height[Size][Size];
public:
FFTWaveTank(): IWaveTank(Size){
fft = kiss_fft_alloc(Size, 1, NULL, NULL);
for(int x = 0; x < Size; x++){
for(int y = 0; y <= SizeHalf; y++){
Cell& cell = cells[y][x];
if(x == 0 && y == 0){
cell.magnitude = 0;
cell.phasePerSecond = 0.f;
cell.phase = 0;
}else{
int cx = std::min(x, Size-x);
float dist = (float)sqrtf(cx*cx+y*y);
float mag = 0.8f / dist / (float)Size;
mag /= dist;
float scal = dist / (float)SizeHalf;
scal *= scal;
mag *= expf(-scal * 3.f);
cell.magnitude = mag;
cell.phase = mt_engine();
cell.phasePerSecond = dist * 1.e+9f * 128 / Size;
}
cell.m00 = GetRandom() - GetRandom();
cell.m01 = GetRandom() - GetRandom();
cell.m10 = GetRandom() - GetRandom();
cell.m11 = GetRandom() - GetRandom();
}
}
}
virtual ~FFTWaveTank(){
kiss_fft_free(fft);
}
virtual void Run() {
// advance cells
for(int x = 0; x < Size; x++){
for(int y = 0; y <= SizeHalf; y++){
Cell& cell = cells[y][x];
uint32_t dphase;
dphase = (uint32_t)(cell.phasePerSecond * dt);
cell.phase += dphase;
unsigned int phase = cell.phase >> 16;
float c, s;
sinCosTable.Compute(phase, s, c);
float u, v;
u = c * cell.m00 + s * cell.m01;
v = c * cell.m10 + s * cell.m11;
spectrum[y][x].r = u * cell.magnitude;
spectrum[y][x].i = v * cell.magnitude;
}
}
// rfft
for(int y = 0; y <= SizeHalf; y++){
for(int x = 0; x < Size; x++)
temp1[x] = spectrum[y][x];
kiss_fft(fft, temp1, temp2);
if(y == 0){
for(int x = 0; x < Size; x++){
temp3[x][0] = temp2[x];
}
}else if(y == SizeHalf){
for(int x = 0; x < Size; x++){
temp3[x][SizeHalf].r = temp2[x].r;
temp3[x][SizeHalf].i = 0.f;
}
}else{
for(int x = 0; x < Size; x++){
temp3[x][y] = temp2[x];
temp3[x][Size-y].r = temp2[x].r;
temp3[x][Size-y].i = -temp2[x].i;
}
}
}
for(int x = 0; x < Size; x++){
kiss_fft(fft, temp3[x], temp2);
for(int y = 0; y < Size; y++){
height[x][y] = temp2[y].r;
}
}
MakeBitmap((float *)height);
}
};
#pragma mark - FTCS PDE Solver
class GLWaterRenderer::StandardWaveTank: public IWaveTank {
float *height;
float *heightFiltered;
float *velocity;
template<bool xy>
void DoPDELine(float *vy, float *y1, float *y2, float *yy) {
int pitch = xy ? size : 1;
for(int i = 0; i < size; i++){
float v1 = *y1, v2 = *y2, v = *yy;
float force = v1 + v2 - (v+v);
force *= dt * 80.f;
*vy += force;
y1 += pitch;
y2 += pitch;
yy += pitch;
vy += pitch;
}
}
template<bool xy>
void Denoise(float *arr) {
int pitch = xy ? size : 1;
#if 1
if((arr[0] > 0.f &&
arr[(size-1)*pitch] < 0.f && arr[pitch] < 0.f) ||
(arr[0] < 0.f &&
arr[(size-1)*pitch] > 0.f && arr[pitch] > 0.f)){
float ttl = (arr[1] + arr[(size-1)*pitch]) * .5f;
arr[0] = ttl;
}
if((arr[(size-1)*pitch] > 0.f &&
arr[(size-2)*pitch] < 0.f && arr[0] < 0.f) ||
(arr[(size-1)*pitch] < 0.f &&
arr[(size-2)*pitch] > 0.f && arr[0] > 0.f)){
float ttl = (arr[0] + arr[(size-2)*pitch]) * .5f;
arr[(size-1)*pitch] = ttl;
}
for(int i = 1 ; i < size - 1; i++){
if((arr[i*pitch] > 0.f &&
arr[(i-1)*pitch] < 0.f && arr[(i+1)*pitch] < 0.f) ||
(arr[i*pitch] < 0.f &&
arr[(i-1)*pitch] > 0.f && arr[(i+1)*pitch] > 0.f)){
float ttl = (arr[(i+1)*pitch] + arr[(i-1)*pitch]) * .5f;
arr[i*pitch] = ttl;
}
}
#else
//Lax-Friedrich
float buf[256]; // TODO: variable size
SPAssert(size <= 256);
for(int i = 0; i < size; i++)
buf[i] = arr[i * pitch] * .5f;
arr[0] = buf[1] + buf[size-1];
arr[(size-1)*pitch] = buf[size-2] + buf[0];
for(int i = 1; i < size - 1; i++)
arr[i*pitch] = buf[i - 1] + buf[i + 1];
#endif
}
public:
StandardWaveTank(int size):
IWaveTank(size){
height = new float[size*size];
heightFiltered = new float[size*size];
velocity = new float[size*size];
std::fill(height, height + size * size, 0.f);
std::fill(velocity, velocity + size * size, 0.f);
}
virtual ~StandardWaveTank(){
delete[] height;
delete[] heightFiltered;
delete[] velocity;
}
virtual void Run() {
// advance time
for(int i = 0; i < samples; i++)
height[i] += velocity[i] * dt;
#ifndef NDEBUG
for(int i = 0; i < samples; i++)
SPAssert(!isnan(height[i]));
for(int i = 0; i < samples; i++)
SPAssert(!isnan(velocity[i]));
#endif
// solve ddz/dtt = c^2 (ddz/dxx + ddz/dyy)
// do ddz/dyy
DoPDELine<false>(velocity,
height + (size-1)*size,
height + size,
height);
DoPDELine<false>(velocity + (size-1)*size,
height + (size-2)*size,
height,
height + (size-1)*size);
for(int y = 1; y < size - 1; y++) {
DoPDELine<false>(velocity + y * size,
height + (y-1) * size,
height + (y+1) * size,
height + y * size);
}
// do ddz/dxx
DoPDELine<true>(velocity,
height + (size-1),
height + 1,
height);
DoPDELine<true>(velocity + (size-1),
height + (size-2),
height,
height + (size-1));
for(int x = 1; x < size - 1; x++) {
DoPDELine<true>(velocity + x,
height + (x-1),
height + (x+1),
height + x);
}
// make average 0
float sum = 0.f;
for(int i = 0; i < samples; i++)
sum += height[i];
sum /= (float)samples;
for(int i = 0; i < samples; i++)
height[i] -= sum;
// limit energy
sum = 0.f;
for(int i = 0; i < samples; i++){
sum += height[i] * height[i];
sum += velocity[i] * velocity[i];
}
sum = sqrtf(sum / (float)samples / 2.f) * 80.f;
if(sum > 1.f){
sum = 1.f / sum;
for(int i = 0; i < samples; i++){
height[i] *= sum;
velocity[i] *= sum;
}
}
// denoise
for(int i = 0; i < size; i++){
Denoise<true>(height + i);
}
for(int i = 0; i < size; i++){
Denoise<false>(height + i*size);
}
// add randomness
int count = (int)floorf(dt * 600.f);
if(count > 400) count = 400;
for(int i = 0; i < count; i++){
int ox = mt_engine() % (size - 2);
int oy = mt_engine() % (size - 2);
static const float gauss[] = {
0.225610111284052f,
0.548779777431897f,
0.225610111284052f
};
float strength = (GetRandom()-GetRandom()) * 0.15f * 100.f;
for(int x = 0; x < 3; x++)
for(int y = 0; y < 3; y++){
velocity[(x+ox)+(y+oy)*size] += strength * gauss[x] * gauss[y];
}
}
for(int i = 0; i < samples; i++)
heightFiltered[i] = height[i];// * height[i] * 100.f;
// build bitmap
MakeBitmap(heightFiltered);
}
};
#pragma mark - Water Renderer
void GLWaterRenderer::PreloadShaders(spades::draw::GLRenderer *renderer) {
auto &settings = renderer->GetSettings();
if((int) settings.r_water >= 3)
renderer->RegisterProgram("Shaders/Water3.program");
else if((int) settings.r_water >= 2)
renderer->RegisterProgram("Shaders/Water2.program");
else
renderer->RegisterProgram("Shaders/Water.program");
}
GLWaterRenderer::GLWaterRenderer(GLRenderer *renderer, client::GameMap *map):
renderer(renderer),
device(renderer->GetGLDevice()),
settings(renderer->GetSettings()),
map(map){
SPADES_MARK_FUNCTION();
if((int) settings.r_water >= 3)
program = renderer->RegisterProgram("Shaders/Water3.program");
else if((int) settings.r_water >= 2)
program = renderer->RegisterProgram("Shaders/Water2.program");
else
program = renderer->RegisterProgram("Shaders/Water.program");
BuildVertices();
tempDepthTexture = device->GenTexture();
device->BindTexture(IGLDevice::Texture2D,
tempDepthTexture);
device->TexImage2D(IGLDevice::Texture2D,
0,
IGLDevice::DepthComponent24,
device->ScreenWidth(),
device->ScreenHeight(),
0,
IGLDevice::DepthComponent,
IGLDevice::UnsignedInt, NULL);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMagFilter,
IGLDevice::Nearest);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMinFilter,
IGLDevice::Nearest);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureWrapS,
IGLDevice::ClampToEdge);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureWrapT,
IGLDevice::ClampToEdge);
tempFramebuffer = device->GenFramebuffer();
device->BindFramebuffer(IGLDevice::Framebuffer,
tempFramebuffer);
device->FramebufferTexture2D(IGLDevice::Framebuffer,
IGLDevice::DepthAttachment,
IGLDevice::Texture2D,
tempDepthTexture, 0);
// create water color texture
texture = device->GenTexture();
device->BindTexture(IGLDevice::Texture2D, texture);
device->TexImage2D(IGLDevice::Texture2D, 0,
IGLDevice::RGBA8,
map->Width(), map->Height(),
0, IGLDevice::RGBA, IGLDevice::UnsignedByte,
NULL);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMagFilter,
IGLDevice::Linear);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMinFilter,
IGLDevice::Linear);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureWrapS,
IGLDevice::Repeat);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureWrapT,
IGLDevice::Repeat);
w = map->Width();
h = map->Height();
updateBitmapPitch = (w + 31) / 32;
updateBitmap.resize(updateBitmapPitch * h);
bitmap.resize(w * h);
std::fill(updateBitmap.begin(), updateBitmap.end(),
0xffffffffUL);
std::fill(bitmap.begin(), bitmap.end(),
0xffffffffUL);
device->TexSubImage2D(IGLDevice::Texture2D,
0, 0, 0, w, h,
IGLDevice::BGRA, IGLDevice::UnsignedByte,
bitmap.data());
// create wave tank simlation
size_t numLayers = ((int) settings.r_water >= 2) ? 3 : 1;
for(size_t i = 0; i < numLayers; i++){
if ((int) settings.r_water >= 3) {
waveTanks.push_back(new FFTWaveTank<8>());
} else {
waveTanks.push_back(new FFTWaveTank<7>());
}
waveTextures.push_back(device->GenTexture());
device->BindTexture(IGLDevice::Texture2D, waveTextures[i]);
device->TexImage2D(IGLDevice::Texture2D, 0,
IGLDevice::RGBA8,
waveTanks[i]->GetSize(), waveTanks[i]->GetSize(),
0, IGLDevice::BGRA, IGLDevice::UnsignedByte,
NULL);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMagFilter,
IGLDevice::Linear);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMinFilter,
IGLDevice::LinearMipmapLinear);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureWrapS,
IGLDevice::Repeat);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureWrapT,
IGLDevice::Repeat);
if((float) settings.r_maxAnisotropy > 1.1f) {
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMaxAnisotropy,
(float) settings.r_maxAnisotropy);
}
}
occlusionQuery = 0;
}
struct GLWaterRenderer::Vertex {
float x, y;
};
void GLWaterRenderer::BuildVertices() {
SPADES_MARK_FUNCTION();
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
int meshSize = 16;
if((int) settings.r_water >= 2)
meshSize = 128;
float meshSizeInv = 1.f / (float)meshSize;
for(int y = -meshSize; y <= meshSize; y++) {
for(int x = -meshSize; x <= meshSize; x++){
Vertex v;
v.x = (float)(x) * meshSizeInv;
v.y = (float)(y) * meshSizeInv;
// higher density near the camera
v.x *= v.x * v.x;
v.y *= v.y * v.y;
vertices.push_back(v);
}
}
#define VID(x, y) (((x)+meshSize)+((y)+meshSize)*(meshSize * 2 + 1))
for(int x = -meshSize; x < meshSize; x++){
for(int y = -meshSize; y < meshSize; y++) {
indices.push_back(VID(x,y));
indices.push_back(VID(x+1,y));
indices.push_back(VID(x,y+1));
indices.push_back(VID(x+1,y));
indices.push_back(VID(x+1,y+1));
indices.push_back(VID(x,y+1));
}
}
buffer = device->GenBuffer();
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->BufferData(IGLDevice::ArrayBuffer,
static_cast<IGLDevice::Sizei> (sizeof(Vertex) * vertices.size()),
vertices.data(), IGLDevice::StaticDraw);
idxBuffer = device->GenBuffer();
device->BindBuffer(IGLDevice::ArrayBuffer, idxBuffer);
device->BufferData(IGLDevice::ArrayBuffer,
static_cast<IGLDevice::Sizei> (sizeof(uint32_t) * indices.size()),
indices.data(), IGLDevice::StaticDraw);
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
numIndices = indices.size();
}
GLWaterRenderer::~GLWaterRenderer(){
SPADES_MARK_FUNCTION();
device->DeleteBuffer(buffer);
device->DeleteBuffer(idxBuffer);
device->DeleteFramebuffer(tempFramebuffer);
device->DeleteTexture(tempDepthTexture);
device->DeleteTexture(texture);
if(occlusionQuery)
device->DeleteQuery(occlusionQuery);
for(size_t i = 0; i < waveTextures.size(); i++) {
device->DeleteTexture(waveTextures[i]);
waveTanks[i]->Join();
delete waveTanks[i];
}
}
void GLWaterRenderer::Render() {
SPADES_MARK_FUNCTION();
GLProfiler profiler(device, "Render");
if(occlusionQuery == 0 && settings.r_occlusionQuery)
occlusionQuery = device->GenQuery();
GLColorBuffer colorBuffer;
{
GLProfiler profiler(device, "Preparation");
colorBuffer = renderer->GetFramebufferManager()->PrepareForWaterRendering(tempFramebuffer,
tempDepthTexture);
}
float fogDist = renderer->GetFogDistance();
Vector3 fogCol = renderer->GetFogColorForSolidPass();
fogCol *= fogCol; // linearize
Vector3 skyCol = renderer->GetFogColor();
skyCol *= skyCol; // linearize
const client::SceneDefinition& def = renderer->GetSceneDef();
float waterLevel = 63.f;
float waterRange = 128.f;
Matrix4 mat = Matrix4::Translate(def.viewOrigin.x,
def.viewOrigin.y,
waterLevel);
mat = mat * Matrix4::Scale(waterRange, waterRange, 1.f);
GLProfiler profiler2(device, "Draw Plane");
// do color
device->DepthFunc(IGLDevice::Less);
device->ColorMask(true, true, true, true);
{
GLProgram *prg = program;
prg->Use();
static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix");
static GLProgramUniform projectionViewMatrix("projectionViewMatrix");
static GLProgramUniform modelMatrix("modelMatrix");
static GLProgramUniform viewModelMatrix("viewModelMatrix");
static GLProgramUniform viewMatrix("viewMatrix");
static GLProgramUniform fogDistance("fogDistance");
static GLProgramUniform fogColor("fogColor");
static GLProgramUniform skyColor("skyColor");
static GLProgramUniform zNearFar("zNearFar");
static GLProgramUniform viewOrigin("viewOrigin");
static GLProgramUniform displaceScale("displaceScale");
static GLProgramUniform fovTan("fovTan");
static GLProgramUniform waterPlane("waterPlane");
projectionViewModelMatrix(prg);
projectionViewMatrix(prg);
modelMatrix(prg);
viewModelMatrix(prg);
viewMatrix(prg);
fogDistance(prg);
fogColor(prg);
skyColor(prg);
zNearFar(prg);
viewOrigin(prg);
displaceScale(prg);
fovTan(prg);
waterPlane(prg);
projectionViewModelMatrix.SetValue(renderer->GetProjectionViewMatrix() * mat);
projectionViewMatrix.SetValue(renderer->GetProjectionViewMatrix());
modelMatrix.SetValue(mat);
viewModelMatrix.SetValue(renderer->GetViewMatrix() * mat);
viewMatrix.SetValue(renderer->GetViewMatrix());
fogDistance.SetValue(fogDist);
fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z);
skyColor.SetValue(skyCol.x, skyCol.y, skyCol.z);
zNearFar.SetValue(def.zNear, def.zFar);
viewOrigin.SetValue(def.viewOrigin.x,
def.viewOrigin.y,
def.viewOrigin.z);
/*displaceScale.SetValue(1.f / renderer->ScreenWidth() / tanf(def.fovX * .5f),
1.f / renderer->ScreenHeight() / tanf(def.fovY) * .5f);*/
displaceScale.SetValue(1.f / tanf(def.fovX * .5f),
1.f / tanf(def.fovY * .5f));
fovTan.SetValue(tanf(def.fovX * .5f), -tanf(def.fovY * .5f),
-tanf(def.fovX * .5f), tanf(def.fovY * .5f));
// make water plane in view coord
Matrix4 wmat = renderer->GetViewMatrix() * mat;
Vector3 dir = wmat.GetAxis(2);
waterPlane.SetValue(dir.x, dir.y, dir.z,
-Vector3::Dot(dir, wmat.GetOrigin()));
static GLProgramUniform screenTexture("screenTexture");
static GLProgramUniform depthTexture("depthTexture");
static GLProgramUniform textureUnif("mainTexture");
static GLProgramUniform waveTextureUnif("waveTexture");
static GLProgramUniform waveTextureUnif1("waveTexture1");
static GLProgramUniform waveTextureUnif2("waveTexture2");
static GLProgramUniform waveTextureUnif3("waveTexture3");
static GLProgramUniform mirrorTexture("mirrorTexture");
static GLProgramUniform mirrorDepthTexture("mirrorDepthTexture");
screenTexture(prg);
depthTexture(prg);
textureUnif(prg);
waveTextureUnif(prg);
waveTextureUnif1(prg);
waveTextureUnif2(prg);
waveTextureUnif3(prg);
mirrorTexture(prg);
mirrorDepthTexture(prg);
device->ActiveTexture(0);
device->BindTexture(IGLDevice::Texture2D, colorBuffer.GetTexture());
screenTexture.SetValue(0);
// depth is not interpolated, so color shouldn't be
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMagFilter,
IGLDevice::Nearest);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMinFilter,
IGLDevice::Nearest);
device->ActiveTexture(1);
device->BindTexture(IGLDevice::Texture2D, tempDepthTexture);
depthTexture.SetValue(1);
device->ActiveTexture(2);
device->BindTexture(IGLDevice::Texture2D, texture);
textureUnif.SetValue(2);
static GLShadowShader shadowShader;
if(waveTextures.size() == 1){
device->ActiveTexture(3);
device->BindTexture(IGLDevice::Texture2D, waveTextures[0]);
waveTextureUnif.SetValue(3);
shadowShader(renderer, prg, 4);
}else if(waveTextures.size() == 3) {
device->ActiveTexture(3);
device->BindTexture(IGLDevice::Texture2D, waveTextures[0]);
waveTextureUnif1.SetValue(3);
// FIXME: out of texture stages; only 8 texture stages are available
// (with macOS's OpenGL compatibility profile!)
device->ActiveTexture(4);
device->BindTexture(IGLDevice::Texture2D, waveTextures[1]);
// waveTextureUnif2.SetValue(4);
waveTextureUnif2.SetValue(3);
device->ActiveTexture(5);
device->BindTexture(IGLDevice::Texture2D, waveTextures[2]);
waveTextureUnif3.SetValue(5);
// mirror
device->ActiveTexture(6);
device->BindTexture(IGLDevice::Texture2D, renderer->GetFramebufferManager()->GetMirrorTexture());
if((float) settings.r_maxAnisotropy > 1.1f) {
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMaxAnisotropy,
(float) settings.r_maxAnisotropy);
}
mirrorTexture.SetValue(6);
if ((int) settings.r_water >= 3) {
device->ActiveTexture(4);
device->BindTexture(IGLDevice::Texture2D, renderer->GetFramebufferManager()->GetMirrorDepthTexture());
mirrorDepthTexture.SetValue(4);
}
shadowShader(renderer, prg, 7);
}else{
SPAssert(false);
}
static GLProgramAttribute positionAttribute("positionAttribute");
positionAttribute(prg);
device->EnableVertexAttribArray(positionAttribute(), true);
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->VertexAttribPointer(positionAttribute(),
2, IGLDevice::FloatType,
false, sizeof(Vertex), NULL);
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer);
if(occlusionQuery)
device->BeginQuery(IGLDevice::SamplesPassed,
occlusionQuery);
device->DrawElements(IGLDevice::Triangles,
static_cast<IGLDevice::Sizei> (numIndices),
IGLDevice::UnsignedInt, NULL);
if(occlusionQuery)
device->EndQuery(IGLDevice::SamplesPassed);
device->BindBuffer(IGLDevice::ElementArrayBuffer, 0);
device->EnableVertexAttribArray(positionAttribute(), false);
device->ActiveTexture(0);
// restore filter mode for color buffer
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMagFilter,
IGLDevice::Linear);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMinFilter,
IGLDevice::Linear);
}
}
static uint32_t LinearlizeColor(uint32_t v){
int r = (uint8_t)(v);
int g = (uint8_t)(v >> 8);
int b = (uint8_t)(v >> 16);
r = (r * r + 128) >> 8;
g = (g * g + 128) >> 8;
b = (b * b + 128) >> 8;
return b|(g<<8)|(r<<16);
}
void GLWaterRenderer::Update(float dt) {
SPADES_MARK_FUNCTION();
GLProfiler profiler(device, "Update");
// update wavetank simulation
{
GLProfiler profiler(device, "Waiting for Simulation To Done");
for(size_t i = 0; i < waveTanks.size(); i++){
waveTanks[i]->Join();
}
}
{
{
GLProfiler profiler(device, "Upload");
for(size_t i = 0; i < waveTanks.size(); i++){
device->BindTexture(IGLDevice::Texture2D, waveTextures[i]);
device->TexSubImage2D(IGLDevice::Texture2D, 0,
0, 0, waveTanks[i]->GetSize(), waveTanks[i]->GetSize(),
IGLDevice::BGRA, IGLDevice::UnsignedByte,
waveTanks[i]->GetBitmap());
}
}
{
GLProfiler profiler(device, "Generate Mipmap");
for(size_t i = 0; i < waveTanks.size(); i++){
device->BindTexture(IGLDevice::Texture2D, waveTextures[i]);
device->GenerateMipmap(IGLDevice::Texture2D);
}
}
}
for(size_t i = 0; i < waveTanks.size(); i++){
switch(i){
case 0:
waveTanks[i]->SetTimeStep(dt);
break;
case 1:
waveTanks[i]->SetTimeStep(dt * 0.15704f / .08f);
break;
case 2:
waveTanks[i]->SetTimeStep(dt * 0.02344f / .08f);
break;
}
waveTanks[i]->Start();
}
{
GLProfiler profiler(device, "Upload Water Color Texture");
device->BindTexture(IGLDevice::Texture2D, texture);
bool fullUpdate = true;
for(size_t i = 0; i < updateBitmap.size(); i++){
if(updateBitmap[i] == 0){
fullUpdate = false;
break;
}
}
if(fullUpdate) {
uint32_t *pixels = bitmap.data();
bool modified = false;
int x = 0, y = 0;
for(int i = w * h; i > 0; i--){
uint32_t col = map->GetColor(x, y, 63);
x++;
if(x == w){
x = 0; y++;
}
col = LinearlizeColor(col);
if(*pixels != col)
modified = true;
else{
pixels++;
continue;
}
*(pixels++) = col;
}
if(modified) {
device->TexSubImage2D(IGLDevice::Texture2D,
0, 0, 0, w, h,
IGLDevice::BGRA, IGLDevice::UnsignedByte,
bitmap.data());
}
for(size_t i = 0; i < updateBitmap.size(); i++){
updateBitmap[i] = 0;
}
}else{
// partial update
for(size_t i = 0; i < updateBitmap.size(); i++){
int y = static_cast<int> (i / updateBitmapPitch);
int x = static_cast<int> ((i - y * updateBitmapPitch) * 32);
if(updateBitmap[i] == 0)
continue;
uint32_t *pixels = bitmap.data() + x + y * w;
bool modified = false;
for(int j = 0; j < 32; j++){
uint32_t col = map->GetColor(x+j, y, 63);
col = LinearlizeColor(col);
if(pixels[j] != col)
modified = true;
else
continue;
pixels[j] = col;
//pixels[j] = GeneratePixel(x + j, y);
}
if(modified) {
device->TexSubImage2D(IGLDevice::Texture2D,
0, x, y, 32, 1,
IGLDevice::BGRA, IGLDevice::UnsignedByte,
pixels);
}
updateBitmap[i] = 0;
}
// partial update - done
}
}
}
void GLWaterRenderer::MarkUpdate(int x, int y) {
x &= w - 1;
y &= h - 1;
updateBitmap[(x >> 5) + y * updateBitmapPitch] |=
1UL << (x & 31);
}
void GLWaterRenderer::GameMapChanged(int x, int y, int z,
client::GameMap *map) {
if(map != this->map)
return;
if(z < 63)
return;
MarkUpdate(x, y);
}
}
}