Porytiles
Loading...
Searching...
No Matches
anim_tile_matcher.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <ranges>
5
8
9namespace porytiles {
10
12 const std::string &anim_name,
13 const Animation<Rgba32> &animation,
14 std::size_t tile_offset,
15 const Rgba32 &extrinsic_transparency,
16 bool is_cross_tileset)
17{
18 if (animation.frames().empty()) {
19 panic("animation must have at least one frame");
20 }
21
22 // Use key frame if available, otherwise fall back to first regular frame (manual mode).
23 const AnimFrame<Rgba32> &representative_frame =
24 animation.has_key_frame() ? animation.key_frame() : animation.frames().begin()->second;
25 const auto &tiles = representative_frame.tiles();
26
27 for (std::size_t i = 0; i < tiles.size(); ++i) {
28 const PixelTile<Rgba32> &tile = tiles[i];
29
30 // Skip transparent tiles. They don't need to be matched.
31 // Transparent tiles are valid for animations without a key frame (manual linking).
32 // For key frame animations, validate_anim_frames() catches transparent tiles earlier.
33 if (tile.is_transparent(extrinsic_transparency)) {
34 continue;
35 }
36
37 // Canonicalize the tile under this entry's ET and track which flips were applied. ET-aware
38 // canonicalization guarantees that tiles with identical opaque-pixel patterns but different transparent
39 // colors land on equivalent orientations under the map's cross-ET comparator.
40 CanonicalPixelTile canonical{tile, extrinsic_transparency};
41
42 KeyframeKey key{static_cast<const PixelTile<Rgba32> &>(canonical), extrinsic_transparency};
43 if (lookup_map_.contains(key)) {
44 // For key frame animations, validate_anim_frames() catches duplicates before we get here.
45 // For non-key-frame animations, duplicate tiles across animations are valid. Skip registration
46 // and let the first registration win.
47 continue;
48 }
49
50 // Calculate absolute tile index in tiles.png
51 const std::size_t absolute_index = tile_offset + i;
52
53 lookup_map_[key] = KeyframeTileInfo{
54 anim_name,
55 absolute_index,
56 i,
57 canonical.h_flip(),
58 canonical.v_flip(),
59 is_cross_tileset,
60 };
61 }
62
63 total_tiles_ += tiles.size();
64
65 if (is_cross_tileset && animation_registrations_.contains(anim_name)) {
66 panic("cross-tileset animation '" + anim_name + "' has the same name as an already-registered animation");
67 }
68 animation_registrations_.try_emplace(anim_name, AnimRegistration{tile_offset, tiles.size()});
69}
70
71std::optional<AnimTileMatch>
72AnimTileMatcher::find_match(const CanonicalPixelTile<Rgba32> &tile, const Rgba32 &extrinsic_transparency) const
73{
74 KeyframeKey lookup_key{static_cast<const PixelTile<Rgba32> &>(tile), extrinsic_transparency};
75
76 const auto it = lookup_map_.find(lookup_key);
77 if (it == lookup_map_.end()) {
78 return std::nullopt;
79 }
80
81 const KeyframeTileInfo &info = it->second;
82
83 // Calculate the effective flip bits
84 // The stored flip tells us how the keyframe tile was flipped to reach canonical form
85 // The input tile's flip tells us how it was flipped to reach canonical form
86 // XOR gives us the flip needed to go from keyframe tile to input tile
87 bool effective_h_flip = info.h_flip != tile.h_flip(); // NOLINT
88 bool effective_v_flip = info.v_flip != tile.v_flip(); // NOLINT
89
90 return AnimTileMatch{
91 info.anim_name,
92 info.tile_index,
93 info.keyframe_tile_idx,
94 effective_h_flip,
95 effective_v_flip,
96 info.is_cross_tileset,
97 };
98}
99
100std::optional<std::size_t> AnimTileMatcher::tile_offset_for(const std::string &anim_name) const
101{
102 const auto it = animation_registrations_.find(anim_name);
103 if (it == animation_registrations_.end()) {
104 return std::nullopt;
105 }
106 return it->second.tile_offset;
107}
108
109std::optional<std::size_t> AnimTileMatcher::tile_count_for(const std::string &anim_name) const
110{
111 const auto it = animation_registrations_.find(anim_name);
112 if (it == animation_registrations_.end()) {
113 return std::nullopt;
114 }
115 return it->second.tile_count;
116}
117
118bool AnimTileMatcher::is_in_animation_range(std::size_t tile_index) const
119{
120 return std::ranges::any_of(animation_registrations_ | std::views::values, [tile_index](const auto &reg) {
121 return tile_index >= reg.tile_offset && tile_index < reg.tile_offset + reg.tile_count;
122 });
123}
124
126{
127 lookup_map_.clear();
128 animation_registrations_.clear();
129 total_tiles_ = 0;
130}
131
132} // namespace porytiles
Represents a single frame of an animation, containing tiles and a frame name.
const std::vector< PixelTile< PixelType > > & tiles() const
void clear()
Clears all registered animations.
std::optional< std::size_t > tile_count_for(const std::string &anim_name) const
Returns the tile count for a registered animation.
bool is_in_animation_range(std::size_t tile_index) const
Checks whether a tile index falls within any registered animation range.
void register_animation(const std::string &anim_name, const Animation< Rgba32 > &animation, std::size_t tile_offset, const Rgba32 &extrinsic_transparency, bool is_cross_tileset=false)
Registers an animation's keyframe tiles for matching.
std::optional< AnimTileMatch > find_match(const CanonicalPixelTile< Rgba32 > &tile, const Rgba32 &extrinsic_transparency) const
Attempts to match a tile against registered animation keyframes.
std::optional< std::size_t > tile_offset_for(const std::string &anim_name) const
Returns the tile offset for a registered animation.
A complete tileset animation with name, configuration, and frame data.
Definition animation.hpp:78
bool has_key_frame() const
Checks if this animation has a key frame set.
const AnimFrame< PixelType > & key_frame() const
Returns the key frame of this animation.
const std::map< std::string, AnimFrame< PixelType > > & frames() const
A PixelTile representation that stores the canonical (lexicographically minimal) orientation among al...
bool h_flip() const
Returns the horizontal flip flag.
bool v_flip() const
Returns the vertical flip flag.
An 8x8 tile backed by literal-array-based per-pixel storage of an arbitrary pixel type.
bool is_transparent() const
Checks if this entire PixelTile is transparent (intrinsic transparency only).
Represents a 32-bit RGBA color.
Definition rgba32.hpp:23
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43
@ canonical
Export tiles in canonical form without applying flip transformations.
Result of matching a tile against animation keyframes.
std::string anim_name
Name of the animation this keyframe belongs to.