GUI Updates

Add DisplaySettings, replace ImGui style editor
Update Debugger interface with memory viewer
Decompose SNES initialization routines
Update DungeonObjectRenderer plan
Add DrawObjectRenderer UI mockup fofr DungeonEditor
This commit is contained in:
scawful
2023-11-21 11:07:04 -05:00
parent f7224c3716
commit 59e7dcc7f0
14 changed files with 712 additions and 94 deletions

View File

@@ -117,6 +117,8 @@ namespace yaze {
namespace app { namespace app {
namespace core { namespace core {
constexpr float kYazeVersion = 0.05;
// ============================================================================ // ============================================================================
// Window Variables // Window Variables
// ============================================================================ // ============================================================================

View File

@@ -7,6 +7,7 @@
#include "app/gui/icons.h" #include "app/gui/icons.h"
#include "app/gui/input.h" #include "app/gui/input.h"
#include "app/rom.h" #include "app/rom.h"
#include "app/zelda3/dungeon/object_names.h"
#include "app/zelda3/dungeon/room_names.h" #include "app/zelda3/dungeon/room_names.h"
#include "zelda3/dungeon/room.h" #include "zelda3/dungeon/room.h"
@@ -32,6 +33,7 @@ absl::Status DungeonEditor::Update() {
} }
DrawToolset(); DrawToolset();
DrawObjectRenderer();
ImGui::Separator(); ImGui::Separator();
if (ImGui::BeginTable("#DungeonEditTable", 3, toolset_table_flags_, if (ImGui::BeginTable("#DungeonEditTable", 3, toolset_table_flags_,
@@ -197,9 +199,8 @@ void DungeonEditor::DrawToolset() {
ImGui::Button(ICON_MD_PEST_CONTROL_RODENT); ImGui::Button(ICON_MD_PEST_CONTROL_RODENT);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Button("Load Dungeon Objects")) { if (ImGui::Button("Dungeon Object Renderer")) {
// object_renderer_.CreateVramFromRoomBlockset(); show_object_render_ = !show_object_render_;
object_renderer_.RenderObjectsAsBitmaps();
} }
ImGui::EndTable(); ImGui::EndTable();
} }
@@ -229,6 +230,47 @@ void DungeonEditor::DrawTileSelector() {
} }
} }
void DungeonEditor::DrawObjectRenderer() {
if (show_object_render_) {
ImGui::Begin("Dungeon Object Renderer", &show_object_render_);
// Create an ImGui table where the left side of the table is a matrix of
// buttons which represent each dungeon object. The right side of the table
// is a canvas which will display the selected dungeon object. The canvas
// will also have a tile selector and a grid overlay.
if (ImGui::BeginTable("DungeonObjectEditorTable", 2,
ImGuiTableFlags_SizingFixedFit, ImVec2(0, 0))) {
ImGui::TableSetupColumn("Dungeon Objects",
ImGuiTableColumnFlags_WidthFixed, 150.0f);
ImGui::TableSetupColumn("Canvas");
ImGui::TableNextColumn();
ImGui::BeginChild("DungeonObjectButtons", ImVec2(150.0f, 0), true);
for (const auto each : zelda3::dungeon::Type1RoomObjectNames) {
ImGui::Button(each.data());
if (ImGui::IsItemClicked()) {
}
}
ImGui::EndChild();
// Right side of the table - Canvas
ImGui::TableNextColumn();
ImGui::BeginChild("DungeonObjectCanvas", ImVec2(0, 0), true);
// TODO: Insert code to display canvas, tile selector, and grid overlay
// here
ImGui::EndChild();
ImGui::EndTable();
}
ImGui::End();
}
}
} // namespace editor } // namespace editor
} // namespace app } // namespace app
} // namespace yaze } // namespace yaze

View File

@@ -34,8 +34,11 @@ class DungeonEditor : public Editor,
void DrawRoomGraphics(); void DrawRoomGraphics();
void DrawTileSelector(); void DrawTileSelector();
void DrawObjectRenderer();
uint16_t current_room_id_ = 0; uint16_t current_room_id_ = 0;
bool is_loaded_ = false; bool is_loaded_ = false;
bool show_object_render_ = false;
gfx::Bitmap room_gfx_bmp_; gfx::Bitmap room_gfx_bmp_;
@@ -46,9 +49,9 @@ class DungeonEditor : public Editor,
gui::Canvas canvas_; gui::Canvas canvas_;
gui::Canvas room_gfx_canvas_; gui::Canvas room_gfx_canvas_;
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags toolset_table_flags_ =
ImGuiTableFlags_Reorderable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable; ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable;
}; };
} // namespace editor } // namespace editor

View File

