yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sprite_graphics_context.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <array>
5#include <set>
6#include <string>
7#include <vector>
8
9#include "absl/strings/str_format.h"
13#include "zelda3/game_data.h"
16
18
19// Capture one OAM entry in one selected ZSM frame. A 16x16 entry may cross a
20// sheet slot, so resolve and validate each 8x8 tile independently.
21absl::StatusOr<GraphicsEditContext> Make(const zsprite::Frame& frame,
22 int frame_index, int oam_index,
23 const std::array<uint8_t, 8>& slots,
24 const gfx::PaletteGroup& palettes,
25 const zelda3::GameData* owner,
26 const std::string& source_label,
27 const std::string& sheet_source) {
28 if (!owner || oam_index < 0 ||
29 oam_index >= static_cast<int>(frame.Tiles.size())) {
30 return absl::InvalidArgumentError("Select a sprite frame and OAM tile");
31 }
32 const auto& oam = frame.Tiles[oam_index];
33 if (oam.palette >= palettes.size() ||
34 palettes.palette_ref(oam.palette).size() < 8) {
35 return absl::FailedPreconditionError("Selected OAM palette is unresolved");
36 }
37 const auto& row = palettes.palette_ref(oam.palette);
38 std::vector<gfx::SnesColor> colors;
39 for (int i = 0; i < 8; ++i)
40 colors.push_back(row[i]);
41 GraphicsEditContext context;
42 context.owner = owner;
43 context.label =
44 absl::StrFormat("%s / frame %d / OAM %d / palette %d", source_label,
45 frame_index, oam_index, oam.palette);
46 context.limitation =
47 "Captured pose and sheet assignments; re-enter from Sprite Editor after "
48 "changing either.";
49 context.resolved.surface = "sprite_oam";
50 context.resolved.graphics_loaded = true;
51 context.resolved.id = frame_index;
52 context.resolved.palette_colors_resolved = true;
53 context.resolved.loader_agrees = true;
54 context.resolved.palettes.push_back(
55 {absl::StrFormat("OAM palette row %d", oam.palette), sheet_source,
56 oam.palette, false});
57 for (int slot = 0; slot < 8; ++slot) {
58 const uint16_t sheet = slots[slot];
59 const bool loaded = owner->sheet_store.HasSheet(sheet);
60 context.resolved.slots.push_back({
61 .slot = slot,
62 .role = slot < 4 ? "static_sprite" : "spriteset_sprite",
63 .palette_group = absl::StrFormat("sprite_row_%d", oam.palette),
64 .sheet_id = sheet,
65 .source = sheet_source,
66 .loader_agrees = loaded,
67 .store_revision = owner->sheet_store.Revision(sheet),
68 .width = 128,
69 .height = 32,
70 });
71 context.resolved.loader_agrees &= loaded;
72 }
73 const int offsets[] = {0, 1, 16, 17};
74 const int count = oam.size ? 4 : 1;
75 for (int i = 0; i < count; ++i) {
76 const int tile = static_cast<int>(oam.id) + offsets[i];
77 if (tile < 0 || tile >= 512) {
78 return absl::OutOfRangeError("Selected OAM tile extends past tile 0x1FF");
79 }
80 const int slot = zelda3::SpriteSlotForTile(tile);
81 const uint16_t sheet = slots[slot];
84 !owner->sheet_store.HasSheet(sheet)) {
85 return absl::FailedPreconditionError(absl::StrFormat(
86 "OAM tile %03X uses unavailable or 2bpp sheet %02X", tile, sheet));
87 }
88 context.targets.push_back({
89 .sheet_id = sheet,
90 .tile_index = static_cast<uint16_t>(tile % 64),
91 .label = absl::StrFormat(
92 "Frame %d / OAM %d / tile %03X / slot %d / sheet %02X / palette %d",
93 frame_index, oam_index, tile, slot, sheet, oam.palette),
94 .palette = gfx::SnesPalette(colors),
95 });
96 }
97 return context;
98}
99
100// The frame, assignments and colors are captured at navigation time; pixels
101// always come from the owning session's sheet store, including unsaved strokes.
102absl::StatusOr<usage_preview::PreviewImage> RenderCapturedFrame(
103 const zsprite::Frame& frame, const std::array<uint8_t, 8>& slots,
104 const gfx::PaletteGroup& palettes, const zelda3::GameData* owner) {
105 if (!owner)
106 return absl::FailedPreconditionError("Sprite ROM session ended");
107 std::set<uint16_t> ids(slots.begin(), slots.end());
108 const auto sheets = usage_preview::SnapshotSessionSheets(ids, owner);
109 std::vector<uint8_t> buffer(0x10000, 0);
110 for (size_t slot = 0; slot < slots.size(); ++slot) {
111 const auto it = sheets.find(slots[slot]);
112 if (it == sheets.end() || it->second.size() != 4096) {
113 return absl::FailedPreconditionError(
114 "Sprite sheet unavailable in source session");
115 }
116 std::copy(it->second.begin(), it->second.end(),
117 buffer.begin() + slot * 4096);
118 }
119 constexpr int kSize = 256;
120 gfx::Bitmap canvas(kSize, kSize, 8, std::vector<uint8_t>(kSize * kSize, 0));
121 SpriteDrawer drawer(buffer.data());
122 drawer.DrawFrame(canvas, frame, 128, 128);
124 image.width = kSize;
125 image.height = kSize;
126 image.pixels = canvas.vector();
127 image.index0_transparent = true;
128 for (size_t row = 0; row < palettes.size() && row < 8; ++row) {
129 const auto& palette = palettes.palette_ref(row);
130 for (size_t index = 0; index < palette.size() && index < 16; ++index) {
131 const auto rgb = palette[index].rgb();
132 image.colors[row * 16 + index] = {static_cast<uint8_t>(rgb.x),
133 static_cast<uint8_t>(rgb.y),
134 static_cast<uint8_t>(rgb.z), 255};
135 }
136 }
137 return image;
138}
139
140} // namespace yaze::editor::sprite_graphics_context
Draws sprite OAM tiles to bitmaps for preview rendering.
void DrawFrame(gfx::Bitmap &bitmap, const zsprite::Frame &frame, int origin_x, int origin_y)
Draw all tiles in a ZSM frame.
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
const std::vector< uint8_t > & vector() const
Definition bitmap.h:411
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
bool HasSheet(uint16_t sheet) const
True when the buffer holds all 4096 bytes of sheet.
uint64_t Revision(uint16_t sheet) const
Locates graphics sheets in the ROM and writes edited sheets back.
absl::StatusOr< GraphicsEditContext > Make(const zsprite::Frame &frame, int frame_index, int oam_index, const std::array< uint8_t, 8 > &slots, const gfx::PaletteGroup &palettes, const zelda3::GameData *owner, const std::string &source_label, const std::string &sheet_source)
absl::StatusOr< usage_preview::PreviewImage > RenderCapturedFrame(const zsprite::Frame &frame, const std::array< uint8_t, 8 > &slots, const gfx::PaletteGroup &palettes, const zelda3::GameData *owner)
SheetPixels SnapshotSessionSheets(const std::set< uint16_t > &ids, const zelda3::GameData *game_data)
GfxSheetStorageKind GetGfxSheetStorageKind(uint16_t sheet_id)
constexpr int SpriteSlotForTile(int tile)
Slot (0-7) holding 8x8 OAM tile tile (name-table bit included).
Which graphics sheets back sprite OAM tiles 0x000-0x1FF.
zelda3::ResolvedGraphicsContext resolved
std::vector< GraphicsEditTarget > targets
std::vector< OamTile > Tiles
Definition zsprite.h:112
Represents a group of palettes.
const SnesPalette & palette_ref(int i) const
std::vector< ResolvedPaletteSource > palettes
std::vector< ResolvedGraphicsSlot > slots
Renders a graphics sheet the way the game uses it, from unsaved sheet pixels, without writing the ROM...