From 23d88e5f1c8f0c8652a07050fcfa8ff126e85d4a Mon Sep 17 00:00:00 2001 From: Mel Date: Fri, 21 Jul 2023 02:17:03 +0200 Subject: Extremely simple chunk-limited lighting --- src/World/BlockType.hpp | 12 +++++--- src/World/Chunk.cpp | 8 ++--- src/World/Chunk.hpp | 10 ++++-- src/World/Generation/ChunkMeshing.cpp | 54 +++++++++++---------------------- src/World/Generation/ChunkMeshing.hpp | 18 +++++------ src/World/Generation/ChunkNeighbors.cpp | 34 +++++++++++++++++++++ src/World/Generation/ChunkNeighbors.hpp | 13 ++++++++ src/World/Generation/Decoration.cpp | 10 +++--- src/World/Generation/Generator.cpp | 13 ++++---- src/World/Generation/Lighting.cpp | 20 ++++++++++++ src/World/Generation/Lighting.hpp | 13 ++++++++ src/World/World.cpp | 17 +++++++---- src/World/World.hpp | 4 +-- 13 files changed, 151 insertions(+), 75 deletions(-) create mode 100644 src/World/Generation/ChunkNeighbors.cpp create mode 100644 src/World/Generation/ChunkNeighbors.hpp create mode 100644 src/World/Generation/Lighting.cpp create mode 100644 src/World/Generation/Lighting.hpp (limited to 'src') 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 DefaultMeshDecisions::face_normals(BlockSide side) { return {normal, normal, normal, normal}; } +Face 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 DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) { std::array, 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 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 @@ -26,12 +24,12 @@ using Face = std::array; template GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) { - GFX::Util::MeshBuilder builder; + GFX::Util::MeshBuilder 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 face_positions(BlockSide side, U32 x, U32 y, U32 z); static Face face_tex_coords(BlockType type, BlockSide side); static Face face_normals(BlockSide side); + static Face face_light(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); static Face 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 Generator::name(I64 chunk_x, I64 chunk_y) { \ +Generator::Map2D Generator::name(I64 chunk_x, I64 chunk_y) { \ Matrix 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& biome_map, Map3D 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& biome_map, Map3Ddecorate_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::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 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 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); -- cgit 1.4.1