Fix boundingbox drawtype, correctly handle all orientations
Affects prominently ChunkBuilder, BlockCursor, InventoryCube. It includes a fix to allow a wider Z range in 2D orthographic projections, and to make the hovering item have a Z that places it above all others. A new file, BlockGeometry.{c,h}pp contains the cube geometry common to all item drawing modules.
This commit is contained in:
parent
c386d62966
commit
9c954a2a01
@ -30,6 +30,7 @@
|
||||
#include <gk/core/Mouse.hpp>
|
||||
#include <gk/gl/GLCheck.hpp>
|
||||
|
||||
#include "BlockGeometry.hpp"
|
||||
#include "ClientApplication.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "EngineConfig.hpp"
|
||||
@ -42,6 +43,7 @@
|
||||
namespace fs = ghc::filesystem;
|
||||
|
||||
ClientApplication::ClientApplication(int argc, char **argv) : gk::CoreApplication(argc, argv) {
|
||||
BlockGeometry::initOrientation();
|
||||
}
|
||||
|
||||
void ClientApplication::init() {
|
||||
|
@ -29,9 +29,11 @@
|
||||
#include <gk/graphics/Color.hpp>
|
||||
#include <gk/gl/GLCheck.hpp>
|
||||
#include <gk/gl/Vertex.hpp>
|
||||
#include <gk/math/Math.hpp>
|
||||
#include <gk/resource/ResourceHandler.hpp>
|
||||
|
||||
#include "Block.hpp"
|
||||
#include "BlockGeometry.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "EngineConfig.hpp"
|
||||
#include "InventoryCube.hpp"
|
||||
@ -49,98 +51,76 @@ InventoryCube::InventoryCube(float size) : m_textureAtlas(gk::ResourceHandler::g
|
||||
m_transform.rotate(135.0f, {0, 0, 1});
|
||||
}
|
||||
|
||||
using namespace BlockGeometry;
|
||||
|
||||
void InventoryCube::updateVertexBuffer(const Block &block) {
|
||||
if (!block.id()) return;
|
||||
|
||||
// Same order as enum BlockFace in TilesDef.hpp
|
||||
gk::Vertex vertices[6][4] = {
|
||||
// West
|
||||
{
|
||||
{{0, m_size, 0, 2}},
|
||||
{{0, 0, 0, 2}},
|
||||
{{0, 0, m_size, 2}},
|
||||
{{0, m_size, m_size, 2}},
|
||||
},
|
||||
gk::Vertex vertices[nFaces][nVertsPerFace];
|
||||
|
||||
// East
|
||||
{
|
||||
{{m_size, 0, 0, 2}},
|
||||
{{m_size, m_size, 0, 2}},
|
||||
{{m_size, m_size, m_size, 2}},
|
||||
{{m_size, 0, m_size, 2}},
|
||||
},
|
||||
|
||||
// South
|
||||
{
|
||||
{{0, 0, 0, 4}},
|
||||
{{m_size, 0, 0, 4}},
|
||||
{{m_size, 0, m_size, 4}},
|
||||
{{0, 0, m_size, 4}},
|
||||
},
|
||||
|
||||
// North
|
||||
{
|
||||
{{m_size, m_size, 0, 4}},
|
||||
{{0, m_size, 0, 4}},
|
||||
{{0, m_size, m_size, 4}},
|
||||
{{m_size, m_size, m_size, 4}},
|
||||
},
|
||||
|
||||
// Bottom
|
||||
{
|
||||
{{m_size, 0, 0, -1}},
|
||||
{{0, 0, 0, -1}},
|
||||
{{0, m_size, 0, -1}},
|
||||
{{m_size, m_size, 0, -1}},
|
||||
},
|
||||
|
||||
// Top
|
||||
{
|
||||
{{m_size, m_size, m_size, 3}},
|
||||
{{0, m_size, m_size, 3}},
|
||||
{{0, 0, m_size, 3}},
|
||||
{{m_size, 0, m_size, 3}},
|
||||
},
|
||||
glm::vec3 vertexPos[nVertsPerCube] {
|
||||
// Order is important. It matches the bit order defined in BlockGeometry::cubeVerts.
|
||||
{0, 0, 0},
|
||||
{m_size, 0, 0},
|
||||
{0, m_size, 0},
|
||||
{m_size, m_size, 0},
|
||||
{0, 0, m_size},
|
||||
{m_size, 0, m_size},
|
||||
{0, m_size, m_size},
|
||||
{m_size, m_size, m_size},
|
||||
};
|
||||
|
||||
for (u8 i = 0 ; i < 6 ; ++i) {
|
||||
const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(block.tiles().getTextureForFace(i));
|
||||
float faceTexCoords[2 * 4] = {
|
||||
blockTexCoords.x, blockTexCoords.y + blockTexCoords.sizeY,
|
||||
blockTexCoords.x + blockTexCoords.sizeX, blockTexCoords.y + blockTexCoords.sizeY,
|
||||
blockTexCoords.x + blockTexCoords.sizeX, blockTexCoords.y,
|
||||
blockTexCoords.x, blockTexCoords.y
|
||||
};
|
||||
const gk::FloatBox &boundingBox = block.boundingBox();
|
||||
|
||||
for(u8 j = 0 ; j < 4 ; j++) {
|
||||
if (block.drawType() == BlockDrawType::BoundingBox) {
|
||||
vertices[i][j].coord3d[0] = vertices[i][j].coord3d[0] * block.boundingBox().sizeX + block.boundingBox().x;
|
||||
vertices[i][j].coord3d[1] = vertices[i][j].coord3d[1] * block.boundingBox().sizeY + block.boundingBox().y;
|
||||
vertices[i][j].coord3d[2] = vertices[i][j].coord3d[2] * block.boundingBox().sizeZ + block.boundingBox().z;
|
||||
constexpr s8f faceValue[nFaces]{2, 2, 4, 4, -1, 3};
|
||||
|
||||
for (u8 f = 0; f < nFaces; ++f) {
|
||||
// Calculate UV's
|
||||
// These are tough to obtain. Note that texture Y grows in the up-down direction, and so does V.
|
||||
// Vertex index in the bitmap array and U/V correspondence is:
|
||||
// U0V0 -> 3 2 <- U1V0
|
||||
// U0V1 -> 0 1 <- U1V1
|
||||
float U0, V0, U1, V1;
|
||||
if (block.drawType() == BlockDrawType::Cactus) {
|
||||
U0 = 0.f;
|
||||
V0 = 0.f;
|
||||
U1 = 1.f;
|
||||
V1 = 1.f;
|
||||
}
|
||||
else {
|
||||
U0 = (f == 0) ? 1.f - (boundingBox.y + boundingBox.sizeY) : (f == 1) ? boundingBox.y :
|
||||
(f == 3) ? 1.f - (boundingBox.x + boundingBox.sizeX) : boundingBox.x;
|
||||
V0 = (f <= 3) ? 1.f - (boundingBox.z + boundingBox.sizeZ) : (f == 4) ? boundingBox.y : 1.f - (boundingBox.y + boundingBox.sizeY);
|
||||
U1 = (f == 0) ? 1.f - boundingBox.y : (f == 1) ? boundingBox.y + boundingBox.sizeY :
|
||||
(f == 3) ? 1.f - boundingBox.x : boundingBox.x + boundingBox.sizeX;
|
||||
V1 = (f <= 3) ? 1.f - boundingBox.z : (f == 4) ? boundingBox.y + boundingBox.sizeY : 1.f - boundingBox.y;
|
||||
}
|
||||
|
||||
const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(block.tiles().getTextureForFace(f));
|
||||
|
||||
for (u8f v = 0; v < nVertsPerFace; ++v) {
|
||||
if (block.drawType() == BlockDrawType::Cactus) {
|
||||
vertices[f][v].coord3d[0] = vertexPos[cubeVerts[f][v]].x - boundingBox.x * faceNormals[f][0] * m_size;
|
||||
vertices[f][v].coord3d[1] = vertexPos[cubeVerts[f][v]].y - boundingBox.y * faceNormals[f][1] * m_size;
|
||||
vertices[f][v].coord3d[2] = vertexPos[cubeVerts[f][v]].z - boundingBox.z * faceNormals[f][2] * m_size;
|
||||
}
|
||||
else if (block.drawType() == BlockDrawType::Cactus) {
|
||||
static constexpr s8 normals[6][3] = {
|
||||
{-1, 0, 0},
|
||||
{1, 0, 0},
|
||||
{0, -1, 0},
|
||||
{0, 1, 0},
|
||||
{0, 0, -1},
|
||||
{0, 0, 1}
|
||||
};
|
||||
|
||||
vertices[i][j].coord3d[0] = vertices[i][j].coord3d[0] + block.boundingBox().x * -normals[i][0] * m_size;
|
||||
vertices[i][j].coord3d[1] = vertices[i][j].coord3d[1] + block.boundingBox().y * -normals[i][1] * m_size;
|
||||
vertices[i][j].coord3d[2] = vertices[i][j].coord3d[2] + block.boundingBox().z * -normals[i][2] * m_size;
|
||||
else {
|
||||
vertices[f][v].coord3d[0] = vertexPos[cubeVerts[f][v]].x * boundingBox.sizeX + boundingBox.x;
|
||||
vertices[f][v].coord3d[1] = vertexPos[cubeVerts[f][v]].y * boundingBox.sizeY + boundingBox.y;
|
||||
vertices[f][v].coord3d[2] = vertexPos[cubeVerts[f][v]].z * boundingBox.sizeZ + boundingBox.z;
|
||||
}
|
||||
vertices[f][v].coord3d[3] = faceValue[f];
|
||||
|
||||
vertices[i][j].texCoord[0] = faceTexCoords[j * 2];
|
||||
vertices[i][j].texCoord[1] = faceTexCoords[j * 2 + 1];
|
||||
float U = (v == 0 || v == 3) ? U0 : U1;
|
||||
float V = (v >= 2) ? V0 : V1;
|
||||
vertices[f][v].texCoord[0] = gk::qlerp(blockTexCoords.x, blockTexCoords.x + blockTexCoords.sizeX, U);
|
||||
vertices[f][v].texCoord[1] = gk::qlerp(blockTexCoords.y, blockTexCoords.y + blockTexCoords.sizeY, V);
|
||||
|
||||
const gk::Color &colorMultiplier = block.colorMultiplier();
|
||||
vertices[i][j].color[0] = colorMultiplier.r;
|
||||
vertices[i][j].color[1] = colorMultiplier.g;
|
||||
vertices[i][j].color[2] = colorMultiplier.b;
|
||||
vertices[i][j].color[3] = colorMultiplier.a;
|
||||
vertices[f][v].color[0] = colorMultiplier.r;
|
||||
vertices[f][v].color[1] = colorMultiplier.g;
|
||||
vertices[f][v].color[2] = colorMultiplier.b;
|
||||
vertices[f][v].color[3] = colorMultiplier.a;
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,23 +139,16 @@ void InventoryCube::draw(gk::RenderTarget &target, gk::RenderStates states) cons
|
||||
|
||||
states.viewMatrix = gk::Transform::Identity;
|
||||
|
||||
// NOTE: This matrix has Y inverted as well as Z, causing the default
|
||||
// rotation to show the bottom of the item instead of the top.
|
||||
states.projectionMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f, -40.0f, DIST_FAR);
|
||||
// NOTE: This matrix has Y inverted as well as Z. This means that
|
||||
// negative Z is closer to the user, and that the bottom side is visible
|
||||
// at start.
|
||||
states.projectionMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f, DIST_2D_FAR, DIST_2D_NEAR);
|
||||
|
||||
states.texture = &m_textureAtlas.texture();
|
||||
states.vertexAttributes = gk::VertexAttribute::Only2d;
|
||||
|
||||
glCheck(glDisable(GL_CULL_FACE));
|
||||
glCheck(glDisable(GL_DEPTH_TEST));
|
||||
|
||||
target.draw(m_vbo, GL_QUADS, 4 * BlockFace::Top, 4, states);
|
||||
// target.draw(m_vbo, GL_QUADS, 4 * 1, 4, states);
|
||||
target.draw(m_vbo, GL_QUADS, 4 * BlockFace::West, 4, states);
|
||||
// target.draw(m_vbo, GL_QUADS, 4 * 3, 4, states);
|
||||
target.draw(m_vbo, GL_QUADS, 4 * BlockFace::North, 4, states);
|
||||
// target.draw(m_vbo, GL_QUADS, 4 * 5, 4, states);
|
||||
|
||||
glCheck(glEnable(GL_CULL_FACE));
|
||||
glCheck(glEnable(GL_DEPTH_TEST));
|
||||
|
||||
target.draw(m_vbo, GL_QUADS, 0, nFaces * nVertsPerFace, states);
|
||||
}
|
||||
|
@ -239,6 +239,6 @@ void MouseItemWidget::updatePosition(float x, float y) {
|
||||
x -= m_parent->getPosition().x + 10 * m_parent->getScale().x;
|
||||
y -= m_parent->getPosition().y + 10 * m_parent->getScale().y;
|
||||
|
||||
setPosition(x / m_parent->getScale().x, y / m_parent->getScale().y, 0);
|
||||
setPosition(x / m_parent->getScale().x, y / m_parent->getScale().y, -20);
|
||||
}
|
||||
|
||||
|
@ -41,46 +41,6 @@
|
||||
#include "Hotbar.hpp"
|
||||
#include "Registry.hpp"
|
||||
|
||||
// Same order as enum BlockFace in TilesDef.hpp
|
||||
static float cubeCoords[6 * 4 * 3] = {
|
||||
// West
|
||||
0, 1, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 1,
|
||||
0, 1, 1,
|
||||
|
||||
// East
|
||||
1, 0, 0,
|
||||
1, 1, 0,
|
||||
1, 1, 1,
|
||||
1, 0, 1,
|
||||
|
||||
// South
|
||||
0, 0, 0,
|
||||
1, 0, 0,
|
||||
1, 0, 1,
|
||||
0, 0, 1,
|
||||
|
||||
// North
|
||||
1, 1, 0,
|
||||
0, 1, 0,
|
||||
0, 1, 1,
|
||||
1, 1, 1,
|
||||
|
||||
// Bottom
|
||||
1, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 1, 0,
|
||||
1, 1, 0,
|
||||
|
||||
// Top
|
||||
0, 0, 1,
|
||||
1, 0, 1,
|
||||
1, 1, 1,
|
||||
0, 1, 1,
|
||||
|
||||
};
|
||||
|
||||
BlockCursor::BlockCursor(ClientPlayer &player, ClientWorld &world, ClientCommandHandler &client)
|
||||
: m_player(player), m_world(world), m_client(client)
|
||||
{
|
||||
@ -114,12 +74,13 @@ void BlockCursor::onEvent(const SDL_Event &event, const Hotbar &hotbar) {
|
||||
s32 y = m_selectedBlock.y;
|
||||
s32 z = m_selectedBlock.z;
|
||||
|
||||
if(face == 0) x++;
|
||||
if(face == 3) x--;
|
||||
if(face == 1) y++;
|
||||
if(face == 4) y--;
|
||||
if(face == 2) z++;
|
||||
if(face == 5) z--;
|
||||
// FIXME: Document where these face numbers come from
|
||||
if (face == 0) ++x;
|
||||
if (face == 3) --x;
|
||||
if (face == 1) ++y;
|
||||
if (face == 4) --y;
|
||||
if (face == 2) ++z;
|
||||
if (face == 5) --z;
|
||||
|
||||
// First, we check if the new block is not replacing another block
|
||||
u32 blockId = m_world.getBlock(x, y, z);
|
||||
@ -134,7 +95,7 @@ void BlockCursor::onEvent(const SDL_Event &event, const Hotbar &hotbar) {
|
||||
|
||||
u32 block = hotbar.currentItem();
|
||||
if (newBlock.isRotatable()) {
|
||||
static constexpr u8 dirToFaceDir[4] = {0, 2, 1, 3};
|
||||
static constexpr u8 dirToFaceDir[4]{0, 2, 1, 3};
|
||||
u16 data = dirToFaceDir[m_player.getOppositeDirection() & 0x3];
|
||||
m_world.setData(x, y, z, data);
|
||||
|
||||
@ -198,55 +159,90 @@ void BlockCursor::update(const Hotbar &hotbar) {
|
||||
}
|
||||
}
|
||||
|
||||
u8f orientation = m_currentBlock->isRotatable() ? m_world.getData(selectedBlock.x, selectedBlock.y, selectedBlock.z) & 0x1F : 0;
|
||||
|
||||
if (m_selectedBlock.w != -1)
|
||||
updateVertexBuffer(*m_currentBlock);
|
||||
updateVertexBuffer(*m_currentBlock, orientation);
|
||||
else
|
||||
m_currentBlock = nullptr;
|
||||
|
||||
if (m_animationStart && m_currentBlock)
|
||||
updateAnimationVertexBuffer(*m_currentBlock, (gk::GameClock::getTicks() - m_animationStart) / (timeToBreak * 100));
|
||||
updateAnimationVertexBuffer(*m_currentBlock, orientation,
|
||||
(gk::GameClock::getTicks() - m_animationStart) / (timeToBreak * 100));
|
||||
}
|
||||
|
||||
void BlockCursor::updateVertexBuffer(const Block &block) {
|
||||
gk::Vertex vertices[24];
|
||||
for (u8 i = 0 ; i < 24 ; ++i) {
|
||||
vertices[i].coord3d[0] = cubeCoords[i * 3] * block.boundingBox().sizeX + block.boundingBox().x;
|
||||
vertices[i].coord3d[1] = cubeCoords[i * 3 + 1] * block.boundingBox().sizeY + block.boundingBox().y;
|
||||
vertices[i].coord3d[2] = cubeCoords[i * 3 + 2] * block.boundingBox().sizeZ + block.boundingBox().z;
|
||||
vertices[i].coord3d[3] = -1;
|
||||
using namespace BlockGeometry;
|
||||
|
||||
void BlockCursor::updateVBOCoords(gk::Vertex vertices[nFaces][nVertsPerFace], const Block &block,
|
||||
float face, u8f orientation)
|
||||
{
|
||||
glm::vec3 bottomLeft{block.boundingBox().x, block.boundingBox().y, block.boundingBox().z};
|
||||
glm::vec3 topRight{block.boundingBox().sizeX, block.boundingBox().sizeY, block.boundingBox().sizeZ};
|
||||
topRight += bottomLeft;
|
||||
|
||||
const glm::mat3 &orientMatrix = orientMatrices[orientation];
|
||||
|
||||
glm::vec3 vertexPos[nVertsPerCube]{
|
||||
// Order is important. It matches the bit order defined in BlockGeometry::cubeVerts.
|
||||
{bottomLeft.x, bottomLeft.y, bottomLeft.z},
|
||||
{topRight.x, bottomLeft.y, bottomLeft.z},
|
||||
{bottomLeft.x, topRight.y, bottomLeft.z},
|
||||
{topRight.x, topRight.y, bottomLeft.z},
|
||||
{bottomLeft.x, bottomLeft.y, topRight.z},
|
||||
{topRight.x, bottomLeft.y, topRight.z},
|
||||
{bottomLeft.x, topRight.y, topRight.z},
|
||||
{topRight.x, topRight.y, topRight.z},
|
||||
};
|
||||
|
||||
if (orientation != 0) {
|
||||
static const glm::vec3 half{0.5, 0.5, 0.5};
|
||||
// Rotate each vertex coordinate around the centre of the cube
|
||||
for (int i = 0; i < nVertsPerCube; ++i) {
|
||||
vertexPos[i] = orientMatrix * (vertexPos[i] - half) + half;
|
||||
}
|
||||
}
|
||||
|
||||
for (u8f f = 0 ; f < nFaces; ++f) {
|
||||
for (u8f v = 0; v < nVertsPerFace; ++v) {
|
||||
vertices[f][v].coord3d[0] = vertexPos[cubeVerts[f][v]].x;
|
||||
vertices[f][v].coord3d[1] = vertexPos[cubeVerts[f][v]].y;
|
||||
vertices[f][v].coord3d[2] = vertexPos[cubeVerts[f][v]].z;
|
||||
vertices[f][v].coord3d[3] = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockCursor::updateVertexBuffer(const Block &block, u8f orientation) {
|
||||
gk::Vertex vertices[nFaces][nVertsPerFace];
|
||||
updateVBOCoords(vertices, block, -1, orientation);
|
||||
|
||||
gk::VertexBuffer::bind(&m_vbo);
|
||||
m_vbo.setData(sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
|
||||
gk::VertexBuffer::bind(nullptr);
|
||||
}
|
||||
|
||||
void BlockCursor::updateAnimationVertexBuffer(const Block &block, int animationPos) {
|
||||
gk::Vertex vertices[24];
|
||||
for (u8 i = 0 ; i < 24 ; ++i) {
|
||||
vertices[i].coord3d[0] = cubeCoords[i * 3] * block.boundingBox().sizeX + block.boundingBox().x;
|
||||
vertices[i].coord3d[1] = cubeCoords[i * 3 + 1] * block.boundingBox().sizeY + block.boundingBox().y;
|
||||
vertices[i].coord3d[2] = cubeCoords[i * 3 + 2] * block.boundingBox().sizeZ + block.boundingBox().z;
|
||||
vertices[i].coord3d[3] = -2;
|
||||
}
|
||||
void BlockCursor::updateAnimationVertexBuffer(const Block &block, u8f orientation, int animationPos) {
|
||||
gk::Vertex vertices[nFaces][nVertsPerFace];
|
||||
updateVBOCoords(vertices, block, -2, orientation);
|
||||
|
||||
GLfloat color[4] = {1, 1, 1, 0.5};
|
||||
for (int i = 0 ; i < 24 ; ++i)
|
||||
memcpy(vertices[i].color, color, 4 * sizeof(GLfloat));
|
||||
for (u8f f = 0; f < nFaces; ++f)
|
||||
for (u8f v = 0; v < nVertsPerFace; ++v)
|
||||
memcpy(&vertices[f][v].color, &color[0], 4 * sizeof(GLfloat));
|
||||
|
||||
if (animationPos != -1) {
|
||||
glm::vec4 blockTexCoords{0.1f * animationPos, 0.0, 0.1f + 0.1f * animationPos, 1.0};
|
||||
float faceTexCoords[2 * 4] = {
|
||||
blockTexCoords.x, blockTexCoords.w,
|
||||
blockTexCoords.z, blockTexCoords.w,
|
||||
blockTexCoords.z, blockTexCoords.y,
|
||||
blockTexCoords.x, blockTexCoords.y
|
||||
float faceTexCoords[nVertsPerFace][nCoordsPerUV] = {
|
||||
{blockTexCoords.x, blockTexCoords.w},
|
||||
{blockTexCoords.z, blockTexCoords.w},
|
||||
{blockTexCoords.z, blockTexCoords.y},
|
||||
{blockTexCoords.x, blockTexCoords.y},
|
||||
};
|
||||
|
||||
for (u8 i = 0 ; i < 6 ; ++i) {
|
||||
for(u8 j = 0 ; j < 4 ; j++) {
|
||||
vertices[j + i * 4].texCoord[0] = faceTexCoords[j * 2];
|
||||
vertices[j + i * 4].texCoord[1] = faceTexCoords[j * 2 + 1];
|
||||
for (u8f f = 0 ; f < nFaces ; ++f) {
|
||||
for(u8f v = 0 ; v < nVertsPerFace ; v++) {
|
||||
vertices[f][v].texCoord[0] = faceTexCoords[v][0];
|
||||
vertices[f][v].texCoord[1] = faceTexCoords[v][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,7 +263,7 @@ void BlockCursor::draw(gk::RenderTarget &target, gk::RenderStates states) const
|
||||
states.transform.translate(m_selectedBlock.x - cameraPosition.x, m_selectedBlock.y - cameraPosition.y, m_selectedBlock.z - cameraPosition.z);
|
||||
|
||||
glCheck(glPolygonMode(GL_FRONT_AND_BACK, GL_LINE));
|
||||
target.draw(m_vbo, GL_QUADS, 0, 24, states);
|
||||
target.draw(m_vbo, GL_QUADS, 0, nFaces * nVertsPerFace, states);
|
||||
glCheck(glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
|
||||
|
||||
if (m_animationStart > 0) {
|
||||
@ -276,7 +272,7 @@ void BlockCursor::draw(gk::RenderTarget &target, gk::RenderStates states) const
|
||||
|
||||
states.texture = m_blockDestroyTexture;
|
||||
|
||||
target.draw(m_animationVBO, GL_QUADS, 0, 24, states);
|
||||
target.draw(m_animationVBO, GL_QUADS, 0, nFaces * nVertsPerFace, states);
|
||||
|
||||
glCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
}
|
||||
@ -289,12 +285,12 @@ glm::ivec4 BlockCursor::findSelectedBlock() const {
|
||||
m_player.camera().getDPosition().y,
|
||||
m_player.camera().getDPosition().z};
|
||||
|
||||
int_fast32_t bestX = int_fast32_t(floor(position.x));
|
||||
int_fast32_t bestY = int_fast32_t(floor(position.y));
|
||||
int_fast32_t bestZ = int_fast32_t(floor(position.z));
|
||||
s32f bestX = s32f(floor(position.x));
|
||||
s32f bestY = s32f(floor(position.y));
|
||||
s32f bestZ = s32f(floor(position.z));
|
||||
|
||||
// Deal with a degenerate case: camera in the middle of a block
|
||||
uint_fast32_t blockID = m_world.getBlock(bestX, bestY, bestZ);
|
||||
u32f blockID = m_world.getBlock(bestX, bestY, bestZ);
|
||||
const Block &block = Registry::getInstance().getBlock(blockID);
|
||||
if (blockID && block.drawType() != BlockDrawType::Liquid) {
|
||||
// We're inside a node, therefore there's no face, but we still need
|
||||
@ -327,7 +323,7 @@ glm::ivec4 BlockCursor::findSelectedBlock() const {
|
||||
// Ray casting algorithm to find out which block we are looking at
|
||||
const double maxReach = 10.;
|
||||
double bestDepth;
|
||||
int_fast8_t bestFace = -1;
|
||||
s8f bestFace = -1;
|
||||
|
||||
glm::dvec3 lookAtN = glm::normalize(lookAt);
|
||||
|
||||
@ -337,4 +333,3 @@ glm::ivec4 BlockCursor::findSelectedBlock() const {
|
||||
|
||||
return glm::ivec4{bestX, bestY, bestZ, bestFace};
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "ClientWorld.hpp"
|
||||
#include "Inventory.hpp"
|
||||
#include "BlockGeometry.hpp"
|
||||
|
||||
class ClientCommandHandler;
|
||||
class ClientPlayer;
|
||||
@ -47,8 +48,10 @@ class BlockCursor : public gk::Drawable {
|
||||
const Block *currentBlock() const { return m_currentBlock; }
|
||||
|
||||
private:
|
||||
void updateVertexBuffer(const Block &block);
|
||||
void updateAnimationVertexBuffer(const Block &block, int animationPos = -1);
|
||||
void updateVertexBuffer(const Block &block, const u8f orientation);
|
||||
void updateAnimationVertexBuffer(const Block &block, const u8f orientation, int animationPos = -1);
|
||||
void updateVBOCoords(gk::Vertex vertices[BlockGeometry::nFaces][BlockGeometry::nVertsPerFace],
|
||||
const Block &block, float face, u8f orientation);
|
||||
|
||||
void draw(gk::RenderTarget &target, gk::RenderStates states) const override;
|
||||
|
||||
|
@ -51,7 +51,7 @@ HUD::HUD(ClientPlayer &player, ClientWorld &world, ClientCommandHandler &client)
|
||||
}
|
||||
|
||||
void HUD::setup() {
|
||||
m_orthoMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f);
|
||||
m_orthoMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f, DIST_2D_FAR, DIST_2D_NEAR);
|
||||
|
||||
m_hotbar.setPosition(Config::screenWidth / getScale().x / 2 - m_hotbar.width() / 2, Config::screenHeight / getScale().y - m_hotbar.height(), 0);
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
* =====================================================================================
|
||||
*/
|
||||
#include "BlockCursorRaycast.hpp"
|
||||
#include "BlockGeometry.hpp"
|
||||
#include "ClientWorld.hpp"
|
||||
#include "Registry.hpp"
|
||||
|
||||
@ -47,16 +48,10 @@ static inline glm::dvec3 intersectAxisPlane(const Axis axis, const double coord,
|
||||
|
||||
static inline void recordHit(const glm::dvec3 &position,
|
||||
const glm::dvec3 &isect,
|
||||
const Axis axis,
|
||||
const bool neg,
|
||||
const int_fast32_t nx,
|
||||
const int_fast32_t ny,
|
||||
const int_fast32_t nz,
|
||||
int_fast32_t &bestX,
|
||||
int_fast32_t &bestY,
|
||||
int_fast32_t &bestZ,
|
||||
int_fast8_t &bestFace,
|
||||
double &bestDepth,
|
||||
const Axis axis, const bool neg,
|
||||
const s32f nx, const s32f ny, const s32f nz,
|
||||
s32f &bestX, s32f &bestY, s32f &bestZ,
|
||||
s8f &bestFace, double &bestDepth,
|
||||
bool &hit)
|
||||
{
|
||||
// Check if we have a record
|
||||
@ -76,11 +71,9 @@ static inline void recordHit(const glm::dvec3 &position,
|
||||
}
|
||||
|
||||
void BlockCursorRaycast::rayCastToAxis(const Axis axis, const glm::dvec3 &position,
|
||||
const glm::dvec3 &lookAt,
|
||||
const double maxReach,
|
||||
int_fast32_t &bestX, int_fast32_t &bestY,
|
||||
int_fast32_t &bestZ,
|
||||
int_fast8_t &bestFace, double &bestDepth,
|
||||
const glm::dvec3 &lookAt, const double maxReach,
|
||||
s32f &bestX, s32f &bestY, s32f &bestZ,
|
||||
s8f &bestFace, double &bestDepth,
|
||||
const ClientWorld &world)
|
||||
{
|
||||
glm::dvec3 isect;
|
||||
@ -90,7 +83,7 @@ void BlockCursorRaycast::rayCastToAxis(const Axis axis, const glm::dvec3 &positi
|
||||
// of 'lookAt' with length 'maxReach' crosses several nodes in
|
||||
// the X direction at integer positions. Determine the first and
|
||||
// last such positions.
|
||||
switch(axis) {
|
||||
switch (axis) {
|
||||
case AXIS_X:
|
||||
posCoord = position.x;
|
||||
lookAtCoord = lookAt.x;
|
||||
@ -105,8 +98,8 @@ void BlockCursorRaycast::rayCastToAxis(const Axis axis, const glm::dvec3 &positi
|
||||
break;
|
||||
}
|
||||
|
||||
int_fast32_t firstNodeRow = lookAtCoord < 0. ? int_fast32_t(floor(posCoord)) : int_fast32_t(ceil(posCoord)) - 1;
|
||||
int_fast32_t lastNodeRow = int_fast32_t(floor(posCoord + lookAtCoord * maxReach));
|
||||
s32f firstNodeRow = lookAtCoord < 0. ? s32f(floor(posCoord)) : s32f(ceil(posCoord)) - 1;
|
||||
s32f lastNodeRow = s32f(floor(posCoord + lookAtCoord * maxReach));
|
||||
|
||||
int_fast8_t dir = (lookAtCoord > 0.) - (lookAtCoord < 0.);
|
||||
if (!dir) {
|
||||
@ -114,39 +107,58 @@ void BlockCursorRaycast::rayCastToAxis(const Axis axis, const glm::dvec3 &positi
|
||||
return;
|
||||
}
|
||||
|
||||
for(int_fast32_t nodeRow = firstNodeRow + dir;
|
||||
dir > 0 ? (nodeRow <= lastNodeRow) : (nodeRow >= lastNodeRow); nodeRow += dir)
|
||||
for (s32f nodeRow = firstNodeRow + dir;
|
||||
dir > 0 ? (nodeRow <= lastNodeRow) : (nodeRow >= lastNodeRow); nodeRow += dir)
|
||||
{
|
||||
isect = intersectAxisPlane(axis, double(nodeRow + (dir < 0)), position, lookAt);
|
||||
|
||||
int_fast32_t nx, ny, nz;
|
||||
s32f nx, ny, nz;
|
||||
nx = axis == AXIS_X ? nodeRow : floor(isect.x);
|
||||
ny = axis == AXIS_Y ? nodeRow : floor(isect.y);
|
||||
nz = axis == AXIS_Z ? nodeRow : floor(isect.z);
|
||||
|
||||
u32 blockID = world.getBlock(nx, ny, nz);
|
||||
const Block &block = Registry::getInstance().getBlock(blockID);
|
||||
if(blockID && block.drawType() != BlockDrawType::Liquid) {
|
||||
|
||||
u8f orientation = block.isRotatable() ? world.getData(nx, ny, nz) & 0x1F : 0;
|
||||
|
||||
const gk::FloatBox &boundingBox = block.boundingBox();
|
||||
glm::vec3 localCorner1{boundingBox.x, boundingBox.y, boundingBox.z};
|
||||
glm::vec3 localCorner2{boundingBox.sizeX, boundingBox.sizeY, boundingBox.sizeZ};
|
||||
localCorner2 += localCorner1;
|
||||
|
||||
if (orientation) {
|
||||
const glm::vec3 half{0.5, 0.5, 0.5};
|
||||
localCorner1 = BlockGeometry::orientMatrices[orientation] * (localCorner1 - half) + half;
|
||||
localCorner2 = BlockGeometry::orientMatrices[orientation] * (localCorner2 - half) + half;
|
||||
if (localCorner2.x < localCorner1.x) std::swap(localCorner1.x, localCorner2.x);
|
||||
if (localCorner2.y < localCorner1.y) std::swap(localCorner1.y, localCorner2.y);
|
||||
if (localCorner2.z < localCorner1.z) std::swap(localCorner1.z, localCorner2.z);
|
||||
}
|
||||
|
||||
if (blockID && block.drawType() != BlockDrawType::Liquid) {
|
||||
// Check bounding box; this should loop over all selection boxes
|
||||
// when they are implemented
|
||||
gk::DoubleBox selBox = block.boundingBox() + gk::Vector3d{double(nx), double(ny), double(nz)};
|
||||
const glm::dvec3 cubePos{double(nx), double(ny), double(nz)};
|
||||
glm::dvec3 corner1 = glm::dvec3(localCorner1) + cubePos;
|
||||
glm::dvec3 corner2 = glm::dvec3(localCorner2) + cubePos;
|
||||
|
||||
bool hit = false;
|
||||
|
||||
// Check if we hit any of the sides of the inner box
|
||||
isect = intersectAxisPlane(AXIS_X, (lookAt.x < 0. ? selBox.x + selBox.sizeX : selBox.x), position, lookAt);
|
||||
if (selBox.y <= isect.y && isect.y <= selBox.y + selBox.sizeY
|
||||
&& selBox.z <= isect.z && isect.z <= selBox.z + selBox.sizeZ)
|
||||
isect = intersectAxisPlane(AXIS_X, (lookAt.x < 0. ? corner2.x : corner1.x), position, lookAt);
|
||||
if (corner1.y <= isect.y && isect.y <= corner2.y
|
||||
&& corner1.z <= isect.z && isect.z <= corner2.z)
|
||||
recordHit(position, isect, AXIS_X, lookAt.x < 0., nx, ny, nz, bestX, bestY, bestZ, bestFace, bestDepth, hit);
|
||||
|
||||
isect = intersectAxisPlane(AXIS_Y, (lookAt.y < 0. ? selBox.y + selBox.sizeY : selBox.y), position, lookAt);
|
||||
if (selBox.x <= isect.x && isect.x <= selBox.x + selBox.sizeX
|
||||
&& selBox.z <= isect.z && isect.z <= selBox.z + selBox.sizeZ)
|
||||
isect = intersectAxisPlane(AXIS_Y, (lookAt.y < 0. ? corner2.y : corner1.y), position, lookAt);
|
||||
if (corner1.x <= isect.x && isect.x <= corner2.x
|
||||
&& corner1.z <= isect.z && isect.z <= corner2.z)
|
||||
recordHit(position, isect, AXIS_Y, lookAt.y < 0., nx, ny, nz, bestX, bestY, bestZ, bestFace, bestDepth, hit);
|
||||
|
||||
isect = intersectAxisPlane(AXIS_Z, (lookAt.z < 0. ? selBox.z + selBox.sizeZ : selBox.z), position, lookAt);
|
||||
if (selBox.x <= isect.x && isect.x <= selBox.x + selBox.sizeX
|
||||
&& selBox.y <= isect.y && isect.y <= selBox.y + selBox.sizeY)
|
||||
isect = intersectAxisPlane(AXIS_Z, (lookAt.z < 0. ? corner2.z : corner1.z), position, lookAt);
|
||||
if (corner1.x <= isect.x && isect.x <= corner2.x
|
||||
&& corner1.y <= isect.y && isect.y <= corner2.y)
|
||||
recordHit(position, isect, AXIS_Z, lookAt.z < 0., nx, ny, nz, bestX, bestY, bestZ, bestFace, bestDepth, hit);
|
||||
|
||||
if (hit)
|
||||
@ -154,4 +166,3 @@ void BlockCursorRaycast::rayCastToAxis(const Axis axis, const glm::dvec3 &positi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#ifndef BLOCKCURSORRAYCAST_HPP_
|
||||
#define BLOCKCURSORRAYCAST_HPP_
|
||||
|
||||
#include <gk/core/IntTypes.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
enum Axis {
|
||||
@ -39,12 +40,10 @@ class ClientWorld;
|
||||
|
||||
namespace BlockCursorRaycast {
|
||||
void rayCastToAxis(const Axis axis, const glm::dvec3 &position,
|
||||
const glm::dvec3 &lookAt,
|
||||
const double maxReach,
|
||||
int_fast32_t &bestX, int_fast32_t &bestY,
|
||||
int_fast32_t &bestZ,
|
||||
int_fast8_t &bestFace, double &bestDepth,
|
||||
const glm::dvec3 &lookAt, const double maxReach,
|
||||
s32f &bestX, s32f &bestY, s32f &bestZ,
|
||||
s8f &bestFace, double &bestDepth,
|
||||
const ClientWorld &world);
|
||||
}
|
||||
} // namespace BlockCursorRaycast
|
||||
|
||||
#endif // BLOCKCURSORRAYCAST_HPP_
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <gk/core/ApplicationStateStack.hpp>
|
||||
|
||||
#include "Config.hpp"
|
||||
#include "EngineConfig.hpp"
|
||||
#include "InterfaceState.hpp"
|
||||
|
||||
InterfaceState::InterfaceState(gk::ApplicationState *parent) : gk::ApplicationState(parent) {
|
||||
@ -43,7 +44,7 @@ InterfaceState::InterfaceState(gk::ApplicationState *parent) : gk::ApplicationSt
|
||||
}
|
||||
|
||||
void InterfaceState::setup() {
|
||||
m_projectionMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f);
|
||||
m_projectionMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f, DIST_2D_FAR, DIST_2D_NEAR);
|
||||
|
||||
m_background.setSize(Config::screenWidth, Config::screenHeight);
|
||||
|
||||
|
@ -24,114 +24,21 @@
|
||||
*
|
||||
* =====================================================================================
|
||||
*/
|
||||
#include <gk/math/Math.hpp>
|
||||
|
||||
#include "BlockGeometry.hpp"
|
||||
#include "ClientChunk.hpp"
|
||||
#include "ChunkBuilder.hpp"
|
||||
#include "Registry.hpp"
|
||||
#include "TextureAtlas.hpp"
|
||||
|
||||
constexpr int nAxes = 6;
|
||||
constexpr int nAxesP2 = 8; // Next power of 2 to nAxes
|
||||
constexpr int nRots = 4;
|
||||
constexpr int nFaces = 6;
|
||||
constexpr int nCrossFaces = 2;
|
||||
constexpr int nVertsPerFace = 4;
|
||||
constexpr int nNormals = 1;
|
||||
constexpr int nCoords = 3;
|
||||
constexpr int nCoordsPerUV = 2;
|
||||
using namespace BlockGeometry;
|
||||
|
||||
// Same order as enum BlockFace in TilesDef.hpp
|
||||
static constexpr ChunkBuilder::tCubeCoord cubeCoords[nFaces][nVertsPerFace + nNormals][nCoords] = {
|
||||
// West
|
||||
{
|
||||
{0, 1, 0},
|
||||
{0, 0, 0},
|
||||
{0, 0, 1},
|
||||
{0, 1, 1},
|
||||
|
||||
{-1, 0, 0}, // normal
|
||||
},
|
||||
|
||||
// East
|
||||
{
|
||||
{1, 0, 0},
|
||||
{1, 1, 0},
|
||||
{1, 1, 1},
|
||||
{1, 0, 1},
|
||||
|
||||
{1, 0, 0},
|
||||
},
|
||||
|
||||
// South
|
||||
{
|
||||
{0, 0, 0},
|
||||
{1, 0, 0},
|
||||
{1, 0, 1},
|
||||
{0, 0, 1},
|
||||
|
||||
{0,-1, 0},
|
||||
},
|
||||
|
||||
// North
|
||||
{
|
||||
{1, 1, 0},
|
||||
{0, 1, 0},
|
||||
{0, 1, 1},
|
||||
{1, 1, 1},
|
||||
|
||||
{0, 1, 0},
|
||||
},
|
||||
|
||||
// Bottom
|
||||
{
|
||||
{1, 0, 0},
|
||||
{0, 0, 0},
|
||||
{0, 1, 0},
|
||||
{1, 1, 0},
|
||||
|
||||
{0, 0,-1},
|
||||
},
|
||||
|
||||
// Top
|
||||
{
|
||||
{1, 1, 1},
|
||||
{0, 1, 1},
|
||||
{0, 0, 1},
|
||||
{1, 0, 1},
|
||||
|
||||
{0, 0, 1},
|
||||
},
|
||||
};
|
||||
|
||||
static constexpr ChunkBuilder::tCubeCoord crossCoords[nCrossFaces][nVertsPerFace][nCoords] = {
|
||||
{
|
||||
{1, 1, 0},
|
||||
{0, 0, 0},
|
||||
{0, 0, 1},
|
||||
{1, 1, 1},
|
||||
},
|
||||
|
||||
{
|
||||
{1, 0, 0},
|
||||
{0, 1, 0},
|
||||
{0, 1, 1},
|
||||
{1, 0, 1},
|
||||
},
|
||||
};
|
||||
|
||||
static ChunkBuilder::tCubeCoord orientCubeCoords[nAxesP2 * nRots][nFaces][nVertsPerFace + nNormals][nCoords];
|
||||
|
||||
ChunkBuilder::ChunkBuilder(TextureAtlas &textureAtlas) : m_textureAtlas(textureAtlas) {
|
||||
static bool isOrientInitialized = false;
|
||||
|
||||
if (!isOrientInitialized) {
|
||||
initializeOrientation();
|
||||
isOrientInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const ClientChunk &chunk, const std::array<gk::VertexBuffer, layers> &vbo) {
|
||||
std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const ClientChunk &chunk,
|
||||
const std::array<gk::VertexBuffer, layers> &vbo)
|
||||
{
|
||||
for (s8f i = 0 ; i < layers ; ++i)
|
||||
m_vertices[i].reserve(CHUNK_WIDTH * CHUNK_DEPTH * CHUNK_HEIGHT * 6 * 4);
|
||||
m_vertices[i].reserve(CHUNK_WIDTH * CHUNK_DEPTH * CHUNK_HEIGHT * nFaces * nVertsPerFace);
|
||||
|
||||
for (s8f z = 0 ; z < CHUNK_HEIGHT ; z++) {
|
||||
for (s8f y = 0 ; y < CHUNK_DEPTH ; y++) {
|
||||
@ -140,18 +47,94 @@ std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const Cli
|
||||
if (!block.id()) continue;
|
||||
if (!chunk.getBlock(x, y, z)) continue;
|
||||
|
||||
const gk::FloatBox &boundingBox = block.boundingBox();
|
||||
|
||||
u8f orientation = block.isRotatable() ? chunk.getData(x, y, z) & 0x1F : 0;
|
||||
const glm::mat3 &orientMatrix = orientMatrices[orientation];
|
||||
|
||||
if (block.drawType() == BlockDrawType::Solid
|
||||
|| block.drawType() == BlockDrawType::Leaves
|
||||
|| block.drawType() == BlockDrawType::Liquid
|
||||
|| block.drawType() == BlockDrawType::Glass
|
||||
|| block.drawType() == BlockDrawType::Cactus
|
||||
|| block.drawType() == BlockDrawType::BoundingBox) {
|
||||
for (s8f i = 0 ; i < nFaces ; i++) {
|
||||
addFace(x, y, z, i, chunk, &block);
|
||||
|| block.drawType() == BlockDrawType::BoundingBox)
|
||||
{
|
||||
glm::vec3 vertexPos[nVertsPerCube]{
|
||||
// Order is important. It matches the bit order defined in BlockGeometry::cubeVerts.
|
||||
{boundingBox.x, boundingBox.y, boundingBox.z},
|
||||
{boundingBox.x + boundingBox.sizeX, boundingBox.y, boundingBox.z},
|
||||
{boundingBox.x, boundingBox.y + boundingBox.sizeY, boundingBox.z},
|
||||
{boundingBox.x + boundingBox.sizeX, boundingBox.y + boundingBox.sizeY, boundingBox.z},
|
||||
{boundingBox.x, boundingBox.y, boundingBox.z + boundingBox.sizeZ},
|
||||
{boundingBox.x + boundingBox.sizeX, boundingBox.y, boundingBox.z + boundingBox.sizeZ},
|
||||
{boundingBox.x, boundingBox.y + boundingBox.sizeY, boundingBox.z + boundingBox.sizeZ},
|
||||
{boundingBox.x + boundingBox.sizeX, boundingBox.y + boundingBox.sizeY, boundingBox.z + boundingBox.sizeZ},
|
||||
};
|
||||
|
||||
if (block.drawType() == BlockDrawType::Cactus) {
|
||||
// Ignore bounding box, initialize it to full node coordinates
|
||||
for (u8f i = 0; i < nVertsPerCube; ++i) {
|
||||
vertexPos[i].x = (i >> 0) & 1;
|
||||
vertexPos[i].y = (i >> 1) & 1;
|
||||
vertexPos[i].z = (i >> 2) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
// vNeighbour is used to find neighbouring cubes per vertex.
|
||||
// Same binary layout.
|
||||
glm::vec3 vNeighbour[nVertsPerCube] = {
|
||||
{-1,-1,-1}, { 1,-1,-1}, {-1, 1,-1}, { 1, 1,-1}, {-1,-1, 1}, { 1,-1, 1}, {-1, 1, 1}, {1, 1, 1},
|
||||
};
|
||||
|
||||
if (orientation) { // don't work extra if it's not oriented differently
|
||||
static const glm::vec3 half{0.5, 0.5, 0.5};
|
||||
// Rotate each vertex coordinate around the centre of the
|
||||
// cube, and each vertex neighbour around the origin
|
||||
for (int i = 0; i < nVertsPerCube; ++i) {
|
||||
vertexPos[i] = orientMatrix * (vertexPos[i] - half) + half;
|
||||
vNeighbour[i] = orientMatrix * vNeighbour[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (s8f f = 0; f < nFaces ; ++f) {
|
||||
// Construct the normal vector to a face
|
||||
const glm::vec3 glmNormal = orientMatrix * faceNormals[f];
|
||||
const gk::Vector3i normal{int(glmNormal.x), int(glmNormal.y), int(glmNormal.z)};
|
||||
|
||||
// Construct an array with the 4 vertex positions of this face
|
||||
glm::vec3 *faceVerts[nVertsPerFace]{&vertexPos[cubeVerts[f][0]], &vertexPos[cubeVerts[f][1]],
|
||||
&vertexPos[cubeVerts[f][2]], &vertexPos[cubeVerts[f][3]]};
|
||||
|
||||
// Construct an array with the 4 vertex neighbours of this face
|
||||
// (as GameKit integer vectors)
|
||||
const gk::Vector3i corner0{int(vNeighbour[cubeVerts[f][0]].x), int(vNeighbour[cubeVerts[f][0]].y), int(vNeighbour[cubeVerts[f][0]].z)};
|
||||
const gk::Vector3i corner1{int(vNeighbour[cubeVerts[f][1]].x), int(vNeighbour[cubeVerts[f][1]].y), int(vNeighbour[cubeVerts[f][1]].z)};
|
||||
const gk::Vector3i corner2{int(vNeighbour[cubeVerts[f][2]].x), int(vNeighbour[cubeVerts[f][2]].y), int(vNeighbour[cubeVerts[f][2]].z)};
|
||||
const gk::Vector3i corner3{int(vNeighbour[cubeVerts[f][3]].x), int(vNeighbour[cubeVerts[f][3]].y), int(vNeighbour[cubeVerts[f][3]].z)};
|
||||
|
||||
const gk::Vector3i *vFaceNeighbours[nVertsPerFace]{&corner0, &corner1, &corner2, &corner3};
|
||||
|
||||
addFace(x, y, z, f, chunk, block, normal, faceVerts, vFaceNeighbours);
|
||||
}
|
||||
}
|
||||
else if (block.drawType() == BlockDrawType::XShape) {
|
||||
addCross(x, y, z, chunk, &block);
|
||||
glm::vec3 vertexPos[nVertsPerCube]{
|
||||
{0, 0, 0},
|
||||
{1, 0, 0},
|
||||
{0, 1, 0},
|
||||
{1, 1, 0},
|
||||
{0, 0, 1},
|
||||
{1, 0, 1},
|
||||
{0, 1, 1},
|
||||
{1, 1, 1},
|
||||
};
|
||||
const glm::vec3 *const faceVertices[nCrossFaces][nVertsPerFace]{
|
||||
{&vertexPos[crossVerts[0][0]], &vertexPos[crossVerts[0][1]],
|
||||
&vertexPos[crossVerts[0][2]], &vertexPos[crossVerts[0][3]]},
|
||||
{&vertexPos[crossVerts[1][0]], &vertexPos[crossVerts[1][1]],
|
||||
&vertexPos[crossVerts[1][2]], &vertexPos[crossVerts[1][3]]},
|
||||
};
|
||||
addCross(x, y, z, chunk, block, faceVertices);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,54 +156,61 @@ std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const Cli
|
||||
return verticesCount;
|
||||
}
|
||||
|
||||
inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const Block *block) {
|
||||
u8 orientation = block->isRotatable() ? chunk.getData(x, y, z) & 0x1F : 0;
|
||||
gk::Vector3i normal{orientCubeCoords[orientation][f][4][0],
|
||||
orientCubeCoords[orientation][f][4][1],
|
||||
orientCubeCoords[orientation][f][4][2]};
|
||||
|
||||
// Get surrounding block for that face
|
||||
inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const Block &block,
|
||||
const gk::Vector3i &normal, const glm::vec3 *const vertexPos[nVertsPerFace],
|
||||
const gk::Vector3i *const neighbourOfs[nVertsPerFace])
|
||||
{
|
||||
// Get surrounding block for the face
|
||||
u16 surroundingBlockID = chunk.getBlock(x + normal.x, y + normal.y, z + normal.z);
|
||||
const Block *surroundingBlock = &Registry::getInstance().getBlock(surroundingBlockID);
|
||||
|
||||
// Skip hidden faces
|
||||
if (surroundingBlock && surroundingBlock->id()
|
||||
&& ((block->drawType() == BlockDrawType::Solid && surroundingBlock->drawType() == BlockDrawType::Solid && surroundingBlock->isOpaque())
|
||||
|| (block->id() == surroundingBlock->id() && (block->drawType() == BlockDrawType::Liquid || block->drawType() == BlockDrawType::Glass))
|
||||
|| (block->drawType() == BlockDrawType::Liquid && surroundingBlock->drawType() == BlockDrawType::Solid)
|
||||
|| (block->drawType() == BlockDrawType::Cactus && surroundingBlock->id() == block->id())))
|
||||
&& ((block.drawType() == BlockDrawType::Solid && surroundingBlock->drawType() == BlockDrawType::Solid && surroundingBlock->isOpaque())
|
||||
|| (block.id() == surroundingBlock->id() && (block.drawType() == BlockDrawType::Liquid || block.drawType() == BlockDrawType::Glass))
|
||||
|| (block.drawType() == BlockDrawType::Liquid && surroundingBlock->drawType() == BlockDrawType::Solid)
|
||||
|| (block.drawType() == BlockDrawType::Cactus && surroundingBlock->id() == block.id())))
|
||||
return;
|
||||
|
||||
const gk::FloatBox &boundingBox = block.boundingBox();
|
||||
|
||||
const BlockData *blockData = chunk.getBlockData(x, y, z);
|
||||
const std::string &texture = block->tiles().getTextureForFace(f, blockData ? blockData->useAltTiles : false);
|
||||
const std::string &texture = block.tiles().getTextureForFace(f, blockData ? blockData->useAltTiles : false);
|
||||
const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(texture);
|
||||
float faceTexCoords[nVertsPerFace][nCoordsPerUV] = {
|
||||
{blockTexCoords.x, blockTexCoords.y + blockTexCoords.sizeY},
|
||||
{blockTexCoords.x + blockTexCoords.sizeX, blockTexCoords.y + blockTexCoords.sizeY},
|
||||
{blockTexCoords.x + blockTexCoords.sizeX, blockTexCoords.y},
|
||||
{blockTexCoords.x, blockTexCoords.y},
|
||||
};
|
||||
|
||||
const gk::FloatBox boundingBox = block->boundingBox();
|
||||
// Calculate UV's
|
||||
// These are tough to obtain. Note that texture Y grows in the up-down direction, and so does V.
|
||||
// Vertex index in the bitmap array and U/V correspondence is:
|
||||
// U0V0 -> 3 2 <- U1V0
|
||||
// U0V1 -> 0 1 <- U1V1
|
||||
float U0, V0, U1, V1;
|
||||
if (block.drawType() == BlockDrawType::Cactus) {
|
||||
U0 = 0.f;
|
||||
V0 = 0.f;
|
||||
U1 = 1.f;
|
||||
V1 = 1.f;
|
||||
}
|
||||
else {
|
||||
U0 = (f == 0) ? 1.f - (boundingBox.y + boundingBox.sizeY) : (f == 1) ? boundingBox.y :
|
||||
(f == 3) ? 1.f - (boundingBox.x + boundingBox.sizeX) : boundingBox.x;
|
||||
V0 = (f <= 3) ? 1.f - (boundingBox.z + boundingBox.sizeZ) : (f == 4) ? boundingBox.y : 1.f - (boundingBox.y + boundingBox.sizeY);
|
||||
U1 = (f == 0) ? 1.f - boundingBox.y : (f == 1) ? boundingBox.y + boundingBox.sizeY :
|
||||
(f == 3) ? 1.f - boundingBox.x : boundingBox.x + boundingBox.sizeX;
|
||||
V1 = (f <= 3) ? 1.f - boundingBox.z : (f == 4) ? boundingBox.y + boundingBox.sizeY : 1.f - boundingBox.y;
|
||||
}
|
||||
|
||||
// Store vertex information
|
||||
// Prepare vertex information for VBO
|
||||
gk::Vertex vertices[nVertsPerFace];
|
||||
for (s8f v = 0 ; v < nVertsPerFace; v++) {
|
||||
tCubeCoord *vertexPosPtr = orientCubeCoords[orientation][f][v];
|
||||
if (block->drawType() == BlockDrawType::BoundingBox) {
|
||||
vertices[v].coord3d[0] = x + vertexPosPtr[0] * boundingBox.sizeX + boundingBox.x;
|
||||
vertices[v].coord3d[1] = y + vertexPosPtr[1] * boundingBox.sizeY + boundingBox.y;
|
||||
vertices[v].coord3d[2] = z + vertexPosPtr[2] * boundingBox.sizeZ + boundingBox.z;
|
||||
}
|
||||
else if (block->drawType() == BlockDrawType::Cactus) {
|
||||
vertices[v].coord3d[0] = x + vertexPosPtr[0] + boundingBox.x * -normal.x;
|
||||
vertices[v].coord3d[1] = y + vertexPosPtr[1] + boundingBox.y * -normal.y;
|
||||
vertices[v].coord3d[2] = z + vertexPosPtr[2] + boundingBox.z * -normal.z;
|
||||
for (s8f v = 0; v < nVertsPerFace; ++v) {
|
||||
if (block.drawType() == BlockDrawType::Cactus) {
|
||||
vertices[v].coord3d[0] = x + vertexPos[v]->x - boundingBox.x * normal.x;
|
||||
vertices[v].coord3d[1] = y + vertexPos[v]->y - boundingBox.y * normal.y;
|
||||
vertices[v].coord3d[2] = z + vertexPos[v]->z - boundingBox.z * normal.z;
|
||||
}
|
||||
else {
|
||||
vertices[v].coord3d[0] = x + vertexPosPtr[0];
|
||||
vertices[v].coord3d[1] = y + vertexPosPtr[1];
|
||||
vertices[v].coord3d[2] = z + vertexPosPtr[2];
|
||||
vertices[v].coord3d[0] = x + vertexPos[v]->x;
|
||||
vertices[v].coord3d[1] = y + vertexPos[v]->y;
|
||||
vertices[v].coord3d[2] = z + vertexPos[v]->z;
|
||||
}
|
||||
|
||||
vertices[v].coord3d[3] = f;
|
||||
@ -229,36 +219,38 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
|
||||
vertices[v].normal[1] = normal.y;
|
||||
vertices[v].normal[2] = normal.z;
|
||||
|
||||
const gk::Color colorMultiplier = block->colorMultiplier();
|
||||
const gk::Color colorMultiplier = block.colorMultiplier();
|
||||
vertices[v].color[0] = colorMultiplier.r;
|
||||
vertices[v].color[1] = colorMultiplier.g;
|
||||
vertices[v].color[2] = colorMultiplier.b;
|
||||
vertices[v].color[3] = colorMultiplier.a;
|
||||
|
||||
vertices[v].texCoord[0] = faceTexCoords[v][0];
|
||||
vertices[v].texCoord[1] = faceTexCoords[v][1];
|
||||
float U = (v == 0 || v == 3) ? U0 : U1;
|
||||
float V = (v >= 2) ? V0 : V1;
|
||||
vertices[v].texCoord[0] = gk::qlerp(blockTexCoords.x, blockTexCoords.x + blockTexCoords.sizeX, U);
|
||||
vertices[v].texCoord[1] = gk::qlerp(blockTexCoords.y, blockTexCoords.y + blockTexCoords.sizeY, V);
|
||||
|
||||
if (Config::isSunSmoothLightingEnabled && block->drawType() != BlockDrawType::Liquid)
|
||||
vertices[v].lightValue[0] = getLightForVertex(Light::Sun, x, y, z, vertexPosPtr, normal, chunk);
|
||||
if (Config::isSunSmoothLightingEnabled && block.drawType() != BlockDrawType::Liquid)
|
||||
vertices[v].lightValue[0] = getLightForVertex(Light::Sun, x, y, z, *neighbourOfs[v], normal, chunk);
|
||||
else
|
||||
vertices[v].lightValue[0] = chunk.lightmap().getSunlight(x + normal.x, y + normal.y, z + normal.z);
|
||||
|
||||
int torchlight = chunk.lightmap().getTorchlight(x, y, z);
|
||||
if (Config::isTorchSmoothLightingEnabled && torchlight == 0 && block->drawType() != BlockDrawType::Liquid)
|
||||
vertices[v].lightValue[1] = getLightForVertex(Light::Torch, x, y, z, vertexPosPtr, normal, chunk);
|
||||
if (Config::isTorchSmoothLightingEnabled && torchlight == 0 && block.drawType() != BlockDrawType::Liquid)
|
||||
vertices[v].lightValue[1] = getLightForVertex(Light::Torch, x, y, z, *neighbourOfs[v], normal, chunk);
|
||||
else
|
||||
vertices[v].lightValue[1] = chunk.lightmap().getTorchlight(x + normal.x, y + normal.y, z + normal.z);
|
||||
|
||||
vertices[v].ambientOcclusion = getAmbientOcclusion(x, y, z, vertexPosPtr, chunk);
|
||||
vertices[v].ambientOcclusion = getAmbientOcclusion(x, y, z, *neighbourOfs[v], chunk);
|
||||
}
|
||||
|
||||
auto addVertex = [&](u8 v) {
|
||||
if (!Config::isAmbientOcclusionEnabled)
|
||||
vertices[v].ambientOcclusion = 5;
|
||||
|
||||
if (block->drawType() == BlockDrawType::Liquid)
|
||||
if (block.drawType() == BlockDrawType::Liquid)
|
||||
m_vertices[Layer::Liquid].emplace_back(vertices[v]);
|
||||
else if (block->drawType() == BlockDrawType::Glass)
|
||||
else if (block.drawType() == BlockDrawType::Glass)
|
||||
m_vertices[Layer::Glass].emplace_back(vertices[v]);
|
||||
else
|
||||
m_vertices[Layer::Solid].emplace_back(vertices[v]);
|
||||
@ -283,42 +275,40 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
|
||||
}
|
||||
}
|
||||
|
||||
inline void ChunkBuilder::addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const Block *block) {
|
||||
const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(block->tiles().getTextureForFace(0));
|
||||
float faceTexCoords[nFaces][nCoordsPerUV] = {
|
||||
inline void ChunkBuilder::addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const Block &block, const glm::vec3 *const vertexPos[nCrossFaces][nVertsPerFace]) {
|
||||
const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(block.tiles().getTextureForFace(0));
|
||||
float faceTexCoords[nVertsPerFace][nCoordsPerUV] = {
|
||||
{blockTexCoords.x, blockTexCoords.y + blockTexCoords.sizeY},
|
||||
{blockTexCoords.x + blockTexCoords.sizeX, blockTexCoords.y + blockTexCoords.sizeY},
|
||||
{blockTexCoords.x + blockTexCoords.sizeX, blockTexCoords.y},
|
||||
{blockTexCoords.x, blockTexCoords.y},
|
||||
};
|
||||
|
||||
static gk::Vector3i normal{0, 0, 0};
|
||||
|
||||
for (int i = 0 ; i < nCrossFaces ; ++i) {
|
||||
for (int f = 0; f < nCrossFaces ; ++f) {
|
||||
gk::Vertex vertices[nVertsPerFace];
|
||||
for (int j = 0 ; j < nVertsPerFace ; ++j) {
|
||||
vertices[j].coord3d[0] = x + crossCoords[i][j][0];
|
||||
vertices[j].coord3d[1] = y + crossCoords[i][j][1];
|
||||
vertices[j].coord3d[2] = z + crossCoords[i][j][2];
|
||||
vertices[j].coord3d[3] = 6;
|
||||
for (int v = 0 ; v < nVertsPerFace ; ++v) {
|
||||
vertices[v].coord3d[0] = x + vertexPos[f][v]->x;
|
||||
vertices[v].coord3d[1] = y + vertexPos[f][v]->y;
|
||||
vertices[v].coord3d[2] = z + vertexPos[f][v]->z;
|
||||
vertices[v].coord3d[3] = 6;
|
||||
|
||||
vertices[j].normal[0] = normal.x;
|
||||
vertices[j].normal[1] = normal.y;
|
||||
vertices[j].normal[2] = normal.z;
|
||||
vertices[v].normal[0] = 0;
|
||||
vertices[v].normal[1] = 0;
|
||||
vertices[v].normal[2] = 0;
|
||||
|
||||
const gk::Color colorMultiplier = block->colorMultiplier();
|
||||
vertices[j].color[0] = colorMultiplier.r;
|
||||
vertices[j].color[1] = colorMultiplier.g;
|
||||
vertices[j].color[2] = colorMultiplier.b;
|
||||
vertices[j].color[3] = colorMultiplier.a;
|
||||
const gk::Color colorMultiplier = block.colorMultiplier();
|
||||
vertices[v].color[0] = colorMultiplier.r;
|
||||
vertices[v].color[1] = colorMultiplier.g;
|
||||
vertices[v].color[2] = colorMultiplier.b;
|
||||
vertices[v].color[3] = colorMultiplier.a;
|
||||
|
||||
vertices[j].texCoord[0] = faceTexCoords[j][0];
|
||||
vertices[j].texCoord[1] = faceTexCoords[j][1];
|
||||
vertices[v].texCoord[0] = faceTexCoords[v][0];
|
||||
vertices[v].texCoord[1] = faceTexCoords[v][1];
|
||||
|
||||
vertices[j].lightValue[0] = chunk.lightmap().getSunlight(x, y, z);
|
||||
vertices[j].lightValue[1] = chunk.lightmap().getTorchlight(x, y, z);
|
||||
vertices[v].lightValue[0] = chunk.lightmap().getSunlight(x, y, z);
|
||||
vertices[v].lightValue[1] = chunk.lightmap().getTorchlight(x, y, z);
|
||||
|
||||
vertices[j].ambientOcclusion = 5;
|
||||
vertices[v].ambientOcclusion = 5;
|
||||
}
|
||||
|
||||
m_vertices[Layer::Flora].emplace_back(vertices[0]);
|
||||
@ -330,14 +320,8 @@ inline void ChunkBuilder::addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk
|
||||
}
|
||||
}
|
||||
|
||||
inline gk::Vector3i ChunkBuilder::getOffsetFromVertex(const tCubeCoord *const vertexPosPtr) const {
|
||||
return gk::Vector3i{vertexPosPtr[0] * 2 - 1, vertexPosPtr[1] * 2 - 1, vertexPosPtr[2] * 2 - 1};
|
||||
}
|
||||
|
||||
// Based on this article: https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/
|
||||
inline u8 ChunkBuilder::getAmbientOcclusion(s8f x, s8f y, s8f z, const tCubeCoord *const vertexPosPtr, const ClientChunk &chunk) {
|
||||
gk::Vector3i offset = getOffsetFromVertex(vertexPosPtr);
|
||||
|
||||
inline u8 ChunkBuilder::getAmbientOcclusion(s8f x, s8f y, s8f z, const gk::Vector3i &offset, const ClientChunk &chunk) {
|
||||
const Block &block0 = Registry::getInstance().getBlock(chunk.getBlock(x + offset.x, y, z + offset.z));
|
||||
const Block &block1 = Registry::getInstance().getBlock(chunk.getBlock(x, y + offset.y, z + offset.z));
|
||||
const Block &block2 = Registry::getInstance().getBlock(chunk.getBlock(x + offset.x, y + offset.y, z + offset.z));
|
||||
@ -349,7 +333,7 @@ inline u8 ChunkBuilder::getAmbientOcclusion(s8f x, s8f y, s8f z, const tCubeCoor
|
||||
return (side1 && side2) ? 0 : 3 - (side1 + side2 + corner);
|
||||
}
|
||||
|
||||
inline u8 ChunkBuilder::getLightForVertex(Light light, s8f x, s8f y, s8f z, const tCubeCoord *const vertexPosPtr, const gk::Vector3i &normal, const ClientChunk &chunk) {
|
||||
inline u8 ChunkBuilder::getLightForVertex(Light light, s8f x, s8f y, s8f z, const gk::Vector3i &offset, const gk::Vector3i &normal, const ClientChunk &chunk) {
|
||||
std::function<s8(const Chunk *chunk, s8, s8, s8)> getLight = [&](const Chunk *chunk, s8 x, s8 y, s8 z) -> s8 {
|
||||
if (x < 0) return chunk->getSurroundingChunk(0) && chunk->getSurroundingChunk(0)->isInitialized() ? getLight(chunk->getSurroundingChunk(0), x + CHUNK_WIDTH, y, z) : -1;
|
||||
if (x >= CHUNK_WIDTH) return chunk->getSurroundingChunk(1) && chunk->getSurroundingChunk(1)->isInitialized() ? getLight(chunk->getSurroundingChunk(1), x - CHUNK_WIDTH, y, z) : -1;
|
||||
@ -364,8 +348,6 @@ inline u8 ChunkBuilder::getLightForVertex(Light light, s8f x, s8f y, s8f z, cons
|
||||
return chunk->isInitialized() ? chunk->lightmap().getTorchlight(x, y, z) : -1;
|
||||
};
|
||||
|
||||
gk::Vector3i offset = getOffsetFromVertex(vertexPosPtr);
|
||||
|
||||
gk::Vector3i minOffset{
|
||||
(normal.x != 0) ? offset.x : 0,
|
||||
(normal.y != 0) ? offset.y : 0,
|
||||
@ -398,66 +380,3 @@ inline u8 ChunkBuilder::getLightForVertex(Light light, s8f x, s8f y, s8f z, cons
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ChunkBuilder::initializeOrientation() {
|
||||
// Build all 24 rotated versions of cubeCoords
|
||||
|
||||
// Local rotation of top face
|
||||
glm::mat3 topRotation = {
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1,
|
||||
};
|
||||
// Matrix for a rotation of 90 degrees CCW around the Z axis, used to
|
||||
// rotate the top face in 90 degree steps around its local Z axis.
|
||||
const glm::mat3 rotate90Z = {
|
||||
// Note glm matrices are column-major, so each row here is
|
||||
// actually a column of the matrix.
|
||||
0, 1, 0,
|
||||
-1, 0, 0,
|
||||
0, 0, 1,
|
||||
};
|
||||
// Rotations that place the top face on each of the axes
|
||||
const glm::mat3 rot2axis[nAxes] = {
|
||||
{ 1, 0, 0, 0, 1, 0, 0, 0, 1}, // top face on +Z, no rotation (identity matrix)
|
||||
{ 1, 0, 0, 0, -1, 0, 0, 0, -1}, // top face on -Z, rotating around the X axis
|
||||
{ 1, 0, 0, 0, 0, -1, 0, 1, 0}, // top face on +Y, rotating around the X axis
|
||||
{ 1, 0, 0, 0, 0, 1, 0, -1, 0}, // top face on -Y, rotating around the X axis
|
||||
{ 0, 0, 1, 0, 1, 0, -1, 0, 0}, // top face on +X, rotating around the Y axis
|
||||
{ 0, 0, -1, 0, 1, 0, 1, 0, 0}, // top face on -X, rotating around the Y axis
|
||||
};
|
||||
|
||||
for (s8f axis = 0; axis < nAxes; ++axis) {
|
||||
for (s8f angle = 0; angle < nRots; ++angle) {
|
||||
glm::mat3 finalMat = rot2axis[axis] * topRotation;
|
||||
|
||||
for (s8f face = 0; face < nFaces; ++face) {
|
||||
for (s8f vNum = 0; vNum < nVertsPerFace + nNormals; ++vNum) {
|
||||
glm::vec3 vertex{cubeCoords[face][vNum][0], cubeCoords[face][vNum][1], cubeCoords[face][vNum][2]};
|
||||
if (vNum < nVertsPerFace)
|
||||
vertex -= glm::vec3{0.5, 0.5, 0.5}; // Translate by centre of the cube
|
||||
vertex = finalMat * vertex;
|
||||
tCubeCoord *pVertex = orientCubeCoords[axis * nRots + angle][face][vNum];
|
||||
if (vNum < nVertsPerFace)
|
||||
vertex += glm::vec3{0.5, 0.5, 0.5}; // Translate back
|
||||
pVertex[0] = tCubeCoord(vertex[0]);
|
||||
pVertex[1] = tCubeCoord(vertex[1]);
|
||||
pVertex[2] = tCubeCoord(vertex[2]);
|
||||
}
|
||||
}
|
||||
|
||||
// Next top rotation
|
||||
topRotation = rotate90Z * topRotation;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we use bit masking, we add eight null orientations to the normal
|
||||
// set of 24 to complete a power of two, to make the code robust against
|
||||
// invalid orientation values.
|
||||
for (s8f axis = nAxes; axis < nAxesP2; ++axis) {
|
||||
for (s8f angle = 0; angle < nRots; ++angle) {
|
||||
memcpy(&orientCubeCoords[axis * nRots + angle], &orientCubeCoords[0],
|
||||
nFaces * (nVertsPerFace + nNormals) * nCoords * sizeof(tCubeCoord));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,7 @@ class TextureAtlas;
|
||||
|
||||
class ChunkBuilder {
|
||||
public:
|
||||
ChunkBuilder(TextureAtlas &textureAtlas);
|
||||
|
||||
using tCubeCoord = s8;
|
||||
ChunkBuilder(TextureAtlas &textureAtlas) : m_textureAtlas(textureAtlas) {}
|
||||
|
||||
static constexpr u8 layers = 4;
|
||||
|
||||
@ -58,19 +56,18 @@ class ChunkBuilder {
|
||||
};
|
||||
|
||||
private:
|
||||
void addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const Block *block);
|
||||
void addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const Block *block);
|
||||
|
||||
gk::Vector3i getOffsetFromVertex(const tCubeCoord *const vertexPosPtr) const;
|
||||
u8 getAmbientOcclusion(s8f x, s8f y, s8f z, const tCubeCoord *const vertexPosPtr, const ClientChunk &chunk);
|
||||
void addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const Block &block,
|
||||
const gk::Vector3i &normal, const glm::vec3 *const vertexPos[4],
|
||||
const gk::Vector3i *const neighbourOfs[4]);
|
||||
void addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const Block &block, const glm::vec3 *const vertexPos[2][4]);
|
||||
|
||||
enum class Light {
|
||||
Sun,
|
||||
Torch
|
||||
};
|
||||
|
||||
u8 getLightForVertex(Light light, s8f x, s8f y, s8f z, const tCubeCoord *const vertexPosPtr, const gk::Vector3i &normal, const ClientChunk &chunk);
|
||||
void initializeOrientation();
|
||||
u8 getAmbientOcclusion(s8f x, s8f y, s8f z, const gk::Vector3i &offset, const ClientChunk &chunk);
|
||||
u8 getLightForVertex(Light light, s8f x, s8f y, s8f z, const gk::Vector3i &offset, const gk::Vector3i &normal, const ClientChunk &chunk);
|
||||
|
||||
std::array<std::vector<gk::Vertex>, layers> m_vertices;
|
||||
|
||||
|
96
common/source/core/BlockGeometry.cpp
Normal file
96
common/source/core/BlockGeometry.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* =====================================================================================
|
||||
*
|
||||
* OpenMiner
|
||||
*
|
||||
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
|
||||
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
|
||||
*
|
||||
* This file is part of OpenMiner.
|
||||
*
|
||||
* OpenMiner is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* OpenMiner 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* =====================================================================================
|
||||
*/
|
||||
#include <mutex>
|
||||
|
||||
#include "BlockGeometry.hpp"
|
||||
|
||||
namespace BlockGeometry {
|
||||
|
||||
glm::mat3 orientMatrices[nOrientations];
|
||||
|
||||
static void doInitOrientation(void);
|
||||
|
||||
void initOrientation() {
|
||||
static int orientInitialized = false;
|
||||
static std::mutex orientInitMutex;
|
||||
std::lock_guard<std::mutex> lock(orientInitMutex);
|
||||
|
||||
if (!orientInitialized) {
|
||||
doInitOrientation();
|
||||
orientInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void doInitOrientation() {
|
||||
// Build all 24 rotation matrices for the orientation field
|
||||
|
||||
// Local rotation of top face
|
||||
glm::mat3 topRotation{
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1,
|
||||
};
|
||||
// Matrix for a rotation of 90 degrees CCW around the Z axis, used to
|
||||
// rotate the top face in 90 degree steps around its local Z axis.
|
||||
const glm::mat3 rotate90Z{
|
||||
// Note glm matrices are column-major, so each row here is
|
||||
// actually a column of the matrix.
|
||||
0, 1, 0,
|
||||
-1, 0, 0,
|
||||
0, 0, 1,
|
||||
};
|
||||
// Rotations that place the top face on each of the axes
|
||||
const glm::mat3 rot2axis[nAxes]{
|
||||
{ 1, 0, 0, 0, 1, 0, 0, 0, 1}, // top face on +Z, no rotation (identity matrix)
|
||||
{ 1, 0, 0, 0, -1, 0, 0, 0, -1}, // top face on -Z, rotating around the X axis
|
||||
{ 1, 0, 0, 0, 0, -1, 0, 1, 0}, // top face on +Y, rotating around the X axis
|
||||
{ 1, 0, 0, 0, 0, 1, 0, -1, 0}, // top face on -Y, rotating around the X axis
|
||||
{ 0, 0, 1, 0, 1, 0, -1, 0, 0}, // top face on +X, rotating around the Y axis
|
||||
{ 0, 0, -1, 0, 1, 0, 1, 0, 0}, // top face on -X, rotating around the Y axis
|
||||
};
|
||||
|
||||
for (int axis = 0; axis < nAxes; ++axis) {
|
||||
for (int angle = 0; angle < nRots; ++angle) {
|
||||
// Rotate the top around itself, then rotate it to each face
|
||||
orientMatrices[axis * 4 + angle] = rot2axis[axis] * topRotation;
|
||||
|
||||
// Next top rotation
|
||||
topRotation = rotate90Z * topRotation;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we use bit masking, we add eight dummy orientations to the normal
|
||||
// set of 24 to complete a power of two, to make the code robust against
|
||||
// invalid orientation values.
|
||||
for (int axis = nAxes; axis < nAxesP2; ++axis) {
|
||||
for (int angle = 0; angle < nRots; ++angle) {
|
||||
orientMatrices[axis * nRots + angle] = orientMatrices[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace BlockGeometry
|
81
common/source/core/BlockGeometry.hpp
Normal file
81
common/source/core/BlockGeometry.hpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* =====================================================================================
|
||||
*
|
||||
* OpenMiner
|
||||
*
|
||||
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
|
||||
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
|
||||
*
|
||||
* This file is part of OpenMiner.
|
||||
*
|
||||
* OpenMiner is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* OpenMiner 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* =====================================================================================
|
||||
*/
|
||||
#ifndef BLOCKGEOMETRY_HPP_
|
||||
#define BLOCKGEOMETRY_HPP_
|
||||
|
||||
#include <gk/core/IntTypes.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
|
||||
namespace BlockGeometry {
|
||||
|
||||
constexpr u8 nAxes = 6;
|
||||
constexpr u8 nAxesP2 = 8; // nAxes rounded up to the next power of 2
|
||||
constexpr u8 nRots = 4;
|
||||
constexpr u8 nFaces = 6;
|
||||
constexpr u8 nCrossFaces = 2;
|
||||
constexpr u8 nVertsPerFace = 4;
|
||||
constexpr u8 nVertsPerCube = 8;
|
||||
constexpr u8 nCoordsPerUV = 2;
|
||||
constexpr u8 nOrientations = nAxesP2 * nRots;
|
||||
|
||||
// Same order as enum BlockFace in TilesDef.hpp
|
||||
constexpr u8f cubeVerts[nFaces][nVertsPerFace]{
|
||||
// Vertex numbers are encoded according to their binary digits,
|
||||
// where bit 0 is X, bit 1 is Y and bit 2 is Z.
|
||||
// ZYX ZYX ZYX ZYX
|
||||
{0b010, 0b000, 0b100, 0b110}, // West
|
||||
{0b001, 0b011, 0b111, 0b101}, // East
|
||||
{0b000, 0b001, 0b101, 0b100}, // South
|
||||
{0b011, 0b010, 0b110, 0b111}, // North
|
||||
{0b010, 0b011, 0b001, 0b000}, // Bottom
|
||||
{0b100, 0b101, 0b111, 0b110}, // Top
|
||||
};
|
||||
|
||||
constexpr u8f crossVerts[nCrossFaces][nVertsPerFace]{
|
||||
// ZYX ZYX ZYX ZYX
|
||||
{0b011, 0b000, 0b100, 0b111}, // NE/SW
|
||||
{0b001, 0b010, 0b110, 0b101}, // NW/SE
|
||||
};
|
||||
|
||||
// Normal of each face
|
||||
// Same order as enum BlockFace in TilesDef.hpp
|
||||
const glm::vec3 faceNormals[nFaces]{
|
||||
{-1, 0, 0}, // West
|
||||
{ 1, 0, 0}, // East
|
||||
{ 0,-1, 0}, // South
|
||||
{ 0, 1, 0}, // North
|
||||
{ 0, 0,-1}, // Bottom
|
||||
{ 0, 0, 1}, // Top
|
||||
};
|
||||
|
||||
extern glm::mat3 orientMatrices[nOrientations];
|
||||
|
||||
void initOrientation(void);
|
||||
|
||||
} // namespace BlockGeometry
|
||||
|
||||
#endif // BLOCKGEOMETRY_HPP_
|
@ -34,6 +34,9 @@ namespace {
|
||||
constexpr float DIST_NEAR = 0.1f;
|
||||
constexpr float DIST_FAR = 1000.0f;
|
||||
|
||||
constexpr float DIST_2D_NEAR = -512.0f;
|
||||
constexpr float DIST_2D_FAR = 512.0f;
|
||||
|
||||
// Chunk size must be a power of two and fit in a signed byte
|
||||
constexpr int CHUNK_WIDTH = 16;
|
||||
constexpr int CHUNK_DEPTH = 16;
|
||||
|
@ -26,6 +26,7 @@
|
||||
*/
|
||||
#include "filesystem.hpp"
|
||||
|
||||
#include "BlockGeometry.hpp"
|
||||
#include "ServerApplication.hpp"
|
||||
#include "ServerBlock.hpp"
|
||||
|
||||
@ -33,6 +34,7 @@ namespace fs = ghc::filesystem;
|
||||
|
||||
ServerApplication::ServerApplication(int argc, char **argv) : m_argumentParser(argc, argv) {
|
||||
std::srand(std::time(nullptr));
|
||||
BlockGeometry::initOrientation();
|
||||
}
|
||||
|
||||
void ServerApplication::init() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user