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
17class DungeonState;
18
23 int x_tiles = 0;
24 int y_tiles = 0;
25 int width_tiles = 0;
26 int height_tiles = 0;
27
28 int width_pixels() const { return width_tiles * 8; }
29 int height_pixels() const { return height_tiles * 8; }
30};
31
46 // Full rendered area (bounding box of all drawn tiles)
47 int min_x_tiles = 0;
48 int min_y_tiles = 0;
49 int width_tiles = 0;
50 int height_tiles = 0;
51
52 // Layer information for BG2 masking
53 bool is_bg2_overlay = false; // True if this object draws to BG2 (Layer 1)
54
55 // Optional tighter selection hitbox for non-rectangular shapes
56 // If not set, selection uses the full render bounds
57 std::optional<SelectionRect> selection_bounds;
58
59 int max_x_tiles() const { return min_x_tiles + width_tiles; }
60 int max_y_tiles() const { return min_y_tiles + height_tiles; }
61 int width_pixels() const { return width_tiles * 8; }
62 int height_pixels() const { return height_tiles * 8; }
63
64 // Pixel-based accessors for the mask rectangle
65 int min_x_pixels() const { return min_x_tiles * 8; }
66 int min_y_pixels() const { return min_y_tiles * 8; }
67
75 bool RequiresBG1Mask() const { return is_bg2_overlay && width_tiles > 0; }
76
84 return selection_bounds.has_value();
85 }
86
98
106 std::tuple<int, int, int, int> GetBG1MaskRect(int obj_x, int obj_y) const {
107 int start_x = (obj_x + min_x_tiles) * 8;
108 int start_y = (obj_y + min_y_tiles) * 8;
109 return {start_x, start_y, width_pixels(), height_pixels()};
110 }
111};
112
122 public:
123 static ObjectGeometry& Get();
124
125 // Look up routine metadata by routine ID. Returns nullptr on unknown IDs.
126 const DrawRoutineInfo* LookupRoutine(int routine_id) const;
127
128 // Measure bounds by routine id using the object's size/id bits.
129 absl::StatusOr<GeometryBounds> MeasureByRoutineId(
130 int routine_id, const RoomObject& object) const;
131
132 // Measure bounds by object ID (resolves object_id -> routine_id internally).
133 // Results are cached by (routine_id, object_id, size).
134 absl::StatusOr<GeometryBounds> MeasureByObjectId(
135 const RoomObject& object) const;
136
137 // Measure the concrete footprint produced for an explicit runtime state.
138 // Unlike MeasureByObjectId(), this does not substitute the largest known
139 // stateful footprint and is therefore suitable for draw-trace validation.
140 // A null state measures the routine's default branch.
141 absl::StatusOr<GeometryBounds> MeasureByObjectIdForState(
142 const RoomObject& object, const DungeonState* state) const;
143
144 // Clear the measurement cache (e.g., after routine registry changes).
145 void ClearCache();
146
147 // Measure bounds for a specific routine metadata entry.
148 absl::StatusOr<GeometryBounds> MeasureRoutine(const DrawRoutineInfo& routine,
149 const RoomObject& object) const;
150
151 // State-specific counterpart to MeasureRoutine(). This bypasses the
152 // editor-selection policy that intentionally measures the largest stable
153 // state for objects such as the empty water face.
154 absl::StatusOr<GeometryBounds> MeasureRoutineForState(
155 const DrawRoutineInfo& routine, const RoomObject& object,
156 const DungeonState* state) const;
157
172 std::pair<int, int> ResolveAnchor(int16_t object_id, uint8_t size_byte) const;
173
185 absl::StatusOr<GeometryBounds> MeasureForLayerCompositing(
186 int routine_id, const RoomObject& object) const;
187
194 static bool IsLayerOneRoutine(int routine_id);
195
202 static bool IsDiagonalCeilingRoutine(int routine_id);
203
215 int routine_id);
216
217 private:
219 void BuildRegistry();
220
221 // Cache key: combines routine_id, object_id, and size into a single uint64.
222 struct CacheKey {
224 int16_t object_id;
225 uint8_t size;
226 bool operator==(const CacheKey& o) const {
227 return routine_id == o.routine_id && object_id == o.object_id &&
228 size == o.size;
229 }
230 };
232 size_t operator()(const CacheKey& k) const {
233 return std::hash<uint64_t>()(
234 (static_cast<uint64_t>(k.routine_id) << 32) |
235 (static_cast<uint64_t>(static_cast<uint16_t>(k.object_id)) << 8) |
236 k.size);
237 }
238 };
239
240 std::vector<DrawRoutineInfo> routines_;
241 std::unordered_map<int, DrawRoutineInfo> routine_map_;
242 mutable std::unordered_map<CacheKey, GeometryBounds, CacheKeyHash> cache_;
243};
244
245} // namespace zelda3
246} // namespace yaze
247
248#endif // YAZE_ZELDA3_DUNGEON_GEOMETRY_OBJECT_GEOMETRY_H
Interface for accessing dungeon game state.
Side-car geometry engine that replays draw routines against an off-screen buffer to calculate real ex...
std::pair< int, int > ResolveAnchor(int16_t object_id, uint8_t size_byte) const
Resolve the canvas anchor (x, y) for a given object's draw routine.
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 > MeasureByObjectIdForState(const RoomObject &object, const DungeonState *state) const
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.
absl::StatusOr< GeometryBounds > MeasureRoutineForState(const DrawRoutineInfo &routine, const RoomObject &object, const DungeonState *state) const
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.