summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--atlas.cpp2
-rw-r--r--atlas.hpp6
m---------corrade0
-rw-r--r--crop-tool/CMakeLists.txt6
-rw-r--r--crop-tool/atlas.cpp2
-rw-r--r--crop-tool/atlas.hpp31
-rw-r--r--crop-tool/crop-tool.cpp235
-rw-r--r--loader-impl.cpp13
m---------magnum0
m---------magnum-integration0
m---------magnum-plugins0
12 files changed, 289 insertions, 8 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a17a7baa..ac9065fb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,6 +53,8 @@ if(NOT BOOTSTRAP_DEPENDS)
find_package(MagnumPlugins QUIET REQUIRED)
find_package(MagnumIntegration QUIET REQUIRED COMPONENTS Glm)
+ add_subdirectory(crop-tool)
+
corrade_add_resource(game_RESOURCES resources.conf)
file(GLOB sources "*.cpp" CONFIGURE_ARGS)
diff --git a/atlas.cpp b/atlas.cpp
index 385cbd17..d40cec11 100644
--- a/atlas.cpp
+++ b/atlas.cpp
@@ -5,7 +5,7 @@
namespace Magnum::Examples {
-atlas_texture::atlas_texture(const Trade::ImageData2D& image, Vector2i dims) :
+atlas_texture::atlas_texture(const ImageView2D& image, Vector2i dims) :
size_{image.size()},
dims_{dims},
tile_size_{size_ / dims}
diff --git a/atlas.hpp b/atlas.hpp
index d2fddff2..671679bb 100644
--- a/atlas.hpp
+++ b/atlas.hpp
@@ -1,8 +1,6 @@
#pragma once
-#include <Magnum/Math/Vector.h>
-#include <Magnum/Math/Vector2.h>
+#include <Magnum/Magnum.h>
#include <Magnum/GL/Texture.h>
-#include <Magnum/Trade/ImageData.h>
#include <array>
namespace Magnum::Examples {
@@ -11,7 +9,7 @@ struct atlas_texture final
{
using vertex_array_type = std::array<Vector3, 4>;
- atlas_texture(const Trade::ImageData2D& img, Vector2i dims);
+ atlas_texture(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_W(Vector3 center, Vector3 size);
diff --git a/corrade b/corrade
-Subproject f2d56f700ac1d1d0d0b665fba4a7846ce4c168f
+Subproject d44ca683c12ed28fce1d27662f24edee2d7e2c6
diff --git a/crop-tool/CMakeLists.txt b/crop-tool/CMakeLists.txt
new file mode 100644
index 00000000..cc38c51b
--- /dev/null
+++ b/crop-tool/CMakeLists.txt
@@ -0,0 +1,6 @@
+find_package(OpenCV QUIET REQUIRED COMPONENTS core imgcodecs imgproc highgui)
+file(GLOB sources "*.cpp" CONFIGURE_ARGS)
+add_executable(crop-tool ${sources})
+target_include_directories(crop-tool SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS})
+target_link_libraries(crop-tool opencv_highgui opencv_imgproc opencv_imgcodecs opencv_core)
+target_link_libraries(crop-tool Corrade::Utility Magnum::Trade)
diff --git a/crop-tool/atlas.cpp b/crop-tool/atlas.cpp
new file mode 100644
index 00000000..0cb6eaf8
--- /dev/null
+++ b/crop-tool/atlas.cpp
@@ -0,0 +1,2 @@
+#include "atlas.hpp"
+#include "../json.hpp"
diff --git a/crop-tool/atlas.hpp b/crop-tool/atlas.hpp
new file mode 100644
index 00000000..ad74fe4d
--- /dev/null
+++ b/crop-tool/atlas.hpp
@@ -0,0 +1,31 @@
+#pragma once
+#include <array>
+#include <vector>
+#include <Magnum/Trade/ImageData.h>
+
+struct anim_frame
+{
+ Magnum::Trade::ImageData2D image;
+ Magnum::Vector2us ground_offset = {};
+};
+
+enum class anim_direction : unsigned char
+{
+ Invalid,
+ N, NE, E, SE, S, SW, W, NW,
+ MIN = N, MAX = NW,
+};
+
+struct anim_direction_group
+{
+ std::vector<anim_frame> frames;
+ anim_direction dir = anim_direction::Invalid;
+};
+
+struct anim
+{
+ std::array<anim_direction_group, (unsigned)anim_direction::MAX> directions;
+ int num_frames = 0, action_frame = 0, fps = default_fps;
+
+ static constexpr int default_fps = 24;
+};
diff --git a/crop-tool/crop-tool.cpp b/crop-tool/crop-tool.cpp
new file mode 100644
index 00000000..1acbc397
--- /dev/null
+++ b/crop-tool/crop-tool.cpp
@@ -0,0 +1,235 @@
+#include "../defs.hpp"
+#include "atlas.hpp"
+#include <Corrade/Utility/Arguments.h>
+#include <Corrade/Utility/Debug.h>
+#include <Corrade/Utility/DebugStl.h>
+#include <opencv2/core/mat.hpp>
+#include <opencv2/imgcodecs/imgcodecs.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+#include <opencv2/highgui.hpp>
+#include <optional>
+#include <tuple>
+#include <filesystem>
+#include <algorithm>
+#include <utility>
+#include <cstring>
+#include <cmath>
+
+#ifdef _WIN32
+# define EX_OK 0 /* successful termination */
+# define EX_USAGE 64 /* command line usage error */
+# define EX_SOFTWARE 70 /* internal software error */
+# define EX_IOERR 74 /* input/output error */
+#else
+# include <sysexits.h>
+#endif
+
+static std::string fix_path_separators(const std::filesystem::path& path)
+{
+ auto str = path.string();
+ std::replace(str.begin(), str.end(), '\\', '/');
+ return str;
+}
+
+struct file
+{
+ std::filesystem::path name;
+ cv::Mat4b mat;
+ cv::Point2i offset, orig_size;
+};
+
+struct dir
+{
+ std::filesystem::path name;
+ std::vector<file> files;
+};
+
+using Corrade::Utility::Error;
+using Corrade::Utility::Debug;
+
+static struct options_ {
+ std::optional<unsigned> width, height;
+ std::optional<double> scale;
+ cv::Vec2i offset{-1, -1};
+} options;
+
+static
+std::tuple<cv::Vec2i, cv::Vec2i, bool>
+find_image_bounds(const std::filesystem::path& path, const cv::Mat4b& mat)
+{
+ cv::Vec2i start{mat.cols, mat.rows}, end{0, 0};
+ for (int y = 0; y < mat.rows; y++)
+ {
+ const auto* ptr = mat.ptr<cv::Vec4b>(y);
+ for (int x = 0; x < mat.cols; x++)
+ {
+ enum {R, G, B, A};
+ cv::Vec4b px = ptr[x];
+ if (px[A] != 0)
+ {
+ start[0] = std::min(x, start[0]);
+ start[1] = std::min(y, start[1]);
+ end[0] = std::max(x+1, end[0]);
+ end[1] = std::max(y+1, end[1]);
+ }
+ }
+ }
+ if (start[0] >= end[0] || start[1] >= end[1])
+ {
+ Error{} << "image" << path.string() << "contains only fully transparent pixels!";
+ return {{}, {}, false};
+ }
+
+ return {start, end, true};
+}
+
+static std::optional<file> load_file(const std::filesystem::path& filename)
+{
+ Debug{} << "load" << fix_path_separators(filename.string());
+
+ auto mat = progn(
+ cv::Mat mat_ = cv::imread(filename.string(), cv::IMREAD_UNCHANGED);
+ if (mat_.empty() || mat_.type() != CV_8UC4)
+ {
+ Error{} << "failed to load" << filename.string() << "as RGBA32 image";
+ return cv::Mat4b{};
+ }
+ return cv::Mat4b(std::move(mat_));
+ );
+
+ if (mat.empty())
+ return {};
+
+ auto [start, end, bounds_ok] = find_image_bounds(filename, mat);
+
+ if (!bounds_ok)
+ return {};
+
+ cv::Size size{end - start}, dest_size;
+
+ if (!options.scale)
+ {
+ if (options.width)
+ options.scale = (double)*options.width / size.width;
+ else if (options.height)
+ options.scale = (double)*options.height / size.height;
+ else
+ std::abort();
+ }
+
+ cv::Vec2i offset = {
+ (int)std::round((options.offset[0] - start[0]) * *options.scale),
+ (int)std::round((options.offset[1] - start[1]) * *options.scale),
+ };
+
+ dest_size = {(int)std::round(*options.scale * size.width),
+ (int)std::round(*options.scale * size.height)};
+
+ if (size.width < dest_size.width || size.height < dest_size.height)
+ {
+ Error{} << "refusing to upscale image" << filename.string();
+ return {};
+ }
+
+ Debug{} << "file" << filename.string() << offset[0] << offset[1];
+
+ cv::Mat4b resized{size};
+ cv::resize(mat({start, size}), resized, dest_size, 0, 0, cv::INTER_LANCZOS4);
+ file ret {filename, resized.clone(), start, size};
+ return std::make_optional(std::move(ret));
+}
+
+static std::optional<dir> load_directory(const std::filesystem::path& dirname)
+{
+ if (std::error_code ec{}; !std::filesystem::exists(dirname / ".", ec))
+ {
+ Error{} << "can't open directory" << dirname.string() << ":" << ec.message();
+ return std::nullopt;
+ }
+
+ Debug{} << "loading" << dirname.string();
+
+ dir ret;
+ for (int i = 1; i <= 9999; i++)
+ {
+ char buf[9];
+ sprintf(buf, "%04d.png", i);
+ auto path = dirname / buf;
+ if (!std::filesystem::exists(path))
+ break;
+ auto file = load_file(path);
+ if (!file)
+ return std::nullopt;
+ ret.files.push_back(std::move(*file));
+ }
+ if (ret.files.empty())
+ {
+ Error{} << "directory" << dirname.string() << "is empty!";
+ return std::nullopt;
+ }
+ return std::make_optional(std::move(ret));
+}
+
+int main(int argc, char** argv)
+{
+ Corrade::Utility::Arguments args{};
+#ifdef _WIN32
+ if (auto* c = strrchr(argv[0], '\\'); c && c[1])
+ {
+ if (auto* s = strrchr(c, '.'); s && !strcmp(".exe", s))
+ *s = '\0';
+ args.setCommand(c+1);
+ }
+#else
+ if (auto* c = strrchr(argv[0], '/'); c && c[1])
+ args.setCommand(c+1);
+#endif
+ args.addOption('o', "output", "output")
+ .addArrayArgument("directories")
+ .addOption('W', "width", "")
+ .addOption('H', "height", "")
+ .addOption('x', "offset")
+ .setHelp("offset", {}, "WxH");
+ args.parse(argc, argv);
+ auto output = args.value<std::string>("output");
+ std::vector<dir> dirs;
+ if (unsigned w = args.value<unsigned>("width"); w != 0)
+ options.width = w;
+ if (unsigned h = args.value<unsigned>("height"); h != 0)
+ options.height = h;
+ if (!(!options.width ^ !options.height))
+ {
+ Error{} << "exactly one of --width, --height must be given";
+ goto usage;
+ }
+
+ {
+ auto str = args.value<std::string>("offset");
+ if (str.empty())
+ {
+ Error{} << "offset argument is required";
+ goto usage;
+ }
+ int x, y;
+ int ret = std::sscanf(str.c_str(), "%dx%d", &x, &y);
+ if (ret != 2)
+ {
+ Error{} << "can't parse offset --" << str;
+ goto usage;
+ }
+ options.offset = {x, y};
+ }
+
+ for (std::size_t i = 0, cnt = args.arrayValueCount("directories"); i < cnt; i++)
+ {
+ auto dir = load_directory(args.arrayValue("directories", i));
+ if (!dir)
+ goto usage;
+ dirs.push_back(std::move(*dir));
+ }
+
+ return 0;
+usage:
+ Error{Error::Flag::NoNewlineAtTheEnd} << Corrade::Containers::StringView{args.usage()};
+ return EX_USAGE;
+}
diff --git a/loader-impl.cpp b/loader-impl.cpp
index a0f316c8..8e7cb081 100644
--- a/loader-impl.cpp
+++ b/loader-impl.cpp
@@ -5,8 +5,10 @@
#include <Corrade/Containers/StringView.h>
#include <Corrade/PluginManager/PluginManager.h>
#include <Corrade/Utility/Resource.h>
+#include <Magnum/ImageView.h>
#include <Magnum/Trade/AbstractImporter.h>
#include <Magnum/Trade/ImageData.h>
+#include <Magnum/Trade/AbstractImageConverter.h>
#include <unordered_map>
#include <utility>
@@ -17,9 +19,13 @@ using atlas_ptr = std::shared_ptr<atlas_texture>;
struct loader_impl final : loader_
{
const Utility::Resource shader_res{"game/shaders"};
- PluginManager::Manager<Trade::AbstractImporter> plugins;
+ PluginManager::Manager<Trade::AbstractImporter> importer_plugins;
Containers::Pointer<Trade::AbstractImporter> tga_importer =
- plugins.loadAndInstantiate("TgaImporter");
+ importer_plugins.loadAndInstantiate("TgaImporter");
+
+ PluginManager::Manager<Trade::AbstractImageConverter> image_converter_plugins;
+ Containers::Pointer<Trade::AbstractImageConverter> tga_converter =
+ image_converter_plugins.loadAndInstantiate("TgaImageConverter");
std::unordered_map<std::string, atlas_ptr> atlas_map;
@@ -44,7 +50,8 @@ atlas_ptr loader_impl::tile_atlas(const Containers::StringView& name, Vector2i s
auto it = atlas_map.find(name);
if (it != atlas_map.end())
return it->second;
- auto atlas = std::make_shared<atlas_texture>(tile_texture(name), size);
+ auto image = tile_texture(name);
+ auto atlas = std::make_shared<atlas_texture>(image, size);
atlas_map[name] = atlas;
return atlas;
}
diff --git a/magnum b/magnum
-Subproject aef0ee4bfba8dd0e6b23bee4e170faf66c3e0e9
+Subproject a218ddfa9434201aaca207ea9c8e87fea91a075
diff --git a/magnum-integration b/magnum-integration
-Subproject f29c091b0b699cdd092c9b80b4cb5535cf48175
+Subproject 6fa1a7754bf8bc612befdc9ec543f8420a0111c
diff --git a/magnum-plugins b/magnum-plugins
-Subproject 0e11d4110eebe8a5a23a206fd6183b6a4b34330
+Subproject 4100de3b63beac723690adfa4a3fd368b5d664b