summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2022-09-30 18:48:50 +0200
committerStanislaw Halik <sthalik@misaki.pl>2022-09-30 18:50:32 +0200
commit5c9863cf0998b5f1b6107ce09b54cd3e8b484221 (patch)
tree7ca268c80a0ec7ac0c1b815d984cf11309b96840
parentd3a29055d8b1dce89c77af0988ea840e949d2450 (diff)
.
-rw-r--r--CMakeLists.txt2
-rw-r--r--anim/serialize.cpp83
-rw-r--r--anim/serialize.hpp2
-rw-r--r--crop-tool/CMakeLists.txt2
-rw-r--r--crop-tool/atlas.cpp (renamed from anim/atlas.cpp)2
-rw-r--r--crop-tool/atlas.hpp (renamed from anim/atlas.hpp)0
-rw-r--r--crop-tool/crop-tool.cpp2
-rw-r--r--fake-json.hpp31
-rw-r--r--json-magnum.hpp100
-rw-r--r--loader-impl.cpp10
-rw-r--r--loader.hpp4
-rw-r--r--main.cpp10
-rw-r--r--shaders/tile-shader.frag1
-rw-r--r--tile-atlas.cpp (renamed from texture-atlas.cpp)20
-rw-r--r--tile-atlas.hpp (renamed from texture-atlas.hpp)8
-rw-r--r--tile-tool/CMakeLists.txt10
-rw-r--r--tile-tool/big-atlas.cpp67
-rw-r--r--tile-tool/big-atlas.hpp33
-rw-r--r--tile-tool/tile-tool.cpp34
-rw-r--r--tile.cpp3
-rw-r--r--tile.hpp6
-rw-r--r--tile/CMakeLists.txt12
-rw-r--r--tile/serialize.cpp17
-rw-r--r--tile/serialize.hpp23
24 files changed, 335 insertions, 147 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9d5a8477..020b81af 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,7 +57,9 @@ if(NOT BOOTSTRAP_DEPENDS)
include_directories(SYSTEM "./include")
add_subdirectory(crop-tool)
+ add_subdirectory(tile-tool)
add_subdirectory(anim)
+ add_subdirectory(tile)
corrade_add_resource(game_RESOURCES resources.conf)
file(GLOB sources "*.cpp" CONFIGURE_ARGS)
diff --git a/anim/serialize.cpp b/anim/serialize.cpp
index b0082c3c..83d2b2f9 100644
--- a/anim/serialize.cpp
+++ b/anim/serialize.cpp
@@ -1,50 +1,12 @@
#include "serialize.hpp"
-#include <algorithm>
-#include <utility>
-#include <fstream>
-#include <exception>
-
#include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/DebugStl.h>
-#include "../fake-json.hpp"
+#include "../json-magnum.hpp"
using Corrade::Utility::Error;
-namespace nlohmann {
-
-template<>
-struct adl_serializer<Magnum::Vector2i> final {
- static void to_json(json& j, const Magnum::Vector2i& x);
- static void from_json(const json& j, Magnum::Vector2i& x);
-};
-
-void adl_serializer<Magnum::Vector2i>::to_json(json& j, const Magnum::Vector2i& val)
-{
- char buf[64];
- snprintf(buf, sizeof(buf), "%d x %d", val[0], val[1]);
- j = buf;
-}
-
-void adl_serializer<Magnum::Vector2i>::from_json(const json& j, Magnum::Vector2i& val)
-{
- std::string str = j;
- int x = 0, y = 0, n = 0;
- int ret = std::sscanf(str.c_str(), "%d x %d%n", &x, &y, &n);
- if (ret != 2 || (std::size_t)n != str.size())
- {
- std::string msg; msg.reserve(64 + str.size());
- msg += "failed to parse string '";
- msg += str;
- msg += "' as Magnum::Vector2i";
- throw std::invalid_argument(msg);
- }
- val = { x, y };
-}
-
-} // namespace nlohmann
-
#if defined __clang__ || defined __CLION_IDE__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wweak-vtables"
@@ -61,47 +23,10 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim, name, nframes, actionframe, fps, groups
std::tuple<anim, bool> anim::from_json(const std::filesystem::path& pathname) noexcept
{
- using namespace nlohmann;
- std::ifstream s;
- s.exceptions(s.exceptions() | std::ios::failbit | std::ios::badbit);
- try {
- s.open(pathname, std::ios_base::in);
- } catch (const std::ios::failure& e) {
- Error{Error::Flag::NoSpace} << "failed to open " << pathname << ": " << e.what();
- return { {}, false };
- }
- anim ret;
- try {
- json j;
- s >> j;
- using nlohmann::from_json;
- from_json(j, ret);
- } catch (const std::exception& e) {
- Error{Error::Flag::NoSpace} << "failed to parse " << pathname << ": " << e.what();
- return { {}, false };
- }
- return { std::move(ret), true };
+ return json_helper<anim>::from_json(pathname);
}
-bool anim::to_json(const std::filesystem::path& pathname) noexcept
+bool anim::to_json(const std::filesystem::path& pathname) const noexcept
{
- try {
- nlohmann::json j = *this;
-
- std::ofstream s;
- s.exceptions(s.exceptions() | std::ios::failbit | std::ios::badbit);
- try {
- s.open(pathname, std::ios_base::out | std::ios_base::trunc);
- } catch (const std::ios::failure& e) {
- Error{} << "failed to open" << pathname << "for writing:" << e.what();
- return false;
- }
- s << j.dump(4);
- s.flush();
- } catch (const std::exception& e) {
- Error{Error::Flag::NoSpace} << "failed writing to " << pathname << ": " << e.what();
- return false;
- }
-
- return true;
+ return json_helper<anim>::to_json(*this, pathname);
}
diff --git a/anim/serialize.hpp b/anim/serialize.hpp
index a8b13d41..49641fb5 100644
--- a/anim/serialize.hpp
+++ b/anim/serialize.hpp
@@ -32,7 +32,7 @@ struct anim_group final
struct anim final
{
static std::tuple<anim, bool> from_json(const std::filesystem::path& pathname) noexcept;
- [[nodiscard]] bool to_json(const std::filesystem::path& pathname) noexcept;
+ [[nodiscard]] bool to_json(const std::filesystem::path& pathname) const noexcept;
static constexpr int default_fps = 24;
std::string name;
diff --git a/crop-tool/CMakeLists.txt b/crop-tool/CMakeLists.txt
index 42aeac2c..251bcac3 100644
--- a/crop-tool/CMakeLists.txt
+++ b/crop-tool/CMakeLists.txt
@@ -3,7 +3,7 @@ set(self "${PROJECT_NAME}-crop-tool")
include_directories(SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS})
link_libraries(Corrade::Utility)
-link_libraries(game-anim)
+link_libraries(${PROJECT_NAME}-anim)
file(GLOB sources "*.cpp" CONFIGURE_ARGS)
add_executable(${self} ${sources})
diff --git a/anim/atlas.cpp b/crop-tool/atlas.cpp
index fe4b5f65..9e16a3cd 100644
--- a/anim/atlas.cpp
+++ b/crop-tool/atlas.cpp
@@ -1,7 +1,7 @@
#undef NDEBUG
#include "atlas.hpp"
-#include "serialize.hpp"
+#include "../anim/serialize.hpp"
#include <cassert>
#include <filesystem>
diff --git a/anim/atlas.hpp b/crop-tool/atlas.hpp
index 5c5e918f..5c5e918f 100644
--- a/anim/atlas.hpp
+++ b/crop-tool/atlas.hpp
diff --git a/crop-tool/crop-tool.cpp b/crop-tool/crop-tool.cpp
index 064f004d..42d54baa 100644
--- a/crop-tool/crop-tool.cpp
+++ b/crop-tool/crop-tool.cpp
@@ -1,7 +1,7 @@
#undef NDEBUG
#include "defs.hpp"
-#include "anim/atlas.hpp"
+#include "atlas.hpp"
#include "anim/serialize.hpp"
#include <cassert>
diff --git a/fake-json.hpp b/fake-json.hpp
deleted file mode 100644
index 57943d37..00000000
--- a/fake-json.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-//#define GAME_REAL_JSON
-#if !defined __CLION_IDE__ || defined GAME_REAL_JSON
-# include <nlohmann/json.hpp>
-#else
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunknown-pragmas"
-#pragma ide diagnostic ignored "NotImplementedFunctions"
-
-namespace nlohmann {
- struct json {
- template<typename t> operator t() const;
- const json& dump(int) const;
- template<typename t> json(const t&);
- json();
- };
- template<typename t> json& operator>>(const t&, json&);
- template<typename t> json& operator<<(t&, const json&);
- template<typename t> struct adl_serializer {
- static void to_json(json&, const t&);
- static void from_json(const json&, t&);
- };
- template<typename t> void from_json(const json&, t&);
- template<typename t> void to_json(const json&, const t&);
-} // namespace nlohmann
-
-#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(...)
-
-#pragma clang diagnostic pop
-#endif
diff --git a/json-magnum.hpp b/json-magnum.hpp
new file mode 100644
index 00000000..bcd17e0a
--- /dev/null
+++ b/json-magnum.hpp
@@ -0,0 +1,100 @@
+#pragma once
+
+#include <cstdio>
+#include <string>
+#include <utility>
+#include <fstream>
+#include <exception>
+#include <nlohmann/json.hpp>
+#include <Corrade/Utility/Debug.h>
+#include <Corrade/Utility/DebugStl.h>
+#include <Magnum/Magnum.h>
+#include <Magnum/Math/Vector2.h>
+
+namespace nlohmann {
+
+template<>
+struct adl_serializer<Magnum::Vector2i> final {
+ static void to_json(json& j, const Magnum::Vector2i& x);
+ static void from_json(const json& j, Magnum::Vector2i& x);
+};
+
+void adl_serializer<Magnum::Vector2i>::to_json(json& j, const Magnum::Vector2i& val)
+{
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%d x %d", val[0], val[1]);
+ j = buf;
+}
+
+void adl_serializer<Magnum::Vector2i>::from_json(const json& j, Magnum::Vector2i& val)
+{
+ std::string str = j;
+ int x = 0, y = 0, n = 0;
+ int ret = std::sscanf(str.c_str(), "%d x %d%n", &x, &y, &n);
+ if (ret != 2 || (std::size_t)n != str.size())
+ {
+ std::string msg; msg.reserve(64 + str.size());
+ msg += "failed to parse string '";
+ msg += str;
+ msg += "' as Magnum::Vector2i";
+ throw std::invalid_argument(msg);
+ }
+ val = { x, y };
+}
+
+} // namespace nlohmann
+
+template<typename t>
+struct json_helper final {
+ [[nodiscard]] static std::tuple<t, bool> from_json(const std::filesystem::path& pathname) noexcept;
+ [[nodiscard]] static bool to_json(const t& self, const std::filesystem::path& pathname) noexcept;
+};
+
+template<typename t>
+std::tuple<t, bool> json_helper<t>::from_json(const std::filesystem::path& pathname) noexcept {
+ using namespace nlohmann;
+ using Corrade::Utility::Error;
+ std::ifstream s;
+ s.exceptions(s.exceptions() | std::ios::failbit | std::ios::badbit);
+ try {
+ s.open(pathname, std::ios_base::in);
+ } catch (const std::ios::failure& e) {
+ Error{Error::Flag::NoSpace} << "failed to open " << pathname << ": " << e.what();
+ return { {}, false };
+ }
+ t ret;
+ try {
+ json j;
+ s >> j;
+ using nlohmann::from_json;
+ from_json(j, ret);
+ } catch (const std::exception& e) {
+ Error{Error::Flag::NoSpace} << "failed to parse " << pathname << ": " << e.what();
+ return { {}, false };
+ }
+ return { std::move(ret), true };
+}
+
+template<typename t>
+bool json_helper<t>::to_json(const t& self, const std::filesystem::path& pathname) noexcept {
+ using Corrade::Utility::Error;
+ try {
+ nlohmann::json j = self;
+
+ std::ofstream s;
+ s.exceptions(s.exceptions() | std::ios::failbit | std::ios::badbit);
+ try {
+ s.open(pathname, std::ios_base::out | std::ios_base::trunc);
+ } catch (const std::ios::failure& e) {
+ Error{} << "failed to open" << pathname << "for writing:" << e.what();
+ return false;
+ }
+ s << j.dump(4);
+ s.flush();
+ } catch (const std::exception& e) {
+ Error{Error::Flag::NoSpace} << "failed writing to " << pathname << ": " << e.what();
+ return false;
+ }
+
+ return true;
+}
diff --git a/loader-impl.cpp b/loader-impl.cpp
index 2008dbae..b761dca4 100644
--- a/loader-impl.cpp
+++ b/loader-impl.cpp
@@ -1,6 +1,6 @@
#include "defs.hpp"
#include "loader.hpp"
-#include "texture-atlas.hpp"
+#include "tile-atlas.hpp"
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/PluginManager/PluginManager.h>
@@ -14,18 +14,18 @@
namespace Magnum::Examples {
-using atlas_ptr = std::shared_ptr<texture_atlas>;
+using atlas_ptr = std::shared_ptr<tile_atlas>;
struct loader_impl final : loader_
{
const Utility::Resource shader_res{"game/shaders"};
PluginManager::Manager<Trade::AbstractImporter> importer_plugins;
Containers::Pointer<Trade::AbstractImporter> tga_importer =
- importer_plugins.loadAndInstantiate("TgaImporter");
+ importer_plugins.loadAndInstantiate("AnyImageImporter");
PluginManager::Manager<Trade::AbstractImageConverter> image_converter_plugins;
Containers::Pointer<Trade::AbstractImageConverter> tga_converter =
- image_converter_plugins.loadAndInstantiate("TgaImageConverter");
+ image_converter_plugins.loadAndInstantiate("AnyImageConverter");
std::unordered_map<std::string, atlas_ptr> atlas_map;
@@ -51,7 +51,7 @@ atlas_ptr loader_impl::tile_atlas(const Containers::StringView& name, Vector2i s
if (it != atlas_map.end())
return it->second;
auto image = tile_texture(name);
- auto atlas = std::make_shared<texture_atlas>(image, size);
+ auto atlas = std::make_shared<struct tile_atlas>(image, size);
atlas_map[name] = atlas;
return atlas;
}
diff --git a/loader.hpp b/loader.hpp
index 8fce195b..cb733fb1 100644
--- a/loader.hpp
+++ b/loader.hpp
@@ -9,13 +9,13 @@
namespace Magnum::Examples {
-struct texture_atlas;
+struct tile_atlas;
struct loader_
{
virtual std::string shader(const Containers::StringView& filename) = 0;
virtual Trade::ImageData2D tile_texture(const Containers::StringView& filename) = 0;
- virtual std::shared_ptr<texture_atlas> tile_atlas(const Containers::StringView& filename, Vector2i size) = 0;
+ virtual std::shared_ptr<tile_atlas> tile_atlas(const Containers::StringView& filename, Vector2i size) = 0;
static void destroy();
loader_(const loader_&) = delete;
diff --git a/main.cpp b/main.cpp
index 83af9e24..47092362 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,4 +1,4 @@
-#include "texture-atlas.hpp"
+#include "tile-atlas.hpp"
#include "loader.hpp"
#include "tile-shader.hpp"
#include "defs.hpp"
@@ -60,12 +60,12 @@ struct app final : Platform::Application
GL::Mesh _mesh, _mesh2;
tile_shader _shader;
- std::shared_ptr<texture_atlas> atlas =
+ std::shared_ptr<tile_atlas> atlas =
//loader.tile_atlas("../share/game/images/tiles.tga", {8,4});
//loader.tile_atlas("../share/game/images/tiles2.tga", {8,5});
loader.tile_atlas("../share/game/images/metal1.tga", {2, 2});
//loader.tile_atlas("../share/game/images/floor1.tga", {4, 4});
- std::shared_ptr<texture_atlas> atlas2 =
+ std::shared_ptr<tile_atlas> atlas2 =
loader.tile_atlas("../share/game/images/metal2.tga", {2, 2});
std::uint64_t time_ticks = 0, time_freq = SDL_GetPerformanceFrequency();
@@ -130,7 +130,7 @@ app::app(const Arguments& arguments):
{
Vector3 center{chunk::N/2.f*TILE_SIZE[0], chunk::N/2.f*TILE_SIZE[1], 0};
- texture_atlas::vertex_array_type walls[] = {
+ tile_atlas::vertex_array_type walls[] = {
atlas2->wall_quad_W(center, Vector3(X, Y, Z)),
atlas2->wall_quad_N(center, Vector3(X, Y, Z)),
atlas2->wall_quad_E(center, Vector3(X, Y, Z)),
@@ -140,7 +140,7 @@ app::app(const Arguments& arguments):
int k = 0;
for (const auto& positions : walls)
{
- auto texcoords = atlas2->texcoords_for_id(k);
+ auto texcoords = atlas2->texcoords_for_id(k % atlas2->size());
auto indices_ = atlas2->indices(k);
for (unsigned x = 0; x < 4; x++)
vertices.push_back({ positions[x], texcoords[x] });
diff --git a/shaders/tile-shader.frag b/shaders/tile-shader.frag
index 1cd1cf3d..f75a0478 100644
--- a/shaders/tile-shader.frag
+++ b/shaders/tile-shader.frag
@@ -1,7 +1,6 @@
precision highp float;
layout(location = 2) uniform sampler2D textureData;
-layout(location = 1) uniform float y_scale;
in vec2 interpolatedTextureCoordinates;
diff --git a/texture-atlas.cpp b/tile-atlas.cpp
index e8e44900..67d5df7f 100644
--- a/texture-atlas.cpp
+++ b/tile-atlas.cpp
@@ -1,11 +1,11 @@
-#include "texture-atlas.hpp"
+#include "tile-atlas.hpp"
#include "defs.hpp"
#include <Magnum/ImageView.h>
#include <Magnum/GL/TextureFormat.h>
namespace Magnum::Examples {
-texture_atlas::texture_atlas(const ImageView2D& image, Vector2i dims) :
+tile_atlas::tile_atlas(const ImageView2D& image, Vector2i dims) :
size_{image.size()},
dims_{dims},
tile_size_{size_ / dims}
@@ -22,7 +22,7 @@ texture_atlas::texture_atlas(const ImageView2D& image, Vector2i dims) :
.setSubImage(0, {}, image);
}
-std::array<Vector2, 4> texture_atlas::texcoords_for_id(int id_) const
+std::array<Vector2, 4> tile_atlas::texcoords_for_id(int id_) const
{
CORRADE_INTERNAL_ASSERT(id_ >= 0 && id_ < dims_.product());
Vector2i id = { id_ % dims_[0], id_ / dims_[0] };
@@ -37,9 +37,9 @@ std::array<Vector2, 4> texture_atlas::texcoords_for_id(int id_) const
}};
}
-using vertex_array_type = texture_atlas::vertex_array_type;
+using vertex_array_type = tile_atlas::vertex_array_type;
-vertex_array_type texture_atlas::floor_quad(Vector3 center, Vector2 size)
+vertex_array_type tile_atlas::floor_quad(Vector3 center, Vector2 size)
{
float x = size[0]*.5f, y = size[1]*.5f;
return {{
@@ -50,7 +50,7 @@ vertex_array_type texture_atlas::floor_quad(Vector3 center, Vector2 size)
}};
}
-vertex_array_type texture_atlas::wall_quad_W(Vector3 center, Vector3 size)
+vertex_array_type tile_atlas::wall_quad_W(Vector3 center, Vector3 size)
{
float x = size[0]*.5f, y = size[1]*.5f, z = size[2];
return {{
@@ -61,7 +61,7 @@ vertex_array_type texture_atlas::wall_quad_W(Vector3 center, Vector3 size)
}};
}
-vertex_array_type texture_atlas::wall_quad_S(Vector3 center, Vector3 size)
+vertex_array_type tile_atlas::wall_quad_S(Vector3 center, Vector3 size)
{
float x = size[0]*.5f, y = size[1]*.5f, z = size[2];
return {{
@@ -72,7 +72,7 @@ vertex_array_type texture_atlas::wall_quad_S(Vector3 center, Vector3 size)
}};
}
-vertex_array_type texture_atlas::wall_quad_E(Vector3 center, Vector3 size)
+vertex_array_type tile_atlas::wall_quad_E(Vector3 center, Vector3 size)
{
float x = size[0]*.5f, y = size[1]*.5f, z = size[2];
return {{
@@ -83,7 +83,7 @@ vertex_array_type texture_atlas::wall_quad_E(Vector3 center, Vector3 size)
}};
}
-vertex_array_type texture_atlas::wall_quad_N(Vector3 center, Vector3 size)
+vertex_array_type tile_atlas::wall_quad_N(Vector3 center, Vector3 size)
{
float x = size[0]*.5f, y = size[1]*.5f, z = size[2];
return {{
@@ -94,7 +94,7 @@ vertex_array_type texture_atlas::wall_quad_N(Vector3 center, Vector3 size)
}};
}
-std::array<UnsignedShort, 6> texture_atlas::indices(int N)
+std::array<UnsignedShort, 6> tile_atlas::indices(int N)
{
CORRADE_INTERNAL_ASSERT(N >= 0);
using u16 = UnsignedShort;
diff --git a/texture-atlas.hpp b/tile-atlas.hpp
index e7f8a260..855214b8 100644
--- a/texture-atlas.hpp
+++ b/tile-atlas.hpp
@@ -5,11 +5,11 @@
namespace Magnum::Examples {
-struct texture_atlas final
+struct tile_atlas final
{
using vertex_array_type = std::array<Vector3, 4>;
- texture_atlas(const ImageView2D& img, Vector2i dims);
+ tile_atlas(const ImageView2D& img, Vector2i dims);
std::array<Vector2, 4> texcoords_for_id(int id) const;
static vertex_array_type floor_quad(Vector3 center, Vector2 size);
static vertex_array_type wall_quad_S(Vector3 center, Vector3 size);
@@ -21,8 +21,8 @@ struct texture_atlas final
constexpr int size() const { return dims_.product(); }
constexpr Vector2i tile_size() const { return tile_size_; }
- texture_atlas(const texture_atlas&) = delete;
- texture_atlas& operator=(const texture_atlas&) = delete;
+ tile_atlas(const tile_atlas&) = delete;
+ tile_atlas& operator=(const tile_atlas&) = delete;
private:
GL::Texture2D tex_;
Vector2i size_, dims_, tile_size_;
diff --git a/tile-tool/CMakeLists.txt b/tile-tool/CMakeLists.txt
new file mode 100644
index 00000000..8f148751
--- /dev/null
+++ b/tile-tool/CMakeLists.txt
@@ -0,0 +1,10 @@
+find_package(OpenCV QUIET REQUIRED COMPONENTS core imgcodecs imgproc)
+set(self "${PROJECT_NAME}-tile-tool")
+
+include_directories(SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS})
+link_libraries(Corrade::Utility)
+link_libraries(${PROJECT_NAME}-tile)
+
+file(GLOB sources "*.cpp" CONFIGURE_ARGS)
+add_executable(${self} ${sources})
+install(TARGETS ${self} RUNTIME DESTINATION "bin")
diff --git a/tile-tool/big-atlas.cpp b/tile-tool/big-atlas.cpp
new file mode 100644
index 00000000..a9f877bd
--- /dev/null
+++ b/tile-tool/big-atlas.cpp
@@ -0,0 +1,67 @@
+#undef NDEBUG
+
+#include "big-atlas.hpp"
+#include <cassert>
+#include <filesystem>
+#include <Corrade/Utility/DebugStl.h>
+#include <opencv2/imgproc/imgproc.hpp>
+#include <opencv2/imgcodecs/imgcodecs.hpp>
+
+std::vector<big_atlas_frame> big_atlas_builder::add_atlas(const std::filesystem::path& filename)
+{
+ using Corrade::Utility::Error;
+ std::vector<big_atlas_frame> ret;
+ cv::Mat mat = cv::imread(filename.string(), cv::IMREAD_UNCHANGED);
+ if (mat.empty() || (mat.type() != CV_8UC4 && mat.type() != CV_8UC3))
+ {
+ Error{} << "failed to load" << filename << "as RGBA32 image";
+ return {};
+ }
+ if (mat.type() == CV_8UC3) {
+ cv::Mat mat2;
+ cv::cvtColor(mat, mat2, cv::COLOR_RGB2RGBA);
+ mat = mat2.clone();
+ }
+
+ Error{} << "file" << filename;
+
+ assert(mat.cols % TILE_SIZE[0] == 0 && mat.rows % TILE_SIZE[1] == 0);
+
+ for (int y = 0; y + TILE_SIZE[1] <= mat.rows; y += TILE_SIZE[1])
+ for (int x = 0; x + TILE_SIZE[0] <= mat.cols; x += TILE_SIZE[0])
+ {
+ Error{} << "convert" << x << y;
+ cv::Rect roi { x, y, TILE_SIZE[0], TILE_SIZE[1] };
+ auto frame = add_frame(mat(roi));
+ ret.push_back(frame);
+ }
+
+ return ret;
+}
+
+big_atlas_frame big_atlas_builder::add_frame(const cv::Mat4b& frame)
+{
+ auto& row = maybe_next_row();
+ big_atlas_frame ret { frame, { row.xpos, row.ypos } };
+ row.frames.push_back(ret);
+ row.xpos += TILE_SIZE[0];
+ maxx = std::max(maxx, row.xpos);
+ return ret;
+}
+
+big_atlas_row& big_atlas_builder::maybe_next_row()
+{
+ auto& row = rows.back();
+
+ if (row.xpos + TILE_SIZE[0] > MAX_TEXTURE_SIZE[0])
+ {
+ ypos += TILE_SIZE[1];
+ rows.emplace_back();
+ auto& row = rows.back();
+ row.ypos = ypos;
+
+ return row;
+ }
+ else
+ return row;
+}
diff --git a/tile-tool/big-atlas.hpp b/tile-tool/big-atlas.hpp
new file mode 100644
index 00000000..77961be0
--- /dev/null
+++ b/tile-tool/big-atlas.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <Magnum/Magnum.h>
+#include <Magnum/Math/Vector2.h>
+#include <opencv2/core/mat.hpp>
+
+namespace std::filesystem { class path; }
+
+struct big_atlas_frame {
+ cv::Mat4b frame;
+ Magnum::Vector2i position;
+};
+
+struct big_atlas_row {
+ std::vector<big_atlas_frame> frames;
+ int xpos = 0, ypos = 0;
+};
+
+struct big_atlas_builder {
+ [[nodiscard]] std::vector<big_atlas_frame> add_atlas(const std::filesystem::path& filename);
+ big_atlas_frame add_frame(const cv::Mat4b& frame);
+ big_atlas_row& maybe_next_row();
+
+private:
+ static constexpr Magnum::Vector2i TILE_SIZE = { 100, 100 },
+ MAX_TEXTURE_SIZE = { 512, 512 };
+
+ std::vector<big_atlas_row> rows = {{}};
+ int ypos = 0, maxx = 0;
+
+ static_assert(!!TILE_SIZE[0] && !!TILE_SIZE[1] && !!MAX_TEXTURE_SIZE[0] && !!MAX_TEXTURE_SIZE[1]);
+ static_assert(MAX_TEXTURE_SIZE[0] >= TILE_SIZE[0] && MAX_TEXTURE_SIZE[1] >= TILE_SIZE[1]);
+};
diff --git a/tile-tool/tile-tool.cpp b/tile-tool/tile-tool.cpp
new file mode 100644
index 00000000..edc882ad
--- /dev/null
+++ b/tile-tool/tile-tool.cpp
@@ -0,0 +1,34 @@
+#include "big-atlas.hpp"
+#include "tile/serialize.hpp"
+#include <tuple>
+#include <filesystem>
+#include <Corrade/Utility/Arguments.h>
+
+using Corrade::Utility::Arguments;
+
+struct options final {
+ std::filesystem::path input_dir, output_file;
+};
+
+static std::tuple<options, Arguments, bool> parse_cmdline(int argc, const char* const* argv) noexcept
+{
+ Corrade::Utility::Arguments args{};
+ args.addOption('o', "output")
+ .addArrayArgument("input");
+ args.parse(argc, argv);
+ options opts;
+ opts.input_dir = args.value<std::string>("input");
+
+ if (opts.input_dir.empty())
+ opts.output_file = opts.input_dir.parent_path() / "big-atlas.json";
+
+ return { std::move(opts), std::move(args), true };
+}
+
+int main(int argc, char** argv)
+{
+ big_atlas_builder builder;
+ builder.add_atlas("images/metal1.png");
+ builder.add_atlas("images/metal2.png");
+ return 0;
+}
diff --git a/tile.cpp b/tile.cpp
index e620bd01..b1963415 100644
--- a/tile.cpp
+++ b/tile.cpp
@@ -1,8 +1,5 @@
#include "tile.hpp"
-#include <limits>
namespace Magnum::Examples {
-world::world() = default;
-
} // namespace Magnum::Examples
diff --git a/tile.hpp b/tile.hpp
index 47876f3c..19a0354e 100644
--- a/tile.hpp
+++ b/tile.hpp
@@ -1,5 +1,5 @@
#pragma once
-#include "texture-atlas.hpp"
+#include "tile-atlas.hpp"
#include "hash.hpp"
#include "defs.hpp"
@@ -17,7 +17,7 @@ static constexpr Vector3 TILE_SIZE = { 50, 50, 50 };
struct tile_image final
{
- std::shared_ptr<texture_atlas> atlas;
+ std::shared_ptr<tile_atlas> atlas;
std::uint8_t variant = 0xff;
explicit operator bool() const noexcept { return !!atlas; }
@@ -140,7 +140,7 @@ struct world final
{
static_assert(sizeof(chunk_coords::x) <= sizeof(std::size_t)/2);
- explicit world();
+ explicit world() = default;
template<typename F> std::shared_ptr<chunk> ensure_chunk(chunk_coords xy, F&& fun);
private:
diff --git a/tile/CMakeLists.txt b/tile/CMakeLists.txt
new file mode 100644
index 00000000..73cb2b58
--- /dev/null
+++ b/tile/CMakeLists.txt
@@ -0,0 +1,12 @@
+find_package(OpenCV QUIET REQUIRED COMPONENTS core imgcodecs imgproc)
+find_package(nlohmann_json QUIET REQUIRED)
+
+set(self "${PROJECT_NAME}-tile")
+
+include_directories(SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS})
+link_libraries(opencv_imgproc opencv_imgcodecs opencv_core)
+link_libraries(Magnum::Magnum nlohmann_json::nlohmann_json)
+
+file(GLOB sources "*.cpp" CONFIGURE_ARGS)
+add_library(${self} STATIC ${sources})
+
diff --git a/tile/serialize.cpp b/tile/serialize.cpp
new file mode 100644
index 00000000..4043c8de
--- /dev/null
+++ b/tile/serialize.cpp
@@ -0,0 +1,17 @@
+#include "serialize.hpp"
+#include <nlohmann/json.hpp>
+#include "json-magnum.hpp"
+
+std::tuple<big_atlas, bool> big_atlas::from_json(const std::filesystem::path& pathname) noexcept
+{
+
+}
+
+bool big_atlas::to_json(const std::filesystem::path& pathname) noexcept
+{
+
+}
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(big_atlas_tile, position)
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(big_atlas_entry, tiles)
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(big_atlas, entries)
diff --git a/tile/serialize.hpp b/tile/serialize.hpp
new file mode 100644
index 00000000..c28aa934
--- /dev/null
+++ b/tile/serialize.hpp
@@ -0,0 +1,23 @@
+#include <string>
+#include <vector>
+#include <tuple>
+#include <unordered_map>
+#include <Magnum/Magnum.h>
+#include <Magnum/Math/Vector2.h>
+
+namespace std::filesystem { class path; }
+
+struct big_atlas_tile final {
+ Magnum::Vector2i position;
+};
+
+struct big_atlas_entry final {
+ std::vector<big_atlas_tile> tiles;
+};
+
+struct big_atlas final {
+ static std::tuple<big_atlas, bool> from_json(const std::filesystem::path& pathname) noexcept;
+ [[nodiscard]] bool to_json(const std::filesystem::path& pathname) noexcept;
+
+ std::unordered_map<std::string, big_atlas_entry> entries;
+};