Implement Tile16 changes commit callback and enhance editor functionality
- Added a callback mechanism in OverworldEditor to refresh the tile16 blockset and overworld map upon committing changes. - Improved the Tile16Editor with new methods for committing changes to the overworld and discarding local modifications. - Enhanced the UI layout for the Tile16 editor, optimizing the display and interaction of tile8 and tile16 controls. - Updated the drawing logic for tile previews and improved palette management features for better user experience.
This commit is contained in:
@@ -149,6 +149,19 @@ absl::Status OverworldEditor::Load() {
|
||||
RETURN_IF_ERROR(
|
||||
tile16_editor_.Initialize(tile16_blockset_bmp_, current_gfx_bmp_,
|
||||
*overworld_.mutable_all_tiles_types()));
|
||||
|
||||
// Set up callback for when tile16 changes are committed
|
||||
tile16_editor_.set_on_changes_committed([this]() -> absl::Status {
|
||||
// Regenerate the overworld editor's tile16 blockset
|
||||
RETURN_IF_ERROR(RefreshTile16Blockset());
|
||||
|
||||
// Force refresh of the current overworld map to show changes
|
||||
RefreshOverworldMap();
|
||||
|
||||
util::logf("Overworld editor refreshed after Tile16 changes");
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
ASSIGN_OR_RETURN(entrance_tiletypes_, zelda3::LoadEntranceTileTypes(rom_));
|
||||
all_gfx_loaded_ = true;
|
||||
return absl::OkStatus();
|
||||
@@ -1286,7 +1299,7 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
for (auto& each : overworld_.entrances()) {
|
||||
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
||||
each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
|
||||
auto color = ImVec4(255, 0, 255, 100);
|
||||
auto color = ImVec4(255, 255, 0, 100);
|
||||
if (each.is_hole_) {
|
||||
color = ImVec4(255, 255, 0, 200);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "imgui/imgui.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/macro.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
@@ -337,10 +338,98 @@ absl::Status Tile16Editor::RefreshTile16Blockset() {
|
||||
return absl::FailedPreconditionError("Tile16 blockset not available");
|
||||
}
|
||||
|
||||
// Regenerate the blockset atlas from current tile data
|
||||
// This would typically involve re-rendering all tiles from their component tile8s
|
||||
// Force regeneration of the blockset atlas from ROM tile16 data
|
||||
// This ensures the blockset reflects any changes made to individual tiles
|
||||
|
||||
// Clear cached tile bitmaps to force regeneration
|
||||
tile16_blockset_->tile_bitmaps.clear();
|
||||
|
||||
// Mark atlas as modified to trigger regeneration
|
||||
tile16_blockset_->atlas.set_modified(true);
|
||||
|
||||
// Update the atlas bitmap
|
||||
core::Renderer::Get().UpdateBitmap(&tile16_blockset_->atlas);
|
||||
|
||||
util::logf("Tile16 blockset refreshed");
|
||||
util::logf("Tile16 blockset refreshed and regenerated");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::RegenerateTile16BitmapFromROM() {
|
||||
// Get the current tile16 data from ROM
|
||||
auto* tile_data = GetCurrentTile16Data();
|
||||
if (!tile_data) {
|
||||
return absl::FailedPreconditionError("Cannot access current tile16 data");
|
||||
}
|
||||
|
||||
// Create a new 16x16 bitmap for the tile16
|
||||
std::vector<uint8_t> tile16_pixels(kTile16PixelCount, 0);
|
||||
|
||||
// Process each quadrant (2x2 grid of 8x8 tiles)
|
||||
for (int quadrant = 0; quadrant < 4; ++quadrant) {
|
||||
gfx::TileInfo* tile_info = nullptr;
|
||||
int quadrant_x = quadrant % 2;
|
||||
int quadrant_y = quadrant / 2;
|
||||
|
||||
// Get the tile info for this quadrant
|
||||
switch (quadrant) {
|
||||
case 0: tile_info = &tile_data->tile0_; break;
|
||||
case 1: tile_info = &tile_data->tile1_; break;
|
||||
case 2: tile_info = &tile_data->tile2_; break;
|
||||
case 3: tile_info = &tile_data->tile3_; break;
|
||||
}
|
||||
|
||||
if (!tile_info) continue;
|
||||
|
||||
// Get the tile8 ID and properties
|
||||
int tile8_id = tile_info->id_;
|
||||
bool x_flip = tile_info->horizontal_mirror_;
|
||||
bool y_flip = tile_info->vertical_mirror_;
|
||||
uint8_t palette = tile_info->palette_;
|
||||
|
||||
// Get the source tile8 bitmap
|
||||
if (tile8_id >= 0 && tile8_id < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[tile8_id].is_active()) {
|
||||
|
||||
const auto& source_tile8 = current_gfx_individual_[tile8_id];
|
||||
|
||||
// Copy the 8x8 tile into the appropriate quadrant of the 16x16 tile
|
||||
for (int ty = 0; ty < kTile8Size; ++ty) {
|
||||
for (int tx = 0; tx < kTile8Size; ++tx) {
|
||||
// Apply flip transformations
|
||||
int src_x = x_flip ? (kTile8Size - 1 - tx) : tx;
|
||||
int src_y = y_flip ? (kTile8Size - 1 - ty) : ty;
|
||||
int src_index = src_y * kTile8Size + src_x;
|
||||
|
||||
// Calculate destination in tile16
|
||||
int dst_x = (quadrant_x * kTile8Size) + tx;
|
||||
int dst_y = (quadrant_y * kTile8Size) + ty;
|
||||
int dst_index = dst_y * kTile16Size + dst_x;
|
||||
|
||||
// Copy pixel with bounds checking
|
||||
if (src_index >= 0 && src_index < static_cast<int>(source_tile8.size()) &&
|
||||
dst_index >= 0 && dst_index < kTile16PixelCount) {
|
||||
uint8_t pixel = source_tile8.data()[src_index];
|
||||
// Apply palette offset if needed
|
||||
tile16_pixels[dst_index] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the current tile16 bitmap with the regenerated data
|
||||
current_tile16_bmp_.Create(kTile16Size, kTile16Size, 8, tile16_pixels);
|
||||
|
||||
// Set the appropriate palette
|
||||
const auto& ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
if (ow_main_pal_group.size() > 0) {
|
||||
current_tile16_bmp_.SetPalette(ow_main_pal_group[0]);
|
||||
}
|
||||
|
||||
// Render the updated bitmap
|
||||
core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_);
|
||||
|
||||
util::logf("Regenerated Tile16 bitmap for tile %d from ROM data", current_tile16_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -469,334 +558,257 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
static bool show_tile8_selector = true;
|
||||
static bool show_tile16_editor = true;
|
||||
|
||||
// View controls
|
||||
if (BeginMenuBar()) {
|
||||
if (BeginMenu("View")) {
|
||||
Checkbox("Tile8 Selector", &show_tile8_selector);
|
||||
Checkbox("Tile16 Editor", &show_tile16_editor);
|
||||
EndMenu();
|
||||
}
|
||||
EndMenuBar();
|
||||
static bool show_advanced_controls = false;
|
||||
|
||||
// Compact header with essential info
|
||||
Text("Tile16 Editor - ID: %02X | Palette: %d", current_tile16_, current_palette_);
|
||||
SameLine();
|
||||
if (SmallButton("Advanced")) {
|
||||
show_advanced_controls = !show_advanced_controls;
|
||||
}
|
||||
|
||||
// Simple 2-column layout: Tile8 Source | Tile16 Editor + Controls
|
||||
if (BeginTable("##Tile16EditLayout", 2,
|
||||
Separator();
|
||||
|
||||
// Redesigned 3-column layout: Tile8 Source | Tile16 Editor | Controls
|
||||
if (BeginTable("##Tile16EditLayout", 3,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
|
||||
TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch, 0.6f);
|
||||
TableSetupColumn("Tile16 Editor", ImGuiTableColumnFlags_WidthStretch, 0.4f);
|
||||
TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch, 0.5F);
|
||||
TableSetupColumn("Tile16 Editor", ImGuiTableColumnFlags_WidthFixed, 100.0F); // Fixed width for 64x64 canvas + padding
|
||||
TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3F);
|
||||
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
// Tile8 selector column
|
||||
if (show_tile8_selector) {
|
||||
TableNextColumn();
|
||||
Text("Tile8 Source (Group: %d)", current_palette_group_);
|
||||
// Tile8 selector column - cleaner design
|
||||
TableNextColumn();
|
||||
Text("Tile8 Source");
|
||||
|
||||
// Compact palette group selector
|
||||
const char* palette_group_names[] = {"OW Main", "OW Aux", "OW Anim",
|
||||
"Dungeon", "Sprites", "Armor", "Sword"};
|
||||
SetNextItemWidth(100);
|
||||
if (Combo("##PaletteGroup", ¤t_palette_group_, palette_group_names, 7)) {
|
||||
RETURN_IF_ERROR(RefreshAllPalettes());
|
||||
}
|
||||
|
||||
// Palette group selector
|
||||
const char* palette_group_names[] = {"OW Main", "OW Aux", "OW Anim",
|
||||
"Dungeon", "Sprites", "Armor",
|
||||
"Sword"};
|
||||
if (Combo("##PaletteGroup", ¤t_palette_group_, palette_group_names,
|
||||
7)) {
|
||||
RETURN_IF_ERROR(RefreshAllPalettes());
|
||||
}
|
||||
|
||||
// Enhanced canvas table integration with proper content size reporting
|
||||
gui::BeginPadding(3);
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// Calculate the actual content size of the tile8 canvas (128x4096 pixels at 4x scale)
|
||||
ImVec2 tile8_content_size(128 * 4.0f,
|
||||
4096); // 4x scale for proper tile8 display
|
||||
gui::BeginChildWithScrollbar("##Tile8SelectorScrollRegion",
|
||||
tile8_content_size);
|
||||
|
||||
// Canvas draws within the properly sized scrollable region
|
||||
// Streamlined tile8 canvas with scrolling
|
||||
if (ImGui::BeginChild("##Tile8ScrollRegion", ImVec2(tile8_source_canvas_.width(), tile8_source_canvas_.height()), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
|
||||
// Enable dragging for scrolling behavior
|
||||
tile8_source_canvas_.set_draggable(true);
|
||||
tile8_source_canvas_.DrawBackground();
|
||||
gui::EndPadding();
|
||||
|
||||
tile8_source_canvas_.DrawContextMenu();
|
||||
|
||||
// Improved tile8 selection detection
|
||||
tile8_source_canvas_.DrawTileSelector(32.0f);
|
||||
// Tile8 selection with improved feedback
|
||||
tile8_source_canvas_.DrawTileSelector(32.0F);
|
||||
|
||||
if (tile8_source_canvas_.WasClicked() ||
|
||||
tile8_source_canvas_.WasDoubleClicked()) {
|
||||
if (tile8_source_canvas_.WasClicked() || tile8_source_canvas_.WasDoubleClicked()) {
|
||||
auto tile_pos = tile8_source_canvas_.GetLastClickPosition();
|
||||
int tile_x = static_cast<int>(tile_pos.x / 32);
|
||||
int tile_y = static_cast<int>(tile_pos.y / 32);
|
||||
int new_tile8 = tile_x + (tile_y * 8);
|
||||
|
||||
// Update current tile8 and refresh palette
|
||||
if (new_tile8 != current_tile8_ && new_tile8 >= 0 &&
|
||||
new_tile8 < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[new_tile8].is_active()) {
|
||||
current_tile8_ = new_tile8;
|
||||
RETURN_IF_ERROR(UpdateTile8Palette(current_tile8_));
|
||||
|
||||
// Debug log for tile selection
|
||||
util::logf("Selected Tile8: %d (pos: %d,%d)", current_tile8_, tile_x,
|
||||
tile_y);
|
||||
util::logf("Selected Tile8: %d", current_tile8_);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore proper tile8 source scaling
|
||||
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4.0f);
|
||||
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4.0F);
|
||||
tile8_source_canvas_.DrawGrid();
|
||||
tile8_source_canvas_.DrawOverlay();
|
||||
}
|
||||
EndChild();
|
||||
|
||||
EndChild();
|
||||
// Tile16 editor column - compact and focused
|
||||
TableNextColumn();
|
||||
|
||||
// Fixed size container to prevent canvas expansion
|
||||
if (ImGui::BeginChild("##Tile16FixedCanvas", ImVec2(90, 90), true,
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
|
||||
tile16_edit_canvas_.DrawBackground(ImVec2(64, 64)); // Fixed 64x64 display size
|
||||
tile16_edit_canvas_.DrawContextMenu();
|
||||
|
||||
Separator();
|
||||
Text("Palette: %d (Group: %d)", current_palette_, current_palette_group_);
|
||||
if (Button("Pal -", ImVec2(40, 0)))
|
||||
RETURN_IF_ERROR(CyclePalette(false));
|
||||
// Draw current tile16 bitmap at 4x scale for clarity (16x16 pixels -> 64x64 display)
|
||||
if (current_tile16_bmp_.is_active()) {
|
||||
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 4.0F);
|
||||
}
|
||||
|
||||
// Handle tile8 painting with improved hover preview
|
||||
if (current_tile8_ >= 0 &&
|
||||
current_tile8_ < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[current_tile8_].is_active()) {
|
||||
|
||||
// Create flipped tile if needed
|
||||
gfx::Bitmap* tile_to_paint = ¤t_gfx_individual_[current_tile8_];
|
||||
gfx::Bitmap flipped_tile;
|
||||
|
||||
if (x_flip || y_flip) {
|
||||
flipped_tile.Create(8, 8, 8, current_gfx_individual_[current_tile8_].vector());
|
||||
auto& data = flipped_tile.mutable_data();
|
||||
|
||||
if (x_flip) {
|
||||
for (int y = 0; y < 8; ++y) {
|
||||
for (int x = 0; x < 4; ++x) {
|
||||
std::swap(data[y * 8 + x], data[y * 8 + (7 - x)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (y_flip) {
|
||||
for (int y = 0; y < 4; ++y) {
|
||||
for (int x = 0; x < 8; ++x) {
|
||||
std::swap(data[y * 8 + x], data[(7 - y) * 8 + x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flipped_tile.SetPalette(current_gfx_individual_[current_tile8_].palette());
|
||||
core::Renderer::Get().RenderBitmap(&flipped_tile);
|
||||
tile_to_paint = &flipped_tile;
|
||||
}
|
||||
|
||||
// Paint with 8x8 tile size at 4x scale (32 display pixels per 8x8 tile)
|
||||
if (tile16_edit_canvas_.DrawTilePainter(*tile_to_paint, 8, 4.0F)) {
|
||||
ImVec2 click_pos = tile16_edit_canvas_.drawn_tile_position();
|
||||
// Convert from display coordinates to tile16 bitmap coordinates
|
||||
// The canvas shows 16x16 pixels at 4x scale (64x64 display), so divide by 4 to get actual pixel coordinates
|
||||
click_pos.x = (click_pos.x - 2) / 4.0F; // Account for padding and 4x scale
|
||||
click_pos.y = (click_pos.y - 2) / 4.0F;
|
||||
|
||||
// Ensure coordinates are within the 16x16 tile bounds
|
||||
click_pos.x = std::max(0.0F, std::min(15.0F, click_pos.x));
|
||||
click_pos.y = std::max(0.0F, std::min(15.0F, click_pos.y));
|
||||
|
||||
RETURN_IF_ERROR(DrawToCurrentTile16(click_pos));
|
||||
}
|
||||
}
|
||||
|
||||
tile16_edit_canvas_.DrawGrid(8.0F); // 8x8 grid
|
||||
tile16_edit_canvas_.DrawOverlay();
|
||||
|
||||
} // End fixed canvas child window
|
||||
ImGui::EndChild();
|
||||
|
||||
// Compact preview below canvas
|
||||
if (current_tile16_bmp_.is_active()) {
|
||||
auto* texture = current_tile16_bmp_.texture();
|
||||
if (texture) {
|
||||
Text("Preview:");
|
||||
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(32.0F, 32.0F));
|
||||
}
|
||||
}
|
||||
|
||||
// Controls column - clean and organized
|
||||
TableNextColumn();
|
||||
Text("Controls");
|
||||
|
||||
// Essential tile8 controls at the top
|
||||
Text("Tile8 Options:");
|
||||
Checkbox("X Flip", &x_flip);
|
||||
SameLine();
|
||||
Checkbox("Y Flip", &y_flip);
|
||||
Checkbox("Priority", &priority_tile);
|
||||
|
||||
// Show current tile8 selection
|
||||
if (current_tile8_ >= 0 &&
|
||||
current_tile8_ < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[current_tile8_].is_active()) {
|
||||
Text("Tile8: %d", current_tile8_);
|
||||
SameLine();
|
||||
if (Button("Pal +", ImVec2(40, 0)))
|
||||
RETURN_IF_ERROR(CyclePalette(true));
|
||||
|
||||
// Quick palette grid
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (i > 0 && i % 4 != 0)
|
||||
SameLine();
|
||||
bool is_current = (current_palette_ == i);
|
||||
if (is_current)
|
||||
PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.7f, 0.4f, 1.0f));
|
||||
if (Button(std::to_string(i).c_str(), ImVec2(20, 20))) {
|
||||
current_palette_ = i;
|
||||
RETURN_IF_ERROR(RefreshAllPalettes());
|
||||
}
|
||||
if (is_current)
|
||||
PopStyleColor();
|
||||
if (i == 3) { /* New line */
|
||||
}
|
||||
auto* tile8_texture = current_gfx_individual_[current_tile8_].texture();
|
||||
if (tile8_texture) {
|
||||
ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(16, 16));
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
// Tile16 editor column with integrated previews and controls
|
||||
if (show_tile16_editor) {
|
||||
TableNextColumn();
|
||||
Text("Tile16 Editor (ID: %02X)", current_tile16_);
|
||||
|
||||
// Wrap tile16 editor in a scrollable region
|
||||
if (ImGui::BeginChild("##Tile16EditorScrollRegion", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
// Main tile16 editing area
|
||||
gui::BeginPadding(3);
|
||||
tile16_edit_canvas_.DrawBackground();
|
||||
gui::EndPadding();
|
||||
|
||||
tile16_edit_canvas_.DrawContextMenu();
|
||||
|
||||
// Draw current tile16 bitmap with better table scaling (2x to fit in smaller space)
|
||||
if (current_tile16_bmp_.is_active()) {
|
||||
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 2.0f);
|
||||
}
|
||||
|
||||
// Handle tile8 painting with flip controls - improved detection
|
||||
if (tile8_source_canvas_.HasValidSelection() && current_tile8_ >= 0 &&
|
||||
current_tile8_ < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[current_tile8_].is_active()) {
|
||||
|
||||
// Create a flipped version of the tile8 for preview/painting if needed
|
||||
gfx::Bitmap* tile_to_paint = ¤t_gfx_individual_[current_tile8_];
|
||||
gfx::Bitmap flipped_tile;
|
||||
|
||||
if (x_flip || y_flip) {
|
||||
// Create flipped version
|
||||
flipped_tile.Create(
|
||||
8, 8, 8, current_gfx_individual_[current_tile8_].vector());
|
||||
auto& data = flipped_tile.mutable_data();
|
||||
|
||||
// Apply flips to the data
|
||||
if (x_flip) {
|
||||
for (int y = 0; y < 8; ++y) {
|
||||
for (int x = 0; x < 4; ++x) { // Only need to swap half
|
||||
int left_idx = y * 8 + x;
|
||||
int right_idx = y * 8 + (7 - x);
|
||||
std::swap(data[left_idx], data[right_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (y_flip) {
|
||||
for (int y = 0; y < 4; ++y) { // Only need to swap half
|
||||
for (int x = 0; x < 8; ++x) {
|
||||
int top_idx = y * 8 + x;
|
||||
int bottom_idx = (7 - y) * 8 + x;
|
||||
std::swap(data[top_idx], data[bottom_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flipped_tile.SetPalette(
|
||||
current_gfx_individual_[current_tile8_].palette());
|
||||
core::Renderer::Get().RenderBitmap(&flipped_tile);
|
||||
tile_to_paint = &flipped_tile;
|
||||
}
|
||||
|
||||
// Use 2x scale to match the tile16 bitmap scaling
|
||||
if (tile16_edit_canvas_.DrawTilePainter(*tile_to_paint, 16, 2.0f)) {
|
||||
ImVec2 click_pos = tile16_edit_canvas_.drawn_tile_position();
|
||||
// Scale down accounting for padding and 2x scale
|
||||
click_pos.x = (click_pos.x - 2) / 2.0f;
|
||||
click_pos.y = (click_pos.y - 2) / 2.0f;
|
||||
RETURN_IF_ERROR(DrawToCurrentTile16(click_pos));
|
||||
}
|
||||
}
|
||||
|
||||
tile16_edit_canvas_.DrawGrid();
|
||||
tile16_edit_canvas_.DrawOverlay();
|
||||
|
||||
// Preview section right below the editor
|
||||
Separator();
|
||||
Text("Preview");
|
||||
if (current_tile16_bmp_.is_active()) {
|
||||
auto* texture = current_tile16_bmp_.texture();
|
||||
if (texture) {
|
||||
Text("1x:");
|
||||
SameLine();
|
||||
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(16, 16));
|
||||
SameLine();
|
||||
Text("2x:");
|
||||
SameLine();
|
||||
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(32, 32));
|
||||
SameLine();
|
||||
Text("4x:");
|
||||
SameLine();
|
||||
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(64, 64));
|
||||
}
|
||||
}
|
||||
|
||||
// Controls integrated right below the editor
|
||||
Separator();
|
||||
|
||||
// Tile8 flip controls - right under the editor
|
||||
Text("Tile8 Controls:");
|
||||
Checkbox("X Flip", &x_flip);
|
||||
SameLine();
|
||||
Checkbox("Y Flip", &y_flip);
|
||||
SameLine();
|
||||
Checkbox("Priority", &priority_tile);
|
||||
|
||||
// Show selected tile8 preview inline
|
||||
if (current_tile8_ >= 0 &&
|
||||
current_tile8_ < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[current_tile8_].is_active()) {
|
||||
Text("Selected Tile8 (%d):", current_tile8_);
|
||||
SameLine();
|
||||
auto* tile8_texture =
|
||||
current_gfx_individual_[current_tile8_].texture();
|
||||
if (tile8_texture) {
|
||||
ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(24, 24));
|
||||
}
|
||||
}
|
||||
|
||||
Separator();
|
||||
|
||||
// Quick palette selector
|
||||
Text("Palette: %d", current_palette_);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (i > 0 && i % 4 != 0) SameLine();
|
||||
bool is_current = (current_palette_ == i);
|
||||
if (is_current)
|
||||
PushStyleColor(ImGuiCol_Button, ImVec4(0.4F, 0.7F, 0.4F, 1.0F));
|
||||
if (Button(std::to_string(i).c_str(), ImVec2(18, 18))) {
|
||||
current_palette_ = i;
|
||||
RETURN_IF_ERROR(RefreshAllPalettes());
|
||||
}
|
||||
if (BeginChild("InfoPaletteChild", ImVec2(270, 120), true)) {
|
||||
gui::DrawTable(tile_edit_table_);
|
||||
}
|
||||
EndChild();
|
||||
// Compact controls section directly below
|
||||
if (BeginTable(
|
||||
"##Tile16CompactControls", 2,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
|
||||
TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||
TableSetupColumn("Advanced", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
TableNextRow();
|
||||
|
||||
// Actions column
|
||||
TableNextColumn();
|
||||
if (BeginChild("ActionsChild", ImVec2(190, 120), true)) {
|
||||
if (Button("Clear Tile16", ImVec2(80, 0)))
|
||||
RETURN_IF_ERROR(ClearTile16());
|
||||
if (Button("Copy", ImVec2(80, 0)))
|
||||
RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_));
|
||||
if (Button("Paste", ImVec2(80, 0)))
|
||||
RETURN_IF_ERROR(PasteTile16FromClipboard());
|
||||
|
||||
Separator();
|
||||
|
||||
bool can_undo = !undo_stack_.empty();
|
||||
bool can_redo = !redo_stack_.empty();
|
||||
|
||||
if (!can_undo)
|
||||
BeginDisabled();
|
||||
if (Button("Undo", ImVec2(80, 0)))
|
||||
RETURN_IF_ERROR(Undo());
|
||||
if (!can_undo)
|
||||
EndDisabled();
|
||||
|
||||
if (!can_redo)
|
||||
BeginDisabled();
|
||||
if (Button("Redo", ImVec2(80, 0)))
|
||||
RETURN_IF_ERROR(Redo());
|
||||
if (!can_redo)
|
||||
EndDisabled();
|
||||
|
||||
Separator();
|
||||
DrawScratchSpace();
|
||||
}
|
||||
EndChild();
|
||||
|
||||
// Advanced settings column
|
||||
TableNextColumn();
|
||||
if (BeginChild("AdvancedChild", ImVec2(0, 120), true)) {
|
||||
if (Button("Palette Settings")) {
|
||||
show_palette_settings_ = !show_palette_settings_;
|
||||
}
|
||||
|
||||
if (Button("Manual Tile8 Inputs")) {
|
||||
ImGui::OpenPopup("ManualTile8Editor");
|
||||
}
|
||||
HOVER_HINT("Edit tile8 IDs and properties directly");
|
||||
|
||||
if (Button("Refresh Blockset")) {
|
||||
RETURN_IF_ERROR(RefreshTile16Blockset());
|
||||
}
|
||||
HOVER_HINT("Regenerate tile16 blockset from ROM data");
|
||||
|
||||
Text("Advanced Palette:");
|
||||
const char* palette_group_names[] = {"OW Main", "OW Aux", "OW Anim",
|
||||
"Dungeon", "Sprites", "Armor",
|
||||
"Sword"};
|
||||
if (Combo("##AdvPaletteGroup", ¤t_palette_group_,
|
||||
palette_group_names, 7)) {
|
||||
RETURN_IF_ERROR(RefreshAllPalettes());
|
||||
}
|
||||
|
||||
Text("Normalization:");
|
||||
int mask_value = static_cast<int>(palette_normalization_mask_);
|
||||
if (SliderInt("##NormMask", &mask_value, 1, 255, "0x%02X")) {
|
||||
palette_normalization_mask_ = static_cast<uint8_t>(mask_value);
|
||||
RETURN_IF_ERROR(LoadTile8()); // Reload with new mask
|
||||
}
|
||||
|
||||
Checkbox("Auto Normalize", &auto_normalize_pixels_);
|
||||
}
|
||||
EndChild();
|
||||
|
||||
// Manual tile8 editor popup
|
||||
DrawManualTile8Inputs();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
// Close the tile16 editor scroll region
|
||||
EndChild();
|
||||
|
||||
EndTable();
|
||||
if (is_current) PopStyleColor();
|
||||
}
|
||||
|
||||
// Close show_controls conditional
|
||||
|
||||
Separator();
|
||||
|
||||
// Essential actions
|
||||
Text("Actions:");
|
||||
if (Button("Clear", ImVec2(50, 0))) {
|
||||
RETURN_IF_ERROR(ClearTile16());
|
||||
}
|
||||
SameLine();
|
||||
if (Button("Copy", ImVec2(50, 0))) {
|
||||
RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_));
|
||||
}
|
||||
if (Button("Paste", ImVec2(50, 0))) {
|
||||
RETURN_IF_ERROR(PasteTile16FromClipboard());
|
||||
}
|
||||
|
||||
Separator();
|
||||
|
||||
// Save/Discard changes
|
||||
Text("Changes:");
|
||||
if (Button("Save", ImVec2(50, 0))) {
|
||||
RETURN_IF_ERROR(CommitChangesToOverworld());
|
||||
}
|
||||
HOVER_HINT("Apply changes to overworld and regenerate blockset");
|
||||
SameLine();
|
||||
if (Button("Discard", ImVec2(50, 0))) {
|
||||
RETURN_IF_ERROR(DiscardChanges());
|
||||
}
|
||||
HOVER_HINT("Reload tile16 from ROM, discarding local changes");
|
||||
|
||||
bool can_undo = !undo_stack_.empty();
|
||||
|
||||
if (!can_undo) BeginDisabled();
|
||||
if (Button("Undo", ImVec2(50, 0))) {
|
||||
RETURN_IF_ERROR(Undo());
|
||||
}
|
||||
if (!can_undo) EndDisabled();
|
||||
|
||||
// Advanced controls (collapsible)
|
||||
if (show_advanced_controls) {
|
||||
Separator();
|
||||
Text("Advanced:");
|
||||
|
||||
if (Button("Palette Settings")) {
|
||||
show_palette_settings_ = !show_palette_settings_;
|
||||
}
|
||||
|
||||
if (Button("Manual Edit")) {
|
||||
ImGui::OpenPopup("ManualTile8Editor");
|
||||
}
|
||||
|
||||
if (Button("Refresh Blockset")) {
|
||||
RETURN_IF_ERROR(RefreshTile16Blockset());
|
||||
}
|
||||
|
||||
// Scratch space in compact form
|
||||
Text("Scratch:");
|
||||
DrawScratchSpace();
|
||||
|
||||
// Manual tile8 editor popup
|
||||
DrawManualTile8Inputs();
|
||||
}
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
// Draw palette settings if enabled
|
||||
DrawPaletteSettings();
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -1402,7 +1414,8 @@ absl::Status Tile16Editor::CommitChangesToBlockset() {
|
||||
}
|
||||
|
||||
// Update individual tile bitmaps (tile_bitmaps is a map)
|
||||
for (auto& [tile_id, tile_bitmap] : tile16_blockset_->tile_bitmaps) {
|
||||
for (auto& pair : tile16_blockset_->tile_bitmaps) {
|
||||
auto& tile_bitmap = pair.second;
|
||||
if (tile_bitmap.modified()) {
|
||||
core::Renderer::Get().UpdateBitmap(&tile_bitmap);
|
||||
tile_bitmap.set_modified(false);
|
||||
@@ -1412,6 +1425,33 @@ absl::Status Tile16Editor::CommitChangesToBlockset() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::CommitChangesToOverworld() {
|
||||
// Write all tile16 changes to ROM
|
||||
RETURN_IF_ERROR(SaveTile16ToROM());
|
||||
|
||||
// Regenerate the tile16 blockset to reflect changes
|
||||
RETURN_IF_ERROR(RefreshTile16Blockset());
|
||||
|
||||
// Update the overworld tilemap to use the new tile16 data
|
||||
RETURN_IF_ERROR(UpdateOverworldTilemap());
|
||||
|
||||
// Notify the parent editor (overworld editor) to regenerate its blockset
|
||||
if (on_changes_committed_) {
|
||||
RETURN_IF_ERROR(on_changes_committed_());
|
||||
}
|
||||
|
||||
util::logf("Committed all Tile16 changes to overworld system");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::DiscardChanges() {
|
||||
// Reload the current tile16 from ROM to discard any local changes
|
||||
RETURN_IF_ERROR(SetCurrentTile(current_tile16_));
|
||||
|
||||
util::logf("Discarded Tile16 changes for tile %d", current_tile16_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// Helper methods for palette management
|
||||
absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) {
|
||||
if (tile8_id < 0 ||
|
||||
@@ -1580,13 +1620,17 @@ void Tile16Editor::DrawPaletteSettings() {
|
||||
}
|
||||
|
||||
Text("Pixel Value Distribution:");
|
||||
for (const auto& [value, count] : pixel_counts) {
|
||||
for (const auto& pair : pixel_counts) {
|
||||
int value = pair.first;
|
||||
int count = pair.second;
|
||||
Text(" Value %d (0x%X): %d pixels", value, value, count);
|
||||
}
|
||||
|
||||
Text("Palette Colors Used:");
|
||||
const auto& palette = current_gfx_individual_[current_tile8_].palette();
|
||||
for (const auto& [value, count] : pixel_counts) {
|
||||
for (const auto& pair : pixel_counts) {
|
||||
int value = pair.first;
|
||||
int count = pair.second;
|
||||
if (value < static_cast<int>(palette.size())) {
|
||||
auto color = palette[value];
|
||||
ImVec4 display_color = color.rgb();
|
||||
|
||||
@@ -104,6 +104,8 @@ class Tile16Editor : public gfx::GfxContext {
|
||||
absl::Status SaveTile16ToROM();
|
||||
absl::Status UpdateOverworldTilemap();
|
||||
absl::Status CommitChangesToBlockset();
|
||||
absl::Status CommitChangesToOverworld();
|
||||
absl::Status DiscardChanges();
|
||||
|
||||
// Helper methods for palette management
|
||||
absl::Status UpdateTile8Palette(int tile8_id);
|
||||
@@ -114,12 +116,18 @@ class Tile16Editor : public gfx::GfxContext {
|
||||
absl::Status UpdateROMTile16Data();
|
||||
absl::Status RefreshTile16Blockset();
|
||||
gfx::Tile16* GetCurrentTile16Data();
|
||||
absl::Status RegenerateTile16BitmapFromROM();
|
||||
|
||||
// Manual tile8 input controls
|
||||
void DrawManualTile8Inputs();
|
||||
|
||||
void set_rom(Rom* rom) { rom_ = rom; }
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
// Callback for when changes are committed to notify parent editor
|
||||
void set_on_changes_committed(std::function<absl::Status()> callback) {
|
||||
on_changes_committed_ = callback;
|
||||
}
|
||||
|
||||
private:
|
||||
Rom* rom_ = nullptr;
|
||||
@@ -198,10 +206,10 @@ class Tile16Editor : public gfx::GfxContext {
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gfx::Bitmap tile16_blockset_bmp_;
|
||||
|
||||
// Canvas for editing the selected tile - smaller size for table fit
|
||||
// Canvas for editing the selected tile - optimized for 2x2 grid of 8x8 tiles (16x16 total)
|
||||
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas",
|
||||
ImVec2(128, 128), // Reduced from kTile16CanvasSize to fit tables
|
||||
gui::CanvasGridSize::k64x64, 2.0f}; // Reduced scale to fit tables
|
||||
ImVec2(64, 64), // Fixed 64x64 display size (16x16 pixels at 4x scale)
|
||||
gui::CanvasGridSize::k8x8, 4.0F}; // 8x8 grid with 4x scale for clarity
|
||||
gfx::Bitmap current_tile16_bmp_;
|
||||
|
||||
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
||||
@@ -220,6 +228,9 @@ class Tile16Editor : public gfx::GfxContext {
|
||||
gfx::SnesPalette palette_;
|
||||
|
||||
absl::Status status_;
|
||||
|
||||
// Callback to notify parent editor when changes are committed
|
||||
std::function<absl::Status()> on_changes_committed_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
@@ -16,19 +16,22 @@ ImVec2 AlignToGrid(ImVec2 pos, float grid_step) {
|
||||
std::floor(pos.y / grid_step) * grid_step);
|
||||
}
|
||||
|
||||
float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size, float global_scale) {
|
||||
if (content_size.x <= 0 || content_size.y <= 0) return global_scale;
|
||||
|
||||
float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size,
|
||||
float global_scale) {
|
||||
if (content_size.x <= 0 || content_size.y <= 0)
|
||||
return global_scale;
|
||||
|
||||
float scale_x = (canvas_size.x * global_scale) / content_size.x;
|
||||
float scale_y = (canvas_size.y * global_scale) / content_size.y;
|
||||
return std::min(scale_x, scale_y);
|
||||
}
|
||||
|
||||
int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale, int tiles_per_row) {
|
||||
int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale,
|
||||
int tiles_per_row) {
|
||||
float scaled_tile_size = tile_size * scale;
|
||||
int tile_x = static_cast<int>(mouse_pos.x / scaled_tile_size);
|
||||
int tile_y = static_cast<int>(mouse_pos.y / scaled_tile_size);
|
||||
|
||||
|
||||
return tile_x + (tile_y * tiles_per_row);
|
||||
}
|
||||
|
||||
@@ -36,35 +39,40 @@ bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
|
||||
if (!rom || palette_manager.palettes_loaded) {
|
||||
return palette_manager.palettes_loaded;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const auto& palette_groups = rom->palette_group();
|
||||
palette_manager.rom_palette_groups.clear();
|
||||
palette_manager.palette_group_names.clear();
|
||||
|
||||
|
||||
// Overworld palettes
|
||||
if (palette_groups.overworld_main.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.overworld_main[0]);
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.overworld_main[0]);
|
||||
palette_manager.palette_group_names.push_back("Overworld Main");
|
||||
}
|
||||
if (palette_groups.overworld_aux.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.overworld_aux[0]);
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.overworld_aux[0]);
|
||||
palette_manager.palette_group_names.push_back("Overworld Aux");
|
||||
}
|
||||
if (palette_groups.overworld_animated.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.overworld_animated[0]);
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.overworld_animated[0]);
|
||||
palette_manager.palette_group_names.push_back("Overworld Animated");
|
||||
}
|
||||
|
||||
|
||||
// Dungeon palettes
|
||||
if (palette_groups.dungeon_main.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.dungeon_main[0]);
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.dungeon_main[0]);
|
||||
palette_manager.palette_group_names.push_back("Dungeon Main");
|
||||
}
|
||||
|
||||
|
||||
// Sprite palettes
|
||||
if (palette_groups.global_sprites.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.global_sprites[0]);
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.global_sprites[0]);
|
||||
palette_manager.palette_group_names.push_back("Global Sprites");
|
||||
}
|
||||
if (palette_groups.armors.size() > 0) {
|
||||
@@ -75,38 +83,43 @@ bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.swords[0]);
|
||||
palette_manager.palette_group_names.push_back("Swords");
|
||||
}
|
||||
|
||||
|
||||
palette_manager.palettes_loaded = true;
|
||||
util::logf("Canvas: Loaded %zu ROM palette groups", palette_manager.rom_palette_groups.size());
|
||||
util::logf("Canvas: Loaded %zu ROM palette groups",
|
||||
palette_manager.rom_palette_groups.size());
|
||||
return true;
|
||||
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
util::logf("Canvas: Failed to load ROM palette groups: %s", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplyPaletteGroup(gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_manager,
|
||||
bool ApplyPaletteGroup(gfx::Bitmap* bitmap,
|
||||
const CanvasPaletteManager& palette_manager,
|
||||
int group_index, int palette_index) {
|
||||
if (!bitmap || group_index < 0 ||
|
||||
group_index >= static_cast<int>(palette_manager.rom_palette_groups.size())) {
|
||||
if (!bitmap || group_index < 0 ||
|
||||
group_index >=
|
||||
static_cast<int>(palette_manager.rom_palette_groups.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const auto& selected_palette = palette_manager.rom_palette_groups[group_index];
|
||||
|
||||
const auto& selected_palette =
|
||||
palette_manager.rom_palette_groups[group_index];
|
||||
|
||||
// Apply the palette based on the index
|
||||
if (palette_index >= 0 && palette_index < 8) {
|
||||
bitmap->SetPaletteWithTransparent(selected_palette, palette_index);
|
||||
} else {
|
||||
bitmap->SetPalette(selected_palette);
|
||||
}
|
||||
|
||||
|
||||
Renderer::Get().UpdateBitmap(bitmap);
|
||||
util::logf("Canvas: Applied palette group %d, index %d to bitmap", group_index, palette_index);
|
||||
util::logf("Canvas: Applied palette group %d, index %d to bitmap",
|
||||
group_index, palette_index);
|
||||
return true;
|
||||
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
util::logf("Canvas: Failed to apply palette: %s", e.what());
|
||||
return false;
|
||||
@@ -114,22 +127,23 @@ bool ApplyPaletteGroup(gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_
|
||||
}
|
||||
|
||||
// Drawing utility functions
|
||||
void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, ImVec4 color, float global_scale) {
|
||||
void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, ImVec4 color,
|
||||
float global_scale) {
|
||||
// Apply global scale to position and size
|
||||
float scaled_x = x * global_scale;
|
||||
float scaled_y = y * global_scale;
|
||||
float scaled_w = w * global_scale;
|
||||
float scaled_h = h * global_scale;
|
||||
|
||||
|
||||
ImVec2 origin(canvas_p0.x + scrolling.x + scaled_x,
|
||||
canvas_p0.y + scrolling.y + scaled_y);
|
||||
ImVec2 size(canvas_p0.x + scrolling.x + scaled_x + scaled_w,
|
||||
canvas_p0.y + scrolling.y + scaled_y + scaled_h);
|
||||
|
||||
uint32_t color_u32 = IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255);
|
||||
|
||||
uint32_t color_u32 = IM_COL32(color.x, color.y, color.z, color.w);
|
||||
draw_list->AddRectFilled(origin, size, color_u32);
|
||||
|
||||
|
||||
// Add a black outline
|
||||
ImVec2 outline_origin(origin.x - 1, origin.y - 1);
|
||||
ImVec2 outline_size(size.x + 1, size.y + 1);
|
||||
@@ -137,62 +151,67 @@ void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
}
|
||||
|
||||
void DrawCanvasText(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
const std::string& text, int x, int y, float global_scale) {
|
||||
const std::string& text, int x, int y, float global_scale) {
|
||||
// Apply global scale to text position
|
||||
float scaled_x = x * global_scale;
|
||||
float scaled_y = y * global_scale;
|
||||
|
||||
ImVec2 text_pos(canvas_p0.x + scrolling.x + scaled_x,
|
||||
|
||||
ImVec2 text_pos(canvas_p0.x + scrolling.x + scaled_x,
|
||||
canvas_p0.y + scrolling.y + scaled_y);
|
||||
|
||||
|
||||
// Draw text with black shadow for better visibility
|
||||
draw_list->AddText(ImVec2(text_pos.x + 1, text_pos.y + 1),
|
||||
IM_COL32(0, 0, 0, 255), text.c_str());
|
||||
draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), text.c_str());
|
||||
}
|
||||
|
||||
void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, uint32_t color) {
|
||||
ImVec2 origin(canvas_p0.x + scrolling.x + x,
|
||||
canvas_p0.y + scrolling.y + y);
|
||||
void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, int x, int y, int w, int h,
|
||||
uint32_t color) {
|
||||
ImVec2 origin(canvas_p0.x + scrolling.x + x, canvas_p0.y + scrolling.y + y);
|
||||
ImVec2 size(canvas_p0.x + scrolling.x + x + w,
|
||||
canvas_p0.y + scrolling.y + y + h);
|
||||
draw_list->AddRect(origin, size, color, 0, 0, 1.5f);
|
||||
}
|
||||
|
||||
void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, ImVec4 color) {
|
||||
uint32_t color_u32 = IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255);
|
||||
void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, int x, int y, int w, int h,
|
||||
ImVec4 color) {
|
||||
uint32_t color_u32 =
|
||||
IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255);
|
||||
DrawCanvasOutline(draw_list, canvas_p0, scrolling, x, y, w, h, color_u32);
|
||||
}
|
||||
|
||||
// Grid utility functions
|
||||
void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 canvas_p1,
|
||||
ImVec2 scrolling, float grid_step, float global_scale) {
|
||||
void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 canvas_p1, ImVec2 scrolling, float grid_step,
|
||||
float global_scale) {
|
||||
const uint32_t grid_color = IM_COL32(200, 200, 200, 50);
|
||||
const float grid_thickness = 0.5f;
|
||||
|
||||
|
||||
float scaled_grid_step = grid_step * global_scale;
|
||||
|
||||
|
||||
for (float x = fmodf(scrolling.x, scaled_grid_step);
|
||||
x < (canvas_p1.x - canvas_p0.x); x += scaled_grid_step) {
|
||||
draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y),
|
||||
ImVec2(canvas_p0.x + x, canvas_p1.y),
|
||||
grid_color, grid_thickness);
|
||||
ImVec2(canvas_p0.x + x, canvas_p1.y), grid_color,
|
||||
grid_thickness);
|
||||
}
|
||||
|
||||
|
||||
for (float y = fmodf(scrolling.y, scaled_grid_step);
|
||||
y < (canvas_p1.y - canvas_p0.y); y += scaled_grid_step) {
|
||||
draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y),
|
||||
ImVec2(canvas_p1.x, canvas_p0.y + y),
|
||||
grid_color, grid_thickness);
|
||||
ImVec2(canvas_p1.x, canvas_p0.y + y), grid_color,
|
||||
grid_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int highlight_tile_id, float grid_step) {
|
||||
if (highlight_tile_id == -1) return;
|
||||
|
||||
void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, int highlight_tile_id,
|
||||
float grid_step) {
|
||||
if (highlight_tile_id == -1)
|
||||
return;
|
||||
|
||||
int tile_x = highlight_tile_id % 8;
|
||||
int tile_y = highlight_tile_id / 8;
|
||||
ImVec2 tile_pos(canvas_p0.x + scrolling.x + tile_x * grid_step,
|
||||
@@ -202,10 +221,11 @@ void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolli
|
||||
draw_list->AddRectFilled(tile_pos, tile_pos_end, IM_COL32(255, 0, 255, 255));
|
||||
}
|
||||
|
||||
void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
ImVec2 canvas_sz, float grid_step, float global_scale) {
|
||||
void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, ImVec2 canvas_sz, float grid_step,
|
||||
float global_scale) {
|
||||
float scaled_grid_step = grid_step * global_scale;
|
||||
|
||||
|
||||
for (float x = fmodf(scrolling.x, scaled_grid_step);
|
||||
x < canvas_sz.x * global_scale; x += scaled_grid_step) {
|
||||
for (float y = fmodf(scrolling.y, scaled_grid_step);
|
||||
@@ -213,19 +233,20 @@ void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling
|
||||
int tile_x = (x - scrolling.x) / scaled_grid_step;
|
||||
int tile_y = (y - scrolling.y) / scaled_grid_step;
|
||||
int tile_id = tile_x + (tile_y * 16);
|
||||
|
||||
|
||||
char hex_id[8];
|
||||
snprintf(hex_id, sizeof(hex_id), "%02X", tile_id);
|
||||
|
||||
|
||||
draw_list->AddText(ImVec2(canvas_p0.x + x + (scaled_grid_step / 2) - 4,
|
||||
canvas_p0.y + y + (scaled_grid_step / 2) - 4),
|
||||
IM_COL32(255, 255, 255, 255), hex_id);
|
||||
canvas_p0.y + y + (scaled_grid_step / 2) - 4),
|
||||
IM_COL32(255, 255, 255, 255), hex_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Layout and interaction utilities
|
||||
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size, bool use_custom) {
|
||||
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size,
|
||||
bool use_custom) {
|
||||
return use_custom ? custom_size : content_region;
|
||||
}
|
||||
|
||||
@@ -239,28 +260,30 @@ bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1) {
|
||||
}
|
||||
|
||||
// Size reporting for ImGui table integration
|
||||
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, float padding) {
|
||||
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale,
|
||||
float padding) {
|
||||
// Calculate minimum size needed to display content with padding
|
||||
ImVec2 min_size = ImVec2(content_size.x * global_scale + padding * 2,
|
||||
content_size.y * global_scale + padding * 2);
|
||||
|
||||
content_size.y * global_scale + padding * 2);
|
||||
|
||||
// Ensure minimum practical size
|
||||
min_size.x = std::max(min_size.x, 64.0f);
|
||||
min_size.y = std::max(min_size.y, 64.0f);
|
||||
|
||||
|
||||
return min_size;
|
||||
}
|
||||
|
||||
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, float min_scale) {
|
||||
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale,
|
||||
float min_scale) {
|
||||
// Calculate preferred size with minimum scale constraint
|
||||
float effective_scale = std::max(global_scale, min_scale);
|
||||
ImVec2 preferred_size = ImVec2(content_size.x * effective_scale + 8.0f,
|
||||
content_size.y * effective_scale + 8.0f);
|
||||
|
||||
content_size.y * effective_scale + 8.0f);
|
||||
|
||||
// Cap to reasonable maximum sizes for table integration
|
||||
preferred_size.x = std::min(preferred_size.x, 800.0f);
|
||||
preferred_size.y = std::min(preferred_size.y, 600.0f);
|
||||
|
||||
|
||||
return preferred_size;
|
||||
}
|
||||
|
||||
@@ -271,7 +294,8 @@ void ReserveCanvasSpace(ImVec2 canvas_size, const std::string& label) {
|
||||
}
|
||||
ImGui::Dummy(canvas_size);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - canvas_size.x); // Move back to start
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() -
|
||||
canvas_size.x); // Move back to start
|
||||
}
|
||||
|
||||
void SetNextCanvasSize(ImVec2 size, bool auto_resize) {
|
||||
@@ -286,34 +310,38 @@ void SetNextCanvasSize(ImVec2 size, bool auto_resize) {
|
||||
|
||||
// High-level composite operations
|
||||
void DrawCanvasGrid(const CanvasRenderContext& ctx, int highlight_tile_id) {
|
||||
if (!ctx.enable_grid) return;
|
||||
|
||||
if (!ctx.enable_grid)
|
||||
return;
|
||||
|
||||
ctx.draw_list->PushClipRect(ctx.canvas_p0, ctx.canvas_p1, true);
|
||||
|
||||
|
||||
// Draw grid lines
|
||||
DrawCanvasGridLines(ctx.draw_list, ctx.canvas_p0, ctx.canvas_p1,
|
||||
ctx.scrolling, ctx.grid_step, ctx.global_scale);
|
||||
|
||||
DrawCanvasGridLines(ctx.draw_list, ctx.canvas_p0, ctx.canvas_p1,
|
||||
ctx.scrolling, ctx.grid_step, ctx.global_scale);
|
||||
|
||||
// Draw highlight if specified
|
||||
if (highlight_tile_id != -1) {
|
||||
DrawCustomHighlight(ctx.draw_list, ctx.canvas_p0, ctx.scrolling,
|
||||
highlight_tile_id, ctx.grid_step * ctx.global_scale);
|
||||
DrawCustomHighlight(ctx.draw_list, ctx.canvas_p0, ctx.scrolling,
|
||||
highlight_tile_id, ctx.grid_step * ctx.global_scale);
|
||||
}
|
||||
|
||||
|
||||
// Draw hex labels if enabled
|
||||
if (ctx.enable_hex_labels) {
|
||||
DrawHexTileLabels(ctx.draw_list, ctx.canvas_p0, ctx.scrolling,
|
||||
ImVec2(ctx.canvas_p1.x - ctx.canvas_p0.x, ctx.canvas_p1.y - ctx.canvas_p0.y),
|
||||
ctx.grid_step, ctx.global_scale);
|
||||
ImVec2(ctx.canvas_p1.x - ctx.canvas_p0.x,
|
||||
ctx.canvas_p1.y - ctx.canvas_p0.y),
|
||||
ctx.grid_step, ctx.global_scale);
|
||||
}
|
||||
|
||||
|
||||
ctx.draw_list->PopClipRect();
|
||||
}
|
||||
|
||||
void DrawCanvasOverlay(const CanvasRenderContext& ctx, const ImVector<ImVec2>& points,
|
||||
const ImVector<ImVec2>& selected_points) {
|
||||
const ImVec2 origin(ctx.canvas_p0.x + ctx.scrolling.x, ctx.canvas_p0.y + ctx.scrolling.y);
|
||||
|
||||
void DrawCanvasOverlay(const CanvasRenderContext& ctx,
|
||||
const ImVector<ImVec2>& points,
|
||||
const ImVector<ImVec2>& selected_points) {
|
||||
const ImVec2 origin(ctx.canvas_p0.x + ctx.scrolling.x,
|
||||
ctx.canvas_p0.y + ctx.scrolling.y);
|
||||
|
||||
// Draw hover points
|
||||
for (int n = 0; n < points.Size; n += 2) {
|
||||
ctx.draw_list->AddRect(
|
||||
@@ -326,20 +354,22 @@ void DrawCanvasOverlay(const CanvasRenderContext& ctx, const ImVector<ImVec2>& p
|
||||
if (!selected_points.empty()) {
|
||||
for (int n = 0; n < selected_points.size(); n += 2) {
|
||||
ctx.draw_list->AddRect(ImVec2(origin.x + selected_points[n].x,
|
||||
origin.y + selected_points[n].y),
|
||||
ImVec2(origin.x + selected_points[n + 1].x + 0x10,
|
||||
origin.y + selected_points[n + 1].y + 0x10),
|
||||
IM_COL32(255, 255, 255, 255), 1.0f);
|
||||
origin.y + selected_points[n].y),
|
||||
ImVec2(origin.x + selected_points[n + 1].x + 0x10,
|
||||
origin.y + selected_points[n + 1].y + 0x10),
|
||||
IM_COL32(255, 255, 255, 255), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector<ImVector<std::string>>& labels,
|
||||
int current_labels, int tile_id_offset) {
|
||||
if (current_labels >= labels.size()) return;
|
||||
|
||||
void DrawCanvasLabels(const CanvasRenderContext& ctx,
|
||||
const ImVector<ImVector<std::string>>& labels,
|
||||
int current_labels, int tile_id_offset) {
|
||||
if (current_labels >= labels.size())
|
||||
return;
|
||||
|
||||
float scaled_grid_step = ctx.grid_step * ctx.global_scale;
|
||||
|
||||
|
||||
for (float x = fmodf(ctx.scrolling.x, scaled_grid_step);
|
||||
x < (ctx.canvas_p1.x - ctx.canvas_p0.x); x += scaled_grid_step) {
|
||||
for (float y = fmodf(ctx.scrolling.y, scaled_grid_step);
|
||||
@@ -351,7 +381,7 @@ void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector<ImVector<st
|
||||
if (tile_id >= labels[current_labels].size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
const std::string& label = labels[current_labels][tile_id];
|
||||
ctx.draw_list->AddText(
|
||||
ImVec2(ctx.canvas_p0.x + x + (scaled_grid_step / 2) - tile_id_offset,
|
||||
@@ -361,6 +391,6 @@ void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector<ImVector<st
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CanvasUtils
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
} // namespace CanvasUtils
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
Reference in New Issue
Block a user