summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt12
-rw-r--r--src/chunk.cpp5
-rw-r--r--src/chunk.hpp60
-rw-r--r--src/floor-mesh.cpp72
-rw-r--r--src/floor-mesh.hpp37
-rw-r--r--src/loader.cpp6
-rw-r--r--src/loader.hpp32
-rw-r--r--src/tile-atlas.cpp39
-rw-r--r--src/tile-atlas.hpp74
-rw-r--r--src/tile.cpp11
-rw-r--r--src/tile.hpp40
-rw-r--r--src/wall-mesh.cpp82
-rw-r--r--src/wall-mesh.hpp47
13 files changed, 517 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..e443a9df
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(self ${PROJECT_NAME})
+file(GLOB sources "*.cpp" "../shaders/*.cpp" "../serialize/*.cpp" CONFIGURE_ARGS)
+add_library(${self} STATIC "${sources}")
+target_link_libraries(
+ ${self} PUBLIC
+ Magnum::GL
+ Magnum::Magnum
+ Magnum::Shaders
+ nlohmann_json::nlohmann_json
+)
+target_include_directories(${self} PRIVATE ${GLM_INCLUDE_DIRS})
+target_include_directories(${self} PRIVATE magnum-integration/src)
diff --git a/src/chunk.cpp b/src/chunk.cpp
new file mode 100644
index 00000000..221f8132
--- /dev/null
+++ b/src/chunk.cpp
@@ -0,0 +1,5 @@
+#include "chunk.hpp"
+
+namespace Magnum::Examples {
+
+} // namespace Magnum::Examples
diff --git a/src/chunk.hpp b/src/chunk.hpp
new file mode 100644
index 00000000..829ee0d3
--- /dev/null
+++ b/src/chunk.hpp
@@ -0,0 +1,60 @@
+#pragma once
+#include "tile.hpp"
+#include <type_traits>
+#include <array>
+
+namespace Magnum::Examples {
+
+struct chunk final
+{
+ constexpr tile& operator[](local_coords xy) { return tiles[xy.to_index()]; }
+ constexpr const tile& operator[](local_coords xy) const { return tiles[xy.to_index()]; }
+ constexpr tile& operator[](std::size_t i) { return tiles[i]; }
+ constexpr const tile& operator[](std::size_t i) const { return tiles[i]; }
+
+ template<typename F>
+ requires std::invocable<F, tile&, std::size_t, local_coords>
+ constexpr inline void foreach_tile(F&& fun) { foreach_tile_<F, chunk&>(std::forward<F>(fun)); }
+
+ template<typename F>
+ requires std::invocable<F, const tile&, std::size_t, local_coords>
+ constexpr inline void foreach_tile(F&& fun) const { foreach_tile_<F, const chunk&>(std::forward<F>(fun)); }
+
+private:
+ template<typename F, typename Self>
+ constexpr void foreach_tile_(F&& fun);
+
+ std::array<tile, TILE_COUNT> tiles = {};
+};
+
+template<typename F, typename Self>
+constexpr void chunk::foreach_tile_(F&& fun)
+{
+ constexpr auto N = TILE_MAX_DIM;
+ std::size_t k = 0;
+ for (std::size_t j = 0; j < N; j++)
+ for (std::size_t i = 0; i < N; i++, k++)
+ fun(const_cast<Self>(*this).tiles[k], k,
+ local_coords{(std::uint8_t)i, (std::uint8_t)j});
+}
+
+#if 0
+struct chunk_coords final {
+ std::int16_t x = 0, y = 0;
+ constexpr std::size_t to_index() const noexcept;
+
+ static constexpr std::size_t max_bits = sizeof(chunk_coords::x)*8 * 3 / 4;
+ static_assert(max_bits*4/3/8 == sizeof(decltype(chunk_coords::x)));
+};
+#endif
+
+#if 0
+struct global_coords final {
+ std::uint32_t x = 0, y = 0;
+ constexpr global_coords() noexcept = default;
+ constexpr global_coords(decltype(x) x, decltype(y) y) noexcept : x{x}, y{y} {}
+ constexpr global_coords(chunk_coords c, local_coords tile) noexcept;
+};
+#endif
+
+} // namespace Magnum::Examples
diff --git a/src/floor-mesh.cpp b/src/floor-mesh.cpp
new file mode 100644
index 00000000..7156661e
--- /dev/null
+++ b/src/floor-mesh.cpp
@@ -0,0 +1,72 @@
+#include "floor-mesh.hpp"
+#include "shaders/tile-shader.hpp"
+#include "tile.hpp"
+#include "chunk.hpp"
+#include "tile-atlas.hpp"
+#include <Magnum/GL/MeshView.h>
+
+namespace Magnum::Examples {
+
+constexpr auto quad_index_count = 6;
+
+floor_mesh::floor_mesh()
+{
+ _mesh.setCount((int)(quad_index_count * TILE_COUNT))
+ .addVertexBuffer(_positions_buffer, 0, tile_shader::Position{})
+ .addVertexBuffer(_vertex_buffer, 0, tile_shader::TextureCoordinates{})
+ .setIndexBuffer(_index_buffer, 0, GL::MeshIndexType::UnsignedShort);
+}
+
+void floor_mesh::set_tile(quad_data& data, tile& x)
+{
+ CORRADE_INTERNAL_ASSERT(x.ground_image);
+
+ auto texcoords = x.ground_image.atlas->texcoords_for_id(x.ground_image.variant);
+ for (size_t i = 0; i < 4; i++)
+ data[i] = { texcoords[i] };
+}
+
+void floor_mesh::draw(tile_shader& shader, chunk& c)
+{
+ std::array<quad_data, TILE_COUNT> data;
+ c.foreach_tile([&](tile& x, std::size_t idx, local_coords) {
+ set_tile(data[idx], x);
+ });
+ _vertex_buffer.setSubData(0, data);
+ Magnum::GL::MeshView mesh{_mesh};
+ mesh.setCount(quad_index_count);
+ const tile_atlas* last_tile_atlas = nullptr;
+ c.foreach_tile([&](tile& x, std::size_t i, local_coords) {
+ mesh.setIndexRange((int)(i*quad_index_count), 0, quad_index_count*TILE_COUNT - 1);
+ if (auto* atlas = x.ground_image.atlas.get(); atlas != last_tile_atlas)
+ {
+ atlas->texture().bind(0);
+ last_tile_atlas = atlas;
+ }
+ shader.draw(mesh);
+ });
+}
+
+std::array<std::array<UnsignedShort, 6>, TILE_COUNT> floor_mesh::make_index_array()
+{
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+ std::array<std::array<UnsignedShort, quad_index_count>, TILE_COUNT> array;
+ for (std::size_t i = 0; i < std::size(array); i++)
+ array[i] = tile_atlas::indices(i);
+ return array;
+}
+
+std::array<std::array<Vector3, 4>, TILE_COUNT> floor_mesh::make_position_array()
+{
+ std::array<std::array<Vector3, 4>, TILE_COUNT> array;
+ constexpr float X = TILE_SIZE[0], Y = TILE_SIZE[1];
+ for (std::size_t j = 0, k = 0; j < TILE_MAX_DIM; j++)
+ for (std::size_t i = 0; i < TILE_MAX_DIM; i++, k++)
+ {
+ Vector3 center {(float)(X*i), (float)(Y*j), 0};
+ array[k] = { tile_atlas::floor_quad(center, {X, Y}) };
+ }
+ return array;
+}
+
+} // namespace Magnum::Examples
diff --git a/src/floor-mesh.hpp b/src/floor-mesh.hpp
new file mode 100644
index 00000000..41351fd9
--- /dev/null
+++ b/src/floor-mesh.hpp
@@ -0,0 +1,37 @@
+#pragma once
+#include "tile.hpp"
+#include <array>
+#include <Corrade/Containers/ArrayViewStl.h>
+#include <Magnum/Magnum.h>
+#include <Magnum/Math/Vector2.h>
+#include <Magnum/GL/Mesh.h>
+#include <Magnum/GL/Buffer.h>
+
+namespace Magnum::Examples {
+
+struct tile_shader;
+struct chunk;
+
+struct floor_mesh final
+{
+ floor_mesh();
+ floor_mesh(floor_mesh&&) = delete;
+ floor_mesh(const floor_mesh&) = delete;
+
+ void draw(tile_shader& shader, chunk& c);
+
+private:
+ struct vertex_data final { Vector2 texcoords; };
+ using quad_data = std::array<vertex_data, 4>;
+
+ static std::array<std::array<UnsignedShort, 6>, TILE_COUNT> make_index_array();
+ static std::array<std::array<Vector3, 4>, TILE_COUNT> make_position_array();
+
+ GL::Mesh _mesh;
+ GL::Buffer _vertex_buffer{std::array<quad_data, TILE_COUNT>{}, Magnum::GL::BufferUsage::DynamicDraw},
+ _index_buffer{make_index_array()}, _positions_buffer{make_position_array()};
+
+ static void set_tile(quad_data& data, tile& x);
+};
+
+} // namespace Magnum::Examples
diff --git a/src/loader.cpp b/src/loader.cpp
new file mode 100644
index 00000000..f6219350
--- /dev/null
+++ b/src/loader.cpp
@@ -0,0 +1,6 @@
+#include "loader.hpp"
+
+namespace Magnum::Examples {
+loader_::loader_() = default;
+loader_::~loader_() = default;
+} // namespace Magnum::Examples
diff --git a/src/loader.hpp b/src/loader.hpp
new file mode 100644
index 00000000..49299bb6
--- /dev/null
+++ b/src/loader.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <Corrade/Containers/StringView.h>
+#include <Magnum/Trade/ImageData.h>
+
+#include <string>
+#include <optional>
+#include <memory>
+
+namespace Magnum::Examples {
+
+struct tile_atlas;
+
+struct loader_
+{
+ virtual std::string shader(Containers::StringView filename) = 0;
+ virtual Trade::ImageData2D tile_texture(Containers::StringView filename) = 0;
+ virtual std::shared_ptr<struct tile_atlas> tile_atlas(Containers::StringView filename, Vector2ui size) = 0;
+ static void destroy();
+
+ loader_(const loader_&) = delete;
+ loader_& operator=(const loader_&) = delete;
+
+ virtual ~loader_();
+
+protected:
+ loader_();
+};
+
+extern loader_& loader; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
+
+} // namespace Magnum::Examples
diff --git a/src/tile-atlas.cpp b/src/tile-atlas.cpp
new file mode 100644
index 00000000..d5d56cce
--- /dev/null
+++ b/src/tile-atlas.cpp
@@ -0,0 +1,39 @@
+#include "tile-atlas.hpp"
+#include <Corrade/Containers/StringView.h>
+#include <Magnum/ImageView.h>
+#include <Magnum/GL/TextureFormat.h>
+
+namespace Magnum::Examples {
+
+tile_atlas::tile_atlas(Containers::StringView name, const ImageView2D& image, Vector2ui dims) :
+ name_{name},
+ size_{image.size()},
+ dims_{dims}
+{
+ CORRADE_INTERNAL_ASSERT(dims_[0] > 0 && dims_[1] > 0);
+ CORRADE_INTERNAL_ASSERT(size_ % dims_ == Vector2ui{});
+ CORRADE_INTERNAL_ASSERT(dims_.product() < 256);
+ tex_.setWrapping(GL::SamplerWrapping::ClampToBorder)
+ .setMagnificationFilter(GL::SamplerFilter::Linear)
+ .setMinificationFilter(GL::SamplerFilter::Linear)
+ .setMaxAnisotropy(0)
+ .setStorage(GL::textureFormat(image.format()), image.size())
+ .setSubImage({}, image);
+}
+
+std::array<Vector2, 4> tile_atlas::texcoords_for_id(std::size_t id_) const
+{
+ const auto sz = size_/dims_;
+ ASSERT(id_ < sz.product());
+ const Vector2ui id = { (unsigned)id_ % dims_[0], (unsigned)id_ / dims_[0] };
+ const Vector2 p0(id * sz), p1(sz);
+ const auto x0 = p0.x(), x1 = p1.x(), y0 = p0.y(), y1 = p1.y();
+ return {{
+ { x0+x1, y0+y1 }, // bottom right
+ { x0+x1, y0 }, // top right
+ { x0, y0+y1 }, // bottom left
+ { x0, y0 } // top left
+ }};
+}
+
+} // namespace Magnum::Examples
diff --git a/src/tile-atlas.hpp b/src/tile-atlas.hpp
new file mode 100644
index 00000000..b32a70d8
--- /dev/null
+++ b/src/tile-atlas.hpp
@@ -0,0 +1,74 @@
+#pragma once
+#include "compat/assert.hpp"
+#include <Magnum/Magnum.h>
+#include <Magnum/GL/RectangleTexture.h>
+#include <array>
+#include <string>
+
+namespace Magnum::Examples {
+
+struct tile_atlas final
+{
+ using quad = std::array<Vector3, 4>;
+
+ tile_atlas(Containers::StringView name, const ImageView2D& img, Vector2ui dimensions);
+
+ std::array<Vector2, 4> texcoords_for_id(std::size_t id) const;
+ static constexpr quad floor_quad(Vector3 center, Vector2 size);
+ static constexpr quad wall_quad_N(Vector3 center, Vector3 size);
+ static constexpr quad wall_quad_W(Vector3 center, Vector3 size);
+ static constexpr std::array<UnsignedShort, 6> indices(std::size_t N);
+ Vector2ui pixel_size() const { return size_; }
+ Vector2ui num_tiles() const { return dims_; }
+ GL::RectangleTexture& texture() { return tex_; }
+ Containers::StringView name() const { return name_; }
+
+private:
+ GL::RectangleTexture tex_;
+ std::string name_;
+ Vector2ui size_, dims_;
+};
+
+constexpr std::array<UnsignedShort, 6> tile_atlas::indices(std::size_t N)
+{
+ using u16 = UnsignedShort;
+ return { /* 3--1 1 */
+ (u16)(0+N*4), (u16)(1+N*4), (u16)(2+N*4), /* | / /| */
+ (u16)(2+N*4), (u16)(1+N*4), (u16)(3+N*4), /* |/ / | */
+ }; /* 2 2--0 */
+}
+
+constexpr tile_atlas::quad tile_atlas::floor_quad(const Vector3 center, const Vector2 size)
+{
+ float x = size[0]*.5f, y = size[1]*.5f;
+ return {{
+ { x + center[0], -y + center[1], center[2]},
+ { x + center[0], y + center[1], center[2]},
+ {-x + center[0], -y + center[1], center[2]},
+ {-x + center[0], y + center[1], center[2]},
+ }};
+}
+
+constexpr tile_atlas::quad tile_atlas::wall_quad_W(const Vector3 center, const Vector3 size)
+{
+ float x = size[0]*.5f, y = size[1]*.5f, z = size[2];
+ return {{
+ {-x + center[0], y + center[1], center[2] },
+ {-x + center[0], y + center[1], z + center[2] },
+ {-x + center[0], -y + center[1], center[2] },
+ {-x + center[0], -y + center[1], z + center[2] },
+ }};
+}
+
+constexpr tile_atlas::quad tile_atlas::wall_quad_N(const Vector3 center, const Vector3 size)
+{
+ float x = size[0]*.5f, y = size[1]*.5f, z = size[2];
+ return {{
+ { x + center[0], -y + center[1], center[2] },
+ { x + center[0], -y + center[1], z + center[2] },
+ {-x + center[0], -y + center[1], center[2] },
+ {-x + center[0], -y + center[1], z + center[2] },
+ }};
+}
+
+} // namespace Magnum::Examples
diff --git a/src/tile.cpp b/src/tile.cpp
new file mode 100644
index 00000000..41d11067
--- /dev/null
+++ b/src/tile.cpp
@@ -0,0 +1,11 @@
+#include "tile.hpp"
+#include "tile-atlas.hpp"
+
+namespace Magnum::Examples {
+
+local_coords::local_coords(std::size_t x, std::size_t y) : x{(std::uint8_t)x}, y{(std::uint8_t)y}
+{
+ CORRADE_INTERNAL_ASSERT(x <= 0xff && y <= 0xff);
+}
+
+} // namespace Magnum::Examples
diff --git a/src/tile.hpp b/src/tile.hpp
new file mode 100644
index 00000000..261c50ee
--- /dev/null
+++ b/src/tile.hpp
@@ -0,0 +1,40 @@
+#pragma once
+#include <Magnum/Magnum.h>
+#include <Magnum/Math/Vector3.h>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+
+namespace Magnum::Examples {
+
+struct tile_atlas;
+constexpr inline Vector3 TILE_SIZE = { 64, 64, 64 };
+constexpr inline std::size_t TILE_MAX_DIM = 16;
+constexpr inline std::size_t TILE_COUNT = TILE_MAX_DIM*TILE_MAX_DIM;
+
+struct tile_image final
+{
+ std::shared_ptr<tile_atlas> atlas;
+ std::uint8_t variant = 0xff;
+
+ explicit operator bool() const noexcept { return !!atlas; }
+};
+
+struct tile final
+{
+ enum class pass_mode : std::uint8_t { pass_blocked, pass_ok, pass_shoot_through, };
+ using enum pass_mode;
+
+ tile_image ground_image, wall_west, wall_north;
+ pass_mode passability = pass_shoot_through;
+};
+
+struct local_coords final {
+ std::uint8_t x = 0, y = 0;
+ constexpr local_coords() = default;
+ local_coords(std::size_t x, std::size_t y);
+ constexpr local_coords(std::uint8_t x, std::uint8_t y) : x{x}, y{y} {}
+ constexpr std::size_t to_index() const { return y*TILE_MAX_DIM + x; }
+};
+
+} //namespace Magnum::Examples
diff --git a/src/wall-mesh.cpp b/src/wall-mesh.cpp
new file mode 100644
index 00000000..ee998e63
--- /dev/null
+++ b/src/wall-mesh.cpp
@@ -0,0 +1,82 @@
+#include "wall-mesh.hpp"
+#include "tile-atlas.hpp"
+#include "shaders/tile-shader.hpp"
+#include "chunk.hpp"
+#include <Magnum/GL/RectangleTexture.h>
+#include <Magnum/GL/MeshView.h>
+
+namespace Magnum::Examples {
+
+constexpr auto quad_index_count = 6;
+
+wall_mesh::wall_mesh()
+{
+ _mesh.setCount((int)(quad_index_count * COUNT))
+ .addVertexBuffer(_vertex_buffer, 0, tile_shader::TextureCoordinates{}, tile_shader::Position{})
+ .setIndexBuffer(_index_buffer, 0, GL::MeshIndexType::UnsignedShort);
+ CORRADE_INTERNAL_ASSERT(_mesh.isIndexed());
+}
+
+void wall_mesh::add_wall(vertex_array& data, texture_array& textures, std::size_t& pos_,
+ tile_image& img, const position_array& positions)
+{
+ const auto pos = pos_++;
+ CORRADE_INTERNAL_ASSERT(pos < data.size());
+ auto texcoords = img.atlas->texcoords_for_id(img.variant);
+ for (std::size_t i = 0; i < 4; i++)
+ {
+ data[pos][i] = { texcoords[i], positions[i] };
+ textures[pos] = &img.atlas->texture();
+ }
+}
+
+void wall_mesh::maybe_add_tile(vertex_array& data, texture_array& textures, std::size_t& pos, tile& x, local_coords pt)
+{
+ constexpr float X = TILE_SIZE[0], Y = TILE_SIZE[1], Z = TILE_SIZE[2];
+ constexpr Vector3 size = {X, Y, Z};
+ Vector3 center{(float)(X*pt.x), (float)(Y*pt.y), 0};
+
+ if (auto& wall = x.wall_north; wall.atlas)
+ add_wall(data, textures, pos, wall, tile_atlas::wall_quad_N(center, size));
+ if (auto& wall = x.wall_west; wall.atlas)
+ add_wall(data, textures, pos, wall, tile_atlas::wall_quad_W(center, size));
+}
+
+void wall_mesh::draw(tile_shader& shader, chunk& c)
+{
+ texture_array textures = {};
+ std::size_t pos = 0;
+ {
+ vertex_array data;
+ c.foreach_tile([&](tile& x, std::size_t, local_coords pt) {
+ maybe_add_tile(data, textures, pos, x, pt);
+ });
+ _vertex_buffer.setSubData(0, Containers::arrayView(data.data(), pos));
+ }
+
+ const GL::RectangleTexture* last_texture = nullptr;
+ Magnum::GL::MeshView mesh{_mesh};
+ for (std::size_t i = 0; i < pos; i++)
+ {
+ auto* const tex = textures[i];
+ CORRADE_INTERNAL_ASSERT(tex != nullptr);
+ mesh.setCount(quad_index_count);
+ mesh.setIndexRange((int)(i*quad_index_count), 0, quad_index_count*COUNT - 1);
+ if (tex != last_texture)
+ tex->bind(0);
+ last_texture = tex;
+ shader.draw(mesh);
+ }
+}
+
+std::array<std::array<UnsignedShort, 6>, wall_mesh::COUNT> wall_mesh::make_index_array()
+{
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+ std::array<std::array<UnsignedShort, 6>, COUNT> array;
+
+ for (std::size_t i = 0; i < std::size(array); i++)
+ array[i] = tile_atlas::indices(i);
+ return array;
+}
+
+} // namespace Magnum::Examples
diff --git a/src/wall-mesh.hpp b/src/wall-mesh.hpp
new file mode 100644
index 00000000..ce1ff30a
--- /dev/null
+++ b/src/wall-mesh.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "tile.hpp"
+#include <array>
+#include <Corrade/Containers/ArrayViewStl.h>
+#include <Magnum/Magnum.h>
+#include <Magnum/Math/Vector2.h>
+#include <Magnum/GL/Mesh.h>
+#include <Magnum/GL/Buffer.h>
+
+namespace Magnum::Examples {
+
+struct tile_shader;
+struct chunk;
+
+struct wall_mesh final
+{
+ wall_mesh();
+ void draw(tile_shader& shader, chunk& c);
+
+private:
+ static constexpr auto COUNT = TILE_MAX_DIM*2 * TILE_MAX_DIM*2;
+
+ using texcoords_array = std::array<Vector2, 4>;
+ using position_array = std::array<Vector3, 4>;
+
+ struct vertex final {
+ typename texcoords_array::value_type texcoords;
+ typename position_array::value_type position;
+ };
+
+ using quad = std::array<vertex, 4>;
+ using vertex_array = std::array<quad, COUNT>;
+ using texture_array = std::array<GL::RectangleTexture*, COUNT>;
+
+ static void maybe_add_tile(vertex_array& data, texture_array& textures, std::size_t& pos,
+ tile& x, local_coords pt);
+ static void add_wall(vertex_array& data, texture_array& textures, std::size_t& pos,
+ tile_image& img, const position_array& positions);
+
+ GL::Mesh _mesh;
+ GL::Buffer _vertex_buffer{vertex_array{}, Magnum::GL::BufferUsage::DynamicDraw},
+ _index_buffer{make_index_array(), Magnum::GL::BufferUsage::StaticDraw};
+ static std::array<std::array<UnsignedShort, 6>, COUNT> make_index_array();
+};
+
+} // namespace Magnum::Examples