From 5c9863cf0998b5f1b6107ce09b54cd3e8b484221 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Fri, 30 Sep 2022 18:48:50 +0200 Subject: . --- CMakeLists.txt | 2 + anim/atlas.cpp | 63 ---------------------------- anim/atlas.hpp | 37 ---------------- anim/serialize.cpp | 83 ++---------------------------------- anim/serialize.hpp | 2 +- crop-tool/CMakeLists.txt | 2 +- crop-tool/atlas.cpp | 63 ++++++++++++++++++++++++++++ crop-tool/atlas.hpp | 37 ++++++++++++++++ crop-tool/crop-tool.cpp | 2 +- fake-json.hpp | 31 -------------- json-magnum.hpp | 100 +++++++++++++++++++++++++++++++++++++++++++ loader-impl.cpp | 10 ++--- loader.hpp | 4 +- main.cpp | 10 ++--- shaders/tile-shader.frag | 1 - texture-atlas.cpp | 107 ----------------------------------------------- texture-atlas.hpp | 31 -------------- tile-atlas.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++ tile-atlas.hpp | 31 ++++++++++++++ tile-tool/CMakeLists.txt | 10 +++++ tile-tool/big-atlas.cpp | 67 +++++++++++++++++++++++++++++ tile-tool/big-atlas.hpp | 33 +++++++++++++++ tile-tool/tile-tool.cpp | 34 +++++++++++++++ tile.cpp | 3 -- tile.hpp | 6 +-- tile/CMakeLists.txt | 12 ++++++ tile/serialize.cpp | 17 ++++++++ tile/serialize.hpp | 23 ++++++++++ 28 files changed, 558 insertions(+), 370 deletions(-) delete mode 100644 anim/atlas.cpp delete mode 100644 anim/atlas.hpp create mode 100644 crop-tool/atlas.cpp create mode 100644 crop-tool/atlas.hpp delete mode 100644 fake-json.hpp create mode 100644 json-magnum.hpp delete mode 100644 texture-atlas.cpp delete mode 100644 texture-atlas.hpp create mode 100644 tile-atlas.cpp create mode 100644 tile-atlas.hpp create mode 100644 tile-tool/CMakeLists.txt create mode 100644 tile-tool/big-atlas.cpp create mode 100644 tile-tool/big-atlas.hpp create mode 100644 tile-tool/tile-tool.cpp create mode 100644 tile/CMakeLists.txt create mode 100644 tile/serialize.cpp create mode 100644 tile/serialize.hpp 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/atlas.cpp b/anim/atlas.cpp deleted file mode 100644 index fe4b5f65..00000000 --- a/anim/atlas.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#undef NDEBUG - -#include "atlas.hpp" -#include "serialize.hpp" - -#include -#include -#include - -void anim_atlas_row::add_entry(const anim_atlas_entry& x) noexcept -{ - auto& frame = *x.frame; - const auto& mat = x.mat; - frame.offset = {xpos, ypos}; - frame.size = {mat.cols, mat.rows}; - - assert(mat.rows > 0 && mat.cols > 0); - data.push_back(x); - xpos += mat.cols; - max_height = std::max(mat.rows, max_height); -} - -void anim_atlas::advance_row() noexcept -{ - auto& row = rows.back(); - if (row.data.empty()) - return; - assert(row.xpos); assert(row.max_height); - ypos += row.max_height; - maxx = std::max(row.xpos, maxx); - rows.push_back({{}, 0, 0, ypos}); -} - -Magnum::Vector2i anim_atlas::offset() const noexcept -{ - const auto& row = rows.back(); - return {row.xpos, row.ypos}; -} - -Magnum::Vector2i anim_atlas::size() const noexcept -{ - const anim_atlas_row& row = rows.back(); - // prevent accidentally writing out of bounds by forgetting to call - // anim_atlas::advance_row() one last time prior to anim_atlas::size() - return {std::max(maxx, row.xpos), ypos + row.max_height}; -} - -bool anim_atlas::dump(const std::filesystem::path& filename) const noexcept -{ - auto sz = size(); - cv::Mat4b mat(sz[1], sz[0]); - mat.setTo(0); - - for (const anim_atlas_row& row : rows) - for (const anim_atlas_entry& x : row.data) - { - auto offset = x.frame->offset, size = x.frame->size; - cv::Rect roi = {offset[0], offset[1], size[0], size[1]}; - x.mat.copyTo(mat(roi)); - } - - return cv::imwrite(filename.string(), mat); -} diff --git a/anim/atlas.hpp b/anim/atlas.hpp deleted file mode 100644 index 5c5e918f..00000000 --- a/anim/atlas.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -struct anim_frame; - -namespace std::filesystem { class path; } - -struct anim_atlas_entry -{ - anim_frame* frame; - cv::Mat4b mat; -}; - -struct anim_atlas_row -{ - std::vector data; - int max_height = 0, xpos = 0, ypos = 0; - - void add_entry(const anim_atlas_entry& x) noexcept; -}; - -class anim_atlas -{ - std::vector rows = {{}}; - int ypos = 0, maxx = 0; - -public: - void add_entry(const anim_atlas_entry& x) noexcept { rows.back().add_entry(x); } - void advance_row() noexcept; - Magnum::Vector2i offset() const noexcept; - Magnum::Vector2i size() const noexcept; - [[nodiscard]] bool dump(const std::filesystem::path& filename) const noexcept; -}; 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 -#include -#include -#include - #include #include -#include "../fake-json.hpp" +#include "../json-magnum.hpp" using Corrade::Utility::Error; -namespace nlohmann { - -template<> -struct adl_serializer final { - static void to_json(json& j, const Magnum::Vector2i& x); - static void from_json(const json& j, Magnum::Vector2i& x); -}; - -void adl_serializer::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::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::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::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::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 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/crop-tool/atlas.cpp b/crop-tool/atlas.cpp new file mode 100644 index 00000000..9e16a3cd --- /dev/null +++ b/crop-tool/atlas.cpp @@ -0,0 +1,63 @@ +#undef NDEBUG + +#include "atlas.hpp" +#include "../anim/serialize.hpp" + +#include +#include +#include + +void anim_atlas_row::add_entry(const anim_atlas_entry& x) noexcept +{ + auto& frame = *x.frame; + const auto& mat = x.mat; + frame.offset = {xpos, ypos}; + frame.size = {mat.cols, mat.rows}; + + assert(mat.rows > 0 && mat.cols > 0); + data.push_back(x); + xpos += mat.cols; + max_height = std::max(mat.rows, max_height); +} + +void anim_atlas::advance_row() noexcept +{ + auto& row = rows.back(); + if (row.data.empty()) + return; + assert(row.xpos); assert(row.max_height); + ypos += row.max_height; + maxx = std::max(row.xpos, maxx); + rows.push_back({{}, 0, 0, ypos}); +} + +Magnum::Vector2i anim_atlas::offset() const noexcept +{ + const auto& row = rows.back(); + return {row.xpos, row.ypos}; +} + +Magnum::Vector2i anim_atlas::size() const noexcept +{ + const anim_atlas_row& row = rows.back(); + // prevent accidentally writing out of bounds by forgetting to call + // anim_atlas::advance_row() one last time prior to anim_atlas::size() + return {std::max(maxx, row.xpos), ypos + row.max_height}; +} + +bool anim_atlas::dump(const std::filesystem::path& filename) const noexcept +{ + auto sz = size(); + cv::Mat4b mat(sz[1], sz[0]); + mat.setTo(0); + + for (const anim_atlas_row& row : rows) + for (const anim_atlas_entry& x : row.data) + { + auto offset = x.frame->offset, size = x.frame->size; + cv::Rect roi = {offset[0], offset[1], size[0], size[1]}; + x.mat.copyTo(mat(roi)); + } + + return cv::imwrite(filename.string(), mat); +} diff --git a/crop-tool/atlas.hpp b/crop-tool/atlas.hpp new file mode 100644 index 00000000..5c5e918f --- /dev/null +++ b/crop-tool/atlas.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +struct anim_frame; + +namespace std::filesystem { class path; } + +struct anim_atlas_entry +{ + anim_frame* frame; + cv::Mat4b mat; +}; + +struct anim_atlas_row +{ + std::vector data; + int max_height = 0, xpos = 0, ypos = 0; + + void add_entry(const anim_atlas_entry& x) noexcept; +}; + +class anim_atlas +{ + std::vector rows = {{}}; + int ypos = 0, maxx = 0; + +public: + void add_entry(const anim_atlas_entry& x) noexcept { rows.back().add_entry(x); } + void advance_row() noexcept; + Magnum::Vector2i offset() const noexcept; + Magnum::Vector2i size() const noexcept; + [[nodiscard]] bool dump(const std::filesystem::path& filename) const noexcept; +}; 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 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 -#else - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunknown-pragmas" -#pragma ide diagnostic ignored "NotImplementedFunctions" - -namespace nlohmann { - struct json { - template operator t() const; - const json& dump(int) const; - template json(const t&); - json(); - }; - template json& operator>>(const t&, json&); - template json& operator<<(t&, const json&); - template struct adl_serializer { - static void to_json(json&, const t&); - static void from_json(const json&, t&); - }; - template void from_json(const json&, t&); - template 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann { + +template<> +struct adl_serializer final { + static void to_json(json& j, const Magnum::Vector2i& x); + static void from_json(const json& j, Magnum::Vector2i& x); +}; + +void adl_serializer::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::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 +struct json_helper final { + [[nodiscard]] static std::tuple from_json(const std::filesystem::path& pathname) noexcept; + [[nodiscard]] static bool to_json(const t& self, const std::filesystem::path& pathname) noexcept; +}; + +template +std::tuple json_helper::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 +bool json_helper::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 #include #include @@ -14,18 +14,18 @@ namespace Magnum::Examples { -using atlas_ptr = std::shared_ptr; +using atlas_ptr = std::shared_ptr; struct loader_impl final : loader_ { const Utility::Resource shader_res{"game/shaders"}; PluginManager::Manager importer_plugins; Containers::Pointer tga_importer = - importer_plugins.loadAndInstantiate("TgaImporter"); + importer_plugins.loadAndInstantiate("AnyImageImporter"); PluginManager::Manager image_converter_plugins; Containers::Pointer tga_converter = - image_converter_plugins.loadAndInstantiate("TgaImageConverter"); + image_converter_plugins.loadAndInstantiate("AnyImageConverter"); std::unordered_map 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(image, size); + auto atlas = std::make_shared(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 tile_atlas(const Containers::StringView& filename, Vector2i size) = 0; + virtual std::shared_ptr 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 atlas = + std::shared_ptr 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 atlas2 = + std::shared_ptr 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/texture-atlas.cpp deleted file mode 100644 index e8e44900..00000000 --- a/texture-atlas.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "texture-atlas.hpp" -#include "defs.hpp" -#include -#include - -namespace Magnum::Examples { - -texture_atlas::texture_atlas(const ImageView2D& image, Vector2i dims) : - size_{image.size()}, - dims_{dims}, - tile_size_{size_ / dims} -{ - CORRADE_INTERNAL_ASSERT(dims_[0] > 0 && dims_[1] > 0); - CORRADE_INTERNAL_ASSERT(tile_size_ * dims_ == size_); - CORRADE_INTERNAL_ASSERT(size_ % dims_ == Vector2i{}); - CORRADE_INTERNAL_ASSERT(dims.product() < 256); - tex_.setWrapping(GL::SamplerWrapping::ClampToEdge) - .setMagnificationFilter(GL::SamplerFilter::Nearest) - .setMinificationFilter(GL::SamplerFilter::Linear) - .setMaxAnisotropy(0) - .setStorage(1, GL::textureFormat(image.format()), image.size()) - .setSubImage(0, {}, image); -} - -std::array texture_atlas::texcoords_for_id(int id_) const -{ - CORRADE_INTERNAL_ASSERT(id_ >= 0 && id_ < dims_.product()); - Vector2i id = { id_ % dims_[0], id_ / dims_[0] }; - auto p0 = Vector2(id * tile_size_) / Vector2(size_); - auto p1 = Vector2(tile_size_) / Vector2(size_); - 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 - }}; -} - -using vertex_array_type = texture_atlas::vertex_array_type; - -vertex_array_type texture_atlas::floor_quad(Vector3 center, 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]}, - }}; -} - -vertex_array_type texture_atlas::wall_quad_W(Vector3 center, 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] }, - }}; -} - -vertex_array_type texture_atlas::wall_quad_S(Vector3 center, 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] }, - }}; -} - -vertex_array_type texture_atlas::wall_quad_E(Vector3 center, 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] }, - }}; -} - -vertex_array_type texture_atlas::wall_quad_N(Vector3 center, 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] }, - }}; -} - -std::array texture_atlas::indices(int N) -{ - CORRADE_INTERNAL_ASSERT(N >= 0); - 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 */ -} - -} // namespace Magnum::Examples diff --git a/texture-atlas.hpp b/texture-atlas.hpp deleted file mode 100644 index e7f8a260..00000000 --- a/texture-atlas.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include -#include - -namespace Magnum::Examples { - -struct texture_atlas final -{ - using vertex_array_type = std::array; - - texture_atlas(const ImageView2D& img, Vector2i dims); - std::array 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); - static vertex_array_type wall_quad_E(Vector3 center, Vector3 size); - static vertex_array_type wall_quad_N(Vector3 center, Vector3 size); - static vertex_array_type wall_quad_W(Vector3 center, Vector3 size); - static std::array indices(int N); - GL::Texture2D& texture() { return tex_; } - 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; -private: - GL::Texture2D tex_; - Vector2i size_, dims_, tile_size_; -}; - -} // namespace Magnum::Examples diff --git a/tile-atlas.cpp b/tile-atlas.cpp new file mode 100644 index 00000000..67d5df7f --- /dev/null +++ b/tile-atlas.cpp @@ -0,0 +1,107 @@ +#include "tile-atlas.hpp" +#include "defs.hpp" +#include +#include + +namespace Magnum::Examples { + +tile_atlas::tile_atlas(const ImageView2D& image, Vector2i dims) : + size_{image.size()}, + dims_{dims}, + tile_size_{size_ / dims} +{ + CORRADE_INTERNAL_ASSERT(dims_[0] > 0 && dims_[1] > 0); + CORRADE_INTERNAL_ASSERT(tile_size_ * dims_ == size_); + CORRADE_INTERNAL_ASSERT(size_ % dims_ == Vector2i{}); + CORRADE_INTERNAL_ASSERT(dims.product() < 256); + tex_.setWrapping(GL::SamplerWrapping::ClampToEdge) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setMinificationFilter(GL::SamplerFilter::Linear) + .setMaxAnisotropy(0) + .setStorage(1, GL::textureFormat(image.format()), image.size()) + .setSubImage(0, {}, image); +} + +std::array tile_atlas::texcoords_for_id(int id_) const +{ + CORRADE_INTERNAL_ASSERT(id_ >= 0 && id_ < dims_.product()); + Vector2i id = { id_ % dims_[0], id_ / dims_[0] }; + auto p0 = Vector2(id * tile_size_) / Vector2(size_); + auto p1 = Vector2(tile_size_) / Vector2(size_); + 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 + }}; +} + +using vertex_array_type = tile_atlas::vertex_array_type; + +vertex_array_type tile_atlas::floor_quad(Vector3 center, 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]}, + }}; +} + +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 {{ + { 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] }, + }}; +} + +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 {{ + {-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] }, + }}; +} + +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 {{ + { 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] }, + }}; +} + +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 {{ + { 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] }, + }}; +} + +std::array tile_atlas::indices(int N) +{ + CORRADE_INTERNAL_ASSERT(N >= 0); + 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 */ +} + +} // namespace Magnum::Examples diff --git a/tile-atlas.hpp b/tile-atlas.hpp new file mode 100644 index 00000000..855214b8 --- /dev/null +++ b/tile-atlas.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include + +namespace Magnum::Examples { + +struct tile_atlas final +{ + using vertex_array_type = std::array; + + tile_atlas(const ImageView2D& img, Vector2i dims); + std::array 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); + static vertex_array_type wall_quad_E(Vector3 center, Vector3 size); + static vertex_array_type wall_quad_N(Vector3 center, Vector3 size); + static vertex_array_type wall_quad_W(Vector3 center, Vector3 size); + static std::array indices(int N); + GL::Texture2D& texture() { return tex_; } + constexpr int size() const { return dims_.product(); } + constexpr Vector2i tile_size() const { return tile_size_; } + + tile_atlas(const tile_atlas&) = delete; + tile_atlas& operator=(const tile_atlas&) = delete; +private: + GL::Texture2D tex_; + Vector2i size_, dims_, tile_size_; +}; + +} // namespace Magnum::Examples 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 +#include +#include +#include +#include + +std::vector big_atlas_builder::add_atlas(const std::filesystem::path& filename) +{ + using Corrade::Utility::Error; + std::vector 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 +#include +#include + +namespace std::filesystem { class path; } + +struct big_atlas_frame { + cv::Mat4b frame; + Magnum::Vector2i position; +}; + +struct big_atlas_row { + std::vector frames; + int xpos = 0, ypos = 0; +}; + +struct big_atlas_builder { + [[nodiscard]] std::vector 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 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 +#include +#include + +using Corrade::Utility::Arguments; + +struct options final { + std::filesystem::path input_dir, output_file; +}; + +static std::tuple 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("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 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 atlas; + std::shared_ptr 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 std::shared_ptr 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 +#include "json-magnum.hpp" + +std::tuple 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 +#include +#include +#include +#include +#include + +namespace std::filesystem { class path; } + +struct big_atlas_tile final { + Magnum::Vector2i position; +}; + +struct big_atlas_entry final { + std::vector tiles; +}; + +struct big_atlas final { + static std::tuple from_json(const std::filesystem::path& pathname) noexcept; + [[nodiscard]] bool to_json(const std::filesystem::path& pathname) noexcept; + + std::unordered_map entries; +}; -- cgit v1.2.3