Move DungeonObjectRenderer to its own file
This commit is contained in:
@@ -17,6 +17,11 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
|
constexpr ImGuiTableFlags kDungeonObjectTableFlags =
|
||||||
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||||
|
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
||||||
|
ImGuiTableFlags_BordersV;
|
||||||
|
|
||||||
using ImGui::TableHeadersRow;
|
using ImGui::TableHeadersRow;
|
||||||
using ImGui::TableNextColumn;
|
using ImGui::TableNextColumn;
|
||||||
using ImGui::TableNextRow;
|
using ImGui::TableNextRow;
|
||||||
@@ -34,9 +39,15 @@ absl::Status DungeonEditor::Update() {
|
|||||||
}
|
}
|
||||||
graphics_bin_ = rom()->graphics_bin();
|
graphics_bin_ = rom()->graphics_bin();
|
||||||
full_palette_ =
|
full_palette_ =
|
||||||
rom()->GetPaletteGroup("dungeon_main")[current_palette_group_id_];
|
rom()->palette_group("dungeon_main")[current_palette_group_id_];
|
||||||
current_palette_group_ =
|
current_palette_group_ =
|
||||||
gfx::CreatePaletteGroupFromLargePalette(full_palette_);
|
gfx::CreatePaletteGroupFromLargePalette(full_palette_);
|
||||||
|
|
||||||
|
// Create a vector of pointers to the current block bitmaps
|
||||||
|
for (int block : rooms_[current_room_id_].blocks()) {
|
||||||
|
room_gfx_sheets_.emplace_back(&graphics_bin_[block]);
|
||||||
|
}
|
||||||
|
|
||||||
is_loaded_ = true;
|
is_loaded_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +65,7 @@ absl::Status DungeonEditor::Update() {
|
|||||||
if (palette_showing_) {
|
if (palette_showing_) {
|
||||||
ImGui::Begin("Palette Editor", &palette_showing_, 0);
|
ImGui::Begin("Palette Editor", &palette_showing_, 0);
|
||||||
current_palette_ =
|
current_palette_ =
|
||||||
rom()->GetPaletteGroup("dungeon_main")[current_palette_group_id_];
|
rom()->palette_group("dungeon_main")[current_palette_group_id_];
|
||||||
gui::SelectablePalettePipeline(current_palette_id_, refresh_graphics_,
|
gui::SelectablePalettePipeline(current_palette_id_, refresh_graphics_,
|
||||||
current_palette_);
|
current_palette_);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
@@ -311,12 +322,8 @@ void DungeonEditor::DrawTileSelector() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DungeonEditor::DrawObjectRenderer() {
|
void DungeonEditor::DrawObjectRenderer() {
|
||||||
if (ImGui::BeginTable(
|
if (ImGui::BeginTable("DungeonObjectEditorTable", 2, kDungeonObjectTableFlags,
|
||||||
"DungeonObjectEditorTable", 2,
|
ImVec2(0, 0))) {
|
||||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
||||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
|
||||||
ImGuiTableFlags_BordersV,
|
|
||||||
ImVec2(0, 0))) {
|
|
||||||
TableSetupColumn("Dungeon Objects", ImGuiTableColumnFlags_WidthStretch,
|
TableSetupColumn("Dungeon Objects", ImGuiTableColumnFlags_WidthStretch,
|
||||||
ImGui::GetContentRegionAvail().x);
|
ImGui::GetContentRegionAvail().x);
|
||||||
TableSetupColumn("Canvas");
|
TableSetupColumn("Canvas");
|
||||||
@@ -330,7 +337,8 @@ void DungeonEditor::DrawObjectRenderer() {
|
|||||||
if (ImGui::Selectable(object_name.data(), selected_object == i)) {
|
if (ImGui::Selectable(object_name.data(), selected_object == i)) {
|
||||||
selected_object = i;
|
selected_object = i;
|
||||||
current_object_ = i;
|
current_object_ = i;
|
||||||
object_renderer_.LoadObject(i);
|
object_renderer_.LoadObject(i,
|
||||||
|
rooms_[current_room_id_].mutable_blocks());
|
||||||
rom()->RenderBitmap(object_renderer_.bitmap());
|
rom()->RenderBitmap(object_renderer_.bitmap());
|
||||||
object_loaded_ = true;
|
object_loaded_ = true;
|
||||||
}
|
}
|
||||||
@@ -347,25 +355,24 @@ void DungeonEditor::DrawObjectRenderer() {
|
|||||||
object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
|
object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
|
||||||
object_canvas_.DrawContextMenu();
|
object_canvas_.DrawContextMenu();
|
||||||
object_canvas_.DrawTileSelector(32);
|
object_canvas_.DrawTileSelector(32);
|
||||||
// if (object_loaded_) {
|
if (object_loaded_) {
|
||||||
// object_canvas_.DrawBitmap(*object_renderer_.bitmap(), 0, 0);
|
object_canvas_.DrawBitmap(*object_renderer_.bitmap(), 0, 0);
|
||||||
// }
|
}
|
||||||
object_canvas_.DrawGrid(32.0f);
|
object_canvas_.DrawGrid(32.0f);
|
||||||
object_canvas_.DrawOverlay();
|
object_canvas_.DrawOverlay();
|
||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object_loaded_) {
|
// if (object_loaded_) {
|
||||||
ImGui::Begin("Memory Viewer", &object_loaded_, 0);
|
// ImGui::Begin("Memory Viewer", &object_loaded_, 0);
|
||||||
auto memory = object_renderer_.memory();
|
// auto memory = object_renderer_.memory();
|
||||||
static MemoryEditor mem_edit;
|
// static MemoryEditor mem_edit;
|
||||||
mem_edit.DrawContents((void*)object_renderer_.mutable_memory(),
|
// mem_edit.DrawContents((void*)object_renderer_.memory_ptr(),
|
||||||
memory.size());
|
// memory.size());
|
||||||
ImGui::End();
|
// ImGui::End();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
@@ -51,38 +51,6 @@ class DungeonEditor : public Editor,
|
|||||||
void DrawTileSelector();
|
void DrawTileSelector();
|
||||||
void DrawObjectRenderer();
|
void DrawObjectRenderer();
|
||||||
|
|
||||||
bool is_loaded_ = false;
|
|
||||||
bool show_object_render_ = false;
|
|
||||||
bool object_loaded_ = false;
|
|
||||||
bool palette_showing_ = false;
|
|
||||||
|
|
||||||
bool refresh_graphics_ = false;
|
|
||||||
int current_object_ = 0;
|
|
||||||
uint64_t current_palette_id_ = 0;
|
|
||||||
uint64_t current_palette_group_id_ = 0;
|
|
||||||
|
|
||||||
uint16_t current_room_id_ = 0;
|
|
||||||
|
|
||||||
gfx::Bitmap room_gfx_bmp_;
|
|
||||||
gfx::SNESPalette current_palette_;
|
|
||||||
|
|
||||||
gfx::SNESPalette full_palette_;
|
|
||||||
|
|
||||||
gfx::PaletteGroup current_palette_group_;
|
|
||||||
|
|
||||||
gui::Canvas canvas_;
|
|
||||||
gui::Canvas room_gfx_canvas_;
|
|
||||||
gui::Canvas object_canvas_;
|
|
||||||
|
|
||||||
gfx::BitmapTable graphics_bin_;
|
|
||||||
|
|
||||||
ImVector<int> active_rooms_;
|
|
||||||
std::vector<zelda3::dungeon::Room> rooms_;
|
|
||||||
std::vector<gfx::BitmapManager> room_graphics_;
|
|
||||||
zelda3::dungeon::DungeonObjectRenderer object_renderer_;
|
|
||||||
|
|
||||||
PaletteEditor palette_editor_;
|
|
||||||
|
|
||||||
enum BackgroundType {
|
enum BackgroundType {
|
||||||
kNoBackground,
|
kNoBackground,
|
||||||
kBackground1,
|
kBackground1,
|
||||||
@@ -94,10 +62,36 @@ class DungeonEditor : public Editor,
|
|||||||
|
|
||||||
int background_type_ = kNoBackground;
|
int background_type_ = kNoBackground;
|
||||||
int placement_type_ = kNoType;
|
int placement_type_ = kNoType;
|
||||||
|
int current_object_ = 0;
|
||||||
|
|
||||||
ImGuiTableFlags toolset_table_flags_ =
|
bool is_loaded_ = false;
|
||||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable |
|
bool object_loaded_ = false;
|
||||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable;
|
bool palette_showing_ = false;
|
||||||
|
bool refresh_graphics_ = false;
|
||||||
|
bool show_object_render_ = false;
|
||||||
|
|
||||||
|
uint16_t current_room_id_ = 0;
|
||||||
|
uint64_t current_palette_id_ = 0;
|
||||||
|
uint64_t current_palette_group_id_ = 0;
|
||||||
|
|
||||||
|
ImVector<int> active_rooms_;
|
||||||
|
|
||||||
|
PaletteEditor palette_editor_;
|
||||||
|
gfx::SNESPalette current_palette_;
|
||||||
|
gfx::SNESPalette full_palette_;
|
||||||
|
gfx::PaletteGroup current_palette_group_;
|
||||||
|
|
||||||
|
gui::Canvas canvas_;
|
||||||
|
gui::Canvas room_gfx_canvas_;
|
||||||
|
gui::Canvas object_canvas_;
|
||||||
|
|
||||||
|
gfx::Bitmap room_gfx_bmp_;
|
||||||
|
gfx::BitmapTable graphics_bin_;
|
||||||
|
|
||||||
|
std::vector<gfx::Bitmap*> room_gfx_sheets_;
|
||||||
|
std::vector<zelda3::dungeon::Room> rooms_;
|
||||||
|
std::vector<gfx::BitmapManager> room_graphics_;
|
||||||
|
zelda3::dungeon::DungeonObjectRenderer object_renderer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
208
src/app/zelda3/dungeon/object_renderer.h
Normal file
208
src/app/zelda3/dungeon/object_renderer.h
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "app/emu/cpu/cpu.h"
|
||||||
|
#include "app/emu/memory/memory.h"
|
||||||
|
#include "app/emu/video/ppu.h"
|
||||||
|
#include "app/gfx/bitmap.h"
|
||||||
|
#include "app/gfx/snes_palette.h"
|
||||||
|
#include "app/gfx/snes_tile.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
#include "app/zelda3/dungeon/object_names.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
namespace zelda3 {
|
||||||
|
namespace dungeon {
|
||||||
|
|
||||||
|
class DungeonObjectRenderer : public SharedROM {
|
||||||
|
public:
|
||||||
|
struct PseudoVram {
|
||||||
|
std::array<uint8_t, 16> sheets;
|
||||||
|
std::vector<gfx::SNESPalette> palettes;
|
||||||
|
};
|
||||||
|
|
||||||
|
DungeonObjectRenderer() = default;
|
||||||
|
|
||||||
|
void LoadObject(uint16_t objectId, std::array<uint8_t, 16>& sheet_ids) {
|
||||||
|
vram_.sheets = sheet_ids;
|
||||||
|
|
||||||
|
rom_data_ = rom()->vector();
|
||||||
|
// Prepare the CPU and memory environment
|
||||||
|
memory_.Initialize(rom_data_);
|
||||||
|
|
||||||
|
// Fetch the subtype pointers for the given object ID
|
||||||
|
auto subtypeInfo = FetchSubtypeInfo(objectId);
|
||||||
|
|
||||||
|
// Configure the object based on the fetched information
|
||||||
|
ConfigureObject(subtypeInfo);
|
||||||
|
|
||||||
|
// Run the CPU emulation for the object's draw routines
|
||||||
|
RenderObject(subtypeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx::Bitmap* bitmap() { return &bitmap_; }
|
||||||
|
auto memory() { return memory_; }
|
||||||
|
auto* memory_ptr() { return &memory_; }
|
||||||
|
auto mutable_memory() { return memory_.data(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct SubtypeInfo {
|
||||||
|
uint32_t subtypePtr;
|
||||||
|
uint32_t routinePtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
SubtypeInfo FetchSubtypeInfo(uint16_t objectId) {
|
||||||
|
SubtypeInfo info;
|
||||||
|
|
||||||
|
// Determine the subtype based on objectId
|
||||||
|
uint8_t subtype = 1;
|
||||||
|
|
||||||
|
// Based on the subtype, fetch the correct pointers
|
||||||
|
switch (subtype) {
|
||||||
|
case 1: // Subtype 1
|
||||||
|
info.subtypePtr = core::subtype1_tiles + (objectId & 0xFF) * 2;
|
||||||
|
info.routinePtr = core::subtype1_tiles + 0x200 + (objectId & 0xFF) * 2;
|
||||||
|
std::cout << "Subtype 1 " << std::hex << info.subtypePtr << std::endl;
|
||||||
|
std::cout << "Subtype 1 " << std::hex << info.routinePtr << std::endl;
|
||||||
|
break;
|
||||||
|
case 2: // Subtype 2
|
||||||
|
info.subtypePtr = core::subtype2_tiles + (objectId & 0x7F) * 2;
|
||||||
|
info.routinePtr = core::subtype2_tiles + 0x80 + (objectId & 0x7F) * 2;
|
||||||
|
break;
|
||||||
|
case 3: // Subtype 3
|
||||||
|
info.subtypePtr = core::subtype3_tiles + (objectId & 0xFF) * 2;
|
||||||
|
info.routinePtr = core::subtype3_tiles + 0x100 + (objectId & 0xFF) * 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Handle unknown subtype
|
||||||
|
throw std::runtime_error("Unknown subtype for object ID: " +
|
||||||
|
std::to_string(objectId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the RTS of the subtype routine
|
||||||
|
while (true) {
|
||||||
|
uint8_t opcode = memory_.ReadByte(info.routinePtr);
|
||||||
|
if (opcode == 0x60) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
info.routinePtr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureObject(const SubtypeInfo& info) {
|
||||||
|
cpu.A = 0x03D8;
|
||||||
|
cpu.X = 0x03D8;
|
||||||
|
cpu.DB = 0x7E;
|
||||||
|
// VRAM target destinations
|
||||||
|
cpu.WriteLong(0xBF, 0x7E2000);
|
||||||
|
cpu.WriteLong(0xCB, 0x7E2080);
|
||||||
|
cpu.WriteLong(0xC2, 0x7E2002);
|
||||||
|
cpu.WriteLong(0xCE, 0x7E2082);
|
||||||
|
cpu.SetAccumulatorSize(false);
|
||||||
|
cpu.SetIndexSize(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example:
|
||||||
|
* the STA $BF, $CD, $C2, $CE are the location of the object in the room
|
||||||
|
* $B2 is used for size loop
|
||||||
|
* so if object size is setted on 07 that draw code will be repeated 7 times
|
||||||
|
* and since Y is increasing by 4 it makes the object draw from left to right
|
||||||
|
|
||||||
|
RoomDraw_Rightwards2x2_1to15or32:
|
||||||
|
#_018B89: JSR RoomDraw_GetSize_1to15or32
|
||||||
|
.next
|
||||||
|
#_018B8C: JSR RoomDraw_Rightwards2x2
|
||||||
|
#_018B8F: DEC.b $B2
|
||||||
|
#_018B91: BNE .next
|
||||||
|
#_018B93: RTS
|
||||||
|
|
||||||
|
RoomDraw_Rightwards2x2:
|
||||||
|
#_019895: LDA.w RoomDrawObjectData+0,X
|
||||||
|
#_019898: STA.b [$BF],Y
|
||||||
|
#_01989A: LDA.w RoomDrawObjectData+2,X
|
||||||
|
#_01989D: STA.b [$CB],Y
|
||||||
|
#_01989F: LDA.w RoomDrawObjectData+4,X
|
||||||
|
#_0198A2: STA.b [$C2],Y
|
||||||
|
#_0198A4: LDA.w RoomDrawObjectData+6,X
|
||||||
|
#_0198A7: STA.b [$CE],Y
|
||||||
|
#_0198A9: INY #4
|
||||||
|
#_0198AD: RTS
|
||||||
|
*/
|
||||||
|
|
||||||
|
void RenderObject(const SubtypeInfo& info) {
|
||||||
|
cpu.PB = 0x01;
|
||||||
|
cpu.PC = cpu.ReadWord(0x01 << 16 | info.routinePtr);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
uint8_t opcode = cpu.ReadByte(cpu.PB << 16 | cpu.PC);
|
||||||
|
cpu.ExecuteInstruction(opcode);
|
||||||
|
cpu.HandleInterrupts();
|
||||||
|
|
||||||
|
if (i > 50) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateObjectBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the underworld, this holds a copy of the entire BG tilemap for
|
||||||
|
// Layer 1 (BG2) in TILEMAPA
|
||||||
|
// Layer 2 (BG1) in TILEMAPB
|
||||||
|
//
|
||||||
|
// In the overworld, this holds the entire map16 space, using both blocks as a
|
||||||
|
// single array TILEMAPA = $7E2000 TILEMAPB = $7E4000
|
||||||
|
void UpdateObjectBitmap() {
|
||||||
|
tilemap_.reserve(0x2000);
|
||||||
|
for (int i = 0; i < 0x2000; ++i) {
|
||||||
|
tilemap_.push_back(0);
|
||||||
|
}
|
||||||
|
int tilemap_offset = 0;
|
||||||
|
|
||||||
|
// Iterate over tilemap in memory to read tile IDs
|
||||||
|
for (int tile_index = 0; tile_index < 512; tile_index++) {
|
||||||
|
// Read the tile ID from memory
|
||||||
|
int tile_id = memory_.ReadWord(0x7E4000 + tile_index);
|
||||||
|
|
||||||
|
int sheet_number = tile_id / 32;
|
||||||
|
int local_id = tile_id % 32;
|
||||||
|
|
||||||
|
int row = local_id / 8;
|
||||||
|
int column = local_id % 8;
|
||||||
|
|
||||||
|
int x = column * 8;
|
||||||
|
int y = row * 8;
|
||||||
|
|
||||||
|
auto sheet = rom()->mutable_graphics_sheet(vram_.sheets[sheet_number]);
|
||||||
|
|
||||||
|
// Copy the tile from VRAM using the read tile_id
|
||||||
|
sheet->Get8x8Tile(tile_id, x, y, tilemap_, tilemap_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_.Create(256, 256, 8, tilemap_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> tilemap_;
|
||||||
|
uint16_t pc_with_rts_;
|
||||||
|
std::vector<uint8_t> rom_data_;
|
||||||
|
emu::MemoryImpl memory_;
|
||||||
|
emu::ClockImpl clock_;
|
||||||
|
emu::CPU cpu{memory_, clock_};
|
||||||
|
emu::Ppu ppu{memory_, clock_};
|
||||||
|
gfx::Bitmap bitmap_;
|
||||||
|
PseudoVram vram_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dungeon
|
||||||
|
} // namespace zelda3
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
@@ -69,9 +69,6 @@ void Room::LoadRoomGraphics(uchar entrance_blockset) {
|
|||||||
if (i >= 6 && i <= 6) {
|
if (i >= 6 && i <= 6) {
|
||||||
// 3-6
|
// 3-6
|
||||||
if (entrance_blockset != 0xFF) {
|
if (entrance_blockset != 0xFF) {
|
||||||
// 6 is wrong for the entrance? -NOP need to fix that
|
|
||||||
// TODO: Find why this is wrong - Thats because of the stairs need to
|
|
||||||
// find a workaround
|
|
||||||
if (roomGfx[entrance_blockset][i - 3] != 0) {
|
if (roomGfx[entrance_blockset][i - 3] != 0) {
|
||||||
blocks_[i] = roomGfx[entrance_blockset][i - 3];
|
blocks_[i] = roomGfx[entrance_blockset][i - 3];
|
||||||
}
|
}
|
||||||
@@ -88,25 +85,34 @@ void Room::LoadRoomGraphics(uchar entrance_blockset) {
|
|||||||
} // 12-16 sprites
|
} // 12-16 sprites
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr int kGfxBufferOffset = 92 * 2048;
|
||||||
|
constexpr int kGfxBufferStride = 512;
|
||||||
|
constexpr int kGfxBufferAnimatedFrameOffset = 7 * 2048;
|
||||||
|
constexpr int kGfxBufferAnimatedFrameStride = 512;
|
||||||
|
constexpr int kGfxBufferRoomOffset = 2048;
|
||||||
|
constexpr int kGfxBufferRoomSpriteOffset = 512;
|
||||||
|
constexpr int kGfxBufferRoomSpriteStride = 2048;
|
||||||
|
constexpr int kGfxBufferRoomSpriteLastLineOffset = 0x88;
|
||||||
|
|
||||||
void Room::CopyRoomGraphicsToBuffer() {
|
void Room::CopyRoomGraphicsToBuffer() {
|
||||||
auto gfx_buffer_data = rom()->graphics_buffer();
|
auto gfx_buffer_data = rom()->graphics_buffer();
|
||||||
|
|
||||||
// Into "room gfx16" 16 of them
|
// Copy room graphics to buffer
|
||||||
int sheetPos = 0;
|
int sheet_pos = 0;
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
int d = 0;
|
int data = 0;
|
||||||
int ioff = blocks_[i] * 2048;
|
int block_offset = blocks_[i] * kGfxBufferRoomOffset;
|
||||||
while (d < 2048) {
|
while (data < kGfxBufferRoomOffset) {
|
||||||
uchar mapByte = gfx_buffer_data[d + ioff];
|
uchar map_byte = gfx_buffer_data[data + block_offset];
|
||||||
if (i < 4) {
|
if (i < 4) {
|
||||||
mapByte += 0x88;
|
map_byte += kGfxBufferRoomSpriteLastLineOffset;
|
||||||
} // Last line of 6, first line of 7 ?
|
}
|
||||||
|
|
||||||
current_gfx16_[d + sheetPos] = mapByte;
|
current_gfx16_[data + sheet_pos] = map_byte;
|
||||||
d++;
|
data++;
|
||||||
}
|
}
|
||||||
|
|
||||||
sheetPos += 2048;
|
sheet_pos += kGfxBufferRoomOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadAnimatedGraphics();
|
LoadAnimatedGraphics();
|
||||||
@@ -243,8 +249,8 @@ void Room::LoadObjects() {
|
|||||||
short oid = 0;
|
short oid = 0;
|
||||||
int layer = 0;
|
int layer = 0;
|
||||||
bool door = false;
|
bool door = false;
|
||||||
bool endRead = false;
|
bool end_read = false;
|
||||||
while (!endRead) {
|
while (!end_read) {
|
||||||
b1 = rom_data[pos];
|
b1 = rom_data[pos];
|
||||||
b2 = rom_data[pos + 1];
|
b2 = rom_data[pos + 1];
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,9 @@ namespace app {
|
|||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
namespace dungeon {
|
namespace dungeon {
|
||||||
|
|
||||||
// public static int room_object_layout_pointer = 0x882D;
|
// room_object_layout_pointer 0x882D
|
||||||
// public static int room_object_pointer = 0x874C; // Long pointer
|
// room_object_pointer 0x874C
|
||||||
// oh eh
|
|
||||||
// in bank 01 ? lol
|
|
||||||
// those are pointer of pointers
|
|
||||||
// 0x882D -> readlong() -> 2FEF04 (04EF2F -> toPC->026F2F) ->
|
// 0x882D -> readlong() -> 2FEF04 (04EF2F -> toPC->026F2F) ->
|
||||||
// that's all the layout "room" pointers
|
|
||||||
|
|
||||||
// 47EF04 ; layout00 ptr
|
// 47EF04 ; layout00 ptr
|
||||||
// AFEF04 ; layout01 ptr
|
// AFEF04 ; layout01 ptr
|
||||||
@@ -40,6 +36,9 @@ namespace dungeon {
|
|||||||
// the object array is terminated by a 0xFFFF there's no layers
|
// the object array is terminated by a 0xFFFF there's no layers
|
||||||
// in normal room when you encounter a 0xFFFF it goes to the next layer
|
// in normal room when you encounter a 0xFFFF it goes to the next layer
|
||||||
|
|
||||||
|
constexpr int room_object_layout_pointer = 0x882D;
|
||||||
|
constexpr int room_object_pointer = 0x874C; // Long pointer
|
||||||
|
|
||||||
constexpr int entrance_gfx_group = 0x5D97;
|
constexpr int entrance_gfx_group = 0x5D97;
|
||||||
constexpr int dungeons_main_bg_palette_pointers = 0xDEC4B; // JP Same
|
constexpr int dungeons_main_bg_palette_pointers = 0xDEC4B; // JP Same
|
||||||
constexpr int dungeons_palettes = 0xDD734;
|
constexpr int dungeons_palettes = 0xDD734;
|
||||||
@@ -48,8 +47,6 @@ constexpr int rooms_sprite_pointer = 0x4C298; // JP Same //2byte bank 09D62E
|
|||||||
constexpr int kRoomHeaderPointer = 0xB5DD; // LONG
|
constexpr int kRoomHeaderPointer = 0xB5DD; // LONG
|
||||||
constexpr int kRoomHeaderPointerBank = 0xB5E7; // JP Same
|
constexpr int kRoomHeaderPointerBank = 0xB5E7; // JP Same
|
||||||
constexpr int gfx_groups_pointer = 0x6237;
|
constexpr int gfx_groups_pointer = 0x6237;
|
||||||
constexpr int room_object_layout_pointer = 0x882D;
|
|
||||||
constexpr int room_object_pointer = 0x874C; // Long pointer
|
|
||||||
constexpr int chests_length_pointer = 0xEBF6;
|
constexpr int chests_length_pointer = 0xEBF6;
|
||||||
constexpr int chests_data_pointer1 = 0xEBFB;
|
constexpr int chests_data_pointer1 = 0xEBFB;
|
||||||
|
|
||||||
@@ -138,6 +135,7 @@ class Room : public SharedROM {
|
|||||||
void LoadRoomFromROM();
|
void LoadRoomFromROM();
|
||||||
|
|
||||||
auto blocks() const { return blocks_; }
|
auto blocks() const { return blocks_; }
|
||||||
|
auto& mutable_blocks() { return blocks_; }
|
||||||
|
|
||||||
RoomObject AddObject(short oid, uint8_t x, uint8_t y, uint8_t size,
|
RoomObject AddObject(short oid, uint8_t x, uint8_t y, uint8_t size,
|
||||||
uint8_t layer) {
|
uint8_t layer) {
|
||||||
@@ -160,14 +158,16 @@ class Room : public SharedROM {
|
|||||||
std::vector<uint8_t> current_gfx16_;
|
std::vector<uint8_t> current_gfx16_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool light = false;
|
||||||
bool is_loaded_ = false;
|
bool is_loaded_ = false;
|
||||||
|
bool IsDark = false;
|
||||||
int animated_frame = 0;
|
bool floor = false;
|
||||||
|
|
||||||
int room_id_ = 0;
|
int room_id_ = 0;
|
||||||
|
int animated_frame = 0;
|
||||||
|
|
||||||
bool light;
|
uchar Tag1;
|
||||||
Background2 bg2;
|
uchar Tag2;
|
||||||
|
|
||||||
uint8_t staircase_plane[4];
|
uint8_t staircase_plane[4];
|
||||||
uint8_t staircase_rooms[4];
|
uint8_t staircase_rooms[4];
|
||||||
@@ -179,6 +179,7 @@ class Room : public SharedROM {
|
|||||||
uint8_t Floor1Graphics;
|
uint8_t Floor1Graphics;
|
||||||
uint8_t Floor2Graphics;
|
uint8_t Floor2Graphics;
|
||||||
uint8_t Layer2Mode;
|
uint8_t Layer2Mode;
|
||||||
|
|
||||||
std::array<uint8_t, 16> blocks_;
|
std::array<uint8_t, 16> blocks_;
|
||||||
std::array<uchar, 16> ChestList;
|
std::array<uchar, 16> ChestList;
|
||||||
|
|
||||||
@@ -186,19 +187,13 @@ class Room : public SharedROM {
|
|||||||
std::vector<zelda3::Sprite> sprites_;
|
std::vector<zelda3::Sprite> sprites_;
|
||||||
std::vector<StaircaseRooms> staircaseRooms;
|
std::vector<StaircaseRooms> staircaseRooms;
|
||||||
|
|
||||||
|
Background2 bg2;
|
||||||
DungeonDestination Pits;
|
DungeonDestination Pits;
|
||||||
DungeonDestination Stair1;
|
DungeonDestination Stair1;
|
||||||
DungeonDestination Stair2;
|
DungeonDestination Stair2;
|
||||||
DungeonDestination Stair3;
|
DungeonDestination Stair3;
|
||||||
DungeonDestination Stair4;
|
DungeonDestination Stair4;
|
||||||
|
|
||||||
uchar Tag1;
|
|
||||||
uchar Tag2;
|
|
||||||
bool IsDark;
|
|
||||||
|
|
||||||
bool floor;
|
|
||||||
|
|
||||||
// std::vector<Chest> chest_list;
|
|
||||||
std::vector<ChestData> chests_in_room;
|
std::vector<ChestData> chests_in_room;
|
||||||
std::vector<RoomObject> tilesObjects;
|
std::vector<RoomObject> tilesObjects;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,188 +14,17 @@
|
|||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/dungeon/object_names.h"
|
#include "app/zelda3/dungeon/object_names.h"
|
||||||
|
#include "app/zelda3/dungeon/object_renderer.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
namespace dungeon {
|
namespace dungeon {
|
||||||
|
|
||||||
class DungeonObjectRenderer : public SharedROM {
|
struct Tile {};
|
||||||
public:
|
|
||||||
struct PseudoVram {
|
|
||||||
std::vector<gfx::Bitmap> sheets;
|
|
||||||
// TODO: Initialize with mock VRAM data
|
|
||||||
};
|
|
||||||
|
|
||||||
DungeonObjectRenderer() {
|
|
||||||
// TODO: Constructor implementation
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadObject(uint16_t objectId) {
|
|
||||||
rom_data_ = rom()->vector();
|
|
||||||
// Prepare the CPU and memory environment
|
|
||||||
memory_.Initialize(rom_data_);
|
|
||||||
|
|
||||||
// Fetch the subtype pointers for the given object ID
|
|
||||||
auto subtypeInfo = FetchSubtypeInfo(objectId);
|
|
||||||
|
|
||||||
// Configure the object based on the fetched information
|
|
||||||
ConfigureObject(subtypeInfo);
|
|
||||||
|
|
||||||
// Run the CPU emulation for the object's draw routines
|
|
||||||
RenderObject(subtypeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::Bitmap* bitmap() { return &bitmap_; }
|
|
||||||
auto memory() { return memory_; }
|
|
||||||
auto mutable_memory() { return memory_.data(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct SubtypeInfo {
|
|
||||||
uint32_t subtypePtr;
|
|
||||||
uint32_t routinePtr;
|
|
||||||
};
|
|
||||||
|
|
||||||
SubtypeInfo FetchSubtypeInfo(uint16_t objectId) {
|
|
||||||
SubtypeInfo info;
|
|
||||||
|
|
||||||
// Determine the subtype based on objectId
|
|
||||||
// Assuming subtype is determined by some bits in objectId; modify as needed
|
|
||||||
uint8_t subtype = 1; // Example: top 8 bits
|
|
||||||
|
|
||||||
// Based on the subtype, fetch the correct pointers
|
|
||||||
switch (subtype) {
|
|
||||||
case 1: // Subtype 1
|
|
||||||
info.subtypePtr = core::subtype1_tiles + (objectId & 0xFF) * 2;
|
|
||||||
info.routinePtr = core::subtype1_tiles + 0x200 + (objectId & 0xFF) * 2;
|
|
||||||
std::cout << "Subtype 1 " << std::hex << info.subtypePtr << std::endl;
|
|
||||||
std::cout << "Subtype 1 " << std::hex << info.routinePtr << std::endl;
|
|
||||||
break;
|
|
||||||
case 2: // Subtype 2
|
|
||||||
info.subtypePtr = core::subtype2_tiles + (objectId & 0x7F) * 2;
|
|
||||||
info.routinePtr = core::subtype2_tiles + 0x80 + (objectId & 0x7F) * 2;
|
|
||||||
break;
|
|
||||||
case 3: // Subtype 3
|
|
||||||
info.subtypePtr = core::subtype3_tiles + (objectId & 0xFF) * 2;
|
|
||||||
info.routinePtr = core::subtype3_tiles + 0x100 + (objectId & 0xFF) * 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Handle unknown subtype
|
|
||||||
throw std::runtime_error("Unknown subtype for object ID: " +
|
|
||||||
std::to_string(objectId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert pointers from ROM-relative to absolute (if necessary)
|
|
||||||
// info.subtypePtr = ConvertToAbsolutePtr(info.subtypePtr);
|
|
||||||
// info.routinePtr = ConvertToAbsolutePtr(info.routinePtr);
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigureObject(const SubtypeInfo& info) {
|
|
||||||
cpu.A = 0x03D8;
|
|
||||||
cpu.X = 0x03D8;
|
|
||||||
cpu.DB = 0x7E;
|
|
||||||
// VRAM target destinations
|
|
||||||
cpu.WriteLong(0xBF, 0x7E2000);
|
|
||||||
cpu.WriteLong(0xCB, 0x7E2080);
|
|
||||||
cpu.WriteLong(0xC2, 0x7E2002);
|
|
||||||
cpu.WriteLong(0xCE, 0x7E2082);
|
|
||||||
cpu.SetAccumulatorSize(false);
|
|
||||||
cpu.SetIndexSize(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example:
|
|
||||||
* the STA $BF, $CD, $C2, $CE are the location of the object in the room
|
|
||||||
* $B2 is used for size loop
|
|
||||||
* so if object size is setted on 07 that draw code will be repeated 7 times
|
|
||||||
* and since Y is increasing by 4 it makes the object draw from left to right
|
|
||||||
|
|
||||||
RoomDraw_Rightwards2x2_1to15or32:
|
|
||||||
#_018B89: JSR RoomDraw_GetSize_1to15or32
|
|
||||||
|
|
||||||
.next
|
|
||||||
#_018B8C: JSR RoomDraw_Rightwards2x2
|
|
||||||
|
|
||||||
#_018B8F: DEC.b $B2
|
|
||||||
#_018B91: BNE .next
|
|
||||||
|
|
||||||
#_018B93: RTS
|
|
||||||
|
|
||||||
RoomDraw_Rightwards2x2:
|
|
||||||
#_019895: LDA.w RoomDrawObjectData+0,X
|
|
||||||
#_019898: STA.b [$BF],Y
|
|
||||||
|
|
||||||
#_01989A: LDA.w RoomDrawObjectData+2,X
|
|
||||||
#_01989D: STA.b [$CB],Y
|
|
||||||
|
|
||||||
#_01989F: LDA.w RoomDrawObjectData+4,X
|
|
||||||
#_0198A2: STA.b [$C2],Y
|
|
||||||
|
|
||||||
#_0198A4: LDA.w RoomDrawObjectData+6,X
|
|
||||||
#_0198A7: STA.b [$CE],Y
|
|
||||||
|
|
||||||
#_0198A9: INY
|
|
||||||
#_0198AA: INY
|
|
||||||
#_0198AB: INY
|
|
||||||
#_0198AC: INY
|
|
||||||
|
|
||||||
#_0198AD: RTS
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void RenderObject(const SubtypeInfo& info) {
|
|
||||||
cpu.PB = 0x01;
|
|
||||||
cpu.PC = cpu.ReadWord(0x01 << 16 | info.routinePtr);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (true) {
|
|
||||||
uint8_t opcode = cpu.ReadByte(cpu.PB << 16 | cpu.PC);
|
|
||||||
cpu.ExecuteInstruction(opcode);
|
|
||||||
cpu.HandleInterrupts();
|
|
||||||
|
|
||||||
if (i > 50) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
|
|
||||||
// UpdateObjectBitmap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateObjectBitmap() {
|
|
||||||
// Object draw data
|
|
||||||
uint8_t room_object_draw_data0 = memory_.ReadByte(0x7E00BF);
|
|
||||||
uint8_t room_object_draw_data1 = memory_.ReadByte(0x7E00CB);
|
|
||||||
uint8_t room_object_draw_data2 = memory_.ReadByte(0x7E00C2);
|
|
||||||
uint8_t room_object_draw_data3 = memory_.ReadByte(0x7E00CE);
|
|
||||||
|
|
||||||
// Used with Y to index the room object draw data
|
|
||||||
uint8_t size_loop = memory_.ReadByte(0x7E00B2);
|
|
||||||
|
|
||||||
// Update the bitmap with this data by copying the tiles from vram.
|
|
||||||
|
|
||||||
std::cout << "Object draw data: " << std::hex << (int)room_object_draw_data0
|
|
||||||
<< " " << (int)room_object_draw_data1 << " "
|
|
||||||
<< (int)room_object_draw_data2 << " "
|
|
||||||
<< (int)room_object_draw_data3 << std::endl;
|
|
||||||
std::cout << "Size loop: " << std::hex << (int)size_loop << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> rom_data_;
|
|
||||||
emu::MemoryImpl memory_;
|
|
||||||
emu::ClockImpl clock_;
|
|
||||||
emu::CPU cpu{memory_, clock_};
|
|
||||||
emu::Ppu ppu{memory_, clock_};
|
|
||||||
gfx::Bitmap bitmap_;
|
|
||||||
PseudoVram vram_;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SpecialObjectType { Chest, BigChest, InterroomStairs };
|
enum class SpecialObjectType { Chest, BigChest, InterroomStairs };
|
||||||
|
|
||||||
struct Tile {};
|
|
||||||
|
|
||||||
enum Background2 {
|
enum Background2 {
|
||||||
Off,
|
Off,
|
||||||
Parallax,
|
Parallax,
|
||||||
@@ -229,12 +58,6 @@ enum ObjectOption {
|
|||||||
Stairs = 32
|
Stairs = 32
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LayerType {
|
|
||||||
LayerType(uint8_t t) : type(t) {}
|
|
||||||
|
|
||||||
uint8_t type;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RoomObject : public SharedROM {
|
class RoomObject : public SharedROM {
|
||||||
public:
|
public:
|
||||||
enum LayerType { BG1 = 0, BG2 = 1, BG3 = 2 };
|
enum LayerType { BG1 = 0, BG2 = 1, BG3 = 2 };
|
||||||
@@ -268,7 +91,6 @@ class RoomObject : public SharedROM {
|
|||||||
GetSizeSized();
|
GetSizeSized();
|
||||||
UpdateSize();
|
UpdateSize();
|
||||||
size_ = previous_size_;
|
size_ = previous_size_;
|
||||||
// collision_point_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetBaseSize() {
|
void GetBaseSize() {
|
||||||
@@ -281,8 +103,6 @@ class RoomObject : public SharedROM {
|
|||||||
size_width_ = width_ - base_width_;
|
size_width_ = width_ - base_width_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual void Draw() { collision_point_.clear(); }
|
|
||||||
|
|
||||||
void UpdateSize() {
|
void UpdateSize() {
|
||||||
width_ = 8;
|
width_ = 8;
|
||||||
height_ = 8;
|
height_ = 8;
|
||||||
@@ -291,8 +111,8 @@ class RoomObject : public SharedROM {
|
|||||||
void AddTiles(int nbr, int pos) {
|
void AddTiles(int nbr, int pos) {
|
||||||
auto rom_data = rom()->data();
|
auto rom_data = rom()->data();
|
||||||
for (int i = 0; i < nbr; i++) {
|
for (int i = 0; i < nbr; i++) {
|
||||||
// tiles.push_back(
|
ASSIGN_OR_LOG_ERROR(auto tile, rom()->ReadTile16(pos + (i * 2)));
|
||||||
// gfx::Tile16(rom_data[pos + (i * 2)], rom_data[pos + (i * 2) + 1]));
|
tiles_.push_back(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,43 +122,42 @@ class RoomObject : public SharedROM {
|
|||||||
ushort tile_under = 0xFFFF);
|
ushort tile_under = 0xFFFF);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int16_t id_;
|
bool all_bgs_ = false;
|
||||||
|
bool lit_ = false;
|
||||||
|
bool deleted_ = false;
|
||||||
|
bool show_rectangle_ = false;
|
||||||
|
bool diagonal_fix_ = false;
|
||||||
|
bool selected_ = false;
|
||||||
|
|
||||||
|
int16_t id_;
|
||||||
uint8_t x_;
|
uint8_t x_;
|
||||||
uint8_t y_;
|
uint8_t y_;
|
||||||
uint8_t size_;
|
uint8_t size_;
|
||||||
|
|
||||||
LayerType layer_;
|
|
||||||
|
|
||||||
std::vector<uint8_t> preview_object_data_;
|
|
||||||
|
|
||||||
bool all_bgs_ = false;
|
|
||||||
bool lit_ = false;
|
|
||||||
std::vector<gfx::Tile16> tiles_;
|
|
||||||
int tile_index_ = 0;
|
|
||||||
std::string name_;
|
|
||||||
uint8_t nx_;
|
uint8_t nx_;
|
||||||
uint8_t ny_;
|
uint8_t ny_;
|
||||||
uint8_t ox_;
|
uint8_t ox_;
|
||||||
uint8_t oy_;
|
uint8_t oy_;
|
||||||
|
uint8_t z_ = 0;
|
||||||
|
uint8_t previous_size_ = 0;
|
||||||
|
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
int base_width_;
|
int base_width_;
|
||||||
int base_height_;
|
int base_height_;
|
||||||
int size_width_;
|
int size_width_;
|
||||||
int size_height_;
|
int size_height_;
|
||||||
ObjectOption options_ = ObjectOption::Nothing;
|
int tile_index_ = 0;
|
||||||
int offset_x_ = 0;
|
int offset_x_ = 0;
|
||||||
int offset_y_ = 0;
|
int offset_y_ = 0;
|
||||||
bool diagonal_fix_ = false;
|
|
||||||
bool selected_ = false;
|
|
||||||
int preview_id_ = 0;
|
int preview_id_ = 0;
|
||||||
uint8_t previous_size_ = 0;
|
|
||||||
bool show_rectangle_ = false;
|
|
||||||
// std::vector<Point> collision_point_;
|
|
||||||
int unique_id_ = 0;
|
int unique_id_ = 0;
|
||||||
uint8_t z_ = 0;
|
|
||||||
bool deleted_ = false;
|
std::string name_;
|
||||||
|
|
||||||
|
LayerType layer_;
|
||||||
|
ObjectOption options_ = ObjectOption::Nothing;
|
||||||
|
std::vector<gfx::Tile16> tiles_;
|
||||||
|
std::vector<uint8_t> preview_object_data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Subtype1 : public RoomObject {
|
class Subtype1 : public RoomObject {
|
||||||
|
|||||||
Reference in New Issue
Block a user