diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 12 | ||||
-rw-r--r-- | src/chunk.cpp | 5 | ||||
-rw-r--r-- | src/chunk.hpp | 60 | ||||
-rw-r--r-- | src/floor-mesh.cpp | 72 | ||||
-rw-r--r-- | src/floor-mesh.hpp | 37 | ||||
-rw-r--r-- | src/loader.cpp | 6 | ||||
-rw-r--r-- | src/loader.hpp | 32 | ||||
-rw-r--r-- | src/tile-atlas.cpp | 39 | ||||
-rw-r--r-- | src/tile-atlas.hpp | 74 | ||||
-rw-r--r-- | src/tile.cpp | 11 | ||||
-rw-r--r-- | src/tile.hpp | 40 | ||||
-rw-r--r-- | src/wall-mesh.cpp | 82 | ||||
-rw-r--r-- | src/wall-mesh.hpp | 47 |
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 |