yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
object_geometry.h
Go to the documentation of this file.
1#ifndef YAZE_ZELDA3_DUNGEON_GEOMETRY_OBJECT_GEOMETRY_H
2#define YAZE_ZELDA3_DUNGEON_GEOMETRY_OBJECT_GEOMETRY_H
3
4#include <cstdint>
5#include <optional>
6#include <tuple>
7#include <unordered_map>
8#include <vector>
9
10#include "absl/status/statusor.h"
13
14namespace yaze {
15namespace zelda3 {
16
21 int x_tiles = 0;
22 int y_tiles = 0;
23 int width_tiles = 0;
24 int height_tiles = 0;
25
26 int width_pixels() const { return width_tiles * 8; }
27 int height_pixels() const { return height_tiles * 8; }
28};
29
44 // Full rendered area (bounding box of all drawn tiles)
45 int min_x_tiles = 0;
46 int min_y_tiles = 0;
47 int width_tiles = 0;
48 int height_tiles = 0;
49
50 // Layer information for BG2 masking
51 bool is_bg2_overlay = false; // True if this object draws to BG2 (Layer 1)
52
53 // Optional tighter selection hitbox for non-rectangular shapes
54 // If not set, selection uses the full render bounds
55 std::optional<SelectionRect> selection_bounds;
56
57 int max_x_tiles() const { return min_x_tiles + width_tiles; }
58 int max_y_tiles() const { return min_y_tiles + height_tiles; }
59 int width_pixels() const { return width_tiles * 8; }
60 int height_pixels() const { return height_tiles * 8; }
61
62 // Pixel-based accessors for the mask rectangle
63 int min_x_pixels() const { return min_x_tiles * 8; }
64 int min_y_pixels() const { return min_y_tiles * 8; }
65
73 bool RequiresBG1Mask() const { return is_bg2_overlay && width_tiles > 0; }
74
81 bool HasTighterSelectionBounds() const { return selection_bounds.has_value(); }
82
94
102 std::tuple<int, int, int, int> GetBG1MaskRect(int obj_x, int obj_y) const {
103 int start_x = (obj_x + min_x_tiles) * 8;
104 int start_y = (obj_y + min_y_tiles) * 8;
105 return {start_x, start_y, width_pixels(), height_pixels()};
106 }
107};
108
118 public:
119 static ObjectGeometry& Get();
120
121 // Look up routine metadata by routine ID. Returns nullptr on unknown IDs.
122 const DrawRoutineInfo* LookupRoutine(int routine_id) const;
123
124 // Measure bounds by routine id using the object's size/id bits.
125 absl::StatusOr<GeometryBounds> MeasureByRoutineId(
126 int routine_id, const RoomObject& object) const;
127
128 // Measure bounds by object ID (resolves object_id -> routine_id internally).
129 // Results are cached by (routine_id, object_id, size).
130 absl::StatusOr<GeometryBounds> MeasureByObjectId(
131 const RoomObject& object) const;
132
133 // Clear the measurement cache (e.g., after routine registry changes).
134 void ClearCache();
135
136 // Measure bounds for a specific routine metadata entry.
137 absl::StatusOr<GeometryBounds> MeasureRoutine(
138 const DrawRoutineInfo& routine, const RoomObject& object) const;
139
151 absl::StatusOr<GeometryBounds> MeasureForLayerCompositing(
152 int routine_id, const RoomObject& object) const;
153
160 static bool IsLayerOneRoutine(int routine_id);
161
168 static bool IsDiagonalCeilingRoutine(int routine_id);
169
181 int routine_id);
182
183 private:
185 void BuildRegistry();
186
187 // Cache key: combines routine_id, object_id, and size into a single uint64.
188 struct CacheKey {
190 int16_t object_id;
191 uint8_t size;
192 bool operator==(const CacheKey& o) const {
193 return routine_id == o.routine_id && object_id == o.object_id &&
194 size == o.size;
195 }
196 };
198 size_t operator()(const CacheKey& k) const {
199 return std::hash<uint64_t>()(
200 (static_cast<uint64_t>(k.routine_id) << 32) |
201 (static_cast<uint64_t>(static_cast<uint16_t>(k.object_id)) << 8) |
202 k.size);
203 }
204 };
205
206 std::vector<DrawRoutineInfo> routines_;
207 std::unordered_map<int, DrawRoutineInfo> routine_map_;
208 mutable std::unordered_map<CacheKey, GeometryBounds, CacheKeyHash> cache_;
209};
210
211} // namespace zelda3
212} // namespace yaze
213
214#endif // YAZE_ZELDA3_DUNGEON_GEOMETRY_OBJECT_GEOMETRY_H
Side-car geometry engine that replays draw routines against an off-screen buffer to calculate real ex...
static bool IsDiagonalCeilingRoutine(int routine_id)
Check if a routine ID corresponds to a diagonal ceiling.
std::vector< DrawRoutineInfo > routines_
absl::StatusOr< GeometryBounds > MeasureForLayerCompositing(int routine_id, const RoomObject &object) const
Measure bounds for a BG2 overlay object and mark it for masking.
absl::StatusOr< GeometryBounds > MeasureRoutine(const DrawRoutineInfo &routine, const RoomObject &object) const
std::unordered_map< int, DrawRoutineInfo > routine_map_
absl::StatusOr< GeometryBounds > MeasureByObjectId(const RoomObject &object) const
const DrawRoutineInfo * LookupRoutine(int routine_id) const
absl::StatusOr< GeometryBounds > MeasureByRoutineId(int routine_id, const RoomObject &object) const
static GeometryBounds ApplySelectionBounds(GeometryBounds render_bounds, int routine_id)
Compute tighter selection bounds for diagonal shapes.
static ObjectGeometry & Get()
std::unordered_map< CacheKey, GeometryBounds, CacheKeyHash > cache_
static bool IsLayerOneRoutine(int routine_id)
Get list of routine IDs that draw to BG2 layer.
Metadata about a draw routine.
Bounding box result for a draw routine execution.
std::tuple< int, int, int, int > GetBG1MaskRect(int obj_x, int obj_y) const
Get the BG1 mask rectangle in pixel coordinates.
std::optional< SelectionRect > selection_bounds
SelectionRect GetSelectionBounds() const
Get the selection bounds for hit testing.
bool RequiresBG1Mask() const
Check if this object requires BG1 masking.
bool HasTighterSelectionBounds() const
Check if this object has a tighter selection hitbox.
size_t operator()(const CacheKey &k) const
bool operator==(const CacheKey &o) const
Simple rectangle for selection bounds.