summaryrefslogtreecommitdiff
path: root/src/World/Generation
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-07-09 20:51:30 +0200
committerMel <einebeere@gmail.com>2023-07-09 20:51:30 +0200
commit680d9d5b7a61ac955fcec8a5622faa5cf4165c11 (patch)
tree385ada2ac5e666ff4e89e073850c9e1a9b29c99e /src/World/Generation
parentfe2baedc760c2f29e2c720f6b1132a2de33c5430 (diff)
downloadmeowcraft-680d9d5b7a61ac955fcec8a5622faa5cf4165c11.tar.zst
meowcraft-680d9d5b7a61ac955fcec8a5622faa5cf4165c11.zip
Ambient occlusion (without corners)
Diffstat (limited to 'src/World/Generation')
-rw-r--r--src/World/Generation/ChunkMeshing.cpp217
-rw-r--r--src/World/Generation/ChunkMeshing.hpp5
2 files changed, 143 insertions, 79 deletions
diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp
index 8996169..8944c53 100644
--- a/src/World/Generation/ChunkMeshing.cpp
+++ b/src/World/Generation/ChunkMeshing.cpp
@@ -1,6 +1,35 @@
#include "ChunkMeshing.hpp"
-namespace MC::World::Generation {
+namespace MC::World::Generation::ChunkMeshing {
+
+Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos) {
+ const Chunk* chunk_to_ask;
+
+ auto overflow = [](I32& c, I32 max) -> I8 {
+ if (c < 0) { c += max; return -1; }
+ if (c >= max) { c -= max; return 1; }
+ return 0;
+ };
+
+ auto xo = overflow(pos.x(), Chunk::Width);
+ auto yo = overflow(pos.y(), Chunk::Height);
+ auto zo = overflow(pos.z(), Chunk::Width);
+
+ // Blocks above and below a chunk are always Air.
+ if (yo != 0) return {};
+
+ if (xo == 1 && zo == 1) { chunk_to_ask = neighbors.south_east; }
+ else if (xo == 1 && zo == -1) { chunk_to_ask = neighbors.north_east; }
+ else if (xo == -1 && zo == 1) { chunk_to_ask = neighbors.south_west; }
+ else if (xo == -1 && zo == -1) { chunk_to_ask = neighbors.north_west; }
+ else if (xo == 1) { chunk_to_ask = neighbors.east; }
+ else if (xo == -1) { chunk_to_ask = neighbors.west; }
+ else if (zo == 1) { chunk_to_ask = neighbors.south; }
+ else if (zo == -1) { chunk_to_ask = neighbors.north; }
+ else { chunk_to_ask = &chunk; }
+
+ return chunk_to_ask->get(pos.x(), pos.y(), pos.z());
+}
std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
U8 atlas_width = 4;
@@ -9,15 +38,21 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
Real width_step = 1.0f / atlas_width;
Real height_step = 1.0f / atlas_height;
- auto block_coords = [=](U8 x, U8 y) {
+ auto block_coords = [=](U8 x, U8 y) -> std::array<Vector<2, F32>, 4> {
auto t = y * height_step;
auto l = x * width_step;
auto b = t + height_step;
auto r = l + width_step;
- return std::array<Vector<2, F32>, 4>{{
- {l, b}, {r, b}, {r, t}, {l, t},
- }};
+ // This is horrible and it was better before I restructured the vertex order...
+ // In the last version the front and back side pairs had the same structure in different winding,
+ // so a back_side check wasn't needed...
+ // Note: BlockSide::Front counts as a back side, because OpenGL has an inverted Z-axis compared to us.
+ Bool back_side = side == BlockSide::Front || side == BlockSide::Bottom || side == BlockSide::Left;
+ if (back_side) {
+ return {{{l, t}, {l, b}, {r, b}, {r, t}}};
+ }
+ return {{{r, t}, {l, t}, {l, b}, {r, b}}};
};
switch (type) {
@@ -25,15 +60,15 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
return block_coords(1, 0);
case BlockType::Grass:
switch (side) {
- case BlockSide::Front:
- case BlockSide::Back:
- case BlockSide::Left:
- case BlockSide::Right:
- return block_coords(2, 0);
- case BlockSide::Bottom:
- return block_coords(1, 0);
- case BlockSide::Top:
- return block_coords(0, 0);
+ case BlockSide::Front:
+ case BlockSide::Back:
+ case BlockSide::Left:
+ case BlockSide::Right:
+ return block_coords(2, 0);
+ case BlockSide::Bottom:
+ return block_coords(1, 0);
+ case BlockSide::Top:
+ return block_coords(0, 0);
}
case BlockType::Stone:
return block_coords(3, 0);
@@ -43,26 +78,26 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
return block_coords(0, 1);
case BlockType::Snow:
switch (side) {
- case BlockSide::Front:
- case BlockSide::Back:
- case BlockSide::Left:
- case BlockSide::Right:
- return block_coords(3, 1);
- case BlockSide::Bottom:
- return block_coords(1, 0);
- case BlockSide::Top:
- return block_coords(2, 1);
+ case BlockSide::Front:
+ case BlockSide::Back:
+ case BlockSide::Left:
+ case BlockSide::Right:
+ return block_coords(3, 1);
+ case BlockSide::Bottom:
+ return block_coords(1, 0);
+ case BlockSide::Top:
+ return block_coords(2, 1);
}
case BlockType::Wood:
switch (side) {
- case BlockSide::Front:
- case BlockSide::Back:
- case BlockSide::Right:
- case BlockSide::Left:
- return block_coords(0, 2);
- case BlockSide::Bottom:
- case BlockSide::Top:
- return block_coords(1, 2);
+ case BlockSide::Front:
+ case BlockSide::Back:
+ case BlockSide::Right:
+ case BlockSide::Left:
+ return block_coords(0, 2);
+ case BlockSide::Bottom:
+ case BlockSide::Top:
+ return block_coords(1, 2);
}
case BlockType::Leaves:
return block_coords(2, 2);
@@ -84,76 +119,101 @@ std::array<Vector<3, F32>, 4> face_normals(BlockSide side) {
}
Bool is_face_visible(Chunk& chunk, const ChunkMeshing::ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
- Vector<3, I32> offset{};
+std::array<F32, 4> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+ std::array<Vector<3, I32>, 4> offsets{};
switch (side) {
- case BlockSide::Front:
- offset[2] = 1;
- break;
- case BlockSide::Back:
- offset[2] = -1;
- break;
- case BlockSide::Top:
- offset[1] = 1;
- break;
- case BlockSide::Bottom:
- offset[1] = -1;
- break;
- case BlockSide::Left:
- offset[0] = -1;
- break;
- case BlockSide::Right:
- offset[0] = 1;
- break;
+ case BlockSide::Front:
+ offsets = {{{0, 1, 1}, {-1, 0, 1}, {0, -1, 1}, {1, 0, 1}}}; // works!
+ break;
+ case BlockSide::Back:
+ offsets = {{{-1, 0, -1}, {0, 1, -1}, {1, 0, -1}, {0, -1, -1}}}; // works!
+ break;
+ case BlockSide::Top:
+ offsets = {{{-1, 1, 0}, {0, 1, 1}, {1, 1, 0}, {0, 1, -1}}};
+ break;
+ case BlockSide::Bottom:
+ offsets = {{{0, -1, 1}, {-1, -1, 0}, {0, -1, -1}, {1, -1, 0}}};
+ break;
+ case BlockSide::Right:
+ offsets = {{{1, 0, -1}, {1, 1, 0}, {1, 0, 1}, {1, -1, 0}}};
+ break;
+ case BlockSide::Left:
+ offsets = {{{-1, 1, 0}, {-1, 0, -1}, {-1, -1, 0}, {-1, 0, 1}}};
+ break;
}
- Vector<3, I32> neighbor_pos{
- (I32)x + offset.x(), (I32)y + offset.y(), (I32)z + offset.z(),
- };
- Chunk* chunk_to_ask;
+ std::array<F32, 4> vertex_ao{};
+
+ for (UInt vertex = 0; vertex < 4; vertex++) {
+ auto a = offsets[vertex];
+ auto b = offsets[(vertex + 1) % 4];
- if (neighbor_pos.z() < 0) {
- chunk_to_ask = &neighbors.north;
- neighbor_pos.z() += Chunk::Width;
- } else if (neighbor_pos.x() >= (I32)Chunk::Width) {
- chunk_to_ask = &neighbors.east;
- neighbor_pos.x() -= Chunk::Width;
- } else if (neighbor_pos.z() >= (I32)Chunk::Width) {
- chunk_to_ask = &neighbors.south;
- neighbor_pos.z() -= Chunk::Width;
- } else if (neighbor_pos.x() < 0) {
- chunk_to_ask = &neighbors.west;
- neighbor_pos.x() += Chunk::Width;
- } else {
- chunk_to_ask = &chunk;
+ auto block_a = get_block_wrapping(chunk, neighbors, {x + a.x(), y + a.y(), z + a.z()});
+ auto block_b = get_block_wrapping(chunk, neighbors, {x + b.x(), y + b.y(), z + b.z()});
+
+ F32 occlusion_a = block_a.empty() ? 0 : 1;
+ F32 occlusion_b = block_b.empty() ? 0 : 1;
+
+ vertex_ao[vertex] = (occlusion_a + occlusion_b) / 2;
}
- auto [neighbor] = chunk_to_ask->get(neighbor_pos.x(), neighbor_pos.y(), neighbor_pos.z());
+ return vertex_ao;
+}
+
+Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+ Vector<3, I32> offset{};
+ switch (side) {
+ case BlockSide::Front:
+ offset[2] = 1;
+ break;
+ case BlockSide::Back:
+ offset[2] = -1;
+ break;
+ case BlockSide::Top:
+ offset[1] = 1;
+ break;
+ case BlockSide::Bottom:
+ offset[1] = -1;
+ break;
+ case BlockSide::Right:
+ offset[0] = 1;
+ break;
+ case BlockSide::Left:
+ offset[0] = -1;
+ break;
+ }
+
+ Vector<3, I32> neighbor_pos{
+ x + offset.x(), y + offset.y(), z + offset.z(),
+ };
+
+ auto [neighbor] = get_block_wrapping(chunk, neighbors, neighbor_pos);
return neighbor.is_transparent();
}
-GFX::Mesh ChunkMeshing::create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) {
+GFX::Mesh create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) {
std::vector<Vector<3, F32>> positions{};
std::vector<Vector<3, F32>> normals{};
std::vector<Vector<2, F32>> tex_coords{};
+ std::vector<F32> ambient_occlusion_values{};
std::vector<U32> indices{};
for (Int x = 0; x < Chunk::Width; x++) {
for (Int y = 0; y < Chunk::Height; y++) {
for (Int z = 0; z < Chunk::Width; z++) {
- auto type = chunk.get(x, y, z).type;
- if (type == BlockType::Air) {
+ auto block = chunk.get(x, y, z);
+ if (block.empty())
continue;
- }
for (auto side: BlockSide::all()) {
- if (!is_face_visible(chunk, neighbors, x, y, z, side)) {
+ if (!is_face_visible(chunk, neighbors, x, y, z, side))
continue;
- }
auto side_positions = side.face();
auto side_normals = face_normals(side);
- auto side_tex_coords = face_tex_coords(type, side);
+ auto side_tex_coords = face_tex_coords(block.type, side);
+ auto side_ao = face_ao_values(chunk, neighbors, x, y, z, side);
for (auto& position : side_positions) {
position = position + Vector<3, F32>{x, y, z};
@@ -164,14 +224,15 @@ GFX::Mesh ChunkMeshing::create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors
positions.insert(positions.end(), side_positions.begin(), side_positions.end());
normals.insert(normals.end(), side_normals.begin(), side_normals.end());
tex_coords.insert(tex_coords.end(), side_tex_coords.begin(), side_tex_coords.end());
- indices.insert(indices.end(), {s, s + 1, s + 3, s + 1, s + 2, s + 3});
+ ambient_occlusion_values.insert(ambient_occlusion_values.end(), side_ao.begin(), side_ao.end());
+ indices.insert(indices.end(), {s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 });
}
}
}
}
return {
- {positions, normals, tex_coords},
+ {positions, normals, tex_coords, ambient_occlusion_values},
indices,
};
}
diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp
index 6ec77af..dd9446b 100644
--- a/src/World/Generation/ChunkMeshing.hpp
+++ b/src/World/Generation/ChunkMeshing.hpp
@@ -5,7 +5,10 @@
namespace MC::World::Generation::ChunkMeshing {
-struct ChunkNeighbors { Chunk &north, &east, &south, &west; };
+struct ChunkNeighbors {
+ Chunk *north, *east, *south, *west;
+ Chunk *north_east, *south_east, *south_west, *north_west;
+};
GFX::Mesh create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors& neighbors);
}