yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_layer_manager.h
Go to the documentation of this file.
1#ifndef YAZE_ZELDA3_DUNGEON_ROOM_LAYER_MANAGER_H
2#define YAZE_ZELDA3_DUNGEON_ROOM_LAYER_MANAGER_H
3
4#include <array>
5#include <cstdint>
6#include <vector>
7
10#include "zelda3/dungeon/room.h"
11
12namespace yaze {
13namespace zelda3 {
14
23enum class LayerType {
24 BG1_Layout, // Base BG1 tiles from room layout
25 BG1_Objects, // Objects drawn to BG1 (Layer 0, 2)
26 BG2_Layout, // Base BG2 tiles from room layout
27 BG2_Objects // Objects drawn to BG2 (Layer 1)
28};
29
33enum class LayerBlendMode {
34 Normal, // Standard alpha blending
35 Translucent, // 50% alpha
36 Addition, // Additive blending
37 Dark, // Darkened blend
38 Off // Layer hidden
39};
40
45 size_t object_index = 0;
46 bool translucent = false;
47 uint8_t alpha = 255; // 0-255 alpha value
48};
49
58 size_t object_index = 0;
59 int layer = 0; // Object's layer (0=BG1, 1=BG2, 2=BG1 priority)
60 int priority = 0; // Object layer value (not visual Z-order)
61 bool is_bg2_object = false; // True if object renders to BG2 buffer
62};
63
80 public:
82
83 // Reset to default state (all layers visible, normal blend)
84 void Reset() {
85 for (int i = 0; i < 4; ++i) {
86 layer_visible_[i] = true;
88 layer_alpha_[i] = 255;
89 }
91 bg2_on_top_ = false;
92 layers_merged_ = false;
94 use_priority_compositing_ = true; // Default to accurate SNES behavior
95 }
96
97 // Priority compositing control
98 // When enabled (default): Uses SNES Mode 1 per-tile priority for Z-ordering
99 // When disabled: Simple back-to-front layer order (BG2 behind, BG1 in front)
100 void SetPriorityCompositing(bool enabled) { use_priority_compositing_ = enabled; }
102
103 // Layer visibility
104 void SetLayerVisible(LayerType layer, bool visible) {
105 layer_visible_[static_cast<int>(layer)] = visible;
106 }
107
108 bool IsLayerVisible(LayerType layer) const {
109 return layer_visible_[static_cast<int>(layer)];
110 }
111
112 // Layer blend mode
114 layer_blend_mode_[static_cast<int>(layer)] = mode;
115 // Update alpha based on blend mode
116 switch (mode) {
118 layer_alpha_[static_cast<int>(layer)] = 255;
119 break;
121 layer_alpha_[static_cast<int>(layer)] = 180;
122 break;
124 layer_alpha_[static_cast<int>(layer)] = 220;
125 break;
127 layer_alpha_[static_cast<int>(layer)] = 120;
128 break;
130 layer_alpha_[static_cast<int>(layer)] = 0;
131 break;
132 }
133 }
134
136 return layer_blend_mode_[static_cast<int>(layer)];
137 }
138
139 uint8_t GetLayerAlpha(LayerType layer) const {
140 return layer_alpha_[static_cast<int>(layer)];
141 }
142
143 // Per-object translucency
144 void SetObjectTranslucency(size_t object_index, bool translucent,
145 uint8_t alpha = 128) {
146 // Find existing entry or add new one
147 for (auto& entry : object_translucency_) {
148 if (entry.object_index == object_index) {
149 entry.translucent = translucent;
150 entry.alpha = alpha;
151 return;
152 }
153 }
154 object_translucency_.push_back({object_index, translucent, alpha});
155 }
156
157 bool IsObjectTranslucent(size_t object_index) const {
158 for (const auto& entry : object_translucency_) {
159 if (entry.object_index == object_index) {
160 return entry.translucent;
161 }
162 }
163 return false;
164 }
165
166 uint8_t GetObjectAlpha(size_t object_index) const {
167 for (const auto& entry : object_translucency_) {
168 if (entry.object_index == object_index && entry.translucent) {
169 return entry.alpha;
170 }
171 }
172 return 255;
173 }
174
176
177 // Color math participation flag (from LayerMergeType.Layer2OnTop)
178 // NOTE: This does NOT affect draw order - BG1 is always above BG2.
179 // This flag controls whether BG2 participates in sub-screen color math
180 // effects like transparency and additive blending.
181 void SetBG2ColorMathEnabled(bool enabled) { bg2_on_top_ = enabled; }
182 bool IsBG2ColorMathEnabled() const { return bg2_on_top_; }
183
184 // Legacy aliases for compatibility
185 void SetBG2OnTop(bool on_top) { bg2_on_top_ = on_top; }
186 bool IsBG2OnTop() const { return bg2_on_top_; }
187
188 // Apply layer settings to room from LayerMergeType
189 // NOTE: This affects BLEND MODES and COLOR MATH, not draw order.
190 // SNES Mode 1 always renders BG1 above BG2 - this is hardware behavior.
191 // Layer visibility checkboxes remain independent of merge type.
192 void ApplyLayerMerging(const LayerMergeType& merge_type) {
193 // Store the current merge type for queries
194 current_merge_type_id_ = merge_type.ID;
195 layers_merged_ = (merge_type.ID != 0); // ID 0 = "Off" = not merged
196
197 // Set BG2 color math participation (does NOT change draw order)
199
200 // Apply blend mode based on merge type
201 // NOTE: Layer2Visible from ROM is informational only - user can still
202 // enable/disable layers via checkboxes. We only set blend modes here.
203 // Layer2Translucent = true means BG2 should use translucent blend
204 if (merge_type.Layer2Translucent) {
207 } else {
210 }
211
212 // BG1 blend mode depends on merge type
213 // DarkRoom (ID 0x08) should darken BG1 to simulate unlit room
214 if (merge_type.ID == 0x08) {
215 // Dark room - BG1 is dimmed (reduced brightness)
218 } else {
221 }
222 }
223
231 // Store visibility before applying
232 bool bg1_layout_vis = IsLayerVisible(LayerType::BG1_Layout);
233 bool bg1_objects_vis = IsLayerVisible(LayerType::BG1_Objects);
234 bool bg2_layout_vis = IsLayerVisible(LayerType::BG2_Layout);
235 bool bg2_objects_vis = IsLayerVisible(LayerType::BG2_Objects);
236
237 // Apply merge settings
238 ApplyLayerMerging(merge_type);
239
240 // Restore visibility
241 SetLayerVisible(LayerType::BG1_Layout, bg1_layout_vis);
242 SetLayerVisible(LayerType::BG1_Objects, bg1_objects_vis);
243 SetLayerVisible(LayerType::BG2_Layout, bg2_layout_vis);
244 SetLayerVisible(LayerType::BG2_Objects, bg2_objects_vis);
245 }
246
251 bool AreLayersMerged() const { return layers_merged_; }
252
257 uint8_t GetMergeTypeId() const { return current_merge_type_id_; }
258
259 // ============================================================================
260 // Object Layer Assignment
261 // ============================================================================
262
278 int GetObjectLayerValue(int object_layer) const {
279 return object_layer;
280 }
281
282 // Legacy function - kept for API compatibility
283 // No longer affects visual order since SNES Mode 1 is fixed (BG1 > BG2)
284 int CalculateObjectPriority(int object_layer) const {
285 return object_layer * 10;
286 }
287
293 static bool IsObjectOnBG2(int object_layer) {
294 // Layer 1 objects render to BG2, layers 0 and 2 render to BG1
295 return object_layer == 1;
296 }
297
303 static LayerType GetObjectLayerType(int object_layer) {
304 return IsObjectOnBG2(object_layer) ? LayerType::BG2_Objects
306 }
307
321 std::array<LayerType, 4> GetDrawOrder() const {
322 // Standard SNES Mode 1 order: BG2 behind BG1
323 // bg2_on_top_ affects blend modes, not draw order
326 }
327
332 switch (layer) {
334 return room.bg1_buffer();
336 return room.object_bg1_buffer();
338 return room.bg2_buffer();
340 return room.object_bg2_buffer();
341 }
342 // Fallback (should never reach)
343 return room.bg1_buffer();
344 }
345
346 static const gfx::BackgroundBuffer& GetLayerBuffer(const Room& room,
347 LayerType layer) {
348 switch (layer) {
350 return room.bg1_buffer();
352 return room.object_bg1_buffer();
354 return room.bg2_buffer();
356 return room.object_bg2_buffer();
357 }
358 // Fallback (should never reach)
359 return room.bg1_buffer();
360 }
361
365 static const char* GetLayerName(LayerType layer) {
366 switch (layer) {
368 return "BG1 Layout";
370 return "BG1 Objects";
372 return "BG2 Layout";
374 return "BG2 Objects";
375 }
376 return "Unknown";
377 }
378
382 static const char* GetBlendModeName(LayerBlendMode mode) {
383 switch (mode) {
385 return "Normal";
387 return "Translucent";
389 return "Addition";
391 return "Dark";
393 return "Off";
394 }
395 return "Unknown";
396 }
397
407 void ApplySurfaceColorMod(SDL_Surface* surface) const {
408 if (!surface) return;
409
410 if (current_merge_type_id_ == 0x08) {
411 // DarkRoom: 50% brightness
412 SDL_SetSurfaceColorMod(surface, 128, 128, 128);
413 } else {
414 // Normal: Full brightness
415 SDL_SetSurfaceColorMod(surface, 255, 255, 255);
416 }
417 }
418
419 // ============================================================================
420 // Layer Compositing
421 // ============================================================================
422
455 void CompositeToOutput(Room& room, gfx::Bitmap& output) const;
456
457 private:
467 static bool IsTransparent(uint8_t pixel) {
468 return pixel == 255;
469 }
470
471 std::array<bool, 4> layer_visible_;
472 std::array<LayerBlendMode, 4> layer_blend_mode_;
473 std::array<uint8_t, 4> layer_alpha_;
474 std::vector<ObjectTranslucency> object_translucency_;
475
476 // Color math participation flag (from ROM's Layer2OnTop)
477 // NOTE: Does NOT affect draw order - BG1 is always above BG2 per SNES Mode 1.
478 // This controls whether BG2 participates in sub-screen color math effects.
479 bool bg2_on_top_ = false;
480
481 // Merge state tracking
482 bool layers_merged_ = false;
484
485 // DEPRECATED: Priority compositing is no longer used.
486 // The correct SNES behavior uses simple back-to-front layer ordering:
487 // BG2 (background) first, then BG1 (foreground) on top.
488 // Per-tile priority is encoded in the tile data itself.
489 // Kept for API compatibility.
491};
492
493} // namespace zelda3
494} // namespace yaze
495
496#endif // YAZE_ZELDA3_DUNGEON_ROOM_LAYER_MANAGER_H
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
RoomLayerManager - Manages layer visibility and compositing.
uint8_t GetObjectAlpha(size_t object_index) const
static bool IsObjectOnBG2(int object_layer)
Check if an object on a given layer should render to BG2.
void SetPriorityCompositing(bool enabled)
static const char * GetBlendModeName(LayerBlendMode mode)
Get blend mode name.
std::array< LayerBlendMode, 4 > layer_blend_mode_
static gfx::BackgroundBuffer & GetLayerBuffer(Room &room, LayerType layer)
Get the bitmap buffer for a layer type.
static const char * GetLayerName(LayerType layer)
Get human-readable name for layer type.
void SetBG2ColorMathEnabled(bool enabled)
static const gfx::BackgroundBuffer & GetLayerBuffer(const Room &room, LayerType layer)
static bool IsTransparent(uint8_t pixel)
Check if a pixel index represents transparency.
int CalculateObjectPriority(int object_layer) const
void SetLayerBlendMode(LayerType layer, LayerBlendMode mode)
void ApplyLayerMerging(const LayerMergeType &merge_type)
void SetLayerVisible(LayerType layer, bool visible)
void ApplySurfaceColorMod(SDL_Surface *surface) const
Apply surface color modulation for DarkRoom effect.
std::array< LayerType, 4 > GetDrawOrder() const
Get the draw order for layers.
bool IsLayerVisible(LayerType layer) const
void SetObjectTranslucency(size_t object_index, bool translucent, uint8_t alpha=128)
std::array< uint8_t, 4 > layer_alpha_
LayerBlendMode GetLayerBlendMode(LayerType layer) const
static LayerType GetObjectLayerType(int object_layer)
Get the appropriate background layer type for an object.
std::array< bool, 4 > layer_visible_
bool IsObjectTranslucent(size_t object_index) const
bool AreLayersMerged() const
Check if layers are currently merged.
uint8_t GetMergeTypeId() const
Get the current merge type ID.
void CompositeToOutput(Room &room, gfx::Bitmap &output) const
Composite all visible layers into a single output bitmap.
void ApplyLayerMergingPreserveVisibility(const LayerMergeType &merge_type)
Apply layer merge settings without changing visibility.
uint8_t GetLayerAlpha(LayerType layer) const
int GetObjectLayerValue(int object_layer) const
Get object layer value for buffer assignment.
std::vector< ObjectTranslucency > object_translucency_
auto & object_bg2_buffer()
Definition room.h:548
auto & bg1_buffer()
Definition room.h:542
auto & object_bg1_buffer()
Definition room.h:546
auto & bg2_buffer()
Definition room.h:543
LayerBlendMode
Layer blend modes for compositing.
LayerType
Layer types for the 4-way visibility system.
Object metadata for tracking layer assignment.
Per-object translucency settings.