Dungeon object updates
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/editor.h"
|
||||
#include "app/core/pipeline.h"
|
||||
#include "app/editor/resources/palette_editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/editor.h"
|
||||
#include "app/core/pipeline.h"
|
||||
#include "app/editor/resources/palette_editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
297
src/app/editor/modules/music_editor.cc
Normal file
297
src/app/editor/modules/music_editor.cc
Normal file
@@ -0,0 +1,297 @@
|
||||
#include "music_editor.h"
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/modules/assembly_editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
// #include "snes_spc/demo/demo_util.h"
|
||||
// #include "snes_spc/demo/wave_writer.h"
|
||||
// #include "snes_spc/snes_spc/spc.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
namespace {
|
||||
|
||||
#define BUF_SIZE 2048
|
||||
|
||||
void PlaySPC() {
|
||||
// /* Create emulator and filter */
|
||||
// SNES_SPC* snes_spc = spc_new();
|
||||
// SPC_Filter* filter = spc_filter_new();
|
||||
// if (!snes_spc || !filter) error("Out of memory");
|
||||
|
||||
// /* Load SPC */
|
||||
// {
|
||||
// /* Load file into memory */
|
||||
// long spc_size;
|
||||
// void* spc = load_file("assets/music/hyrule_field.spc", &spc_size);
|
||||
|
||||
// /* Load SPC data into emulator */
|
||||
// error(spc_load_spc(snes_spc, spc, spc_size));
|
||||
// free(spc); /* emulator makes copy of data */
|
||||
|
||||
// /* Most SPC files have garbage data in the echo buffer, so clear that */
|
||||
// spc_clear_echo(snes_spc);
|
||||
|
||||
// /* Clear filter before playing */
|
||||
// spc_filter_clear(filter);
|
||||
// }
|
||||
|
||||
// /* Record 20 seconds to wave file */
|
||||
// wave_open(spc_sample_rate, "out.wav");
|
||||
// wave_enable_stereo();
|
||||
// while (wave_sample_count() < 30 * spc_sample_rate * 2) {
|
||||
// /* Play into buffer */
|
||||
// short buf[BUF_SIZE];
|
||||
// error(spc_play(snes_spc, BUF_SIZE, buf));
|
||||
|
||||
// /* Filter samples */
|
||||
// spc_filter_run(filter, buf, BUF_SIZE);
|
||||
|
||||
// wave_write(buf, BUF_SIZE);
|
||||
// }
|
||||
|
||||
// /* Cleanup */
|
||||
// spc_filter_delete(filter);
|
||||
// spc_delete(snes_spc);
|
||||
// wave_close();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MusicEditor::Update() {
|
||||
if (ImGui::BeginTable("MusicEditorColumns", 2, music_editor_flags_,
|
||||
ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("Assembly");
|
||||
ImGui::TableSetupColumn("Composition");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
assembly_editor_.InlineUpdate();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
DrawToolset();
|
||||
DrawChannels();
|
||||
DrawPianoRoll();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void MusicEditor::DrawChannels() {
|
||||
if (ImGui::BeginTabBar("MyTabBar", ImGuiTabBarFlags_None)) {
|
||||
for (int i = 1; i <= 8; ++i) {
|
||||
if (ImGui::BeginTabItem(absl::StrFormat("%d", i).data())) {
|
||||
DrawPianoStaff();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
static const int NUM_KEYS = 25;
|
||||
static bool keys[NUM_KEYS];
|
||||
|
||||
void MusicEditor::DrawPianoStaff() {
|
||||
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9);
|
||||
ImGui::BeginChild(child_id, ImVec2(0, 170), false)) {
|
||||
const int NUM_LINES = 5;
|
||||
const int LINE_THICKNESS = 2;
|
||||
const int LINE_SPACING = 40;
|
||||
|
||||
// Get the draw list for the current window
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Draw the staff lines
|
||||
ImVec2 canvas_p0 =
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
|
||||
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + ImGui::GetContentRegionAvail().y);
|
||||
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(32, 32, 32, 255));
|
||||
for (int i = 0; i < NUM_LINES; i++) {
|
||||
auto line_start = ImVec2(canvas_p0.x, canvas_p0.y + i * LINE_SPACING);
|
||||
auto line_end = ImVec2(canvas_p1.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + i * LINE_SPACING);
|
||||
draw_list->AddLine(line_start, line_end, IM_COL32(200, 200, 200, 255),
|
||||
LINE_THICKNESS);
|
||||
}
|
||||
|
||||
// Draw the ledger lines
|
||||
const int NUM_LEDGER_LINES = 3;
|
||||
for (int i = -NUM_LEDGER_LINES; i <= NUM_LINES + NUM_LEDGER_LINES; i++) {
|
||||
if (i % 2 == 0) continue; // skip every other line
|
||||
auto line_start = ImVec2(canvas_p0.x, canvas_p0.y + i * LINE_SPACING / 2);
|
||||
auto line_end = ImVec2(canvas_p1.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + i * LINE_SPACING / 2);
|
||||
draw_list->AddLine(line_start, line_end, IM_COL32(150, 150, 150, 255),
|
||||
LINE_THICKNESS);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void MusicEditor::DrawPianoRoll() {
|
||||
// Render the piano roll
|
||||
float key_width = ImGui::GetContentRegionAvail().x / NUM_KEYS;
|
||||
float white_key_height = ImGui::GetContentRegionAvail().y * 0.8f;
|
||||
float black_key_height = ImGui::GetContentRegionAvail().y * 0.5f;
|
||||
ImGui::Text("Piano Roll");
|
||||
ImGui::Separator();
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Draw the staff lines
|
||||
ImVec2 canvas_p0 =
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
|
||||
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + ImGui::GetContentRegionAvail().x,
|
||||
canvas_p0.y + ImGui::GetContentRegionAvail().y);
|
||||
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(200, 200, 200, 255));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.f, 0.f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.f);
|
||||
for (int i = 0; i < NUM_KEYS; i++) {
|
||||
// Calculate the position and size of the key
|
||||
ImVec2 key_pos = ImVec2(i * key_width, 0.0f);
|
||||
ImVec2 key_size;
|
||||
ImVec4 key_color;
|
||||
ImVec4 text_color;
|
||||
if (i % 12 == 1 || i % 12 == 3 || i % 12 == 6 || i % 12 == 8 ||
|
||||
i % 12 == 10) {
|
||||
// This is a black key
|
||||
key_size = ImVec2(key_width * 0.6f, black_key_height);
|
||||
key_color = ImVec4(0, 0, 0, 255);
|
||||
text_color = ImVec4(255, 255, 255, 255);
|
||||
} else {
|
||||
// This is a white key
|
||||
key_size = ImVec2(key_width, white_key_height);
|
||||
key_color = ImVec4(255, 255, 255, 255);
|
||||
text_color = ImVec4(0, 0, 0, 255);
|
||||
}
|
||||
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.f, 0.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, key_color);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
|
||||
if (ImGui::Button(kSongNotes[i].data(), key_size)) {
|
||||
keys[i] ^= 1;
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImVec2 button_pos = ImGui::GetItemRectMin();
|
||||
ImVec2 button_size = ImGui::GetItemRectSize();
|
||||
if (keys[i]) {
|
||||
ImVec2 dest;
|
||||
dest.x = button_pos.x + button_size.x;
|
||||
dest.y = button_pos.y + button_size.y;
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(button_pos, dest,
|
||||
IM_COL32(200, 200, 255, 200));
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void MusicEditor::DrawSongToolset() {
|
||||
if (ImGui::BeginTable("DWToolset", 8, toolset_table_flags_, ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("#1");
|
||||
ImGui::TableSetupColumn("#play");
|
||||
ImGui::TableSetupColumn("#rewind");
|
||||
ImGui::TableSetupColumn("#fastforward");
|
||||
ImGui::TableSetupColumn("volumeController");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void MusicEditor::DrawToolset() {
|
||||
static bool is_playing = false;
|
||||
static int selected_option = 0;
|
||||
static int current_volume = 0;
|
||||
static bool has_loaded_song = false;
|
||||
const int MAX_VOLUME = 100;
|
||||
|
||||
if (is_playing) {
|
||||
if (!has_loaded_song) {
|
||||
// PlaySPC();
|
||||
// current_song_ = Mix_LoadMUS("out.wav");
|
||||
// Mix_PlayMusic(current_song_, -1);
|
||||
// has_loaded_song = true;
|
||||
}
|
||||
|
||||
// // If there is no music playing
|
||||
// if (Mix_PlayingMusic() == 0) {
|
||||
// Mix_PlayMusic(current_song_, -1);
|
||||
// }
|
||||
// // If music is being played
|
||||
// else {
|
||||
// // If the music is paused
|
||||
// if (Mix_PausedMusic() == 1) {
|
||||
// // Resume the music
|
||||
// Mix_ResumeMusic();
|
||||
// }
|
||||
// // If the music is playing
|
||||
// else {
|
||||
// // Pause the music
|
||||
// Mix_PauseMusic();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
gui::ItemLabel("Select a song to edit: ", gui::ItemLabelFlags::Left);
|
||||
ImGui::Combo("#songs_in_game", &selected_option, kGameSongs, 30);
|
||||
|
||||
gui::ItemLabel("Controls: ", gui::ItemLabelFlags::Left);
|
||||
if (ImGui::BeginTable("SongToolset", 6, toolset_table_flags_, ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("#play");
|
||||
ImGui::TableSetupColumn("#rewind");
|
||||
ImGui::TableSetupColumn("#fastforward");
|
||||
ImGui::TableSetupColumn("#volume");
|
||||
ImGui::TableSetupColumn("#debug");
|
||||
|
||||
ImGui::TableSetupColumn("#slider");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(is_playing ? ICON_MD_STOP : ICON_MD_PLAY_ARROW)) {
|
||||
if (is_playing) {
|
||||
Mix_HaltMusic();
|
||||
has_loaded_song = false;
|
||||
}
|
||||
is_playing = !is_playing;
|
||||
}
|
||||
|
||||
BUTTON_COLUMN(ICON_MD_FAST_REWIND)
|
||||
BUTTON_COLUMN(ICON_MD_FAST_FORWARD)
|
||||
BUTTON_COLUMN(ICON_MD_VOLUME_UP)
|
||||
if (ImGui::Button(ICON_MD_ACCESS_TIME)) {
|
||||
music_tracker_.LoadSongs(*rom());
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SliderInt("Volume", ¤t_volume, 0, 100);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
const int SONG_DURATION = 120; // duration of the song in seconds
|
||||
static int current_time = 0; // current time in the song in seconds
|
||||
|
||||
// Display the current time in the song
|
||||
gui::ItemLabel("Current Time: ", gui::ItemLabelFlags::Left);
|
||||
ImGui::Text("%d:%02d", current_time / 60, current_time % 60);
|
||||
ImGui::SameLine();
|
||||
// Display the song duration/progress using a progress bar
|
||||
ImGui::ProgressBar((float)current_time / SONG_DURATION);
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
90
src/app/editor/modules/music_editor.h
Normal file
90
src/app/editor/modules/music_editor.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/modules/assembly_editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/music/tracker.h"
|
||||
// #include "snes_spc/demo/demo_util.h"
|
||||
// #include "snes_spc/demo/wave_writer.h"
|
||||
// #include "snes_spc/snes_spc/spc.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
static const char* kGameSongs[] = {"Title",
|
||||
"Light World",
|
||||
"Beginning",
|
||||
"Rabbit",
|
||||
"Forest",
|
||||
"Intro",
|
||||
"Town",
|
||||
"Warp",
|
||||
"Dark world",
|
||||
"Master sword",
|
||||
"File select",
|
||||
"Soldier",
|
||||
"Mountain",
|
||||
"Shop",
|
||||
"Fanfare",
|
||||
"Castle",
|
||||
"Palace (Pendant)",
|
||||
"Cave (Same as Secret Way)",
|
||||
"Clear (Dungeon end)",
|
||||
"Church",
|
||||
"Boss",
|
||||
"Dungeon (Crystal)",
|
||||
"Psychic",
|
||||
"Secret Way (Same as Cave)",
|
||||
"Rescue",
|
||||
"Crystal",
|
||||
"Fountain",
|
||||
"Pyramid",
|
||||
"Kill Agahnim",
|
||||
"Ganon Room",
|
||||
"Last Boss"};
|
||||
|
||||
static constexpr absl::string_view kSongNotes[] = {
|
||||
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C",
|
||||
"C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
|
||||
class MusicEditor : public SharedROM {
|
||||
public:
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void DrawChannels();
|
||||
void DrawPianoStaff();
|
||||
void DrawPianoRoll();
|
||||
void DrawSongToolset();
|
||||
void DrawToolset();
|
||||
|
||||
zelda3::Tracker music_tracker_;
|
||||
|
||||
// Mix_Music* current_song_ = NULL;
|
||||
|
||||
AssemblyEditor assembly_editor_;
|
||||
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;
|
||||
ImGuiTableFlags music_editor_flags_ = ImGuiTableFlags_SizingFixedFit |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable;
|
||||
|
||||
ImGuiTableFlags channel_table_flags_ =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable |
|
||||
ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
||||
ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
291
src/app/editor/modules/palette_editor.cc
Normal file
291
src/app/editor/modules/palette_editor.cc
Normal file
@@ -0,0 +1,291 @@
|
||||
#include "palette_editor.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
static inline float ImSaturate(float f) {
|
||||
return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f;
|
||||
}
|
||||
|
||||
#define IM_F32_TO_INT8_SAT(_VAL) \
|
||||
((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
|
||||
|
||||
int CustomFormatString(char* buf, size_t buf_size, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
#ifdef IMGUI_USE_STB_SPRINTF
|
||||
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
|
||||
#else
|
||||
int w = vsnprintf(buf, buf_size, fmt, args);
|
||||
#endif
|
||||
va_end(args);
|
||||
if (buf == nullptr) return w;
|
||||
if (w == -1 || w >= (int)buf_size) w = (int)buf_size - 1;
|
||||
buf[w] = 0;
|
||||
return w;
|
||||
}
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
absl::Status PaletteEditor::Update() {
|
||||
if (ImGui::BeginTable("paletteEditorTable", 2,
|
||||
ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame,
|
||||
ImVec2(0, 0))) {
|
||||
ImGui::TableSetupColumn("Palette Groups",
|
||||
ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
ImGui::TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch,
|
||||
ImGui::GetContentRegionAvail().x);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
for (int category = 0; category < kNumPalettes; ++category) {
|
||||
if (ImGui::TreeNode(kPaletteCategoryNames[category].data())) {
|
||||
status_ = DrawPaletteGroup(category);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Test Column");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
CLEAR_AND_RETURN_STATUS(status_)
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::EditColorInPalette(gfx::SNESPalette& palette, int index) {
|
||||
if (index >= palette.size()) {
|
||||
// Handle error: the index is out of bounds
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current color
|
||||
auto currentColor = palette.GetColor(index).GetRGB();
|
||||
if (ImGui::ColorPicker4("Color Picker", (float*)&palette[index])) {
|
||||
// The color was modified, update it in the palette
|
||||
palette(index, currentColor);
|
||||
}
|
||||
}
|
||||
|
||||
void PaletteEditor::ResetColorToOriginal(
|
||||
gfx::SNESPalette& palette, int index,
|
||||
const gfx::SNESPalette& originalPalette) {
|
||||
if (index >= palette.size() || index >= originalPalette.size()) {
|
||||
// Handle error: the index is out of bounds
|
||||
return;
|
||||
}
|
||||
|
||||
auto originalColor = originalPalette.GetColor(index).GetRGB();
|
||||
palette(index, originalColor);
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::DrawPaletteGroup(int category) {
|
||||
if (!rom()->isLoaded()) {
|
||||
return absl::NotFoundError("ROM not open, no palettes to display");
|
||||
}
|
||||
|
||||
const auto size =
|
||||
rom()->GetPaletteGroup(kPaletteGroupNames[category].data()).size();
|
||||
auto palettes = rom()->GetPaletteGroup(kPaletteGroupNames[category].data());
|
||||
// if (static bool init = false; !init) {
|
||||
// InitializeSavedPalette(palettes[0]);
|
||||
// }
|
||||
static bool edit_color = false;
|
||||
for (int j = 0; j < size; j++) {
|
||||
ImGui::Text("%d", j);
|
||||
|
||||
auto palette = palettes[j];
|
||||
auto pal_size = palette.size();
|
||||
|
||||
for (int n = 0; n < pal_size; n++) {
|
||||
ImGui::PushID(n);
|
||||
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
auto popup_id =
|
||||
absl::StrCat(kPaletteCategoryNames[category].data(), j, "_", n);
|
||||
|
||||
// Small icon of the color in the palette
|
||||
if (gui::SNESColorButton(popup_id, palette[n], palette_button_flags)) {
|
||||
edit_color = true;
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextItem(popup_id.c_str())) {
|
||||
RETURN_IF_ERROR(HandleColorPopup(palette, category, j, n))
|
||||
}
|
||||
|
||||
if (edit_color) {
|
||||
// The color button was clicked, open the popup
|
||||
if (ImGui::ColorEdit4(popup_id.c_str(),
|
||||
gfx::ToFloatArray(palette[n]).data(),
|
||||
palette_button_flags)) {
|
||||
EditColorInPalette(palette, n);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::HandleColorPopup(gfx::SNESPalette& palette, int i,
|
||||
int j, int n) {
|
||||
auto col = gfx::ToFloatArray(palette[n]);
|
||||
if (ImGui::ColorEdit4("Edit Color", col.data(), color_popup_flags)) {
|
||||
RETURN_IF_ERROR(rom()->UpdatePaletteColor(kPaletteGroupNames[i].data(), j,
|
||||
n, palette[n]))
|
||||
}
|
||||
|
||||
if (ImGui::Button("Copy as..", ImVec2(-1, 0))) ImGui::OpenPopup("Copy");
|
||||
|
||||
if (ImGui::BeginPopup("Copy")) {
|
||||
int cr = IM_F32_TO_INT8_SAT(col[0]);
|
||||
int cg = IM_F32_TO_INT8_SAT(col[1]);
|
||||
int cb = IM_F32_TO_INT8_SAT(col[2]);
|
||||
char buf[64];
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff)", col[0],
|
||||
col[1], col[2]);
|
||||
|
||||
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d)", cr, cg, cb);
|
||||
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb);
|
||||
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::DisplayPalette(gfx::SNESPalette& palette, bool loaded) {
|
||||
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
|
||||
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
|
||||
ImGuiColorEditFlags_NoDragDrop |
|
||||
ImGuiColorEditFlags_NoOptions;
|
||||
|
||||
// Generate a default palette. The palette will persist and can be edited.
|
||||
static bool init = false;
|
||||
if (loaded && !init) {
|
||||
InitializeSavedPalette(palette);
|
||||
init = true;
|
||||
}
|
||||
|
||||
static ImVec4 backup_color;
|
||||
bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags);
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
open_popup |= ImGui::Button("Palette");
|
||||
if (open_popup) {
|
||||
ImGui::OpenPopup("mypicker");
|
||||
backup_color = color;
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("mypicker")) {
|
||||
TEXT_WITH_SEPARATOR("Current Overworld Palette");
|
||||
ImGui::ColorPicker4("##picker", (float*)&color,
|
||||
misc_flags | ImGuiColorEditFlags_NoSidePreview |
|
||||
ImGuiColorEditFlags_NoSmallPreview);
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup(); // Lock X position
|
||||
ImGui::Text("Current ==>");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Previous");
|
||||
|
||||
if (ImGui::Button("Update Map Palette")) {
|
||||
}
|
||||
|
||||
ImGui::ColorButton(
|
||||
"##current", color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40));
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ColorButton(
|
||||
"##previous", backup_color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40)))
|
||||
color = backup_color;
|
||||
|
||||
// List of Colors in Overworld Palette
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Palette");
|
||||
for (int n = 0; n < IM_ARRAYSIZE(saved_palette_); n++) {
|
||||
ImGui::PushID(n);
|
||||
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
if (ImGui::ColorButton("##palette", saved_palette_[n],
|
||||
palette_button_flags_2, ImVec2(20, 20)))
|
||||
color = ImVec4(saved_palette_[n].x, saved_palette_[n].y,
|
||||
saved_palette_[n].z, color.w); // Preserve alpha!
|
||||
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 3);
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 4);
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void PaletteEditor::DrawPortablePalette(gfx::SNESPalette& palette) {
|
||||
static bool init = false;
|
||||
if (!init) {
|
||||
InitializeSavedPalette(palette);
|
||||
init = true;
|
||||
}
|
||||
|
||||
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)100);
|
||||
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
ImGui::BeginGroup(); // Lock X position
|
||||
ImGui::Text("Palette");
|
||||
for (int n = 0; n < IM_ARRAYSIZE(saved_palette_); n++) {
|
||||
ImGui::PushID(n);
|
||||
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
if (ImGui::ColorButton("##palette", saved_palette_[n],
|
||||
palette_button_flags_2, ImVec2(20, 20)))
|
||||
ImVec4(saved_palette_[n].x, saved_palette_[n].y, saved_palette_[n].z,
|
||||
1.0f); // Preserve alpha!
|
||||
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 3);
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 4);
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
122
src/app/editor/modules/palette_editor.h
Normal file
122
src/app/editor/modules/palette_editor.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
constexpr int kNumPalettes = 11;
|
||||
|
||||
static constexpr absl::string_view kPaletteCategoryNames[] = {
|
||||
"Sword", "Shield", "Clothes", "World Colors",
|
||||
"Area Colors", "Enemies", "Dungeons", "World Map",
|
||||
"Dungeon Map", "Triforce", "Crystal"};
|
||||
|
||||
static constexpr absl::string_view kPaletteGroupNames[] = {
|
||||
"swords", "shields", "armors", "ow_main",
|
||||
"ow_aux", "global_sprites", "dungeon_main", "ow_mini_map",
|
||||
"ow_mini_map", "3d_object", "3d_object"};
|
||||
|
||||
struct PaletteChange {
|
||||
std::string groupName;
|
||||
size_t paletteIndex;
|
||||
size_t colorIndex;
|
||||
gfx::SNESColor originalColor;
|
||||
gfx::SNESColor newColor;
|
||||
};
|
||||
|
||||
class PaletteEditorHistory {
|
||||
public:
|
||||
// Record a change in the palette editor
|
||||
void RecordChange(const std::string& groupName, size_t paletteIndex,
|
||||
size_t colorIndex, const gfx::SNESColor& originalColor,
|
||||
const gfx::SNESColor& newColor) {
|
||||
// Check size and remove the oldest if necessary
|
||||
if (recentChanges.size() >= maxHistorySize) {
|
||||
recentChanges.pop_front();
|
||||
}
|
||||
|
||||
// Push the new change
|
||||
recentChanges.push_back(
|
||||
{groupName, paletteIndex, colorIndex, originalColor, newColor});
|
||||
}
|
||||
|
||||
// Get recent changes for display in the palette editor
|
||||
const std::deque<PaletteChange>& GetRecentChanges() const {
|
||||
return recentChanges;
|
||||
}
|
||||
|
||||
// Restore the original color
|
||||
gfx::SNESColor GetOriginalColor(const std::string& groupName,
|
||||
size_t paletteIndex,
|
||||
size_t colorIndex) const {
|
||||
for (const auto& change : recentChanges) {
|
||||
if (change.groupName == groupName &&
|
||||
change.paletteIndex == paletteIndex &&
|
||||
change.colorIndex == colorIndex) {
|
||||
return change.originalColor;
|
||||
}
|
||||
}
|
||||
// Handle error or return default (this is just an example,
|
||||
// handle as appropriate for your application)
|
||||
return gfx::SNESColor();
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<PaletteChange> recentChanges;
|
||||
static const size_t maxHistorySize = 50; // or any other number you deem fit
|
||||
};
|
||||
|
||||
class PaletteEditor : public SharedROM {
|
||||
public:
|
||||
absl::Status Update();
|
||||
absl::Status DrawPaletteGroups();
|
||||
|
||||
void EditColorInPalette(gfx::SNESPalette& palette, int index);
|
||||
void ResetColorToOriginal(gfx::SNESPalette& palette, int index,
|
||||
const gfx::SNESPalette& originalPalette);
|
||||
void DisplayPalette(gfx::SNESPalette& palette, bool loaded);
|
||||
void DrawPortablePalette(gfx::SNESPalette& palette);
|
||||
|
||||
private:
|
||||
absl::Status DrawPaletteGroup(int category);
|
||||
absl::Status HandleColorPopup(gfx::SNESPalette& palette, int i, int j, int n);
|
||||
|
||||
void InitializeSavedPalette(const gfx::SNESPalette& palette) {
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
saved_palette_[n].x = palette.GetColor(n).GetRGB().x / 255;
|
||||
saved_palette_[n].y = palette.GetColor(n).GetRGB().y / 255;
|
||||
saved_palette_[n].z = palette.GetColor(n).GetRGB().z / 255;
|
||||
saved_palette_[n].w = 255; // Alpha
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status status_;
|
||||
|
||||
PaletteEditorHistory history_;
|
||||
|
||||
ImVec4 saved_palette_[256] = {};
|
||||
ImVec4 current_color_;
|
||||
|
||||
ImGuiColorEditFlags color_popup_flags =
|
||||
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha;
|
||||
ImGuiColorEditFlags palette_button_flags =
|
||||
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoTooltip;
|
||||
ImGuiColorEditFlags palette_button_flags_2 = ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/editor.h"
|
||||
#include "app/core/pipeline.h"
|
||||
#include "app/editor/resources/palette_editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/editor.h"
|
||||
#include "app/core/pipeline.h"
|
||||
#include "app/editor/resources/palette_editor.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
Reference in New Issue
Block a user