diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 605fdae4e6..966d944216 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -29,6 +29,7 @@ set(CUBOS_ENGINE_SOURCE "src/cubos/engine/tools/entity_inspector/plugin.cpp" "src/cubos/engine/tools/scene_editor/plugin.cpp" "src/cubos/engine/tools/debug_camera/plugin.cpp" + "src/cubos/engine/tools/voxel_palette_editor/plugin.cpp" "src/cubos/engine/transform/plugin.cpp" "src/cubos/engine/transform/local_to_world.cpp" diff --git a/engine/include/cubos/engine/tools/voxel_palette_editor/plugin.hpp b/engine/include/cubos/engine/tools/voxel_palette_editor/plugin.hpp new file mode 100644 index 0000000000..e7baf52b1d --- /dev/null +++ b/engine/include/cubos/engine/tools/voxel_palette_editor/plugin.hpp @@ -0,0 +1,22 @@ +/// @dir +/// @brief @ref voxel-palette-editor-tool-plugin plugin directory. + +/// @file +/// @brief Plugin entry point. +/// @ingroup voxel-palette-editor-tool-plugin + +#pragma once + +#include + +namespace cubos::engine::tools +{ + /// @defgroup voxel-palette-editor-tool-plugin Palette editor + /// @ingroup tool-plugins + /// @brief Allows the user to open and inspect/edit a palette asset. + + /// @brief Plugin entry function. + /// @param cubos @b CUBOS. main class + /// @ingroup voxel-palette-editor-tool-plugin + void voxelPaletteEditorPlugin(Cubos& cubos); +} // namespace cubos::engine::tools diff --git a/engine/src/cubos/engine/tools/voxel_palette_editor/plugin.cpp b/engine/src/cubos/engine/tools/voxel_palette_editor/plugin.cpp new file mode 100644 index 0000000000..98df24d675 --- /dev/null +++ b/engine/src/cubos/engine/tools/voxel_palette_editor/plugin.cpp @@ -0,0 +1,190 @@ +// todo: voxelpaletteeditor rename + +#include + +#include +#include + +#include +#include +#include +#include +#include + +using cubos::core::data::old::Debug; +using cubos::core::ecs::EventReader; +using cubos::core::ecs::Read; +using cubos::core::ecs::Write; + +using namespace cubos::engine; + +using tools::AssetSelectedEvent; + +struct SelectedPaletteInfo +{ + Asset asset; + VoxelPalette paletteCopy; + bool modified; + Asset next; +}; + +static void savePaletteUiGuard(Write assets, Write selectedPalette) +{ + // The popup only shows when we've already opened a palette and want to show another one. + // Thus if we haven't set the next asset variable we can just stop here. + if (selectedPalette->next.isNull()) + { + return; + } + + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5F, 0.5F)); + + if (ImGui::BeginPopupModal("Save Palette?", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + bool optionSelected = false; + + ImGui::Text("Do you want to save the modified palette?"); + ImGui::Separator(); + + if (ImGui::Button("Yes", ImVec2(80, 0))) + { + CUBOS_INFO("Saving palette asset {} modificaations", Debug(selectedPalette->next)); + assets->store(selectedPalette->asset, selectedPalette->paletteCopy); + assets->save(selectedPalette->asset); + optionSelected = true; + } + + ImGui::SetItemDefaultFocus(); + ImGui::SameLine(); + + if (ImGui::Button("No", ImVec2(80, 0))) + { + CUBOS_DEBUG("Discarding palette asset {} modifications", Debug(selectedPalette->next)); + optionSelected = true; + } + + ImGui::SameLine(); + + if (ImGui::Button("Cancel", ImVec2(80, 0))) + { + ImGui::CloseCurrentPopup(); + } + + if (optionSelected) + { + selectedPalette->asset = assets->load(selectedPalette->next); + selectedPalette->paletteCopy = assets->read(selectedPalette->asset).get(); + selectedPalette->modified = false; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + +static void checkAssetEventSystem(EventReader reader, Write assets, + Write selectedPalette) +{ + for (const auto& event : reader) + { + if (assets->type(event.asset) == typeid(VoxelPalette)) + { + CUBOS_INFO("Opening palette asset {}", Debug(event.asset)); + if (!selectedPalette->asset.isNull() && selectedPalette->modified) + { + CUBOS_DEBUG("Opening save palette UI guard"); + ImGui::OpenPopup("Save Palette?"); + selectedPalette->next = event.asset; + } + else + { + selectedPalette->asset = assets->load(event.asset); + selectedPalette->paletteCopy = assets->read(selectedPalette->asset).get(); + } + } + } + + // When the 'Save Palette?' option is opened using ImGui::OpenPopup, this will be displayed. + savePaletteUiGuard(assets, selectedPalette); +} + +static void voxelPaletteEditorSystem(Write assets, Write, Write selectedPalette, + Write activePalette) +{ + if (assets->status(selectedPalette->asset) != Assets::Status::Loaded) + { + return; + } + + ImGui::Begin("Palette Editor"); + + bool wasMaterialModified = false; + std::pair modifiedMaterial; + + for (uint16_t i = 0; i < selectedPalette->paletteCopy.size(); ++i) + { + const uint16_t materialIndex = i + 1; + auto& material = (VoxelMaterial&)selectedPalette->paletteCopy.get(materialIndex); + + std::string label = "Material " + std::to_string(materialIndex); + if (ImGui::ColorEdit4(label.c_str(), &material.color.r)) + { + CUBOS_DEBUG("Modified material"); + modifiedMaterial = std::pair(materialIndex, material); + wasMaterialModified = true; + } + } + + if (wasMaterialModified) + { + CUBOS_DEBUG("Storing as new asset because palette was modified"); + auto [idx, material] = modifiedMaterial; + selectedPalette->paletteCopy.set(idx, material); + selectedPalette->modified = true; + } + + // Add material / Make Active / Save + + if (ImGui::Button("Add Material") || selectedPalette->paletteCopy.size() == 0) + { + selectedPalette->paletteCopy.push(VoxelMaterial{{1.0F, 0.0F, 1.0F, 1.0F}}); + selectedPalette->modified = true; + } + + ImGui::SameLine(); + + if (ImGui::Button("Make Active")) + { + activePalette->asset = selectedPalette->asset; + } + + ImGui::SameLine(); + + if (ImGui::Button("Save")) + { + assets->store(selectedPalette->asset, selectedPalette->paletteCopy); + assets->save(selectedPalette->asset); + selectedPalette->modified = false; + } + + if (activePalette->asset == selectedPalette->asset) + { + ImGui::TextColored({0, 255, 0, 255}, "This is your current active palette!"); + } + + ImGui::End(); +} + +void cubos::engine::tools::voxelPaletteEditorPlugin(Cubos& cubos) +{ + cubos.addPlugin(rendererPlugin); + cubos.addPlugin(imguiPlugin); + cubos.addPlugin(assetExplorerPlugin); + cubos.addPlugin(voxelsPlugin); + + cubos.addResource(); + + cubos.system(checkAssetEventSystem).tagged("cubos.imgui"); + cubos.system(voxelPaletteEditorSystem).tagged("cubos.imgui"); +} diff --git a/tools/tesseratos/assets/main.pal b/tools/tesseratos/assets/main.pal new file mode 100644 index 0000000000..a9cadd754e Binary files /dev/null and b/tools/tesseratos/assets/main.pal differ diff --git a/tools/tesseratos/assets/main.pal.meta b/tools/tesseratos/assets/main.pal.meta new file mode 100644 index 0000000000..cb745e8dc5 --- /dev/null +++ b/tools/tesseratos/assets/main.pal.meta @@ -0,0 +1,3 @@ +{ + "id": "6f42ae5a-59d1-5df3-8720-83b8df6dd536" +} \ No newline at end of file diff --git a/tools/tesseratos/assets/main_copy.pal b/tools/tesseratos/assets/main_copy.pal new file mode 100644 index 0000000000..c40685a62e Binary files /dev/null and b/tools/tesseratos/assets/main_copy.pal differ diff --git a/tools/tesseratos/assets/main_copy.pal.meta b/tools/tesseratos/assets/main_copy.pal.meta new file mode 100644 index 0000000000..7ca85064f3 --- /dev/null +++ b/tools/tesseratos/assets/main_copy.pal.meta @@ -0,0 +1,3 @@ +{ + "id": "1abc415a-abcd-15f1-0555-1242df6dabc6" +} diff --git a/tools/tesseratos/src/main.cpp b/tools/tesseratos/src/main.cpp index d25306f8f8..40674b4a3d 100644 --- a/tools/tesseratos/src/main.cpp +++ b/tools/tesseratos/src/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -23,8 +24,9 @@ static void mockCamera(Write camera, Commands cmds) .entity(); } -static void mockSettings(Write settings) +static void setSettingsSystem(Write settings) { + settings->setBool("assets.io.readOnly", false); settings->setString("assets.io.path", TESSERATOS_ASSETS_FOLDER); } @@ -35,11 +37,12 @@ int main(int argc, char** argv) cubos.addPlugin(tools::sceneEditorPlugin); cubos.addPlugin(tools::entityInspectorPlugin); cubos.addPlugin(tools::worldInspectorPlugin); + cubos.addPlugin(tools::voxelPaletteEditorPlugin); cubos.addPlugin(tools::assetExplorerPlugin); cubos.addPlugin(tools::debugCameraPlugin); cubos.startupSystem(mockCamera).tagged("setup"); - cubos.startupSystem(mockSettings).tagged("setup"); + cubos.startupSystem(setSettingsSystem).tagged("setup"); cubos.run(); } \ No newline at end of file