summaryrefslogtreecommitdiff
path: root/src/World
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-07-10 04:42:49 +0200
committerMel <einebeere@gmail.com>2023-07-10 04:42:49 +0200
commit354a49d852d8f9ed9b66d7780ba43ce3a9ec59d7 (patch)
tree5f311f6aa47db9a231164a82403cc0e1e53864e8 /src/World
parent22aeb320b193705f493747714404c8ddc752919b (diff)
downloadmeowcraft-354a49d852d8f9ed9b66d7780ba43ce3a9ec59d7.tar.zst
meowcraft-354a49d852d8f9ed9b66d7780ba43ce3a9ec59d7.zip
Separate transparent water mesh (not sorted, bad)
Diffstat (limited to 'src/World')
-rw-r--r--src/World/BlockSide.hpp26
-rw-r--r--src/World/BlockType.hpp1
-rw-r--r--src/World/Generation/ChunkMeshing.cpp278
-rw-r--r--src/World/Generation/ChunkMeshing.hpp69
-rw-r--r--src/World/World.cpp10
-rw-r--r--src/World/World.hpp10
6 files changed, 220 insertions, 174 deletions
diff --git a/src/World/BlockSide.hpp b/src/World/BlockSide.hpp
index 831982a..b2d2c00 100644
--- a/src/World/BlockSide.hpp
+++ b/src/World/BlockSide.hpp
@@ -3,7 +3,6 @@
#include "../Common/Sizes.hpp"
#include <vector>
#include <array>
-#include "../Math/Vector.hpp"
namespace MC::World {
@@ -25,31 +24,6 @@ public:
operator Value() const { return m_side; }
- std::array<Vector<3, F32>, 4> face() {
- // Winding order: (0, 1, 2) (2, 3, 0)
- // Note: OpenGL Coordinate system has a flipped z axis.
- switch (m_side) {
- case Front: return {{
- {0, 1, 1}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}
- }};
- case Back: return {{
- {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}
- }};
- case Top: return {{
- {0, 1, 1}, {1, 1, 1}, {1, 1, 0}, {0, 1, 0}
- }};
- case Bottom: return {{
- {0, 0, 1}, {0, 0, 0}, {1, 0, 0}, {1, 0, 1}
- }};
- case Right: return {{
- {1, 1, 0}, {1, 1, 1}, {1, 0, 1}, {1, 0, 0}
- }};
- case Left: return {{
- {0, 1, 0}, {0, 0, 0}, {0, 0, 1}, {0, 1, 1}
- }};
- }
- }
-
static std::vector<BlockSide> all() {
return {
Front, Back, Top, Bottom, Left, Right,
diff --git a/src/World/BlockType.hpp b/src/World/BlockType.hpp
index dbf6f78..edca3d7 100644
--- a/src/World/BlockType.hpp
+++ b/src/World/BlockType.hpp
@@ -30,6 +30,7 @@ public:
switch (m_block) {
case Air:
case Leaves:
+ case Water:
return true;
default:
return false;
diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp
index 130bbad..7158734 100644
--- a/src/World/Generation/ChunkMeshing.cpp
+++ b/src/World/Generation/ChunkMeshing.cpp
@@ -2,36 +2,49 @@
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;
+ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) {
+ using namespace Detail;
- auto overflow = [](I32& c, I32 max) -> I8 {
- if (c < 0) { c += max; return -1; }
- if (c >= max) { c -= max; return 1; }
- return 0;
+ return {
+ create_mesh<DefaultMeshDecisions>(chunk, neighbors),
+ create_mesh<WaterMeshDecisions>(chunk, neighbors)
};
+}
- 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 {};
+namespace Detail {
- 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; }
+std::array<Vector<3, F32>, 4> DefaultMeshDecisions::face_positions(BlockSide side, U32 x, U32 y, U32 z) {
+ // Winding order: (0, 1, 2) (2, 3, 0)
+ // Note: OpenGL Coordinate system has a flipped z axis.
+ std::array<Vector<3, F32>, 4> face{};
+ switch (side) {
+ case BlockSide::Front:
+ face = {{{0, 1, 1}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}}};
+ break;
+ case BlockSide::Back:
+ face = {{{0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}};
+ break;
+ case BlockSide::Top:
+ face = {{{0, 1, 1}, {1, 1, 1}, {1, 1, 0}, {0, 1, 0}}};
+ break;
+ case BlockSide::Bottom:
+ face = {{{0, 0, 1}, {0, 0, 0}, {1, 0, 0}, {1, 0, 1}}};
+ break;
+ case BlockSide::Right:
+ face = {{{1, 1, 0}, {1, 1, 1}, {1, 0, 1}, {1, 0, 0}}};
+ break;
+ case BlockSide::Left:
+ face = {{{0, 1, 0}, {0, 0, 0}, {0, 0, 1}, {0, 1, 1}}};
+ break;
+ }
- return chunk_to_ask->get(pos.x(), pos.y(), pos.z());
+ for (auto& p : face) {
+ p += {x, y, z};
+ }
+ return face;
}
-std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
+std::array<Vector<2, F32>, 4> DefaultMeshDecisions::face_tex_coords(BlockType type, BlockSide side) {
U8 atlas_width = 4;
U8 atlas_height = 4;
@@ -56,75 +69,67 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
};
switch (type) {
- case BlockType::Dirt:
+ case BlockType::Dirt:
+ 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 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 BlockType::Stone:
- return block_coords(3, 0);
- case BlockType::Sand:
- return block_coords(1, 1);
- case BlockType::Water:
- 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 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 BlockType::Leaves:
- return block_coords(2, 2);
- case BlockType::Air:
- return {};
+ case BlockSide::Top:
+ return block_coords(0, 0);
+ }
+ case BlockType::Stone:
+ return block_coords(3, 0);
+ case BlockType::Sand:
+ return block_coords(1, 1);
+ case BlockType::Water:
+ 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 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 BlockType::Leaves:
+ return block_coords(2, 2);
+ case BlockType::Air:
+ return {};
}
}
-std::array<Vector<3, F32>, 4> face_normals(BlockSide side) {
- auto is_side = [=](BlockSide s) -> Real { return s == side; };
-
- Vector<3, F32> normal = {
- is_side(BlockSide::Right) - is_side(BlockSide::Left),
- is_side(BlockSide::Top) - is_side(BlockSide::Bottom),
- is_side(BlockSide::Front) - is_side(BlockSide::Back),
- };
-
+std::array<Vector<3, F32>, 4> DefaultMeshDecisions::face_normals(BlockSide side) {
+ Vector<3, F32> normal{get_face_normal(side)};
return {normal, normal, normal, normal};
}
-std::array<F32, 4> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+std::array<F32, 4> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
std::array<Vector<3, I32>, 8> offsets{};
// Given a block position, these offsets can be added to it to get the 8 blocks necessary to calculate AO.
// There are 4 corners and 4 sides, corners are visually distinguished by the lack of spaces and
// but you can recognize them by the fact that they have no 0 offsets.
// Note: Is there a way to compute these? I tried but I couldn't... If the vertex windings ever change again I don't want to hardcode these...
- auto nowhere = Vector<3, I32>::max();
switch (side) {
case BlockSide::Front:
offsets = {{{0, 1, 1}, {-1,1,1}, {-1, 0, 1}, {-1,-1,1}, {0, -1, 1}, {1,-1,1}, {1, 0, 1}, {1,1,1}}};
@@ -172,80 +177,69 @@ std::array<F32, 4> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors,
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;
- }
+Chunk::BlockData DefaultMeshDecisions::get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos) {
+ const Chunk* chunk_to_ask;
- Vector<3, I32> neighbor_pos{
- x + offset.x(), y + offset.y(), z + offset.z(),
+ auto overflow = [](I32& c, I32 max) -> I8 {
+ if (c < 0) { c += max; return -1; }
+ if (c >= max) { c -= max; return 1; }
+ return 0;
};
- auto [neighbor] = get_block_wrapping(chunk, neighbors, neighbor_pos);
- return neighbor.is_transparent();
+ 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());
}
-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{};
+Vector<3, I32> DefaultMeshDecisions::get_face_normal(BlockSide side) {
+ auto is_side = [=](BlockSide s) -> I8 { return s == side; };
- 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 block = chunk.get(x, y, z);
- if (block.empty())
- continue;
+ return {
+ is_side(BlockSide::Right) - is_side(BlockSide::Left),
+ is_side(BlockSide::Top) - is_side(BlockSide::Bottom),
+ is_side(BlockSide::Front) - is_side(BlockSide::Back),
+ };
+}
- for (auto side: BlockSide::all()) {
- if (!is_face_visible(chunk, neighbors, x, y, z, side))
- continue;
+Bool DefaultMeshDecisions::is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+ Vector<3, I32> offset = get_face_normal(side);
+ auto neighbor_position = offset + Vector<3, I32>{x, y, z};
- auto side_positions = side.face();
- auto side_normals = face_normals(side);
- auto side_tex_coords = face_tex_coords(block.type, side);
- auto side_ao = face_ao_values(chunk, neighbors, x, y, z, side);
+ auto block = get_block_wrapping(chunk, neighbors, neighbor_position);
+ return block.type.is_transparent();
+}
- for (auto& position : side_positions) {
- position = position + Vector<3, F32>{x, y, z};
- }
+Bool DefaultMeshDecisions::should_ignore_block(Chunk::BlockData block) {
+ return block.empty() || block.type == BlockType::Water;
+}
- U32 s = positions.size();
+Bool WaterMeshDecisions::is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+ Vector<3, I32> offset = get_face_normal(side);
+ auto neighbor_position = offset + Vector<3, I32>{x, y, z};
- 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());
- 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 });
- }
- }
- }
- }
+ auto [neighbor] = get_block_wrapping(chunk, neighbors, neighbor_position);
+ return neighbor.is_transparent() && neighbor != BlockType::Water;
+}
+
+Bool WaterMeshDecisions::should_ignore_block(Chunk::BlockData block) {
+ return block.type != BlockType::Water;
+}
- return {
- {positions, normals, tex_coords, ambient_occlusion_values},
- indices,
- };
}
} \ No newline at end of file
diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp
index dd9446b..75023f5 100644
--- a/src/World/Generation/ChunkMeshing.hpp
+++ b/src/World/Generation/ChunkMeshing.hpp
@@ -9,6 +9,73 @@ 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);
+struct ChunkMesh { GFX::Mesh land_mesh, water_mesh; };
+ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors);
+
+namespace Detail {
+
+template <typename Decisions>
+GFX::Mesh create_mesh(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 block = chunk.get(x, y, z);
+ if (Decisions::should_ignore_block(block))
+ continue;
+
+ for (auto side: BlockSide::all()) {
+ if (!Decisions::is_face_visible(chunk, neighbors, x, y, z, side))
+ continue;
+
+ auto side_positions = Decisions::face_positions(side, x, y, z);
+ auto side_normals = Decisions::face_normals(side);
+ auto side_tex_coords = Decisions::face_tex_coords(block.type, side);
+ auto side_ao = Decisions::face_ao_values(chunk, neighbors, x, y, z, side);
+
+ U32 s = positions.size();
+
+ 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());
+ 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, ambient_occlusion_values},
+ indices,
+ };
+}
+
+class DefaultMeshDecisions {
+public:
+ static std::array<Vector<3, F32>, 4> face_positions(BlockSide side, U32 x, U32 y, U32 z);
+ static std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side);
+ static std::array<Vector<3, F32>, 4> face_normals(BlockSide side);
+ static std::array<F32, 4> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
+
+ static Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos);
+ static Vector<3, I32> get_face_normal(BlockSide side);
+
+ static Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
+ static Bool should_ignore_block(Chunk::BlockData block);
+};
+
+class WaterMeshDecisions final : public DefaultMeshDecisions {
+public:
+ static Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
+ static Bool should_ignore_block(Chunk::BlockData block);
+};
+
+}
}
diff --git a/src/World/World.cpp b/src/World/World.cpp
index d77ebe8..53f92da 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -135,8 +135,14 @@ void World::try_to_create_mesh_for_chunk(ChunkData& data) {
neighbors[5], neighbors[7], neighbors[2], neighbors[0],
};
- data.mesh_data = create_mesh_for_chunk(data.chunk.value(), chunk_neighbors);
- data.mesh = GFX::Binder::load(data.mesh_data.value());
+ auto meshes = mesh_chunk(data.chunk.value(), chunk_neighbors);
+
+ data.land_mesh_data = meshes.land_mesh;
+ data.land_mesh = GFX::Binder::load(data.land_mesh_data.value());
+
+ data.water_mesh_data = meshes.water_mesh;
+ data.water_mesh = GFX::Binder::load(data.water_mesh_data.value());
+
data.status = ChunkStatus::Done;
}
diff --git a/src/World/World.hpp b/src/World/World.hpp
index a094526..c931ee4 100644
--- a/src/World/World.hpp
+++ b/src/World/World.hpp
@@ -24,8 +24,12 @@ public:
ChunkIndex index;
ChunkStatus status;
std::optional<Chunk> chunk = {};
- std::optional<GFX::Mesh> mesh_data = {};
- std::optional<GFX::BindableMesh> mesh = {};
+
+ std::optional<GFX::Mesh> land_mesh_data = {};
+ std::optional<GFX::Mesh> water_mesh_data = {};
+
+ std::optional<GFX::BindableMesh> land_mesh = {};
+ std::optional<GFX::BindableMesh> water_mesh = {};
};
std::vector<ChunkData*> get_visible_chunks(Vector<3> position);
@@ -45,7 +49,7 @@ private:
static U64 timestamp();
- U8 m_view_distance_radius = 13;
+ U8 m_view_distance_radius = 10;
struct GenerationResult {
Chunk chunk;