summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-07-29 03:31:42 +0200
committerMel <einebeere@gmail.com>2023-07-29 03:31:42 +0200
commit6b69d4b5b648253f894707723af0e2eae9f71445 (patch)
tree0cd0b6c7b18c30abbb2618f553f144d1d06dacba
parent2eef7cf49b7a15559ee7bb6719411bcf67386213 (diff)
downloadmeowcraft-6b69d4b5b648253f894707723af0e2eae9f71445.tar.zst
meowcraft-6b69d4b5b648253f894707723af0e2eae9f71445.zip
Move chunk reification to worker threads and set stage for chunk-unbound lighting
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/Compute/Queue.hpp5
-rw-r--r--src/GFX/Binder.cpp84
-rw-r--r--src/GFX/Binder.hpp48
-rw-r--r--src/GFX/Mesh.cpp75
-rw-r--r--src/GFX/Mesh.hpp18
-rw-r--r--src/Util/ImageViewer.cpp4
-rw-r--r--src/Util/ImageViewer.hpp7
-rw-r--r--src/World/Chunk.cpp8
-rw-r--r--src/World/Chunk.hpp9
-rw-r--r--src/World/ChunkRegistry.hpp17
-rw-r--r--src/World/Clouds.cpp6
-rw-r--r--src/World/Clouds.hpp8
-rw-r--r--src/World/Generation/ChunkMeshing.cpp88
-rw-r--r--src/World/Generation/ChunkMeshing.hpp42
-rw-r--r--src/World/Generation/ChunkNeighbors.cpp26
-rw-r--r--src/World/Generation/ChunkNeighbors.hpp13
-rw-r--r--src/World/Generation/Lighting.cpp9
-rw-r--r--src/World/Generation/Lighting.hpp4
-rw-r--r--src/World/Position.hpp10
-rw-r--r--src/World/World.cpp103
-rw-r--r--src/World/World.hpp24
-rw-r--r--src/main.cpp5
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);