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
224 // Apply room effect-specific visual hints on top of merge settings.
225 // This keeps the editor preview closer to in-game behavior for effect-driven
226 // rooms (e.g. Moving Water) without changing layer visibility policy.
228 switch (effect) {
230 // Water rooms: BG2 shows water with translucent overlay on BG1.
231 // SNES uses HDMA-driven color math to blend water layer.
236 }
241 }
242 break;
243
245 // Conveyor belt rooms: BG2 scrolls independently.
246 // No blend change needed - BG2 is opaque floor tiles.
247 break;
248
250 // Dark rooms where lighting a torch reveals BG2 floor.
251 // BG1 is the dark overlay, BG2 is the revealed floor.
252 // In-game, HDMA window controls which scanlines show BG2.
253 // For editor preview: darken BG1, keep BG2 visible.
256 break;
257
259 // Lightning/flash effect (Ganon fight). No persistent blend change.
260 break;
261
263 // Ganon's room: special rendering with translucent BG2 for
264 // the Triforce floor pattern showing through.
269 }
270 break;
271
272 default:
273 break;
274 }
275 }
276
284 // Store visibility before applying
285 bool bg1_layout_vis = IsLayerVisible(LayerType::BG1_Layout);
286 bool bg1_objects_vis = IsLayerVisible(LayerType::BG1_Objects);
287 bool bg2_layout_vis = IsLayerVisible(LayerType::BG2_Layout);
288 bool bg2_objects_vis = IsLayerVisible(LayerType::BG2_Objects);
289
290 // Apply merge settings
291 ApplyLayerMerging(merge_type);
292
293 // Restore visibility
294 SetLayerVisible(LayerType::BG1_Layout, bg1_layout_vis);
295 SetLayerVisible(LayerType::BG1_Objects, bg1_objects_vis);
296 SetLayerVisible(LayerType::BG2_Layout, bg2_layout_vis);
297 SetLayerVisible(LayerType::BG2_Objects, bg2_objects_vis);
298 }
299
304 bool AreLayersMerged() const { return layers_merged_; }
305
310 uint8_t GetMergeTypeId() const { return current_merge_type_id_; }
311
312 // ============================================================================
313 // Object Layer Assignment
314 // ============================================================================
315
331 int GetObjectLayerValue(int object_layer) const {
332 return object_layer;
333 }
334
335 // Legacy function - kept for API compatibility
336 // No longer affects visual order since SNES Mode 1 is fixed (BG1 > BG2)
337 int CalculateObjectPriority(int object_layer) const {
338 return object_layer * 10;
339 }
340
346 static bool IsObjectOnBG2(int object_layer) {
347 // Layer 1 objects render to BG2, layers 0 and 2 render to BG1
348 return object_layer == 1;
349 }
350
356 static LayerType GetObjectLayerType(int object_layer) {
357 return IsObjectOnBG2(object_layer) ? LayerType::BG2_Objects
359 }
360
374 std::array<LayerType, 4> GetDrawOrder() const {
375 // Standard SNES Mode 1 order: BG2 behind BG1
376 // bg2_on_top_ affects blend modes, not draw order
379 }
380
385 switch (layer) {
387 return room.bg1_buffer();
389 return room.object_bg1_buffer();
391 return room.bg2_buffer();
393 return room.object_bg2_buffer();
394 }
395 // Fallback (should never reach)
396 return room.bg1_buffer();
397 }
398
399 static const gfx::BackgroundBuffer& GetLayerBuffer(const Room& room,
400 LayerType layer) {
401 switch (layer) {
403 return room.bg1_buffer();
405 return room.object_bg1_buffer();
407 return room.bg2_buffer();
409 return room.object_bg2_buffer();
410 }
411 // Fallback (should never reach)
412 return room.bg1_buffer();
413 }
414
418 static const char* GetLayerName(LayerType layer) {
419 switch (layer) {
421 return "BG1 Layout";
423 return "BG1 Objects";
425 return "BG2 Layout";
427 return "BG2 Objects";
428 }
429 return "Unknown";
430 }
431
435 static const char* GetBlendModeName(LayerBlendMode mode) {
436 switch (mode) {
438 return "Normal";
440 return "Translucent";
442 return "Addition";
444 return "Dark";
446 return "Off";
447 }
448 return "Unknown";
449 }
450
460 void ApplySurfaceColorMod(SDL_Surface* surface) const {
461 if (!surface) return;
462
463 if (current_merge_type_id_ == 0x08) {
464 // DarkRoom: 50% brightness
465 SDL_SetSurfaceColorMod(surface, 128, 128, 128);
466 } else {
467 // Normal: Full brightness
468 SDL_SetSurfaceColorMod(surface, 255, 255, 255);
469 }
470 }
471
472 // ============================================================================
473 // Layer Compositing
474 // ============================================================================
475
508 void CompositeToOutput(Room& room, gfx::Bitmap& output) const;
509
510 private:
521 static bool IsTransparent(uint8_t pixel) {
522 return pixel == 255;
523 }
524
525 std::array<bool, 4> layer_visible_;
526 std::array<LayerBlendMode, 4> layer_blend_mode_;
527 std::array<uint8_t, 4> layer_alpha_;
528 std::vector<ObjectTranslucency> object_translucency_;
529
530 // Color math participation flag (from ROM's Layer2OnTop)
531 // NOTE: Does NOT affect draw order - BG1 is always above BG2 per SNES Mode 1.
532 // This controls whether BG2 participates in sub-screen color math effects.
533 bool bg2_on_top_ = false;
534
535 // Merge state tracking
536 bool layers_merged_ = false;
538
539 // When enabled, CompositeToOutput uses per-pixel priority buffers to emulate
540 // SNES Mode 1 ordering (BG2 pri=1 can appear above BG1 pri=0).
542};
543
544} // namespace zelda3
545} // namespace yaze
546
547#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.
void ApplyRoomEffect(EffectKey effect)
std::vector< ObjectTranslucency > object_translucency_
auto & object_bg2_buffer()
Definition room.h:636
auto & bg1_buffer()
Definition room.h:630
auto & object_bg1_buffer()
Definition room.h:634
auto & bg2_buffer()
Definition room.h:631
LayerBlendMode
Layer blend modes for compositing.
@ Ganon_Room
Definition room.h:89
@ Moving_Floor
Definition room.h:84
@ Red_Flashes
Definition room.h:87
@ Moving_Water
Definition room.h:85
@ Torch_Show_Floor
Definition room.h:88
LayerType
Layer types for the 4-way visibility system.
Object metadata for tracking layer assignment.
Per-object translucency settings.