353 lines
12 KiB
C++
353 lines
12 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 "GLMapRenderer.h"
|
|
#include "../Client/GameMap.h"
|
|
#include "GLProgram.h"
|
|
#include "GLProgramAttribute.h"
|
|
#include "GLProgramUniform.h"
|
|
#include "GLMapChunk.h"
|
|
#include "GLRenderer.h"
|
|
#include "GLProgram.h"
|
|
#include "GLImage.h"
|
|
#include "IGLDevice.h"
|
|
#include "../Core/Debug.h"
|
|
#include "GLMapShadowRenderer.h"
|
|
#include "GLShadowShader.h"
|
|
#include "../Core/Settings.h"
|
|
#include "GLDynamicLightShader.h"
|
|
#include "GLProfiler.h"
|
|
|
|
SPADES_SETTING(r_physicalLighting, "0");
|
|
|
|
namespace spades {
|
|
namespace draw {
|
|
GLMapRenderer::GLMapRenderer(client::GameMap *m, GLRenderer *r):
|
|
gameMap(m), renderer(r) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
device = renderer->GetGLDevice();
|
|
|
|
numChunkWidth = gameMap->Width() / GLMapChunk::Size;
|
|
numChunkHeight = gameMap->Height() / GLMapChunk::Size;
|
|
numChunkDepth = gameMap->Depth() / GLMapChunk::Size;
|
|
|
|
numChunks = numChunkWidth * numChunkHeight * numChunkDepth;
|
|
|
|
chunks = new GLMapChunk *[numChunks];
|
|
chunkInfos = new ChunkRenderInfo[numChunks];
|
|
|
|
for(int i = 0; i < numChunks; i++)
|
|
chunks[i] = new GLMapChunk(this, gameMap,
|
|
i / numChunkDepth / numChunkHeight,
|
|
(i / numChunkDepth) % numChunkHeight,
|
|
i % numChunkDepth);
|
|
|
|
|
|
if(r_physicalSolidLighting)
|
|
basicProgram = renderer->RegisterProgram("Shaders/BasicBlockPhys.program");
|
|
else
|
|
basicProgram = renderer->RegisterProgram("Shaders/BasicBlock.program");
|
|
dlightProgram = renderer->RegisterProgram("Shaders/BasicBlockDynamicLit.program");
|
|
aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.tga");
|
|
detailImage = (GLImage *)renderer->RegisterImage("Textures/detail.jpg");
|
|
|
|
static const uint8_t squareVertices[] = {
|
|
0,0, 1,0, 0,1,
|
|
1,0, 1,1, 0,1
|
|
};
|
|
squareVertexBuffer = device->GenBuffer();
|
|
device->BindBuffer(IGLDevice::ArrayBuffer, squareVertexBuffer);
|
|
device->BufferData(IGLDevice::ArrayBuffer, sizeof(squareVertices),
|
|
squareVertices, IGLDevice::StaticDraw);
|
|
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
|
|
}
|
|
|
|
GLMapRenderer::~GLMapRenderer() {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
device->DeleteBuffer(squareVertexBuffer);
|
|
for(int i = 0; i < numChunks; i++)
|
|
delete chunks[i];
|
|
delete[] chunks;
|
|
delete[] chunkInfos;
|
|
|
|
}
|
|
void GLMapRenderer::GameMapChanged(int x, int y, int z, client::GameMap *map) {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
/*GetChunk(x >> GLMapChunk::SizeBits,
|
|
y >> GLMapChunk::SizeBits,
|
|
z >> GLMapChunk::SizeBits)->SetNeedsUpdate();*/
|
|
//int fx = x & (GLMapChunk::Size - 1);
|
|
//int fy = y & (GLMapChunk::Size - 1);
|
|
int fz = z & (GLMapChunk::Size - 1);
|
|
int sx = -1;
|
|
int sy = -1;
|
|
int sz = fz == 0 ? -1 : 0;
|
|
int ex = 1;
|
|
int ey = 1;
|
|
int ez = fz == (GLMapChunk::Size - 1) ? 1 : 0;
|
|
for(int cx = sx; cx <= ex; cx++)
|
|
for(int cy = sy; cy <= ey; cy++)
|
|
for(int cz = sz; cz <= ez; cz++){
|
|
int xx = x + cx, yy = y + cy, zz = z + cz;
|
|
xx >>= GLMapChunk::SizeBits;
|
|
yy >>= GLMapChunk::SizeBits;
|
|
zz >>= GLMapChunk::SizeBits;
|
|
xx &= numChunkWidth - 1;
|
|
yy &= numChunkHeight - 1;
|
|
if(xx >= 0 && yy >= 0 && zz >= 0 &&
|
|
xx < numChunkWidth && yy < numChunkHeight &&
|
|
zz < numChunkDepth) {
|
|
GetChunk(xx, yy, zz)->SetNeedsUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLMapRenderer::RealizeChunks(spades::Vector3 eye) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
float cullDistance = 128.f;
|
|
float releaseDistance = 160.f;
|
|
for(int i = 0; i < numChunks; i++){
|
|
float dist = chunks[i]->DistanceFromEye(eye);
|
|
chunkInfos[i].distance = dist;
|
|
if(dist < cullDistance)
|
|
chunks[i]->SetRealized(true);
|
|
else if(dist > releaseDistance)
|
|
chunks[i]->SetRealized(false);
|
|
}
|
|
}
|
|
|
|
void GLMapRenderer::Prerender() {
|
|
SPADES_MARK_FUNCTION();
|
|
//Vector3 eye = renderer->GetSceneDef().viewOrigin;
|
|
|
|
// nothing to do now
|
|
}
|
|
|
|
void GLMapRenderer::RenderSunlightPass() {
|
|
SPADES_MARK_FUNCTION();
|
|
GLProfiler profiler(device, "Map");
|
|
|
|
Vector3 eye = renderer->GetSceneDef().viewOrigin;
|
|
|
|
device->ActiveTexture(0);
|
|
aoImage->Bind(IGLDevice::Texture2D);
|
|
device->TexParamater(IGLDevice::Texture2D,
|
|
IGLDevice::TextureMinFilter,
|
|
IGLDevice::Linear);
|
|
|
|
device->ActiveTexture(1);
|
|
detailImage->Bind(IGLDevice::Texture2D);
|
|
|
|
|
|
device->Enable(IGLDevice::CullFace, true);
|
|
device->Enable(IGLDevice::DepthTest, true);
|
|
|
|
basicProgram->Use();
|
|
|
|
static GLShadowShader shadowShader;
|
|
shadowShader(renderer, basicProgram, 2);
|
|
|
|
static GLProgramUniform fogDistance("fogDistance");
|
|
fogDistance(basicProgram);
|
|
fogDistance.SetValue(renderer->GetFogDistance());
|
|
|
|
static GLProgramUniform viewSpaceLight("viewSpaceLight");
|
|
viewSpaceLight(basicProgram);
|
|
Vector3 vspLight = (renderer->GetViewMatrix() * MakeVector4(0, -1, -1, 0)).GetXYZ();
|
|
viewSpaceLight.SetValue(vspLight.x, vspLight.y, vspLight.z);
|
|
|
|
static GLProgramUniform fogColor("fogColor");
|
|
fogColor(basicProgram);
|
|
Vector3 fogCol = renderer->GetFogColorForSolidPass();
|
|
fogCol *= fogCol; // linearize
|
|
fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z);
|
|
|
|
static GLProgramUniform aoUniform("ambientOcclusionTexture");
|
|
aoUniform(basicProgram);
|
|
aoUniform.SetValue(0);
|
|
|
|
static GLProgramUniform detailTextureUnif("detailTexture");
|
|
detailTextureUnif(basicProgram);
|
|
detailTextureUnif.SetValue(1);
|
|
|
|
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
|
|
|
|
static GLProgramAttribute positionAttribute("positionAttribute");
|
|
static GLProgramAttribute ambientOcclusionCoordAttribute("ambientOcclusionCoordAttribute");
|
|
static GLProgramAttribute colorAttribute("colorAttribute");
|
|
static GLProgramAttribute normalAttribute("normalAttribute");
|
|
|
|
positionAttribute(basicProgram);
|
|
ambientOcclusionCoordAttribute(basicProgram);
|
|
colorAttribute(basicProgram);
|
|
normalAttribute(basicProgram);
|
|
|
|
device->EnableVertexAttribArray(positionAttribute(), true);
|
|
if(ambientOcclusionCoordAttribute() != -1)
|
|
device->EnableVertexAttribArray(ambientOcclusionCoordAttribute(), true);
|
|
device->EnableVertexAttribArray(colorAttribute(), true);
|
|
if(normalAttribute() != -1)
|
|
device->EnableVertexAttribArray(normalAttribute(), true);
|
|
|
|
static GLProgramUniform projectionViewMatrix("projectionViewMatrix");
|
|
projectionViewMatrix(basicProgram);
|
|
projectionViewMatrix.SetValue(renderer->GetProjectionViewMatrix());
|
|
|
|
static GLProgramUniform viewMatrix("viewMatrix");
|
|
viewMatrix(basicProgram);
|
|
viewMatrix.SetValue(renderer->GetViewMatrix());
|
|
|
|
RealizeChunks(eye);
|
|
|
|
// draw from nearest to farthest
|
|
int cx = (int)floorf(eye.x) / GLMapChunk::Size;
|
|
int cy = (int)floorf(eye.y) / GLMapChunk::Size;
|
|
int cz = (int)floorf(eye.z) / GLMapChunk::Size;
|
|
DrawColumnSunlight(cx, cy, cz, eye);
|
|
for(int dist = 1; dist <= 128 / GLMapChunk::Size; dist++) {
|
|
for(int x = cx - dist; x <= cx + dist; x++){
|
|
DrawColumnSunlight(x, cy + dist, cz, eye);
|
|
DrawColumnSunlight(x, cy - dist, cz, eye);
|
|
}
|
|
for(int y = cy - dist + 1; y <= cy + dist - 1; y++){
|
|
DrawColumnSunlight(cx + dist, y, cz, eye);
|
|
DrawColumnSunlight(cx - dist, y, cz, eye);
|
|
}
|
|
}
|
|
|
|
|
|
device->EnableVertexAttribArray(positionAttribute(), false);
|
|
if(ambientOcclusionCoordAttribute() != -1)
|
|
device->EnableVertexAttribArray(ambientOcclusionCoordAttribute(), false);
|
|
device->EnableVertexAttribArray(colorAttribute(), false);
|
|
if(normalAttribute() != -1)
|
|
device->EnableVertexAttribArray(normalAttribute(), false);
|
|
|
|
device->ActiveTexture(1);
|
|
device->BindTexture(IGLDevice::Texture2D, 0);
|
|
device->ActiveTexture(0);
|
|
device->BindTexture(IGLDevice::Texture2D, 0);
|
|
}
|
|
|
|
|
|
|
|
void GLMapRenderer::RenderDynamicLightPass(std::vector<GLDynamicLight> lights) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
GLProfiler profiler(device, "Map");
|
|
|
|
if(lights.empty())
|
|
return;
|
|
|
|
|
|
Vector3 eye = renderer->GetSceneDef().viewOrigin;
|
|
|
|
device->ActiveTexture(0);
|
|
detailImage->Bind(IGLDevice::Texture2D);
|
|
|
|
device->Enable(IGLDevice::CullFace, true);
|
|
device->Enable(IGLDevice::DepthTest, true);
|
|
|
|
dlightProgram->Use();
|
|
|
|
static GLProgramUniform fogDistance("fogDistance");
|
|
fogDistance(dlightProgram);
|
|
fogDistance.SetValue(renderer->GetFogDistance());
|
|
|
|
static GLProgramUniform detailTextureUnif("detailTexture");
|
|
detailTextureUnif(dlightProgram);
|
|
detailTextureUnif.SetValue(0);
|
|
|
|
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
|
|
|
|
static GLProgramAttribute positionAttribute("positionAttribute");
|
|
static GLProgramAttribute colorAttribute("colorAttribute");
|
|
static GLProgramAttribute normalAttribute("normalAttribute");
|
|
|
|
positionAttribute(dlightProgram);
|
|
colorAttribute(dlightProgram);
|
|
normalAttribute(dlightProgram);
|
|
|
|
device->EnableVertexAttribArray(positionAttribute(), true);
|
|
device->EnableVertexAttribArray(colorAttribute(), true);
|
|
device->EnableVertexAttribArray(normalAttribute(), true);
|
|
|
|
static GLProgramUniform projectionViewMatrix("projectionViewMatrix");
|
|
projectionViewMatrix(dlightProgram);
|
|
projectionViewMatrix.SetValue(renderer->GetProjectionViewMatrix());
|
|
|
|
static GLProgramUniform viewMatrix("viewMatrix");
|
|
viewMatrix(dlightProgram);
|
|
viewMatrix.SetValue(renderer->GetViewMatrix());
|
|
|
|
RealizeChunks(eye);
|
|
|
|
// draw from nearest to farthest
|
|
int cx = (int)floorf(eye.x) / GLMapChunk::Size;
|
|
int cy = (int)floorf(eye.y) / GLMapChunk::Size;
|
|
int cz = (int)floorf(eye.z) / GLMapChunk::Size;
|
|
DrawColumnDLight(cx, cy, cz, eye, lights);
|
|
// TODO: optimize call
|
|
// ex. don't call a chunk'r render method if
|
|
// no dlight lights it
|
|
for(int dist = 1; dist <= 128 / GLMapChunk::Size; dist++) {
|
|
for(int x = cx - dist; x <= cx + dist; x++){
|
|
DrawColumnDLight(x, cy + dist, cz, eye, lights);
|
|
DrawColumnDLight(x, cy - dist, cz, eye, lights);
|
|
}
|
|
for(int y = cy - dist + 1; y <= cy + dist - 1; y++){
|
|
DrawColumnDLight(cx + dist, y, cz, eye, lights);
|
|
DrawColumnDLight(cx - dist, y, cz, eye, lights);
|
|
}
|
|
}
|
|
|
|
|
|
device->EnableVertexAttribArray(positionAttribute(), false);
|
|
device->EnableVertexAttribArray(colorAttribute(), false);
|
|
device->EnableVertexAttribArray(normalAttribute(), false);
|
|
|
|
device->ActiveTexture(0);
|
|
device->BindTexture(IGLDevice::Texture2D, 0);
|
|
}
|
|
|
|
void GLMapRenderer::DrawColumnSunlight(int cx, int cy, int cz, spades::Vector3 eye){
|
|
cx &= numChunkWidth -1;
|
|
cy &= numChunkHeight - 1;
|
|
for(int z = std::max(cz, 0); z < numChunkDepth; z++)
|
|
GetChunk(cx, cy, z)->RenderSunlightPass();
|
|
for(int z = std::min(cz - 1, 63); z >= 0; z--)
|
|
GetChunk(cx, cy, z)->RenderSunlightPass();
|
|
}
|
|
|
|
void GLMapRenderer::DrawColumnDLight(int cx, int cy, int cz, spades::Vector3 eye, const std::vector<GLDynamicLight>& lights){
|
|
cx &= numChunkWidth -1;
|
|
cy &= numChunkHeight - 1;
|
|
for(int z = std::max(cz, 0); z < numChunkDepth; z++)
|
|
GetChunk(cx, cy, z)->RenderDLightPass(lights);
|
|
for(int z = std::min(cz - 1, 63); z >= 0; z--)
|
|
GetChunk(cx, cy, z)->RenderDLightPass(lights);
|
|
}
|
|
}
|
|
}
|