diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | doc/wall-tilesets.md | 48 | ||||
-rw-r--r-- | images/wall.json | 4 | ||||
-rw-r--r-- | serialize/wall-atlas.cpp | 62 | ||||
-rw-r--r-- | serialize/wall-atlas.hpp | 15 | ||||
-rw-r--r-- | src/tile-atlas.cpp | 2 | ||||
-rw-r--r-- | src/wall-atlas.cpp | 54 | ||||
-rw-r--r-- | src/wall-atlas.hpp | 65 |
8 files changed, 250 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 37bfac1b..199a4008 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,12 +246,12 @@ add_subdirectory(draw) add_subdirectory(serialize) add_subdirectory(editor) add_subdirectory(test) +add_subdirectory(anim-crop-tool) find_package(benchmark QUIET) if(TARGET benchmark::benchmark OR TARGET benchmark) add_subdirectory(bench) endif() -add_subdirectory(anim-crop-tool) install(DIRECTORY images anim scenery vobj DESTINATION "share/floormat") diff --git a/doc/wall-tilesets.md b/doc/wall-tilesets.md new file mode 100644 index 00000000..42ff2fd1 --- /dev/null +++ b/doc/wall-tilesets.md @@ -0,0 +1,48 @@ +Here's what a wall tileset looks like. For reference see this RPG Codex +post: + +- <<https://rpgcodex.net/forums/threads/codexian-game-development-thread.69297/post-8726752>> +- <<https://archive.ph/uthRB>> + +Note: all textures in `floormat` are flat and unprojected. The renderer +projects then and adds any necessary effects. + +Notes on textures: + +- Can specify various seam modes. + 1. `"any"`: tiles are placed in arbitrary order. + 2. `"seamless"`: texture is made seamless from left to right, and + top to bottom. +- Can have a tint specified for it. The formula is $a C + m$. Tint is + useful to put a bit of contrast between North and West walls. +- Split into N tiles, width of 64 and height of 192 is used. +- An entire set of textures can be borrowed from a different rotation, + either optionally mirrored and with a tint added on top of it. +- Can have multiple Z levels (in Tiled parlance it's "layers"), with + optional Z = -1 layer for foundations/ground/soil. Levels are repeated + if there's more Z levels than the tileset has. + +For all NESW rotations the following separate textures are available: + +1. Wall texture. Split into N tiles to be used on ingame 64x64x192 tiles. + - The wall can be "borrowed" from a different rotation, optionally +2. Wall's "depth" texture which specifies that the wall isn't paper-thin + depending on its height. Split into N variants, typically N can be 1 + or the same amount as wall textures. +3. Inner wall layer. This can be paint, wallpaper or decals placed on + top of the wall proper. A separate depth layer cannot be specified. + Can be partly transparent (decals such as grafitti) or translucent + (weathering marks made out of Perlin noise, etc). +5. Corner decorations for beginning and end of the wall. For a right + angle of N+W walls, end decoration of N and beginning decoration of W + is used. +6. Wall's side when the wall simply ends without forming a corner with a + wall from another rotation. +7. Cut-out part for windows. With a stencil mask. +8. Cut-out part for doors without a frame. With a stencil mask. + +Each texture set can be borrowed from another rotation, + +What about coordinates and which tile variant is picked? TODO + +JSON/Lisp format. TODO diff --git a/images/wall.json b/images/wall.json index 897ae1de..a212b90e 100644 --- a/images/wall.json +++ b/images/wall.json @@ -20,6 +20,10 @@ "size": "4 x 4" }, { + "name": "concrete1", + "size": "3 x 1" + }, + { "name": "wall1", "size": "4 x 1" } diff --git a/serialize/wall-atlas.cpp b/serialize/wall-atlas.cpp new file mode 100644 index 00000000..2e0e8cb2 --- /dev/null +++ b/serialize/wall-atlas.cpp @@ -0,0 +1,62 @@ +#include "wall-atlas.hpp" +#include "magnum-vector2i.hpp" +#include "magnum-vector.hpp" +#include "compat/exception.hpp" +#include <Corrade/Containers/StringStl.h> +#include <nlohmann/json.hpp> + +namespace floormat { + +namespace { + +constexpr StringView rotation_names[] = { "n"_s, "e"_s, "s"_s, "w"_s, }; + +size_t rotation_from_name(StringView s) +{ + for (auto i = 0uz; auto n : rotation_names) + { + if (n == s) + return i; + i++; + } + fm_throw("bad rotation name '{}'"_cf, fmt::string_view{s.data(), s.size()}); +} + +void read_frameset_metadata(nlohmann::json& j, wall_frames& ret) +{ + if (j.contains("pixel-size")) + ret.pixel_size = j["pixel-size"]; + if (j.contains("tint")) + { + auto& t = j["tint"]; + fm_soft_assert(t.contains("mult") || t.contains("add")); + if (t.contains("mult")) + ret.tint_mult = Vector4(t["mult"]); + if (t.contains("add")) + ret.tint_add = Vector3(t["add"]); + } + if (j.contains("from-rotation")) + ret.from_rotation = (uint8_t)rotation_from_name(std::string{j["from-rotation"]}); + if (j.contains("mirrored")) + ret.mirrored = j["mirrored"]; +} + +} // namespace + +} // namespace floormat + +namespace nlohmann { + +using namespace floormat; + +void adl_serializer<std::shared_ptr<wall_atlas>>::to_json(json& j, const std::shared_ptr<const wall_atlas>& x) +{ + +} + +void adl_serializer<std::shared_ptr<wall_atlas>>::from_json(const json& j, std::shared_ptr<wall_atlas>& x) +{ + +} + +} // namespace nlohmann diff --git a/serialize/wall-atlas.hpp b/serialize/wall-atlas.hpp new file mode 100644 index 00000000..5e454f87 --- /dev/null +++ b/serialize/wall-atlas.hpp @@ -0,0 +1,15 @@ +#pragma once +#include "src/wall-atlas.hpp" +#include <memory> +#include <nlohmann/json_fwd.hpp> + +namespace nlohmann { + +template<> +struct adl_serializer<std::shared_ptr<floormat::wall_atlas>> +{ + static void to_json(json& j, const std::shared_ptr<const floormat::wall_atlas>& x); + static void from_json(const json& j, std::shared_ptr<floormat::wall_atlas>& x); +}; + +} // namespace nlohmann diff --git a/src/tile-atlas.cpp b/src/tile-atlas.cpp index 298e7f9e..1867189f 100644 --- a/src/tile-atlas.cpp +++ b/src/tile-atlas.cpp @@ -20,7 +20,7 @@ tile_atlas::tile_atlas(StringView path, StringView name, const ImageView2D& imag fm_soft_assert(size_ % Vector2ui{tile_count} == Vector2ui()); tex_.setLabel(path_) .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setMagnificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Nearest) .setMinificationFilter(GL::SamplerFilter::Linear) .setMaxAnisotropy(1) .setBorderColor(Color4{1, 0, 0, 1}) diff --git a/src/wall-atlas.cpp b/src/wall-atlas.cpp new file mode 100644 index 00000000..9b711f60 --- /dev/null +++ b/src/wall-atlas.cpp @@ -0,0 +1,54 @@ +#include "wall-atlas.hpp" +#include "compat/assert.hpp" +#include <utility> +#include <Magnum/ImageView.h> +#include <Magnum/GL/TextureFormat.h> + +namespace floormat { + +size_t wall_atlas::enum_to_index(enum rotation r) +{ + fm_debug_assert(r < rotation_COUNT); + + auto x = uint8_t(r); + x >>= 1; + return x; +} + +wall_atlas::wall_atlas(wall_info info, const ImageView2D& image, + ArrayView<const wall_frame_set> rotations, + ArrayView<const wall_frame> frames) : + _array{NoInit, frames.size()}, _info(std::move(info)) +{ + fm_assert(info.depth > 0); + fm_assert(rotations.size() <= _rotations.size()); + _rotation_count = (uint8_t)rotations.size(); + for (auto i = 0uz; const auto& fr : frames) + _array[i++] = fr; + for (auto i = 0uz; const auto& r : rotations) + _rotations[i++] = r; + + _texture.setLabel(_name) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setMinificationFilter(GL::SamplerFilter::Linear) + .setMaxAnisotropy(1) + .setBorderColor(Color4{1, 0, 0, 1}) + .setStorage(1, GL::textureFormat(image.format()), image.size()) + .setSubImage(0, {}, image); +} + +wall_atlas::wall_atlas() = default; +wall_atlas::~wall_atlas() noexcept = default; +const wall_frame_set& wall_atlas::frameset(size_t i) const { return _rotations[i]; } +const wall_frame_set& wall_atlas::frameset(enum rotation r) const { return frameset(enum_to_index(r)); } +const ArrayView<const wall_frame> wall_atlas::array() const { return _array; } +StringView wall_atlas::name() const { return _info.name; } + +ArrayView<const wall_frame> wall_frames::items(const wall_atlas& a) const +{ + fm_debug_assert(index != (uint32_t)-1); + return { a.array() + index, count }; +} + +} // namespace floormat diff --git a/src/wall-atlas.hpp b/src/wall-atlas.hpp new file mode 100644 index 00000000..02d0ce91 --- /dev/null +++ b/src/wall-atlas.hpp @@ -0,0 +1,65 @@ +#pragma once +#include "src/rotation.hpp" +#include <array> +#include <Corrade/Containers/Array.h> +#include <Corrade/Containers/String.h> +#include <Magnum/Math/Vector2.h> +#include <Magnum/Math/Color.h> +#include <Magnum/GL/Texture.h> + +namespace floormat { + +struct wall_atlas; + +struct wall_frame +{ + Vector2ui offset = { (unsigned)-1, (unsigned)-1 }; +}; + +struct wall_frames +{ + uint32_t index = (uint32_t)-1, count = (uint32_t)-1; + Vector2ui pixel_size; + Color4 tint_mult{1,1,1,1}; + Color3 tint_add; + uint8_t from_rotation = (uint8_t)-1; + bool mirrored : 1 = false; + ArrayView<const wall_frame> items(const wall_atlas& a) const; +}; + +struct wall_frame_set +{ + wall_frames wall, overlay, side, top; + wall_frames corner_L, corner_R; +}; + +struct wall_info +{ + String name = "(unnamed)"_s; + float depth = 1; +}; + +struct wall_atlas final +{ + wall_atlas(); + wall_atlas(wall_info info, + const ImageView2D& image, + ArrayView<const wall_frame_set> rotations, + ArrayView<const wall_frame> frames); + ~wall_atlas() noexcept; + static size_t enum_to_index(enum rotation x); + const wall_frame_set& frameset(size_t i) const; + const wall_frame_set& frameset(enum rotation r) const; + const ArrayView<const wall_frame> array() const; + StringView name() const; + +private: + String _name; + std::array<wall_frame_set, 4> _rotations; + Array<wall_frame> _array; + GL::Texture2D _texture; + wall_info _info; + uint8_t _rotation_count = 0; +}; + +} // namespace floormat |