diff options
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/Compute/Queue.hpp | 5 | ||||
| -rw-r--r-- | src/GFX/Binder.cpp | 84 | ||||
| -rw-r--r-- | src/GFX/Binder.hpp | 48 | ||||
| -rw-r--r-- | src/GFX/Mesh.cpp | 75 | ||||
| -rw-r--r-- | src/GFX/Mesh.hpp | 18 | ||||
| -rw-r--r-- | src/Util/ImageViewer.cpp | 4 | ||||
| -rw-r--r-- | src/Util/ImageViewer.hpp | 7 | ||||
| -rw-r--r-- | src/World/Chunk.cpp | 8 | ||||
| -rw-r--r-- | src/World/Chunk.hpp | 9 | ||||
| -rw-r--r-- | src/World/ChunkRegistry.hpp | 17 | ||||
| -rw-r--r-- | src/World/Clouds.cpp | 6 | ||||
| -rw-r--r-- | src/World/Clouds.hpp | 8 | ||||
| -rw-r--r-- | src/World/Generation/ChunkMeshing.cpp | 88 | ||||
| -rw-r--r-- | src/World/Generation/ChunkMeshing.hpp | 42 | ||||
| -rw-r--r-- | src/World/Generation/ChunkNeighbors.cpp | 26 | ||||
| -rw-r--r-- | src/World/Generation/ChunkNeighbors.hpp | 13 | ||||
| -rw-r--r-- | src/World/Generation/Lighting.cpp | 9 | ||||
| -rw-r--r-- | src/World/Generation/Lighting.hpp | 4 | ||||
| -rw-r--r-- | src/World/Position.hpp | 10 | ||||
| -rw-r--r-- | src/World/World.cpp | 103 | ||||
| -rw-r--r-- | src/World/World.hpp | 24 | ||||
| -rw-r--r-- | src/main.cpp | 5 |
23 files changed, 349 insertions, 265 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 58ddaf0..de94623 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ add_executable(meowcraft src/GFX/Mesh.cpp src/GFX/Mesh.hpp src/Math/Vector.hpp src/Math/Common.hpp - src/GFX/Binder.cpp src/GFX/Binder.hpp src/GFX/Shading/Shader.cpp src/GFX/Shading/Shader.hpp src/GFX/Shading/Program.cpp src/GFX/Shading/Program.hpp src/Math/Matrix.hpp diff --git a/src/Compute/Queue.hpp b/src/Compute/Queue.hpp index 06469f8..e201932 100644 --- a/src/Compute/Queue.hpp +++ b/src/Compute/Queue.hpp @@ -25,6 +25,11 @@ public: m_control->work.jobs.emplace(id, priority, execute); } + USize size() const { + std::scoped_lock work_lock(m_control->work.mutex); + return m_control->work.jobs.size(); + } + struct Result { I id{}; X res; diff --git a/src/GFX/Binder.cpp b/src/GFX/Binder.cpp deleted file mode 100644 index 49f82a7..0000000 --- a/src/GFX/Binder.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include <GL/glew.h> -#include <cassert> -#include "Binder.hpp" -#include "Mesh.hpp" - -namespace MC::GFX { - -BindableMesh Binder::load(const Mesh& mesh) { - auto vao = create_vao(); - if (!mesh.indices().empty()) { - store_indices(mesh.indices().data(), mesh.indices().size()); - } - - Int attribute_index = 0; - for (const auto& attribute : mesh.attributes()) { - store_in_attribute_list( - attribute_index++, - attribute.attribute_size, - attribute.type_size, - attribute.data, - attribute.data_size - ); - } - unbind_vao(); - - return {vao, mesh.indices().size(), mesh.attributes().size()}; -} - -U32 Binder::create_vao() { - GLuint vao; - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - return vao; -} - -void Binder::unbind_vao() { - glBindVertexArray(0); -} - -void Binder::store_indices(const U32* indices, USize indices_size) { - GLuint ebo; - glGenBuffers(1, &ebo); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_size * sizeof(U32), indices, GL_STATIC_DRAW); -} - - -void Binder::store_in_attribute_list(U32 attribute, Int attribute_size, Int type_size, const void* data, long data_size) { - assert(type_size == sizeof(F32)); - - GLuint vbo; - glGenBuffers(1, &vbo); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, data_size * attribute_size * type_size, data, GL_STATIC_DRAW); - glVertexAttribPointer(attribute, attribute_size, GL_FLOAT, GL_FALSE, attribute_size * type_size, nullptr); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -void BindableMesh::bind() const { - glBindVertexArray(m_vao); - for (Int i = 0; i < m_attribute_count; i++) { - glEnableVertexAttribArray(i); - } -} - -void BindableMesh::unbind() const { - glBindVertexArray(0); - for (Int i = 0; i < m_attribute_count; i++) { - glDisableVertexAttribArray(i); - } -} - -Bool BindableMesh::has_indices() const { - return m_has_indices; -} - -USize BindableMesh::size() const { - return m_vertex_count; -} - -} \ No newline at end of file diff --git a/src/GFX/Binder.hpp b/src/GFX/Binder.hpp deleted file mode 100644 index 6103e09..0000000 --- a/src/GFX/Binder.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "../Common/Sizes.hpp" -#include "Mesh.hpp" - -namespace MC::GFX { - -class BindableMesh { -public: - void bind() const; - void unbind() const; - - Bool has_indices() const; - USize size() const; - -private: - BindableMesh( - U32 vao, - USize vertex_count, - USize attribute_count - ) : m_vao(vao), - m_vertex_count(vertex_count), - m_has_indices(vertex_count > 0), - m_attribute_count(attribute_count) {} - - U32 m_vao; - USize m_vertex_count; - Bool m_has_indices; - USize m_attribute_count; - - friend class Binder; -}; - -class Binder { -public: - Binder() = default; - - static BindableMesh load(const Mesh& mesh); - -private: - static U32 create_vao(); - static void unbind_vao(); - - static void store_in_attribute_list(U32 attribute, Int attribute_size, Int type_size, const void* data, long data_size); - static void store_indices(const U32* indices, USize indices_size); -}; - -} \ No newline at end of file diff --git a/src/GFX/Mesh.cpp b/src/GFX/Mesh.cpp index c45b5c9..0633e11 100644 --- a/src/GFX/Mesh.cpp +++ b/src/GFX/Mesh.cpp @@ -2,12 +2,77 @@ namespace MC::GFX { -const std::vector<U32>& Mesh::indices() const { - return m_indices; +USize Mesh::size() const { + return m_indices.size(); } -const std::vector<Mesh::Attribute>& Mesh::attributes() const { - return m_attributes; +void Mesh::bind() { + if (!m_vao.has_value()) upload(); + + glBindVertexArray(m_vao.value()); + for (Int i = 0; i < m_attributes.size(); i++) { + glEnableVertexAttribArray(i); + } +} + +void Mesh::unbind() const { + glBindVertexArray(0); + for (Int i = 0; i < m_attributes.size(); i++) { + glDisableVertexAttribArray(i); + } +} + +void Mesh::upload() { + auto vao = create_vao(); + if (!m_indices.empty()) { + store_indices(m_indices.data(), m_indices.size()); + } + + Int attribute_index = 0; + for (const auto& attribute : m_attributes) { + store_in_attribute_list( + attribute_index++, + attribute.attribute_size, + attribute.type_size, + attribute.data, + attribute.data_size + ); + } + unbind_vao(); + + m_vao = {vao}; +} + +GLuint Mesh::create_vao() { + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + return vao; } -} \ No newline at end of file +void Mesh::unbind_vao() { + glBindVertexArray(0); +} + +void Mesh::store_indices(const U32* indices, USize indices_size) { + GLuint ebo; + glGenBuffers(1, &ebo); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_size * sizeof(U32), indices, GL_STATIC_DRAW); +} + +void Mesh::store_in_attribute_list(U32 attribute, Int attribute_size, Int type_size, const void* data, long data_size) { + assert(type_size == sizeof(F32)); + + GLuint vbo; + glGenBuffers(1, &vbo); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, data_size * attribute_size * type_size, data, GL_STATIC_DRAW); + glVertexAttribPointer(attribute, attribute_size, GL_FLOAT, GL_FALSE, attribute_size * type_size, nullptr); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +} diff --git a/src/GFX/Mesh.hpp b/src/GFX/Mesh.hpp index 69625ff..435afe1 100644 --- a/src/GFX/Mesh.hpp +++ b/src/GFX/Mesh.hpp @@ -3,6 +3,8 @@ #include <utility> #include <vector> #include <algorithm> +#include <optional> +#include <GL/glew.h> #include "../Common/Sizes.hpp" #include "../Math/Common.hpp" @@ -58,12 +60,24 @@ public: std::vector<Attribute> attributes ) : m_attributes(std::move(attributes)) {} - const std::vector<U32>& indices() const; - const std::vector<Attribute>& attributes() const; + USize size() const; + + void bind(); + void unbind() const; private: + void upload(); + + static GLuint create_vao(); + static void unbind_vao(); + + static void store_in_attribute_list(U32 attribute, Int attribute_size, Int type_size, const void* data, long data_size); + static void store_indices(const U32* indices, USize indices_size); + std::vector<Attribute> m_attributes; std::vector<U32> m_indices; + + std::optional<GLuint> m_vao; }; } \ No newline at end of file diff --git a/src/Util/ImageViewer.cpp b/src/Util/ImageViewer.cpp index 6c88c78..4c78750 100644 --- a/src/Util/ImageViewer.cpp +++ b/src/Util/ImageViewer.cpp @@ -12,7 +12,7 @@ ImageViewer::ImageViewer( {GFX::Shading::Shader::Type::Vertex, vertex}, {GFX::Shading::Shader::Type::Fragment, fragment} ), - m_mesh(GFX::Binder::load(create_mesh(window_aspect, image.width(), image.height()))) { + m_mesh(create_mesh(window_aspect, image.width(), image.height())) { m_program.bind(); auto model_uniform = m_program.uniform("model_matrix"); auto view_uniform = m_program.uniform("view_matrix"); @@ -25,7 +25,7 @@ ImageViewer::ImageViewer( m_program.unbind(); } -void ImageViewer::render() const { +void ImageViewer::render() { m_program.bind(); m_texture.bind(); m_mesh.bind(); diff --git a/src/Util/ImageViewer.hpp b/src/Util/ImageViewer.hpp index 1e18103..aa48b1e 100644 --- a/src/Util/ImageViewer.hpp +++ b/src/Util/ImageViewer.hpp @@ -1,8 +1,7 @@ #pragma once -#include <ostream> +#include "../GFX/Mesh.hpp" #include "../GFX/Image/RawImage.hpp" -#include "../GFX/Binder.hpp" #include "../GFX/Texture.hpp" #include "../GFX/Shading/Program.hpp" @@ -12,7 +11,7 @@ class ImageViewer { public: explicit ImageViewer(const GFX::Image::RawImage& image, Real window_aspect); - void render() const; + void render(); private: static GFX::Mesh create_mesh(Real window_aspect, U32 image_width, U32 image_height); @@ -21,7 +20,7 @@ private: static const Char* vertex; static const Char* fragment; - GFX::BindableMesh m_mesh; + GFX::Mesh m_mesh; GFX::Shading::Program m_program; GFX::Texture m_texture; }; diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index 31a2c90..8b204c0 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -31,6 +31,14 @@ Vector<3> Chunk::position() const { return m_position; } +Bool Chunk::is_damaged() const { + return m_damaged; +} + +void Chunk::damage() { + m_damaged = true; +} + Bool Chunk::is_valid_position(U32 x, U32 y, U32 z) { return x < Width && y < Height && z < Width; } diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index 32d3ab3..1aa2dd1 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -21,8 +21,8 @@ public: m_position{(Real)x * Width, 0.0f, (Real)y * Width} {} struct BlockData { - BlockData() : type(BlockType::Air) {} - BlockData(BlockType t) : type(t), light{} {} + BlockData() : type(BlockType::Air), light{200} {} + BlockData(BlockType t) : type(t), light{200} {} BlockType type; U8 light; @@ -52,6 +52,9 @@ public: ChunkIndex index() const; Vector<3> position() const; + Bool is_damaged() const; + void damage(); + static Bool is_valid_position(U32 x, U32 y, U32 z); private: static U64 pos(U32 x, U32 y, U32 z); @@ -60,6 +63,8 @@ private: Vector<3> m_position; std::vector<BlockData> m_blocks; + Bool m_damaged = false; + Details m_details; }; diff --git a/src/World/ChunkRegistry.hpp b/src/World/ChunkRegistry.hpp index 86e5d06..2c515c5 100644 --- a/src/World/ChunkRegistry.hpp +++ b/src/World/ChunkRegistry.hpp @@ -5,7 +5,6 @@ #include "Chunk.hpp" #include "ChunkIndex.hpp" #include "Position.hpp" -#include "../GFX/Binder.hpp" namespace MC::World { @@ -14,7 +13,9 @@ public: enum class Status { Empty, WaitingForGeneration, + NeedsReification, WaitingForReification, + Damaged, Done }; @@ -24,11 +25,17 @@ public: Status status; std::optional<Chunk> chunk = {}; - std::optional<GFX::Mesh> land_mesh_data = {}; - std::optional<GFX::Mesh> water_mesh_data = {}; + std::optional<GFX::Mesh> land_mesh = {}; + std::optional<GFX::Mesh> water_mesh = {}; - std::optional<GFX::BindableMesh> land_mesh = {}; - std::optional<GFX::BindableMesh> water_mesh = {}; + Status get_status() const { + if (status == Status::Done && chunk.value().is_damaged()) { return Status::Damaged; } + return status; + } + + void damage() { + if (status == Status::Done) chunk.value().damage(); + } }; Data& get(ChunkIndex index); diff --git a/src/World/Clouds.cpp b/src/World/Clouds.cpp index 2e0f76c..2567985 100644 --- a/src/World/Clouds.cpp +++ b/src/World/Clouds.cpp @@ -15,7 +15,7 @@ Clouds::Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_co {GFX::Shading::Shader::Type::Vertex, vertex}, {GFX::Shading::Shader::Type::Fragment, fragment} ), - m_mesh(GFX::Binder::load(create_mesh(create_cloud_matrix()))), + m_mesh(create_mesh(create_cloud_matrix())), m_model_uniform(), m_view_uniform(), m_projection_uniform() { m_program.bind(); @@ -38,7 +38,7 @@ void Clouds::update(const Time& time) { m_x_offset += 5.0 * time.delta(); } -void Clouds::render(const GFX::Camera& camera) const { +void Clouds::render(const GFX::Camera& camera) { auto position = camera.position(); I32 center_x = std::round((position.x() - m_x_offset) / TileSize); @@ -51,7 +51,7 @@ void Clouds::render(const GFX::Camera& camera) const { } } -void Clouds::render_single_instance(const GFX::Camera& camera, Int x, Int y) const { +void Clouds::render_single_instance(const GFX::Camera& camera, Int x, Int y) { Vector<3> position{TileSize * x + m_x_offset, Height, TileSize * y}; m_program.bind(); diff --git a/src/World/Clouds.hpp b/src/World/Clouds.hpp index 35e7155..4bd0637 100644 --- a/src/World/Clouds.hpp +++ b/src/World/Clouds.hpp @@ -1,10 +1,10 @@ #pragma once #include "../Time.hpp" -#include "../GFX/Binder.hpp" #include "../GFX/Shading/Program.hpp" #include "../GFX/Shading/Uniform.hpp" #include "../GFX/Camera.hpp" +#include "../GFX/Mesh.hpp" namespace MC::World { @@ -13,7 +13,7 @@ public: Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color, Vector<3, F32> sun_direction); void update(const Time& time); - void render(const GFX::Camera& camera) const; + void render(const GFX::Camera& camera); private: constexpr static U32 CloudMatrixSize = 128; constexpr static Int Height = 200; @@ -22,7 +22,7 @@ private: using CloudMatrix = Matrix<CloudMatrixSize, CloudMatrixSize, Bool>; - void render_single_instance(const GFX::Camera& camera, Int x, Int y) const; + void render_single_instance(const GFX::Camera& camera, Int x, Int y); static CloudMatrix create_cloud_matrix(); static GFX::Mesh create_mesh(const CloudMatrix& cloud_matrix); @@ -33,7 +33,7 @@ private: Real m_x_offset = 0.0; GFX::Shading::Program m_program; - GFX::BindableMesh m_mesh; + GFX::Mesh m_mesh; GFX::Shading::Uniform m_model_uniform; GFX::Shading::Uniform m_view_uniform; diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp index 002dd79..3855de4 100644 --- a/src/World/Generation/ChunkMeshing.cpp +++ b/src/World/Generation/ChunkMeshing.cpp @@ -2,15 +2,53 @@ namespace MC::World::Generation::ChunkMeshing { -ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) { +ChunkMesh mesh_chunk(Chunk& chunk, const SurroundingContext& context) { using namespace Detail; return { - create_mesh<DefaultMeshDecisions>(chunk, neighbors), - create_mesh<WaterMeshDecisions>(chunk, neighbors) + create_mesh<DefaultMeshDecisions>(chunk, context), + create_mesh<WaterMeshDecisions>(chunk, context) }; } +SurroundingContext::Block& SurroundingContext::at(Position::BlockOffset p) { + return m_blocks[pos(p)]; +} + +const SurroundingContext::Block& SurroundingContext::at(Position::BlockOffset p) const { + return m_blocks[pos(p)]; +} + +USize SurroundingContext::pos(Position::BlockOffset p) { + // First we calculate the index as if there were no gaps. + USize pos = 0; + pos += p.x() + 1; + pos += (p.z() + 1) * (Chunk::Width + 2); + + // Then we substract the gaps. + Int g = (p.z() + 1) - (p.x() < 0); + UInt gap_count = std::clamp<Int>(g, 0, Chunk::Width); + pos -= gap_count * Chunk::Width; + + pos += p.y() * surrounding_block_count; + return pos; +} + +SurroundingContext create_meshing_context(const Chunk& chunk, ChunkNeighbors& neighbors) { + SurroundingContext context{}; + for (I16 x = -1; x <= (I16)Chunk::Width; x++) { + for (I16 z = -1; z <= (I16)Chunk::Width; z++) { + for (I16 y = 0; y < (I16)Chunk::Height; y++) { + Position::BlockOffset pos{x, y, z}; + if (pos.fits_within_chunk()) continue; + auto [does_exist, block] = get_block_wrapping(chunk, neighbors, {pos.x(), pos.y(), pos.z()}); + context.at(pos) = {does_exist, block}; + } + } + } + return context; +} + namespace Detail { Face<Vertex> DefaultMeshDecisions::face_positions(BlockSide side, U32 x, U32 y, U32 z) { @@ -124,15 +162,15 @@ 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); +Face<Light> DefaultMeshDecisions::face_light(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) { + auto neighbor = get_opposing_neighbor(chunk, context, x, y, z, side).block; 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) { +Face<AO> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const SurroundingContext& context, 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 @@ -167,13 +205,15 @@ Face<AO> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors auto b = offsets[++offset_index]; // corner auto c = offsets[++offset_index % 8]; - 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()}); - auto block_c = get_block_wrapping(chunk, neighbors, {x + c.x(), y + c.y(), z + c.z()}); + auto p = [=](auto o) -> Position::BlockOffset { return {(I16)((I16)x + o.x()), (I16)((I16)y + o.y()), (I16)((I16)z + o.z())}; }; + auto block_a = get_block_from_chunk_or_context(chunk, context, p(a)); + auto block_b = get_block_from_chunk_or_context(chunk, context, p(b)); + auto block_c = get_block_from_chunk_or_context(chunk, context, p(c)); - F32 occlusion_a = block_a.empty() ? 0 : 1; - F32 occlusion_b = block_b.empty() ? 0 : 1; - F32 occlusion_c = block_c.empty() ? 0 : 1; + auto is_occupied = [](auto b) -> F32 { return b.does_exist && !b.block.empty(); }; + F32 occlusion_a = is_occupied(block_a); + F32 occlusion_b = is_occupied(block_b); + F32 occlusion_c = is_occupied(block_c); if (occlusion_a + occlusion_c == 2.0f) { vertex_ao[vertex] = 1.0f; @@ -195,15 +235,24 @@ Vector<3, I32> DefaultMeshDecisions::get_face_normal(BlockSide side) { }; } -Chunk::BlockData DefaultMeshDecisions::get_opposing_neighbor(const Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) { +SurroundingContext::Block DefaultMeshDecisions::get_block_from_chunk_or_context( + const Chunk& chunk, const SurroundingContext& context, Position::BlockOffset pos +) { + if (pos.fits_within_chunk()) return {true, chunk.at(pos)}; + return context.at(pos); +} + +SurroundingContext::Block DefaultMeshDecisions::get_opposing_neighbor(const Chunk& chunk, const SurroundingContext& context, 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 pos = offset + Vector<3, I32>{x, y, z}; - return get_block_wrapping(chunk, neighbors, neighbor_position); + return get_block_from_chunk_or_context(chunk, context, {(I16)pos.x(), (I16)pos.y(), (I16)pos.z()}); } -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); +Bool DefaultMeshDecisions::is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) { + auto [does_exist, block] = get_opposing_neighbor(chunk, context, x, y, z, side); + // Faces on the chunk border are hidden by default, even if the opposing chunk does not exist. + if (!does_exist) return false; return block.type.is_translucent(); } @@ -211,8 +260,9 @@ Bool DefaultMeshDecisions::should_ignore_block(Chunk::BlockData block) { return block.empty() || block.type == BlockType::Water; } -Bool WaterMeshDecisions::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); +Bool WaterMeshDecisions::is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) { + auto [does_exist, block] = get_opposing_neighbor(chunk, context, x, y, z, side); + if (!does_exist) return false; // See above. return block.type.is_translucent() && block.type != BlockType::Water; } diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp index fb5fea9..5401580 100644 --- a/src/World/Generation/ChunkMeshing.hpp +++ b/src/World/Generation/ChunkMeshing.hpp @@ -5,11 +5,32 @@ #include "../BlockSide.hpp" #include "../Chunk.hpp" #include "ChunkNeighbors.hpp" +#include "../ChunkRegistry.hpp" namespace MC::World::Generation::ChunkMeshing { +// Literally just the blocks squarely surrounding a chunk. +// Example context for a 2x1x2 chunk: +// c c c c +// c x x c +// c x x c +// c c c c +class SurroundingContext { +public: + struct Block { Bool does_exist; Chunk::BlockData block; }; + + Block& at(Position::BlockOffset p); + const Block& at(Position::BlockOffset p) const; +private: + static USize pos(Position::BlockOffset p); + static constexpr USize surrounding_block_count = Chunk::Width * 4 + 4; + + Block m_blocks[surrounding_block_count * Chunk::Height] = {}; +}; +SurroundingContext create_meshing_context(const Chunk& chunk, ChunkNeighbors& neighbors); + struct ChunkMesh { GFX::Mesh land_mesh, water_mesh; }; -ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors); +ChunkMesh mesh_chunk(Chunk& chunk, const SurroundingContext& context); namespace Detail { @@ -23,7 +44,7 @@ template <typename T> using Face = std::array<T, 4>; template <typename Decisions> -GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) { +GFX::Mesh create_mesh(Chunk& chunk, const SurroundingContext& context) { GFX::Util::MeshBuilder<Normal, TexCoord, Light, AO> builder; for (Int x = 0; x < Chunk::Width; x++) { @@ -34,7 +55,7 @@ GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) { continue; for (auto side: BlockSide::all()) { - if (!Decisions::is_face_visible(chunk, neighbors, x, y, z, side)) + if (!Decisions::is_face_visible(chunk, context, x, y, z, side)) continue; U32 s = builder.vertex_count(); @@ -42,8 +63,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_light(chunk, neighbors, x, y, z, side)); - builder.attributes<3>(Decisions::face_ao_values(chunk, neighbors, x, y, z, side)); + builder.attributes<2>(Decisions::face_light(chunk, context, x, y, z, side)); + builder.attributes<3>(Decisions::face_ao_values(chunk, context, x, y, z, side)); builder.indices(std::array{ s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 }); } } @@ -58,19 +79,20 @@ 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 Face<Light> face_light(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); + static Face<AO> face_ao_values(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); 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 SurroundingContext::Block get_block_from_chunk_or_context(const Chunk& chunk, const SurroundingContext& context, Position::BlockOffset pos); + static SurroundingContext::Block get_opposing_neighbor(const Chunk& chunk, const SurroundingContext& context, 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 is_face_visible(Chunk& chunk, const SurroundingContext& context, 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 is_face_visible(Chunk& chunk, const SurroundingContext& context, 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 index f34466e..c3ae5cb 100644 --- a/src/World/Generation/ChunkNeighbors.cpp +++ b/src/World/Generation/ChunkNeighbors.cpp @@ -2,7 +2,28 @@ namespace MC::World::Generation { -Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos) { +ChunkNeighbors find_chunk_neighbors(ChunkIndex chunk, ChunkRegistry& chunks) { + UInt neighbor_index = 0; + std::array<Chunk*, 8> neighbors{}; + for (I32 x = -1; x <= 1; x++) { + for (I32 y = -1; y <= 1; y++) { + if (x == 0 && y == 0) continue; + + auto& neighbor_data = chunks.get({chunk.x + x, chunk.y + y}); + if (neighbor_data.chunk.has_value()) neighbors[neighbor_index++] = &neighbor_data.chunk.value(); + } + } + + // Layout of neighboring chunks in `neighbors` array: + // (-1; -1) > (-1; 0) > (-1; 1) > (0; -1) + // ( 0; 1) > ( 1; -1) > ( 1; 0) > (1; 1) + return { + neighbors[3], neighbors[6], neighbors[4], neighbors[1], + neighbors[5], neighbors[7], neighbors[2], neighbors[0], + }; +} + +GetBlockWrappingResult 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 { @@ -28,7 +49,8 @@ Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& ne 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()); + if (!chunk_to_ask) return {false}; + return {true, 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 index 1207c60..83fca4c 100644 --- a/src/World/Generation/ChunkNeighbors.hpp +++ b/src/World/Generation/ChunkNeighbors.hpp @@ -1,13 +1,22 @@ #pragma once #include "../Chunk.hpp" +#include "../ChunkRegistry.hpp" namespace MC::World::Generation { struct ChunkNeighbors { Chunk *north, *east, *south, *west; Chunk *north_east, *south_east, *south_west, *north_west; + + Bool all_exist() const { return north && east && south && west && north_east && south_east && south_west && north_west; } }; -Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos); +ChunkNeighbors find_chunk_neighbors(ChunkIndex chunk, ChunkRegistry& chunks); + +struct GetBlockWrappingResult { + bool does_exist; + Chunk::BlockData block; +}; +GetBlockWrappingResult 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/Lighting.cpp b/src/World/Generation/Lighting.cpp index 39d8320..f7c7bbf 100644 --- a/src/World/Generation/Lighting.cpp +++ b/src/World/Generation/Lighting.cpp @@ -23,12 +23,12 @@ void Lighting::add_block(Chunk& chunk, Position::BlockLocal position, U8 light) enqueue(chunk.index(), position, light); } -void Lighting::illuminate(ChunkRegistry& chunks) { +void Lighting::illuminate(Chunk& chunk) { while (!m_queue.empty()) { auto op = m_queue.front(); m_queue.pop(); - process(chunks, op); + process(chunk, op); } } @@ -36,10 +36,7 @@ void Lighting::enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origi m_queue.push({chunk, position, origin}); } -void Lighting::process(ChunkRegistry& chunks, Operation op) { - auto& chunk_data = chunks.get(op.chunk); - auto& chunk = chunk_data.chunk.value(); - +void Lighting::process(Chunk& chunk, Operation op) { for (auto direction : Position::axis_directions) { auto neighbor_pos = op.position.offset(direction); if (!neighbor_pos.fits_within_chunk()) continue; diff --git a/src/World/Generation/Lighting.hpp b/src/World/Generation/Lighting.hpp index 72af0e1..4587266 100644 --- a/src/World/Generation/Lighting.hpp +++ b/src/World/Generation/Lighting.hpp @@ -13,7 +13,7 @@ public: void add_chunk(Chunk& chunk); void add_block(Chunk& chunk, Position::BlockLocal position, U8 light); - void illuminate(ChunkRegistry& chunks); + void illuminate(Chunk& chunk); private: static constexpr U8 SunBrightness = 200; static constexpr U8 DefaultFalloff = 10; @@ -25,7 +25,7 @@ private: }; void enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origin); - void process(ChunkRegistry& chunks, Operation op); + void process(Chunk& chunk, Operation op); std::queue<Operation> m_queue; }; diff --git a/src/World/Position.hpp b/src/World/Position.hpp index 49cbbb6..b603ee4 100644 --- a/src/World/Position.hpp +++ b/src/World/Position.hpp @@ -9,9 +9,9 @@ namespace MC::World::Position { using World = Vector<3>; // Offset between block positions within single chunk. -class OffsetBlock : public Vector<3, I8> { +class BlockOffset : public Vector<3, I16> { public: - OffsetBlock(I8 x, I8 y, I8 z) : Vector(x, y, z) {} + BlockOffset(I16 x, I16 y, I16 z) : Vector(x, y, z) {} Bool fits_within_chunk() const { using namespace ChunkDimensions; return x() >= 0 && x() < Width && y() >= 0 && y() < Height && z() >= 0 && z() < Width; @@ -28,8 +28,8 @@ public: class BlockLocal : public Vector<3, U8> { public: BlockLocal(U8 x, U8 y, U8 z) : Vector(x, y, z) {} - BlockLocal(OffsetBlock offset) : BlockLocal(offset.x(), offset.y(), offset.z()) {} - OffsetBlock offset(OffsetBlock by) { + BlockLocal(BlockOffset offset) : BlockLocal(offset.x(), offset.y(), offset.z()) {} + BlockOffset offset(BlockOffset by) { return { static_cast<I8>(x() + by.x()), static_cast<I8>(y() + by.y()), @@ -38,7 +38,7 @@ public: } }; -const std::array<OffsetBlock, 6> axis_directions = {{ +const std::array<BlockOffset, 6> axis_directions = {{ {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}, }}; diff --git a/src/World/World.cpp b/src/World/World.cpp index 5548866..3d273bd 100644 --- a/src/World/World.cpp +++ b/src/World/World.cpp @@ -5,8 +5,8 @@ namespace MC::World { -std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Vector<3> position) { - load_finished_chunks_from_queue(); +std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Position::World position) { + process_chunk_updates(); auto visible_chunks = get_visible_chunk_indices(position); @@ -14,24 +14,30 @@ std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Vector<3> position) chunks.reserve(visible_chunks.size()); for (auto index : visible_chunks) { auto& data = m_registry.get(index); - if (data.status == ChunkRegistry::Status::Empty) { - request_generation(index, position.distance(index.middle())); + + if (data.get_status() == ChunkRegistry::Status::Empty) { + request_generation(index, calculate_priority(index, position, RequestType::Initial)); data.status = ChunkRegistry::Status::WaitingForGeneration; continue; } - if (data.status == ChunkRegistry::Status::WaitingForReification) { - try_to_reify_chunk(data); - } - if (data.status == ChunkRegistry::Status::Done) { - chunks.push_back(&data); + if (data.get_status() == ChunkRegistry::Status::NeedsReification || data.get_status() == ChunkRegistry::Status::Damaged) { + auto do_all_exist = Generation::find_chunk_neighbors(index, m_registry).all_exist(); + if (m_reification_queue.size() <= 50 && do_all_exist) { + auto request_type = data.get_status() == ChunkRegistry::Status::Damaged ? RequestType::Update : RequestType::Initial; + request_reification(index, calculate_priority(index, position, request_type)); + data.status = ChunkRegistry::Status::WaitingForReification; + } } + + // TODO: Use a better indicator than `land_mesh.has_value()`. + if (data.land_mesh.has_value()) chunks.push_back(&data); } return chunks; } -std::vector<ChunkIndex> World::get_visible_chunk_indices(const Vector<3> position) const { +std::vector<ChunkIndex> World::get_visible_chunk_indices(const Position::World position) const { I32 center_x = std::round(position.x() / Chunk::Width); I32 center_y = std::round(position.z() / Chunk::Width); @@ -53,61 +59,62 @@ std::vector<ChunkIndex> World::get_visible_chunk_indices(const Vector<3> positio return indices; } -void World::load_finished_chunks_from_queue() { - auto results = m_queue.done(); - for (auto& [id, res] : results) { - m_registry.get(id) = {id, ChunkRegistry::Status::WaitingForReification, {res.chunk}}; +void World::process_chunk_updates() { + auto generation_results = m_generation_queue.done(); + for (auto& [id, res] : generation_results) { + m_registry.get(id) = {id, ChunkRegistry::Status::NeedsReification, {res.chunk}}; + log_chunk_time(res.generation_duration); } + + auto reification_results = m_reification_queue.done(); + for (auto& [id, res] : reification_results) { + m_registry.get(id) = { + id, ChunkRegistry::Status::Done, + {res.chunk}, + res.chunk_mesh.land_mesh, + res.chunk_mesh.water_mesh + }; + + // TODO: Damage surrounding chunks. + } } void World::request_generation(ChunkIndex index, Real priority) { - m_queue.add(index, priority, [=]() -> GenerationResult { + m_generation_queue.add(index, priority, [=]() -> GenerationResult { auto start = Time::now(); auto chunk = m_generator.generate(index.x, index.y); return {chunk, Time::now() - start}; }); } -void World::try_to_reify_chunk(ChunkRegistry::Data& data) { - auto index = data.index; +void World::request_reification(ChunkIndex index, Real priority) { + auto& data = m_registry.get(index); auto& chunk = data.chunk.value(); - UInt neighbor_index = 0; - std::array<Chunk*, 8> neighbors; - for (I32 x = -1; x <= 1; x++) { - for (I32 y = -1; y <= 1; y++) { - if (x == 0 && y == 0) continue; + auto neighbors = Generation::find_chunk_neighbors(index, m_registry); + auto meshing_context = Generation::ChunkMeshing::create_meshing_context(data.chunk.value(), neighbors); + m_reification_queue.add(index, priority, [=]() mutable -> ReificationResult { + Generation::Lighting intitial_lighting{}; + intitial_lighting.add_chunk(chunk); + intitial_lighting.illuminate(chunk); - auto& neighbor_data = m_registry.get({index.x + x, index.y + y}); - if (!neighbor_data.chunk.has_value()) return; // All neighbors need to be generated first. - - neighbors[neighbor_index++] = &neighbor_data.chunk.value(); - } - } - - // Layout of neighboring chunks in `neighbors` array: - // (-1; -1) > (-1; 0) > (-1; 1) > (0; -1) - // ( 0; 1) > ( 1; -1) > ( 1; 0) > (1; 1) - Generation::ChunkNeighbors chunk_neighbors { - neighbors[3], neighbors[6], neighbors[4], neighbors[1], - neighbors[5], neighbors[7], neighbors[2], neighbors[0], - }; - - // Lighting - m_lighting.add_chunk(chunk); - m_lighting.illuminate(m_registry); - - // Meshing - auto meshes = Generation::ChunkMeshing::mesh_chunk(chunk, chunk_neighbors); + auto meshes = Generation::ChunkMeshing::mesh_chunk(chunk, meshing_context); + return {chunk, meshes}; + }); +} - data.land_mesh_data = meshes.land_mesh; - data.land_mesh = GFX::Binder::load(data.land_mesh_data.value()); +Real World::calculate_priority(ChunkIndex chunk, Position::World player_position, RequestType request_type) { + auto chunk_position = chunk.middle(); + auto flat_distance = Position::World{player_position.x(), chunk_position.y(), player_position.z()}.distance(chunk_position); - data.water_mesh_data = meshes.water_mesh; - data.water_mesh = GFX::Binder::load(data.water_mesh_data.value()); + Real request_type_penalty; + switch (request_type) { + case RequestType::Initial: request_type_penalty = 0; break; + case RequestType::Update: request_type_penalty = 500; break; + } - data.status = ChunkRegistry::Status::Done; + return flat_distance + request_type_penalty; } void World::log_chunk_time(U64 chunk_time_ms) { diff --git a/src/World/World.hpp b/src/World/World.hpp index db9d0db..92367f6 100644 --- a/src/World/World.hpp +++ b/src/World/World.hpp @@ -4,33 +4,41 @@ #include "ChunkIndex.hpp" #include "ChunkRegistry.hpp" #include "../Compute/Queue.hpp" +#include "Generation/ChunkMeshing.hpp" #include "Generation/Lighting.hpp" namespace MC::World { class World { public: - World() : m_queue(8) {} - - std::vector<ChunkRegistry::Data*> get_visible_chunks(Vector<3> position); + std::vector<ChunkRegistry::Data*> get_visible_chunks(Position::World position); Real get_average_chunk_time() const; private: - std::vector<ChunkIndex> get_visible_chunk_indices(Vector<3> position) const; + std::vector<ChunkIndex> get_visible_chunk_indices(Position::World position) const; - void load_finished_chunks_from_queue(); + void process_chunk_updates(); void request_generation(ChunkIndex index, Real priority); - void try_to_reify_chunk(ChunkRegistry::Data& data); + void request_reification(ChunkIndex index, Real priority); + + enum class RequestType { Initial, Update }; + static Real calculate_priority(ChunkIndex chunk, Position::World player_position, RequestType request_type); void log_chunk_time(U64 chunk_time_ms); - U8 m_view_distance_radius = 10; + U8 m_view_distance_radius = 12; struct GenerationResult { Chunk chunk; U64 generation_duration; }; - Compute::Queue<GenerationResult, ChunkIndex> m_queue; + Compute::Queue<GenerationResult, ChunkIndex> m_generation_queue{4}; + struct ReificationResult { + Chunk chunk; + Generation::ChunkMeshing::ChunkMesh chunk_mesh; + }; + Compute::Queue<ReificationResult, ChunkIndex> m_reification_queue{4}; + Generation::Generator m_generator; Generation::Lighting m_lighting; ChunkRegistry m_registry; diff --git a/src/main.cpp b/src/main.cpp index 1c6aa27..815de28 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,6 @@ #include "Common/Sizes.hpp" #include "GFX/Window.hpp" #include "GFX/Camera.hpp" -#include "GFX/Binder.hpp" #include "Math/MVP.hpp" #include "GFX/Shading/Program.hpp" #include "GFX/Texture.hpp" @@ -23,7 +22,7 @@ #define FOV 90 void run(); -void render(const MC::GFX::BindableMesh&, const MC::GFX::Texture&); +void render(MC::GFX::Mesh&, MC::GFX::Texture&); void process_input(MC::GFX::Window&, MC::GFX::Camera&, MC::Time&); void setup_gl(); void fix_macos_render(const MC::GFX::Window&); @@ -135,7 +134,7 @@ void run() { } } -void render(const MC::GFX::BindableMesh& mesh, const MC::GFX::Texture& texture) { +void render(MC::GFX::Mesh& mesh, MC::GFX::Texture& texture) { texture.bind(); mesh.bind(); glDrawElements(GL_TRIANGLES, mesh.size(), GL_UNSIGNED_INT, nullptr); |
