Diggler/Chunk.cpp
Dorian Wouters f2062d6fe7 Faster atlas creation
Block selection
Chunk de/compression now uses internal buffer directly (0-copy)
Optimized Chunk vertices list order (faster vert access from GPU cache)
F5 Debug info: added triangle count
Implemented ladder climb
Road + jump pad makes you jump farther
Fixed bad fog color blending (alpha-channel)
Changed LZFX and enet compilation to Release, -O3
2016-01-02 20:03:37 +01:00

365 lines
12 KiB
C++

#include "Chunk.hpp"
#include "Platform.hpp"
#include "GlobalProperties.hpp"
#include "Blocks.hpp"
#include "Game.hpp"
#include "ChunkChangeHelper.hpp"
#include <cstring>
#include <cstddef>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#define CXY (CX*CY)
#define I(x,y,z) (x+y*CX+z*CXY)
#define SHOW_CHUNK_UPDATES 0
namespace Diggler {
Chunk::Renderer Chunk::R = {0};
Texture *Chunk::TextureAtlas = nullptr;
struct GLCoord {
uint8 x, y, z, w;
uint16 tx, ty;
float r, g, b;
};
constexpr float Chunk::CullSphereRadius;
constexpr float Chunk::MidX, Chunk::MidY, Chunk::MidZ;
Chunk::Chunk(int scx, int scy, int scz, Game *G) : blk2(nullptr), blk(nullptr),
scx(scx), scy(scy), scz(scz), G(G), vbo(nullptr), lavaCount(0) {
dirty = true;
blk = new BlockType[CX*CY*CZ];
memset(blk, (int)BlockType::Air, CX*CY*CZ*sizeof(BlockType));
//for (int i=0; i < CX*CY*CZ; ++i)
// blk[i] = BlockType::Air;
if (GlobalProperties::IsClient) {
vbo = new VBO;
ibo = new VBO;
if (R.prog == nullptr) {
loadShader();
TextureAtlas = G->B->getAtlas();
}
}
if (GlobalProperties::IsServer) {
blk2 = new BlockType[CX*CY*CZ];
memset(blk, (int)BlockType::Air, CX*CY*CZ*sizeof(BlockType));
}
}
void Chunk::loadShader() {
bool w = G->RP->wavingLiquids;
ProgramManager::FlagsT flags = PM_3D | PM_TEXTURED | PM_COLORED | PM_FOG;
if (w)
flags |= PM_WAVE;
R.prog = G->PM->getProgram(flags);
R.att_coord = R.prog->att("coord");
R.att_color = R.prog->att("color");
R.att_texcoord = R.prog->att("texcoord");
R.att_wave = w ? R.prog->att("wave") : -1;
R.uni_mvp = R.prog->uni("mvp");
R.uni_unicolor = R.prog->uni("unicolor");
R.uni_fogStart = R.prog->uni("fogStart");
R.uni_fogEnd = R.prog->uni("fogEnd");
R.uni_time = w ? R.prog->uni("time") : -1;
}
void Chunk::onRenderPropertiesChanged() {
loadShader();
}
Chunk::~Chunk() {
delete[] blk;
delete[] blk2;
delete vbo; delete ibo;
}
BlockType Chunk::get(int x, int y, int z) {
if ((x < 0 || y < 0 || z < 0 || x >= CX || y >= CY || z >= CZ) && G) {
return G->SC->get(scx * CX + x, scy * CY + y, scz * CZ + z);
}
return blk[I(x,y,z)];
}
void Chunk::set2(int x, int y, int z, BlockType type) {
if ((x < 0 || y < 0 || z < 0 || x >= CX || y >= CY || z >= CZ) && G)
return G->SC->set2(scx * CX + x, scy * CY + y, scz * CZ + z, type);
register BlockType *b = &(blk2[I(x,y,z)]);
if (*b == BlockType::Lava)
lavaCount--;
if (type == BlockType::Lava)
lavaCount++;
*b = type;
if (G && G->CCH)
G->CCH->add(scx * CX + x, scy * CY + y, scz * CZ + z, type);
}
void Chunk::set(int x, int y, int z, BlockType type) {
mut.lock();
if ((x < 0 || y < 0 || z < 0 || x >= CX || y >= CY || z >= CZ) && G)
return G->SC->set(scx * CX + x, scy * CY + y, scz * CZ + z, type);
register BlockType *b = &(blk[I(x,y,z)]);
if (*b == BlockType::Lava)
lavaCount--;
if (type == BlockType::Lava)
lavaCount++;
*b = type;
if (G) {
if (G->CCH)
G->CCH->add(scx * CX + x, scy * CY + y, scz * CZ + z, type);
if (GlobalProperties::IsClient) {
int u = x==CX-1?1:(x==0)?-1:0,
v = y==CY-1?1:(y==0)?-1:0,
w = z==CZ-1?1:(z==0)?-1:0;
Chunk *nc;
if (u && (nc = G->SC->getChunk(scx+u, scy, scz)))
nc->dirty = true;
if (v && (nc = G->SC->getChunk(scx, scy+v, scz)))
nc->dirty = true;
if (w && (nc = G->SC->getChunk(scx, scy, scz+w)))
nc->dirty = true;
}
}
dirty = true;
mut.unlock();
}
void Chunk::updateServerPrepare() {
memcpy(blk2, blk, CX*CY*CZ*sizeof(BlockType));
}
void Chunk::updateServer() {
if (lavaCount == 0)
return;
mut.lock();
for (int x=0; x < CX; x++)
for (int y=0; y < CY; y++)
for (int z=0; z < CZ; z++) {
if (blk[I(x,y,z)] == BlockType::Lava) {
BlockType under = get(x, y-1, z);
if (under == BlockType::Air) {
set2(x, y-1, z, BlockType::Lava);
} else if (under != BlockType::Lava) {
if (get(x+1, y, z) == BlockType::Air)
set2(x+1, y, z, BlockType::Lava);
if (get(x-1, y, z) == BlockType::Air)
set2(x-1, y, z, BlockType::Lava);
if (get(x, y, z+1) == BlockType::Air)
set2(x, y, z+1, BlockType::Lava);
if (get(x, y, z-1) == BlockType::Air)
set2(x, y, z-1, BlockType::Lava);
}
}
}
mut.unlock();
}
void Chunk::updateServerSwap() {
std::swap(blk, blk2);
}
struct RGB { float r, g, b; };
void Chunk::updateClient() {
mut.lock();
Blocks &B = *G->B;
GLCoord vertex[CX * CY * CZ * 6 /* faces */ * 4 /* vertices */ / 2 /* face removing (HSR) makes a lower vert max */];
GLushort index[CX * CY * CZ * 6 /* faces */ * 4 /* indices */ / 2 /* HSR */];
int v = 0, i = 0;
bool hasWaves = G->RP->wavingLiquids;
BlockType bt, bu /*BlockUp*/, bn /*BlockNear*/;
bool mayDisp;
const AtlasCreator::Coord *tc;
for(uint8 x = 0; x < CX; x++) {
for(uint8 y = 0; y < CY; y++) {
for(uint8 z = 0; z < CZ; z++) {
bt = blk[I(x,y,z)];
// Empty block?
if (!bt)
continue;
#if 0
BlockType
/* -X face*/
bNNZ = get(x-1, y-1, z),
bNPZ = get(x-1, y+1, z),
bNZN = get(x-1, y, z-1),
bNZP = get(x-1, y, z+1),
/* +X face*/
bPNZ = get(x+1, y-1, z),
bPPZ = get(x+1, y+1, z),
bPZN = get(x+1, y, z-1),
bPZP = get(x+1, y, z+1),
/* Top & bottom */
bZPN = get(x, y+1, z-1),
bZPP = get(x, y+1, z+1),
bZNN = get(x, y-1, z-1),
bZNP = get(x, y+1, z+1);
RGB bl = {.6f, .6f, .6f}, br = {.6f, .6f, .6f},
tl = {.6f, .6f, .6f}, tr = {.6f, .6f, .6f};
if (bNZN == BlockType::Lava || bNNZ == BlockType::Lava) { bl.r = 1.6f; bl.g = 1.2f; }
if (bNNZ == BlockType::Lava || bNZP == BlockType::Lava) { br.r = 1.6f; br.g = 1.2f; }
if (bNZP == BlockType::Lava || bNPZ == BlockType::Lava) { tr.r = 1.6f; tr.g = 1.2f; }
if (bNPZ == BlockType::Lava || bNZN == BlockType::Lava) { tl.r = 1.6f; tl.g = 1.2f; }
vertex[v++] = {x, y, z, tc->x, tc->v, bl.r, bl.g, bl.b};
vertex[v++] = {x, y, z + 1, tc->u, tc->v, br.r, br.g, br.b};
vertex[v++] = {x, y + 1, z, tc->x, tc->y, tl.r, tl.g, tl.b};
vertex[v++] = {x, y + 1, z + 1, tc->u, tc->y, tr.r, tr.g, tr.b};
#endif
uint8 w;
if (hasWaves) {
w = (bt == BlockType::Lava && get(x, y+1, z) != BlockType::Lava) ? 8 : 0;
bu = get(x, y+1, z);
mayDisp = (bt != BlockType::Lava) || (bt == BlockType::Lava && bu == BlockType::Lava);
} else {
w = 0;
mayDisp = false;
}
// View from negative x
bn = get(x - 1, y, z);
if ((mayDisp && bn == BlockType::Lava && get(x-1, y+1, z) != BlockType::Lava)
|| Blocks::isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = B.gTC(bt, FaceDirection::XDec);
vertex[v++] = {x, y, z, 0, tc->x, tc->v, .6f, .6f, .6f};
vertex[v++] = {x, y, z + 1, 0, tc->u, tc->v, .6f, .6f, .6f};
vertex[v++] = {x, y + 1, z, w, tc->x, tc->y, .6f, .6f, .6f};
vertex[v++] = {x, y + 1, z + 1, w, tc->u, tc->y, .6f, .6f, .6f};
}
// View from positive x
bn = get(x + 1, y, z);
if ((mayDisp && bn == BlockType::Lava && get(x+1, y+1, z) != BlockType::Lava)
|| Blocks::isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = B.gTC(bt, FaceDirection::XInc);
vertex[v++] = {x + 1, y, z, 0, tc->u, tc->v, .6f, .6f, .6f};
vertex[v++] = {x + 1, y + 1, z, w, tc->u, tc->y, .6f, .6f, .6f};
vertex[v++] = {x + 1, y, z + 1, 0, tc->x, tc->v, .6f, .6f, .6f};
vertex[v++] = {x + 1, y + 1, z + 1, w, tc->x, tc->y, .6f, .6f, .6f};
}
// Negative Y
bn = get(x, y - 1, z);
if ((hasWaves && bn == BlockType::Lava)
|| Blocks::isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
float shade = (blk[I(x,y,z)] == BlockType::Shock) ? 1.5f : .2f;;
tc = B.gTC(bt, FaceDirection::YDec);
vertex[v++] = {x, y, z, 0, tc->u, tc->v, shade, shade, shade};
vertex[v++] = {x + 1, y, z, 0, tc->u, tc->y, shade, shade, shade};
vertex[v++] = {x, y, z + 1, 0, tc->x, tc->v, shade, shade, shade};
vertex[v++] = {x + 1, y, z + 1, 0, tc->x, tc->y, shade, shade, shade};
}
// Positive Y
bn = get(x, y + 1, z);
if ((hasWaves && bt == BlockType::Lava && bu != BlockType::Lava)
|| Blocks::isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = B.gTC(bt, FaceDirection::YInc);
vertex[v++] = {x, y + 1, z, w, tc->x, tc->v, .8f, .8f, .8f};
vertex[v++] = {x, y + 1, z + 1, w, tc->u, tc->v, .8f, .8f, .8f};
vertex[v++] = {x + 1, y + 1, z, w, tc->x, tc->y, .8f, .8f, .8f};
vertex[v++] = {x + 1, y + 1, z + 1, w, tc->u, tc->y, .8f, .8f, .8f};
}
// Negative Z
bn = get(x, y, z - 1);
if ((mayDisp && bn == BlockType::Lava && get(x, y+1, z-1) != BlockType::Lava)
|| Blocks::isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = B.gTC(bt, FaceDirection::ZDec);
vertex[v++] = {x, y, z, 0, tc->u, tc->v, .4f, .4f, .4f};
vertex[v++] = {x, y + 1, z, w, tc->u, tc->y, .4f, .4f, .4f};
vertex[v++] = {x + 1, y, z, 0, tc->x, tc->v, .4f, .4f, .4f};
vertex[v++] = {x + 1, y + 1, z, w, tc->x, tc->y, .4f, .4f, .4f};
}
// Positive Z
bn = get(x, y, z + 1);
if ((mayDisp && bn == BlockType::Lava && get(x, y+1, z+1) != BlockType::Lava)
|| Blocks::isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = B.gTC(bt, FaceDirection::ZInc);
vertex[v++] = {x, y, z + 1, 0, tc->x, tc->v, .4f, .4f, .4f};
vertex[v++] = {x + 1, y, z + 1, 0, tc->u, tc->v, .4f, .4f, .4f};
vertex[v++] = {x, y + 1, z + 1, w, tc->x, tc->y, .4f, .4f, .4f};
vertex[v++] = {x + 1, y + 1, z + 1, w, tc->u, tc->y, .4f, .4f, .4f};
}
}
}
}
vertices = v;
vbo->setData(vertex, v);
indices = i;
ibo->setData(index, i);
dirty = false;
mut.unlock();
}
void Chunk::render(const glm::mat4 &transform) {
if (dirty)
updateClient();
if (!indices)
return;
R.prog->bind();
glEnableVertexAttribArray(R.att_coord);
glEnableVertexAttribArray(R.att_texcoord);
glEnableVertexAttribArray(R.att_color);
glEnableVertexAttribArray(R.att_wave);
glUniformMatrix4fv(R.uni_mvp, 1, GL_FALSE, glm::value_ptr(transform));
glUniform1f(R.uni_fogStart, G->RP->fogStart);
glUniform1f(R.uni_fogEnd, G->RP->fogEnd);
glUniform1f(R.uni_time, G->Time);
TextureAtlas->bind();
vbo->bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->id);
glVertexAttribPointer(R.att_coord, 3, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(GLCoord), 0);
glVertexAttribPointer(R.att_wave, 1, GL_BYTE, GL_TRUE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, w));
glVertexAttribPointer(R.att_texcoord, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, tx));
glVertexAttribPointer(R.att_color, 3, GL_FLOAT, GL_FALSE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, r));
glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_SHORT, nullptr);
glDisableVertexAttribArray(R.att_wave);
glDisableVertexAttribArray(R.att_color);
glDisableVertexAttribArray(R.att_texcoord);
glDisableVertexAttribArray(R.att_coord);
}
void Chunk::renderBatched(const glm::mat4& transform) {
#if SHOW_CHUNK_UPDATES
glUniform4f(R.uni_unicolor, 1.f, changed ? 0.f : 1.f, changed ? 0.f : 1.f, 1.f);
#endif
if (dirty)
updateClient();
if (!indices)
return;
glUniformMatrix4fv(R.uni_mvp, 1, GL_FALSE, glm::value_ptr(transform));
vbo->bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->id);
glVertexAttribPointer(R.att_coord, 3, GL_BYTE, GL_FALSE, sizeof(GLCoord), 0);
glVertexAttribPointer(R.att_wave, 1, GL_BYTE, GL_TRUE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, w));
glVertexAttribPointer(R.att_texcoord, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, tx));
glVertexAttribPointer(R.att_color, 3, GL_FLOAT, GL_FALSE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, r));
glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_SHORT, nullptr);
}
}