openspades/Sources/Draw/GLMapChunk.cpp

483 lines
13 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 "GLMapChunk.h"
#include <algorithm>
#include "GLMapRenderer.h"
#include "IGLDevice.h"
#include "GLProgramAttribute.h"
#include "GLProgramUniform.h"
#include "../Core/Debug.h"
#include "GLRenderer.h"
#include "../Client/GameMap.h"
#include "../Core/Settings.h"
#include "GLDynamicLightShader.h"
SPADES_SETTING(r_water, "2");
namespace spades {
namespace draw {
GLMapChunk::GLMapChunk(spades::draw::GLMapRenderer *r, client::GameMap *mp,
int cx, int cy, int cz){
SPADES_MARK_FUNCTION();
renderer = r;
device = r->device;
map = mp;
chunkX = cx;
chunkY = cy;
chunkZ = cz;
needsUpdate = true;
realized = false;
centerPos = MakeVector3(cx * Size + Size / 2,
cy * Size + Size / 2,
cz * Size + Size / 2);
radius = (float)Size * 0.5f * sqrtf(3.f);
aabb = AABB3(cx * Size, cy * Size, cz * Size,
Size, Size, Size);
buffer = 0;
iBuffer = 0;
}
GLMapChunk::~GLMapChunk() {
SetRealized(false);
}
void GLMapChunk::SetRealized(bool b){
SPADES_MARK_FUNCTION_DEBUG();
if(realized == b)
return;
if(!b){
if(buffer){
device->DeleteBuffer(buffer);
buffer = 0;
}
if(iBuffer){
device->DeleteBuffer(iBuffer);
iBuffer = 0;
}
std::vector<Vertex> i;
i.swap(vertices);
std::vector<uint16_t> i2;
i2.swap(indices);
}else{
needsUpdate = true;
}
realized = b;
}
uint8_t GLMapChunk::calcAOID(int x, int y, int z,
int ux, int uy, int uz,
int vx, int vy, int vz){
int v = 0;
if(IsSolid(x - ux, y - uy, z - uz))
v |= 1;
if(IsSolid(x + ux, y + uy, z + uz))
v |= 1 << 1;
if(IsSolid(x - vx, y - vy, z - vz))
v |= 1 << 2;
if(IsSolid(x + vx, y + vy, z + vz))
v |= 1 << 3;
if(IsSolid(x - ux + vx, y - uy + vy, z - uz + vz))
v |= 1 << 4;
if(IsSolid(x - ux - vx, y - uy - vy, z - uz - vz))
v |= 1 << 5;
if(IsSolid(x + ux + vx, y + uy + vy, z + uz + vz))
v |= 1 << 6;
if(IsSolid(x + ux - vx, y + uy - vy, z + uz - vz))
v |= 1 << 7;
return (uint8_t)v;
}
/**
* @param aoX Global X coordinate of the cell to evaluate ambient occlusion.
* @param aoY Global Y coordinate of the cell to evaluate ambient occlusion.
* @param aoZ Global Z coordinate of the cell to evaluate ambient occlusion.
* @param x Chunk local X coordinate
* @param y Chunk local Y coordinate
* @param z Chunk local Z coordinate
*/
void GLMapChunk::EmitVertex(int x, int y, int z,
int aoX, int aoY, int aoZ,
int ux, int uy, int vx, int vy,
uint32_t color,
int nx, int ny, int nz) {
SPADES_MARK_FUNCTION_DEBUG();
int uz = (ux == 0 && uy == 0) ? 1 : 0;
int vz = (vx == 0 && vy == 0) ? 1 : 0;
Vertex inst;
// evaluate ambient occlusion
unsigned int aoID = calcAOID(aoX, aoY, aoZ,
ux, uy, uz, vx, vy, vz);
if(nz == 1 || ny == 1){
inst.shading = 0;
}else if(nx == 1 || nx == -1){
inst.shading = 0;//50;
}else if(nz == -1){
inst.shading = 220;
}else{
inst.shading = 255;
}
inst.x = x;
inst.y = y;
inst.z = z;
inst.colorRed = (uint8_t)(color);
inst.colorGreen = (uint8_t)(color >> 8);
inst.colorBlue = (uint8_t)(color >> 16);
inst.nx = nx;
inst.ny = ny;
inst.nz = nz;
unsigned int aoTexX = aoID & 15;
unsigned int aoTexY = aoID >> 4;
aoTexX *= 16; aoTexY *= 16;
uint16_t idx = (uint16_t)vertices.size();
inst.x = x; inst.y = y; inst.z = z;
inst.aoX = aoTexX; inst.aoY = aoTexY;
vertices.push_back(inst);
inst.x = x + ux; inst.y = y + uy; inst.z = z + uz;
inst.aoX = aoTexX + 15; inst.aoY = aoTexY;
vertices.push_back(inst);
inst.x = x + vx; inst.y = y + vy; inst.z = z + vz;
inst.aoX = aoTexX; inst.aoY = aoTexY + 15;
vertices.push_back(inst);
inst.x = x + ux + vx; inst.y = y + uy + vy; inst.z = z + uz + vz;
inst.aoX = aoTexX + 15; inst.aoY = aoTexY + 15;
vertices.push_back(inst);
indices.push_back(idx);
indices.push_back(idx+1);
indices.push_back(idx+2);
indices.push_back(idx+1);
indices.push_back(idx+3);
indices.push_back(idx+2);
}
bool GLMapChunk::IsSolid(int x, int y, int z) {
if(z < 0) return false;
if(z >= 64) return true;
// FIXME: variable map size
x &= 511;
y &= 511;
if(z == 63){
if(r_water){
return map->IsSolid(x, y, 62);
}else{
return map->IsSolid(x, y, 63);
}
}else{
return map->IsSolid(x, y, z);
}
}
void GLMapChunk::Update() {
SPADES_MARK_FUNCTION();
vertices.clear();
indices.clear();
if(buffer){
device->DeleteBuffer(buffer);
buffer = 0;
}
if(iBuffer){
device->DeleteBuffer(iBuffer);
iBuffer = 0;
}
int rchunkX = chunkX * Size;
int rchunkY = chunkY * Size;
int rchunkZ = chunkZ * Size;
int x, y, z;
for(x = 0; x < Size; x++){
for(y = 0; y < Size; y++){
for(z = 0; z < Size; z++){
int xx = x + rchunkX;
int yy = y + rchunkY;
int zz = z + rchunkZ;
if(!IsSolid(xx, yy, zz))
continue;
uint32_t col = map->GetColor(xx, yy, zz);
//col = 0xffffffff;
// damaged block?
int health = col >> 24;
if(health < 100){
col &= 0xffffff;
col &= 0xfefefe;
col >>= 1;
}
if(!IsSolid(xx, yy, zz + 1)){
EmitVertex(x + 1, y, z + 1, xx, yy, zz + 1,
-1,0, 0,1,
col,
0, 0, 1);
}
if(!IsSolid(xx, yy, zz - 1)){
EmitVertex(x, y, z , xx, yy, zz - 1,
1,0, 0,1,
col,
0, 0, -1);
}
if(!IsSolid(xx - 1, yy, zz)){
EmitVertex(x, y + 1, z, xx - 1, yy, zz,
0,0, 0,-1,
col,
-1, 0, 0);
}
if(!IsSolid(xx + 1, yy, zz)){
EmitVertex(x + 1, y , z, xx + 1, yy, zz,
0,0, 0,1,
col,
1, 0, 0);
}
if(!IsSolid(xx, yy - 1, zz)){
EmitVertex(x, y, z, xx, yy - 1, zz,
0,0, 1,0,
col,
0, -1, 0);
}
if(!IsSolid(xx, yy + 1, zz)){
EmitVertex(x + 1, y + 1, z, xx, yy + 1, zz,
0,0, -1,0,
col,
0, 1, 0);
}
}
}
}
if(vertices.size() == 0)
return;
buffer = device->GenBuffer();
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->BufferData(IGLDevice::ArrayBuffer, vertices.size() * sizeof(Vertex),
vertices.data(), IGLDevice::DynamicDraw);
if(!indices.empty()){
iBuffer = device->GenBuffer();
device->BindBuffer(IGLDevice::ArrayBuffer, iBuffer);
device->BufferData(IGLDevice::ArrayBuffer, indices.size() * sizeof(uint16_t),
indices.data(), IGLDevice::DynamicDraw);
}
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
}
void GLMapChunk::RenderSunlightPass() {
SPADES_MARK_FUNCTION();
Vector3 eye = renderer->renderer->GetSceneDef().viewOrigin;
if(!realized)
return;
if(needsUpdate){
Update();
needsUpdate = false;
}
if(!buffer){
// empty chunk
return;
}
AABB3 bx = aabb;
Vector3 diff = eye - centerPos;
float sx = 0.f, sy = 0.f;
// FIXME: variable map size?
if(diff.x > 256.f) sx += 512.f;
if(diff.y > 256.f) sy += 512.f;
if(diff.x < -256.f) sx -= 512.f;
if(diff.y < -256.f) sy -= 512.f;
bx.min.x += sx; bx.min.y += sy;
bx.max.x += sx; bx.max.y += sy;
if(!renderer->renderer->BoxFrustrumCull(bx))
return;
GLProgram *basicProgram = renderer->basicProgram;
static GLProgramUniform chunkPosition("chunkPosition");
chunkPosition(basicProgram);
chunkPosition.SetValue((float)(chunkX * Size) + sx,
(float)(chunkY * Size) + sy,
(float)(chunkZ * Size));
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->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->VertexAttribPointer(positionAttribute(), 3,
IGLDevice::UnsignedByte, false,
sizeof(Vertex), (void *)0);
if(ambientOcclusionCoordAttribute() != -1)
device->VertexAttribPointer(ambientOcclusionCoordAttribute(), 2,
IGLDevice::UnsignedShort, false,
sizeof(Vertex), (void *)4);
device->VertexAttribPointer(colorAttribute(), 4,
IGLDevice::UnsignedByte, true,
sizeof(Vertex), (void *)8);
if(normalAttribute() != -1)
device->VertexAttribPointer(normalAttribute(), 3,
IGLDevice::Byte, false,
sizeof(Vertex), (void *)12);
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
device->BindBuffer(IGLDevice::ElementArrayBuffer,
iBuffer);
device->DrawElements(IGLDevice::Triangles,
indices.size(),
IGLDevice::UnsignedShort, NULL);
device->BindBuffer(IGLDevice::ElementArrayBuffer,
0);
}
void GLMapChunk::RenderDLightPass(std::vector<GLDynamicLight> lights) {
SPADES_MARK_FUNCTION();
Vector3 eye = renderer->renderer->GetSceneDef().viewOrigin;
if(!realized)
return;
if(needsUpdate){
Update();
needsUpdate = false;
}
if(!buffer){
// empty chunk
return;
}
AABB3 bx = aabb;
Vector3 diff = eye - centerPos;
float sx = 0.f, sy = 0.f;
// FIXME: variable map size?
if(diff.x > 256.f) sx += 512.f;
if(diff.y > 256.f) sy += 512.f;
if(diff.x < -256.f) sx -= 512.f;
if(diff.y < -256.f) sy -= 512.f;
bx.min.x += sx; bx.min.y += sy;
bx.max.x += sx; bx.max.y += sy;
if(!renderer->renderer->BoxFrustrumCull(bx))
return;
GLProgram *program = renderer->dlightProgram;
static GLProgramUniform chunkPosition("chunkPosition");
chunkPosition(program);
chunkPosition.SetValue((float)(chunkX * Size) + sx,
(float)(chunkY * Size) + sy,
(float)(chunkZ * Size));
static GLProgramAttribute positionAttribute("positionAttribute");
static GLProgramAttribute colorAttribute("colorAttribute");
static GLProgramAttribute normalAttribute("normalAttribute");
positionAttribute(program);
colorAttribute(program);
normalAttribute(program);
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->VertexAttribPointer(positionAttribute(), 3,
IGLDevice::UnsignedByte, false,
sizeof(Vertex), (void *)0);
device->VertexAttribPointer(colorAttribute(), 4,
IGLDevice::UnsignedByte, true,
sizeof(Vertex), (void *)8);
device->VertexAttribPointer(normalAttribute(), 3,
IGLDevice::Byte, false,
sizeof(Vertex), (void *)12);
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
device->BindBuffer(IGLDevice::ElementArrayBuffer,
iBuffer);
for(size_t i = 0; i < lights.size(); i++){
static GLDynamicLightShader lightShader;
lightShader(renderer->renderer, program,lights[i], 1);
if(!GLDynamicLightShader::Cull(lights[i], bx))
continue;
device->DrawElements(IGLDevice::Triangles,
indices.size(),
IGLDevice::UnsignedShort, NULL);
}
device->BindBuffer(IGLDevice::ElementArrayBuffer,
0);
}
float GLMapChunk::DistanceFromEye(const Vector3 &eye) {
Vector3 diff = eye - centerPos;
// FIXME: variable map size
if(diff.x < -256.f) diff.x += 512.f;
if(diff.y < -256.f) diff.y += 512.f;
if(diff.x > 256.f) diff.x -= 512.f;
if(diff.y > 256.f) diff.y -= 512.f;
float dist = fabsf(diff.x);
dist = std::max(dist, fabsf(diff.y));
dist = std::max(dist, fabsf(diff.z));
//return std::max(diff.GetLength() - radius, 0.f);
return std::max(dist - ((float)Size * .5f), 0.f);
}
}
}