summaryrefslogtreecommitdiff
path: root/src/World
diff options
context:
space:
mode:
Diffstat (limited to 'src/World')
-rw-r--r--src/World/BlockType.hpp12
-rw-r--r--src/World/Chunk.cpp8
-rw-r--r--src/World/Chunk.hpp10
-rw-r--r--src/World/Generation/ChunkMeshing.cpp54
-rw-r--r--src/World/Generation/ChunkMeshing.hpp18
-rw-r--r--src/World/Generation/ChunkNeighbors.cpp34
-rw-r--r--src/World/Generation/ChunkNeighbors.hpp13
-rw-r--r--src/World/Generation/Decoration.cpp10
-rw-r--r--src/World/Generation/Generator.cpp13
-rw-r--r--src/World/Generation/Lighting.cpp20
-rw-r--r--src/World/Generation/Lighting.hpp13
-rw-r--r--src/World/World.cpp17
-rw-r--r--src/World/World.hpp4
13 files changed, 151 insertions, 75 deletions
diff --git a/src/World/BlockType.hpp b/src/World/BlockType.hpp
index edca3d7..a63d96a 100644
--- a/src/World/BlockType.hpp
+++ b/src/World/BlockType.hpp
@@ -26,14 +26,18 @@ public:
operator Value() const { return m_block; }
- Bool is_transparent() const {
+ Bool is_translucent() const { return opacity() != 1.0; }
+
+ Real opacity() const {
switch (m_block) {
case Air:
- case Leaves:
+ return 0.0;
case Water:
- return true;
+ return 0.05;
+ case Leaves:
+ return 0.2;
default:
- return false;
+ return 1.0;
}
}
diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp
index 94e757e..ab727bd 100644
--- a/src/World/Chunk.cpp
+++ b/src/World/Chunk.cpp
@@ -3,16 +3,16 @@
namespace MC::World {
-void Chunk::set(U32 x, U32 y, U32 z, BlockData data) {
- m_blocks.at(pos(x, y, z)) = data;
+const Chunk::BlockData& Chunk::at(U32 x, U32 y, U32 z) const {
+ return m_blocks.at(pos(x, y, z));
}
-Chunk::BlockData Chunk::get(U32 x, U32 y, U32 z) const {
+Chunk::BlockData& Chunk::at(U32 x, U32 y, U32 z) {
return m_blocks.at(pos(x, y, z));
}
Bool Chunk::is_empty(U32 x, U32 y, U32 z) const {
- return get(x, y, z).empty();
+ return at(x, y, z).empty();
}
Vector<3> Chunk::position() const {
diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp
index bc44137..5f4844e 100644
--- a/src/World/Chunk.hpp
+++ b/src/World/Chunk.hpp
@@ -4,7 +4,6 @@
#include "BiomeType.hpp"
#include "BlockType.hpp"
#include "../GFX/Mesh.hpp"
-#include "BlockSide.hpp"
namespace MC::World {
@@ -18,13 +17,18 @@ public:
m_position{(Real)x * Width, 0.0f, (Real)y * Width} {}
struct BlockData {
+ BlockData() : type(BlockType::Air) {}
+ BlockData(BlockType t) : type(t), light{} {}
+
BlockType type;
+ U8 light;
Bool empty() const { return type == BlockType::Air; }
};
- void set(U32 x, U32 y, U32 z, BlockData data);
- BlockData get(U32 x, U32 y, U32 z) const;
+ const BlockData& at(U32 x, U32 y, U32 z) const;
+ BlockData& at(U32 x, U32 y, U32 z);
+
Bool is_empty(U32 x, U32 y, U32 z) const;
struct Details {
diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp
index 2e9f191..002dd79 100644
--- a/src/World/Generation/ChunkMeshing.cpp
+++ b/src/World/Generation/ChunkMeshing.cpp
@@ -124,6 +124,14 @@ Face<Normal> DefaultMeshDecisions::face_normals(BlockSide side) {
return {normal, normal, normal, normal};
}
+Face<Light> DefaultMeshDecisions::face_light(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+ auto neighbor = get_opposing_neighbor(chunk, neighbors, x, y, z, side);
+ if (!neighbor.type.is_translucent()) return {};
+
+ auto light = (F32)neighbor.light / (F32)255;
+ return {light, light, light, light};
+}
+
Face<AO> 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.
@@ -177,35 +185,6 @@ Face<AO> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors
return vertex_ao;
}
-Chunk::BlockData DefaultMeshDecisions::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());
-}
-
Vector<3, I32> DefaultMeshDecisions::get_face_normal(BlockSide side) {
auto is_side = [=](BlockSide s) -> I8 { return s == side; };
@@ -216,12 +195,16 @@ Vector<3, I32> DefaultMeshDecisions::get_face_normal(BlockSide side) {
};
}
-Bool DefaultMeshDecisions::is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+Chunk::BlockData DefaultMeshDecisions::get_opposing_neighbor(const 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 block = get_block_wrapping(chunk, neighbors, neighbor_position);
- return block.type.is_transparent();
+ return get_block_wrapping(chunk, neighbors, neighbor_position);
+}
+
+Bool DefaultMeshDecisions::is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+ auto block = get_opposing_neighbor(chunk, neighbors, x, y, z, side);
+ return block.type.is_translucent();
}
Bool DefaultMeshDecisions::should_ignore_block(Chunk::BlockData block) {
@@ -229,11 +212,8 @@ Bool DefaultMeshDecisions::should_ignore_block(Chunk::BlockData block) {
}
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};
-
- auto [neighbor] = get_block_wrapping(chunk, neighbors, neighbor_position);
- return neighbor.is_transparent() && neighbor != BlockType::Water;
+ auto block = get_opposing_neighbor(chunk, neighbors, x, y, z, side);
+ return block.type.is_translucent() && block.type != BlockType::Water;
}
Bool WaterMeshDecisions::should_ignore_block(Chunk::BlockData block) {
diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp
index 7e202ab..fb5fea9 100644
--- a/src/World/Generation/ChunkMeshing.hpp
+++ b/src/World/Generation/ChunkMeshing.hpp
@@ -2,15 +2,12 @@
#include "../../GFX/Mesh.hpp"
#include "../../GFX/Util/MeshBuilder.hpp"
+#include "../BlockSide.hpp"
#include "../Chunk.hpp"
+#include "ChunkNeighbors.hpp"
namespace MC::World::Generation::ChunkMeshing {
-struct ChunkNeighbors {
- Chunk *north, *east, *south, *west;
- Chunk *north_east, *south_east, *south_west, *north_west;
-};
-
struct ChunkMesh { GFX::Mesh land_mesh, water_mesh; };
ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors);
@@ -19,6 +16,7 @@ namespace Detail {
using Vertex = Vector<3, F32>;
using Normal = Vector<3, F32>;
using TexCoord = Vector<2, F32>;
+using Light = F32;
using AO = F32;
template <typename T>
@@ -26,12 +24,12 @@ using Face = std::array<T, 4>;
template <typename Decisions>
GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) {
- GFX::Util::MeshBuilder<Normal, TexCoord, AO> builder;
+ GFX::Util::MeshBuilder<Normal, TexCoord, Light, AO> builder;
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);
+ auto block = chunk.at(x, y, z);
if (Decisions::should_ignore_block(block))
continue;
@@ -44,7 +42,8 @@ GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) {
builder.positions(Decisions::face_positions(side, x, y, z));
builder.attributes<0>(Decisions::face_normals(side));
builder.attributes<1>(Decisions::face_tex_coords(block.type, side));
- builder.attributes<2>(Decisions::face_ao_values(chunk, neighbors, x, y, z, side));
+ builder.attributes<2>(Decisions::face_light(chunk, neighbors, x, y, z, side));
+ builder.attributes<3>(Decisions::face_ao_values(chunk, neighbors, x, y, z, side));
builder.indices(std::array{ s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 });
}
}
@@ -59,10 +58,11 @@ public:
static Face<Vertex> face_positions(BlockSide side, U32 x, U32 y, U32 z);
static Face<TexCoord> face_tex_coords(BlockType type, BlockSide side);
static Face<Normal> face_normals(BlockSide side);
+ static Face<Light> face_light(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
static Face<AO> 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 Chunk::BlockData get_opposing_neighbor(const Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, 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);
diff --git a/src/World/Generation/ChunkNeighbors.cpp b/src/World/Generation/ChunkNeighbors.cpp
new file mode 100644
index 0000000..f34466e
--- /dev/null
+++ b/src/World/Generation/ChunkNeighbors.cpp
@@ -0,0 +1,34 @@
+#include "ChunkNeighbors.hpp"
+
+namespace MC::World::Generation {
+
+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->at(pos.x(), pos.y(), pos.z());
+}
+
+} \ No newline at end of file
diff --git a/src/World/Generation/ChunkNeighbors.hpp b/src/World/Generation/ChunkNeighbors.hpp
new file mode 100644
index 0000000..1207c60
--- /dev/null
+++ b/src/World/Generation/ChunkNeighbors.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "../Chunk.hpp"
+
+namespace MC::World::Generation {
+struct ChunkNeighbors {
+ Chunk *north, *east, *south, *west;
+ Chunk *north_east, *south_east, *south_west, *north_west;
+};
+
+Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos);
+
+} \ No newline at end of file
diff --git a/src/World/Generation/Decoration.cpp b/src/World/Generation/Decoration.cpp
index f3f7c46..bcabffa 100644
--- a/src/World/Generation/Decoration.cpp
+++ b/src/World/Generation/Decoration.cpp
@@ -7,8 +7,10 @@ void Decorator::put_block(Chunk& chunk, Pos pos, BlockType block) {
if (!Chunk::is_valid_position(pos.x(), pos.y(), pos.z())) {
return;
}
- if (chunk.is_empty(pos.x(), pos.y(), pos.z())) {
- chunk.set(pos.x(), pos.y(), pos.z(), {block});
+
+ auto& place = chunk.at(pos.x(), pos.y(), pos.z());
+ if (place.empty()) {
+ place = {block};
}
}
@@ -48,7 +50,7 @@ void TreeDecorator::decorate_chunk(Chunk& chunk) {
if (!is_valid_position(pos))
continue;
- auto block_below = chunk.get(x, y-1, z);
+ auto& block_below = chunk.at(x, y-1, z);
if (block_below.empty())
continue;
@@ -64,7 +66,7 @@ void TreeDecorator::decorate_chunk(Chunk& chunk) {
continue;
draw_tree(chunk, pos);
- chunk.set(x, y-1, z, {BlockType::Dirt});
+ block_below = {BlockType::Dirt};
last_tree = pos;
break;
}
diff --git a/src/World/Generation/Generator.cpp b/src/World/Generation/Generator.cpp
index 88e69a7..9d63fc1 100644
--- a/src/World/Generation/Generator.cpp
+++ b/src/World/Generation/Generator.cpp
@@ -24,7 +24,7 @@ Chunk Generator::generate(I64 chunk_x, I64 chunk_y) {
}
#define SIMPLE_MAP_GENERATOR(name, getter) \
-Generator::Map2D<Real> Generator::name(I64 chunk_x, I64 chunk_y) { \
+Generator::Map2D<Real> Generator::name(I64 chunk_x, I64 chunk_y) { \
Matrix<Chunk::Width, Chunk::Width> map{}; \
for (UInt x = 0; x < Chunk::Width; x++) { \
for (UInt y = 0; y < Chunk::Width; y++) { \
@@ -161,19 +161,20 @@ void Generator::decorate_soil(Chunk& chunk, Map2D<BiomeType>& biome_map, Map3D<B
auto column_depth = 0;
for (UInt y = Chunk::Height - 1; y > 0; y--) {
+ auto& place = chunk.at(x, y, z);
auto block = terrain_map(x, y, z);
if (block) {
if (column_depth == 0 && y >= water_height - 1) {
- chunk.set(x, y, z, {top_block});
+ place = {top_block};
} else if (column_depth < dirt_depth) {
- chunk.set(x, y, z, {soil_block});
+ place = {soil_block};
} else {
- chunk.set(x, y, z, {BlockType::Stone});
+ place = {BlockType::Stone};
}
column_depth++;
} else {
if (y < water_height) {
- chunk.set(x, y, z, {BlockType::Water});
+ place = {BlockType::Water};
}
column_depth = 0;
}
@@ -181,7 +182,7 @@ void Generator::decorate_soil(Chunk& chunk, Map2D<BiomeType>& biome_map, Map3D<B
}
}
- for (auto& decorator : s_decorators) {
+ for (auto decorator : s_decorators) {
decorator->decorate_chunk(chunk);
}
}
diff --git a/src/World/Generation/Lighting.cpp b/src/World/Generation/Lighting.cpp
new file mode 100644
index 0000000..8df6e2a
--- /dev/null
+++ b/src/World/Generation/Lighting.cpp
@@ -0,0 +1,20 @@
+#include "Lighting.hpp"
+
+namespace MC::World::Generation::Lighting {
+
+void light_chunk(Chunk& chunk, ChunkNeighbors& _) {
+ for (UInt x = 0; x < Chunk::Width; x++) {
+ for (UInt z = 0; z < Chunk::Width; z++) {
+ U8 current_light_exposure = LightSun;
+ for (UInt y = Chunk::Height - 1; y != 0; y--) {
+ auto& block = chunk.at(x, y, z);
+ if (!block.type.is_translucent()) break;
+
+ current_light_exposure = (Real)current_light_exposure * (1 - block.type.opacity());
+ block.light = current_light_exposure;
+ }
+ }
+ }
+}
+
+}
diff --git a/src/World/Generation/Lighting.hpp b/src/World/Generation/Lighting.hpp
new file mode 100644
index 0000000..7bb8fcc
--- /dev/null
+++ b/src/World/Generation/Lighting.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "../Chunk.hpp"
+#include "ChunkNeighbors.hpp"
+
+namespace MC::World::Generation::Lighting {
+
+constexpr U8 LightSun = 200;
+constexpr U8 LightTorch = 100;
+
+void light_chunk(Chunk& chunk, ChunkNeighbors& neighbors);
+
+}
diff --git a/src/World/World.cpp b/src/World/World.cpp
index 8685e8a..912a5cf 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -1,5 +1,6 @@
#include "World.hpp"
#include "Generation/ChunkMeshing.hpp"
+#include "Generation/Lighting.hpp"
#include "../Time.hpp"
namespace MC::World {
@@ -19,8 +20,8 @@ std::vector<World::ChunkData*> World::get_visible_chunks(Vector<3> position) {
continue;
}
- if (data.status == ChunkStatus::NeedsMesh) {
- try_to_create_mesh_for_chunk(data);
+ if (data.status == ChunkStatus::WaitingForReification) {
+ try_to_reify_chunk(data);
}
if (data.status == ChunkStatus::Done) {
chunks.push_back(&data);
@@ -65,7 +66,7 @@ std::vector<ChunkIndex> World::get_visible_chunk_indices(const Vector<3> positio
void World::load_finished_chunks_from_queue() {
auto results = m_queue.done();
for (auto& [id, res] : results) {
- get(id) = {id, ChunkStatus::NeedsMesh, {res.chunk}};
+ get(id) = {id, ChunkStatus::WaitingForReification, {res.chunk}};
log_chunk_time(res.generation_duration);
}
}
@@ -90,7 +91,7 @@ World::ChunkData& World::get(ChunkIndex index) {
return entry->second;
}
-void World::try_to_create_mesh_for_chunk(ChunkData& data) {
+void World::try_to_reify_chunk(ChunkData& data) {
auto index = data.index;
UInt neighbor_index = 0;
@@ -109,12 +110,16 @@ void World::try_to_create_mesh_for_chunk(ChunkData& data) {
// Layout of neighboring chunks in `neighbors` array:
// (-1; -1) > (-1; 0) > (-1; 1) > (0; -1)
// ( 0; 1) > ( 1; -1) > ( 1; 0) > (1; 1)
- Generation::ChunkMeshing::ChunkNeighbors chunk_neighbors {
+ Generation::ChunkNeighbors chunk_neighbors {
neighbors[3], neighbors[6], neighbors[4], neighbors[1],
neighbors[5], neighbors[7], neighbors[2], neighbors[0],
};
- auto meshes = mesh_chunk(data.chunk.value(), chunk_neighbors);
+ // Lighting
+ Generation::Lighting::light_chunk(data.chunk.value(), chunk_neighbors);
+
+ // Meshing
+ auto meshes = Generation::ChunkMeshing::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());
diff --git a/src/World/World.hpp b/src/World/World.hpp
index 8a11dbe..3635d8a 100644
--- a/src/World/World.hpp
+++ b/src/World/World.hpp
@@ -17,7 +17,7 @@ public:
enum class ChunkStatus {
Empty,
WaitingForGeneration,
- NeedsMesh,
+ WaitingForReification,
Done
};
@@ -41,7 +41,7 @@ private:
std::vector<ChunkIndex> get_visible_chunk_indices(Vector<3> position) const;
void load_finished_chunks_from_queue();
void request_generation(ChunkIndex index, Real priority);
- void try_to_create_mesh_for_chunk(ChunkData& data);
+ void try_to_reify_chunk(ChunkData& data);
void log_chunk_time(U64 chunk_time_ms);