@@ -24,6 +24,7 @@
#include "app/gui/canvas.h" #include "app/gui/canvas.h"
#include "app/gui/icons.h" #include "app/gui/icons.h"
#include "app/gui/input.h" #include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/gui/widgets.h" #include "app/gui/widgets.h"
#include "app/rom.h" #include "app/rom.h"
@@ -176,12 +177,45 @@ void MasterEditor::DrawInfoPopup() {
} }
void MasterEditor::DrawYazeMenu() { void MasterEditor::DrawYazeMenu() {
static bool show_display_settings = false;
static bool show_command_line_interface = false;
MENU_BAR() MENU_BAR()
DrawFileMenu(); DrawFileMenu();
DrawEditMenu(); DrawEditMenu();
DrawViewMenu(); DrawViewMenu();
DrawHelpMenu(); DrawHelpMenu();
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetStyle().ItemSpacing.x -
ImGui::CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 150);
// Modify the style of the button to have no background color
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
if (ImGui::Button(ICON_MD_DISPLAY_SETTINGS)) {
show_display_settings = !show_display_settings;
}
if (ImGui::Button(ICON_MD_TERMINAL)) {
show_command_line_interface = !show_command_line_interface;
}
ImGui::PopStyleColor();
ImGui::Text(absl::StrCat("yaze v", core::kYazeVersion).c_str());
END_MENU_BAR() END_MENU_BAR()
if (show_display_settings) {
ImGui::Begin("Display Settings", &show_display_settings,
ImGuiWindowFlags_None);
gui::DrawDisplaySettings();
ImGui::End();
}
if (show_command_line_interface) {
ImGui::Begin("Command Line Interface", &show_command_line_interface,
ImGuiWindowFlags_None);
ImGui::Text("Enter a command:");
ImGui::End();
}
} }
void MasterEditor::DrawFileMenu() { void MasterEditor::DrawFileMenu() {
@@ -266,7 +300,6 @@ void MasterEditor::DrawEditMenu() {
void MasterEditor::DrawViewMenu() { void MasterEditor::DrawViewMenu() {
static bool show_imgui_metrics = false; static bool show_imgui_metrics = false;
static bool show_imgui_style_editor = false;
static bool show_memory_editor = false; static bool show_memory_editor = false;
static bool show_asm_editor = false; static bool show_asm_editor = false;
static bool show_imgui_demo = false; static bool show_imgui_demo = false;
@@ -303,12 +336,6 @@ void MasterEditor::DrawViewMenu() {
ImGui::End(); ImGui::End();
} }
if (show_imgui_style_editor) {
ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor);
ImGui::ShowStyleEditor();
ImGui::End();
}
if (show_memory_viewer) { if (show_memory_viewer) {
ImGui::Begin("Memory Viewer (ImGui)", &show_memory_viewer); ImGui::Begin("Memory Viewer (ImGui)", &show_memory_viewer);
@@ -339,13 +366,7 @@ void MasterEditor::DrawViewMenu() {
ImGui::MenuItem("Palette Editor", nullptr, &show_palette_editor); ImGui::MenuItem("Palette Editor", nullptr, &show_palette_editor);
ImGui::MenuItem("Memory Viewer", nullptr, &show_memory_viewer); ImGui::MenuItem("Memory Viewer", nullptr, &show_memory_viewer);
ImGui::MenuItem("ImGui Demo", nullptr, &show_imgui_demo); ImGui::MenuItem("ImGui Demo", nullptr, &show_imgui_demo);
ImGui::Separator(); ImGui::MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics);
if (ImGui::BeginMenu("GUI Tools")) {
ImGui::MenuItem("Metrics (ImGui)", nullptr, &show_imgui_metrics);
ImGui::MenuItem("Style Editor (ImGui)", nullptr,
&show_imgui_style_editor);
ImGui::EndMenu();
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }

View File

@@ -55,7 +55,7 @@ absl::Status OverworldEditor::Update() {
if (ImGui::BeginTable(kOWEditTable.data(), 2, kOWEditFlags, ImVec2(0, 0))) { if (ImGui::BeginTable(kOWEditTable.data(), 2, kOWEditFlags, ImVec2(0, 0))) {
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch, TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x); ImGui::GetContentRegionAvail().x);
TableSetupColumn("Tile Selector"); TableSetupColumn("Tile Selector", ImGuiTableColumnFlags_WidthFixed, 256);
TableHeadersRow(); TableHeadersRow();
TableNextRow(); TableNextRow();
TableNextColumn(); TableNextColumn();

View File

@@ -48,7 +48,7 @@ static constexpr absl::string_view kOverworldSettingsColumnNames[] = {
constexpr ImGuiTableFlags kOWMapFlags = ImGuiTableFlags_Borders; constexpr ImGuiTableFlags kOWMapFlags = ImGuiTableFlags_Borders;
constexpr ImGuiTableFlags kToolsetTableFlags = ImGuiTableFlags_SizingFixedFit; constexpr ImGuiTableFlags kToolsetTableFlags = ImGuiTableFlags_SizingFixedFit;
constexpr ImGuiTableFlags kOWEditFlags = ImGuiTableFlags_Reorderable | constexpr ImGuiTableFlags kOWEditFlags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable |
ImGuiTableFlags_SizingStretchSame; ImGuiTableFlags_SizingStretchSame;
constexpr absl::string_view kWorldList = constexpr absl::string_view kWorldList =
@@ -184,10 +184,6 @@ class OverworldEditor : public Editor,
gfx::BitmapTable graphics_bin_; gfx::BitmapTable graphics_bin_;
gfx::BitmapTable current_graphics_set_; gfx::BitmapTable current_graphics_set_;
gfx::BitmapTable sprite_previews_; gfx::BitmapTable sprite_previews_;
ImGuiTableFlags ow_edit_flags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame;
}; };
} // namespace editor } // namespace editor
} // namespace app } // namespace app

View File

@@ -1,12 +1,14 @@
#include "app/emu/emulator.h" #include "app/emu/emulator.h"
#include <imgui/imgui.h> #include <imgui/imgui.h>
#include <imgui_memory_editor.h>
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
#include "app/core/constants.h" #include "app/core/constants.h"
#include "app/emu/snes.h" #include "app/emu/snes.h"
#include "app/gui/icons.h"
#include "app/rom.h" #include "app/rom.h"
namespace yaze { namespace yaze {
@@ -20,6 +22,65 @@ bool ShouldDisplay(const InstructionEntry& entry, const char* filter,
// filter and showAll flag // filter and showAll flag
return true; return true;
} }
void DrawMemoryWindow(Memory* memory) {
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
static ImGuiTableFlags flags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
if (auto outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
ImGui::BeginTable("table1", 4, flags, outer_size)) {
// Table headers
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Memory Area");
ImGui::TableNextColumn();
ImGui::Text("Start Address");
ImGui::TableNextColumn();
ImGui::Text("Size");
ImGui::TableNextColumn();
ImGui::Text("Mapping");
// Retrieve memory information from MemoryImpl
MemoryImpl* memoryImpl = dynamic_cast<MemoryImpl*>(memory);
if (memoryImpl) {
// Display memory areas
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM");
ImGui::TableNextColumn();
ImGui::Text("0x000000");
ImGui::TableNextColumn();
ImGui::Text("%d MB", memoryImpl->rom_.size());
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("RAM");
ImGui::TableNextColumn();
ImGui::Text("0x7E0000");
ImGui::TableNextColumn();
ImGui::Text("%d KB", memoryImpl->ram_.size());
ImGui::TableNextColumn();
ImGui::Text("LoROM");
}
ImGui::EndTable();
if (ImGui::Button("Open Memory Viewer", ImVec2(200, 50))) {
ImGui::OpenPopup("Memory Viewer");
}
if (ImGui::BeginPopupModal("Memory Viewer", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
static MemoryEditor mem_edit;
mem_edit.DrawContents((void*)memoryImpl->data(), memoryImpl->size());
ImGui::EndPopup();
}
}
}
} // namespace } // namespace
using ImGui::NextColumn; using ImGui::NextColumn;
@@ -30,7 +91,12 @@ using ImGui::Text;
void Emulator::Run() { void Emulator::Run() {
if (!snes_.running() && loading_) { if (!snes_.running() && loading_) {
if (rom()->isLoaded()) { if (loading_ && !memory_setup_) {
snes_.SetupMemory(*rom());
memory_setup_ = true;
}
if (rom()->isLoaded() && power_) {
snes_.Init(*rom()); snes_.Init(*rom());
running_ = true; running_ = true;
} }
@@ -38,6 +104,13 @@ void Emulator::Run() {
RenderNavBar(); RenderNavBar();
ImGui::Button(ICON_MD_ARROW_FORWARD_IOS);
ImGui::SameLine();
ImGui::Button(ICON_MD_DOUBLE_ARROW);
ImGui::SameLine();
ImGui::Button(ICON_MD_SUBDIRECTORY_ARROW_RIGHT);
ImGui::SameLine();
if (running_) { if (running_) {
HandleEvents(); HandleEvents();
UpdateEmulator(); UpdateEmulator();
@@ -67,7 +140,9 @@ void Emulator::RenderNavBar() {
if (ImGui::BeginMenu("Game")) { if (ImGui::BeginMenu("Game")) {
MENU_ITEM("Load ROM") { loading_ = true; } MENU_ITEM("Load ROM") { loading_ = true; }
MENU_ITEM("Power On") { power_ = true; }
MENU_ITEM("Power Off") { MENU_ITEM("Power Off") {
power_ = false;
running_ = false; running_ = false;
loading_ = false; loading_ = false;
debugger_ = false; debugger_ = false;
@@ -133,6 +208,7 @@ void Emulator::RenderDebugger() {
TableNextColumn(); TableNextColumn();
RenderBreakpointList(); RenderBreakpointList();
DrawMemoryWindow(snes_.Memory());
ImGui::EndTable(); ImGui::EndTable();
} }
}; };

View File

@@ -39,9 +39,11 @@ class Emulator : public SharedROM {
SNES snes_; SNES snes_;
bool power_ = false;
bool loading_ = false;
bool running_ = false; bool running_ = false;
bool debugger_ = true; bool debugger_ = true;
bool loading_ = false; bool memory_setup_ = false;
bool integrated_debugger_mode_ = true; bool integrated_debugger_mode_ = true;
bool separate_debugger_mode_ = false; bool separate_debugger_mode_ = false;
}; };

View File

@@ -3,6 +3,7 @@
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include <string>
#include <vector> #include <vector>
#include "app/emu/debug/log.h" #include "app/emu/debug/log.h"
@@ -135,9 +136,21 @@ class Memory {
virtual uint8_t at(int i) const = 0; virtual uint8_t at(int i) const = 0;
}; };
enum class MemoryMapping { SNES_LOROM = 0, PC_ADDRESS = 1 };
class MemoryImpl : public Memory, public Loggable { class MemoryImpl : public Memory, public Loggable {
public: public:
void Initialize(const std::vector<uint8_t>& romData) { void Initialize(const std::vector<uint8_t>& romData,
MemoryMapping mapping = MemoryMapping::SNES_LOROM) {
mapping_ = mapping;
if (mapping == MemoryMapping::PC_ADDRESS) {
memory_.resize(romData.size());
std::copy(romData.begin(), romData.end(), memory_.begin());
return;
}
memory_.reserve(0x1000000); // 16 MB
const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB
const size_t SRAM_SIZE = 0x10000; // 64 KB const size_t SRAM_SIZE = 0x10000; // 64 KB
const size_t SYSTEM_RAM_SIZE = 0x20000; // 128 KB const size_t SYSTEM_RAM_SIZE = 0x20000; // 128 KB
@@ -322,6 +335,7 @@ class MemoryImpl : public Memory, public Loggable {
auto size() const { return memory_.size(); } auto size() const { return memory_.size(); }
auto begin() const { return memory_.begin(); } auto begin() const { return memory_.begin(); }
auto end() const { return memory_.end(); } auto end() const { return memory_.end(); }
auto data() const { return memory_.data(); }
// Define memory regions // Define memory regions
std::vector<uint8_t> rom_; std::vector<uint8_t> rom_;
@@ -334,6 +348,10 @@ class MemoryImpl : public Memory, public Loggable {
uint32_t bank = address >> 16; uint32_t bank = address >> 16;
uint32_t offset = address & 0xFFFF; uint32_t offset = address & 0xFFFF;
if (mapping_ == MemoryMapping::PC_ADDRESS) {
return address;
}
if (bank <= 0x3F) { if (bank <= 0x3F) {
if (offset <= 0x1FFF) { if (offset <= 0x1FFF) {
return offset; // Shadow RAM return offset; // Shadow RAM
@@ -364,10 +382,12 @@ class MemoryImpl : public Memory, public Loggable {
std::vector<Observer*> observers_; std::vector<Observer*> observers_;
// Memory (64KB) // Memory (64KB)
std::array<uint8_t, 0x10000> memory_; std::vector<uint8_t> memory_;
// Stack Pointer // Stack Pointer
uint16_t SP_ = 0x01FF; uint16_t SP_ = 0x01FF;
MemoryMapping mapping_ = MemoryMapping::SNES_LOROM;
}; };
} // namespace emu } // namespace emu

View File

@@ -108,22 +108,15 @@ ROMInfo SNES::ReadRomHeader(uint32_t offset) {
} }
void SNES::Init(ROM& rom) { void SNES::Init(ROM& rom) {
// Setup observers for the memory space
memory_.AddObserver(&apu);
memory_.AddObserver(&ppu);
// Load the ROM into memory and set up the memory mapping
memory_.Initialize(rom.vector());
// Read the ROM header
auto header_offset = GetHeaderOffset(memory_);
rom_info_ = ReadRomHeader(header_offset);
// Perform a long jump into a FastROM bank (if the ROM speed is FastROM) // Perform a long jump into a FastROM bank (if the ROM speed is FastROM)
// Disable the emulation flag (switch to 65816 native mode) // Disable the emulation flag (switch to 65816 native mode)
// Initialize CPU // Initialize CPU
cpu.Init(); cpu.Init();
// Read the ROM header
auto header_offset = GetHeaderOffset(memory_);
rom_info_ = ReadRomHeader(header_offset);
cpu.PC = rom_info_.resetVector; cpu.PC = rom_info_.resetVector;
// Initialize PPU // Initialize PPU

View File

@@ -65,12 +65,22 @@ class SNES : public DMA {
auto Cpu() -> CPU& { return cpu; } auto Cpu() -> CPU& { return cpu; }
auto Ppu() -> PPU& { return ppu; } auto Ppu() -> PPU& { return ppu; }
auto Memory() -> MemoryImpl* { return &memory_; }
void SetCpuMode(int mode) { cpu_mode_ = mode; } void SetCpuMode(int mode) { cpu_mode_ = mode; }
CPU::UpdateMode GetCpuMode() const { CPU::UpdateMode GetCpuMode() const {
return static_cast<CPU::UpdateMode>(cpu_mode_); return static_cast<CPU::UpdateMode>(cpu_mode_);
} }
void SetupMemory(ROM& rom) {
// Setup observers for the memory space
memory_.AddObserver(&apu);
memory_.AddObserver(&ppu);
// Load the ROM into memory and set up the memory mapping
memory_.Initialize(rom.vector());
}
private: private:
void WriteToRegister(uint16_t address, uint8_t value) { void WriteToRegister(uint16_t address, uint8_t value) {
memory_.WriteByte(address, value); memory_.WriteByte(address, value);

View File

@@ -8,15 +8,376 @@ namespace app {
namespace gui { namespace gui {
void TextWithSeparators(const absl::string_view &text) { void DrawDisplaySettings(ImGuiStyle* ref) {
// You can pass in a reference ImGuiStyle structure to compare to, revert to
// and save to (without a reference style pointer, we will use one compared
// locally as a reference)
ImGuiStyle& style = ImGui::GetStyle();
static ImGuiStyle ref_saved_style;
// Default to using internal storage as reference
static bool init = true;
if (init && ref == NULL) ref_saved_style = style;
init = false;
if (ref == NULL) ref = &ref_saved_style;
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
if (ImGui::ShowStyleSelector("Colors##Selector")) ref_saved_style = style;
ImGui::ShowFontSelector("Fonts##Selector");
// Simplified Settings (expose floating-pointer border sizes as boolean
// representing 0.0f or 1.0f)
if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f,
"%.0f"))
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the
// same value as FrameRounding
{
bool border = (style.WindowBorderSize > 0.0f);
if (ImGui::Checkbox("WindowBorder", &border)) {
style.WindowBorderSize = border ? 1.0f : 0.0f;
}
}
ImGui::SameLine();
{
bool border = (style.FrameBorderSize > 0.0f);
if (ImGui::Checkbox("FrameBorder", &border)) {
style.FrameBorderSize = border ? 1.0f : 0.0f;
}
}
ImGui::SameLine();
{
bool border = (style.PopupBorderSize > 0.0f);
if (ImGui::Checkbox("PopupBorder", &border)) {
style.PopupBorderSize = border ? 1.0f : 0.0f;
}
}
// Save/Revert button
if (ImGui::Button("Save Ref")) *ref = ref_saved_style = style;
ImGui::SameLine();
if (ImGui::Button("Revert Ref")) style = *ref;
ImGui::SameLine();
ImGui::Separator();
if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) {
if (ImGui::BeginTabItem("Sizes")) {
ImGui::SeparatorText("Main");
ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f,
20.0f, "%.0f");
ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f,
20.0f, "%.0f");
ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f,
20.0f, "%.0f");
ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing,
0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding,
0.0f, 10.0f, "%.0f");
ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f,
"%.0f");
ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f,
"%.0f");
ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f,
"%.0f");
ImGui::SeparatorText("Borders");
ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f,
1.0f, "%.0f");
ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f,
"%.0f");
ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f,
"%.0f");
ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f,
"%.0f");
ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f,
"%.0f");
ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f,
2.0f, "%.0f");
ImGui::SeparatorText("Rounding");
ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f,
"%.0f");
ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f,
"%.0f");
ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f,
"%.0f");
ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f,
"%.0f");
ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f,
12.0f, "%.0f");
ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f,
"%.0f");
ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f,
"%.0f");
ImGui::SeparatorText("Tables");
ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f,
20.0f, "%.0f");
ImGui::SliderAngle("TableAngledHeadersAngle",
&style.TableAngledHeadersAngle, -50.0f, +50.0f);
ImGui::SeparatorText("Widgets");
ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign,
0.0f, 1.0f, "%.2f");
int window_menu_button_position = style.WindowMenuButtonPosition + 1;
if (ImGui::Combo("WindowMenuButtonPosition",
(int*)&window_menu_button_position,
"None\0Left\0Right\0"))
style.WindowMenuButtonPosition = window_menu_button_position - 1;
ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition,
"Left\0Right\0");
ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign,
0.0f, 1.0f, "%.2f");
ImGui::SameLine();
ImGui::SliderFloat2("SelectableTextAlign",
(float*)&style.SelectableTextAlign, 0.0f, 1.0f,
"%.2f");
ImGui::SameLine();
ImGui::SliderFloat("SeparatorTextBorderSize",
&style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
ImGui::SliderFloat2("SeparatorTextAlign",
(float*)&style.SeparatorTextAlign, 0.0f, 1.0f,
"%.2f");
ImGui::SliderFloat2("SeparatorTextPadding",
(float*)&style.SeparatorTextPadding, 0.0f, 40.0f,
"%.0f");
ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f,
12.0f, "%.0f");
ImGui::SeparatorText("Tooltips");
for (int n = 0; n < 2; n++)
if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse"
: "HoverFlagsForTooltipNav")) {
ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse
: &style.HoverFlagsForTooltipNav;
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNone", p,
ImGuiHoveredFlags_DelayNone);
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayShort", p,
ImGuiHoveredFlags_DelayShort);
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p,
ImGuiHoveredFlags_DelayNormal);
ImGui::CheckboxFlags("ImGuiHoveredFlags_Stationary", p,
ImGuiHoveredFlags_Stationary);
ImGui::CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p,
ImGuiHoveredFlags_NoSharedDelay);
ImGui::TreePop();
}
ImGui::SeparatorText("Misc");
ImGui::SliderFloat2("DisplaySafeAreaPadding",
(float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f,
"%.0f");
ImGui::SameLine();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Colors")) {
static int output_dest = 0;
static bool output_only_modified = true;
if (ImGui::Button("Export")) {
if (output_dest == 0)
ImGui::LogToClipboard();
else
ImGui::LogToTTY();
ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE);
for (int i = 0; i < ImGuiCol_COUNT; i++) {
const ImVec4& col = style.Colors[i];
const char* name = ImGui::GetStyleColorName(i);
if (!output_only_modified ||
memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
ImGui::LogText(
"colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, "
"%.2ff);" IM_NEWLINE,
name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
}
ImGui::LogFinish();
}
ImGui::SameLine();
ImGui::SetNextItemWidth(120);
ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
ImGui::SameLine();
ImGui::Checkbox("Only Modified Colors", &output_only_modified);
static ImGuiTextFilter filter;
filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
static ImGuiColorEditFlags alpha_flags = 0;
if (ImGui::RadioButton("Opaque",
alpha_flags == ImGuiColorEditFlags_None)) {
alpha_flags = ImGuiColorEditFlags_None;
}
ImGui::SameLine();
if (ImGui::RadioButton("Alpha",
alpha_flags == ImGuiColorEditFlags_AlphaPreview)) {
alpha_flags = ImGuiColorEditFlags_AlphaPreview;
}
ImGui::SameLine();
if (ImGui::RadioButton(
"Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) {
alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;
}
ImGui::SameLine();
ImGui::SetNextWindowSizeConstraints(
ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10),
ImVec2(FLT_MAX, FLT_MAX));
ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border,
ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar |
ImGuiWindowFlags_NavFlattened);
ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
for (int i = 0; i < ImGuiCol_COUNT; i++) {
const char* name = ImGui::GetStyleColorName(i);
if (!filter.PassFilter(name)) continue;
ImGui::PushID(i);
ImGui::ColorEdit4("##color", (float*)&style.Colors[i],
ImGuiColorEditFlags_AlphaBar | alpha_flags);
if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) {
// Tips: in a real user application, you may want to merge and use
// an icon font into the main font, so instead of "Save"/"Revert"
// you'd use icons! Read the FAQ and docs/FONTS.md about using icon
// fonts. It's really easy and super convenient!
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
if (ImGui::Button("Save")) {
ref->Colors[i] = style.Colors[i];
}
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
if (ImGui::Button("Revert")) {
style.Colors[i] = ref->Colors[i];
}
}
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
ImGui::TextUnformatted(name);
ImGui::PopID();
}
ImGui::PopItemWidth();
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Fonts")) {
ImGuiIO& io = ImGui::GetIO();
ImFontAtlas* atlas = io.Fonts;
ImGui::ShowFontAtlas(atlas);
// Post-baking font scaling. Note that this is NOT the nice way of
// scaling fonts, read below. (we enforce hard clamping manually as by
// default DragFloat/SliderFloat allows CTRL+Click text to get out of
// bounds).
const float MIN_SCALE = 0.3f;
const float MAX_SCALE = 2.0f;
static float window_scale = 1.0f;
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::DragFloat(
"window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE,
"%.2f",
ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
ImGui::SetWindowFontScale(window_scale);
ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE,
MAX_SCALE, "%.2f",
ImGuiSliderFlags_AlwaysClamp); // Scale everything
ImGui::PopItemWidth();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Rendering")) {
ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
ImGui::SameLine();
ImGui::Checkbox("Anti-aliased lines use texture",
&style.AntiAliasedLinesUseTex);
ImGui::SameLine();
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
ImGui::DragFloat("Curve Tessellation Tolerance",
&style.CurveTessellationTol, 0.02f, 0.10f, 10.0f,
"%.2f");
if (style.CurveTessellationTol < 0.10f)
style.CurveTessellationTol = 0.10f;
// When editing the "Circle Segment Max Error" value, draw a preview of
// its effect on auto-tessellated circles.
ImGui::DragFloat("Circle Tessellation Max Error",
&style.CircleTessellationMaxError, 0.005f, 0.10f, 5.0f,
"%.2f", ImGuiSliderFlags_AlwaysClamp);
const bool show_samples = ImGui::IsItemActive();
if (show_samples) ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
if (show_samples && ImGui::BeginTooltip()) {
ImGui::TextUnformatted("(R = radius, N = number of segments)");
ImGui::Spacing();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
const float min_widget_width = ImGui::CalcTextSize("N: MMM\nR: MMM").x;
for (int n = 0; n < 8; n++) {
const float RAD_MIN = 5.0f;
const float RAD_MAX = 70.0f;
const float rad =
RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
ImGui::BeginGroup();
ImGui::Text("R: %.f\nN: %d", rad,
draw_list->_CalcCircleAutoSegmentCount(rad));
const float canvas_width = std::max(min_widget_width, rad * 2.0f);
const float offset_x = floorf(canvas_width * 0.5f);
const float offset_y = floorf(RAD_MAX);
const ImVec2 p1 = ImGui::GetCursorScreenPos();
draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad,
ImGui::GetColorU32(ImGuiCol_Text));
ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
/*
const ImVec2 p2 = ImGui::GetCursorScreenPos();
draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y),
rad, ImGui::GetColorU32(ImGuiCol_Text));
ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
*/
ImGui::EndGroup();
ImGui::SameLine();
}
ImGui::EndTooltip();
}
ImGui::SameLine();
ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f,
"%.2f"); // Not exposing zero here so user doesn't
// "lose" the UI (zero alpha clips all
// widgets). But application code could have a
// toggle to switch between zero and non-zero.
ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f,
1.0f, "%.2f");
ImGui::SameLine();
ImGui::PopItemWidth();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::PopItemWidth();
}
void TextWithSeparators(const absl::string_view& text) {
ImGui::Separator(); ImGui::Separator();
ImGui::Text("%s", text.data()); ImGui::Text("%s", text.data());
ImGui::Separator(); ImGui::Separator();
} }
void ColorsYaze() { void ColorsYaze() {
ImGuiStyle *style = &ImGui::GetStyle(); ImGuiStyle* style = &ImGui::GetStyle();
ImVec4 *colors = style->Colors; ImVec4* colors = style->Colors;
style->WindowPadding = ImVec2(10.f, 10.f); style->WindowPadding = ImVec2(10.f, 10.f);
style->FramePadding = ImVec2(10.f, 2.f); style->FramePadding = ImVec2(10.f, 2.f);

View File

@@ -9,6 +9,8 @@ namespace yaze {
namespace app { namespace app {
namespace gui { namespace gui {
void DrawDisplaySettings(ImGuiStyle* ref = nullptr);
void TextWithSeparators(const absl::string_view& text); void TextWithSeparators(const absl::string_view& text);
void ColorsYaze(); void ColorsYaze();

View File

@@ -23,72 +23,125 @@ class DungeonObjectRenderer : public SharedROM {
public: public:
struct PseudoVram { struct PseudoVram {
std::vector<gfx::Bitmap> sheets; std::vector<gfx::Bitmap> sheets;
// TODO: Initialize with mock VRAM data
}; };
void CreateVramFromRoomBlockset() { DungeonObjectRenderer() {
// auto bitmap_manager = rom()->bitmap_manager(); // TODO: Constructor implementation
// uint16_t room_id = 0;
// auto room_blockset = rom()->room_blockset_ids[room_id];
// for (const auto blockset_id : room_blockset) {
// auto blockset = bitmap_manager[(uint16_t)blockset_id];
// vram_.sheets.push_back(*blockset.get());
// }
} }
void RenderObjectsAsBitmaps() { void LoadObject(uint16_t objectId) {
rom_data_ = rom()->vector(); // Prepare the CPU and memory environment
memory_.Initialize(rom_data_); memory_.Initialize(rom()->vector());
cpu.Init();
auto subtype1_ptr = core::subtype1_tiles; // Fetch the subtype pointers for the given object ID
auto subtype1_routine_ptr = core::subtype1_tiles + 0x200; auto subtypeInfo = FetchSubtypeInfo(objectId);
std::array<uint16_t, 256> routine_ptrs;
for (int i = 0; i < 256; i++) { // Configure the object based on the fetched information
uint16_t actual_ptr = rom()->toint16(subtype1_routine_ptr + (i * 2)); ConfigureObject(subtypeInfo);
routine_ptrs[i] = actual_ptr;
std::cout << std::hex << routine_ptrs[i] << std::endl; // Run the CPU emulation for the object's draw routines
RenderObject(subtypeInfo);
}
private:
struct SubtypeInfo {
uint16_t subtypePtr;
uint16_t routinePtr;
// Additional fields as needed
};
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 = (objectId >> 8) & 0xFF; // 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;
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));
} }
int i = 0; // Convert pointers from ROM-relative to absolute (if necessary)
for (const auto routine_ptr : routine_ptrs) { // info.subtypePtr = ConvertToAbsolutePtr(info.subtypePtr);
cpu.PC = routine_ptr - 2; // info.routinePtr = ConvertToAbsolutePtr(info.routinePtr);
cpu.PB = 0x00;
auto cycles_to_run = clock_.GetCycleCount(); return info;
}
while (true) { void ConfigureObject(const SubtypeInfo& info) {
auto opcode = cpu.FetchByte(); // TODO: Use the information in info to set up the object's initial state
// Fetch and execute an instruction // This may include setting CPU registers, loading specific tiles into VRAM,
cpu.ExecuteInstruction(opcode); // etc.
}
// Handle any interrupts, if necessary void RenderObject(const SubtypeInfo& info) {
cpu.HandleInterrupts(); // Assuming that the routine pointer and other necessary setup is done in
// ConfigureObject Start CPU at the routine's entry point
cpu.PC =
info.routinePtr; // info should be a member or passed as a parameter
cpu.PB = 0x01; // Set the program bank; adjust based on your memory mapping
// Check if the instruction is RTS // Run the CPU emulation loop
if (opcode == 0x60) { while (true) {
break; // Fetch the next opcode
} uint8_t opcode = cpu.FetchByte();
i++;
if (i > 50) { // Execute the fetched instruction
break; cpu.ExecuteInstruction(opcode);
}
// Handle any interrupts, if necessary
cpu.HandleInterrupts();
// Check if the end of the routine is reached (typically RTS instruction)
if (opcode == 0x60) { // RTS opcode
break;
} }
// Additional checks can be added here, e.g., maximum cycles or
// instructions
// Update the PPU state if necessary
// ppu.Update();
// After PPU update, reflect any changes in the Bitmap(s)
// UpdateBitmapFromPPU();
} }
auto subtype2_ptr = core::subtype2_tiles; // Post-rendering operations (if any)
auto subtype2_routine_ptr = // PostRenderOperations();
core::subtype2_tiles + 0x80; // Where the draw routines start
std::array<uint16_t, 128> subtype2_routine_ptrs;
for (int i = 0; i < 128; i++) {
subtype2_routine_ptrs[i] = subtype2_routine_ptr + i * 2;
}
auto subtype3_ptr = core::subtype3_tiles;
auto subtype3_routine_ptr =
core::subtype3_tiles + 0x100; // Where the draw routines start
} }
// Helper function to update Bitmap from PPU state
void UpdateBitmapFromPPU() {
// TODO: Implement logic to transfer PPU state changes to the Bitmap
// This involves reading the tile data and other graphics info from PPU
// and rendering it to the Bitmap object
}
// Optional: Handle any operations after rendering
void PostRenderOperations() {
// TODO: Implement any cleanup or additional processing needed after
// rendering
}
// Members
std::vector<uint8_t> rom_data_; std::vector<uint8_t> rom_data_;
emu::MemoryImpl memory_; emu::MemoryImpl memory_;
emu::ClockImpl clock_; emu::ClockImpl clock_;
@@ -98,6 +151,43 @@ class DungeonObjectRenderer : public SharedROM {
PseudoVram vram_; PseudoVram vram_;
}; };
// void CreateVramFromRoomBlockset() {
// // auto bitmap_manager = rom()->bitmap_manager();
// // uint16_t room_id = 0;
// // auto room_blockset = rom()->room_blockset_ids[room_id];
// // for (const auto blockset_id : room_blockset) {
// // auto blockset = bitmap_manager[(uint16_t)blockset_id];
// // vram_.sheets.push_back(*blockset.get());
// // }
// }
// int i = 0;
// for (const auto routine_ptr : routine_ptrs) {
// cpu.PC = routine_ptr - 2;
// cpu.PB = 0x01;
// auto cycles_to_run = clock_.GetCycleCount();
// while (true) {
// auto opcode = cpu.FetchByte();
// // Fetch and execute an instruction
// cpu.ExecuteInstruction(opcode);
// // Handle any interrupts, if necessary
// cpu.HandleInterrupts();
// // Check if the instruction is RTS
// if (opcode == 0x60) {
// break;
// }
// i++;
// if (i > 50) {
// break;
// }
// }
// }
enum class SpecialObjectType { Chest, BigChest, InterroomStairs }; enum class SpecialObjectType { Chest, BigChest, InterroomStairs };
struct Tile {}; struct Tile {};