From 8bdaeb566516eee5bc95366fec3b50b0dd80b339 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Mon, 24 Jul 2023 00:43:21 -0500 Subject: [PATCH] VideoCommon: add graphics mod editor main --- .../GraphicsModEditor/EditorMain.cpp | 667 ++++++++++++++++++ .../GraphicsModEditor/EditorMain.h | 89 +++ 2 files changed, 756 insertions(+) create mode 100644 Source/Core/VideoCommon/GraphicsModEditor/EditorMain.cpp create mode 100644 Source/Core/VideoCommon/GraphicsModEditor/EditorMain.h diff --git a/Source/Core/VideoCommon/GraphicsModEditor/EditorMain.cpp b/Source/Core/VideoCommon/GraphicsModEditor/EditorMain.cpp new file mode 100644 index 0000000000..00b3eaf387 --- /dev/null +++ b/Source/Core/VideoCommon/GraphicsModEditor/EditorMain.cpp @@ -0,0 +1,667 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/GraphicsModEditor/EditorMain.h" + +#include +#include + +#include +#include + +#include "Common/CommonPaths.h" +#include "Common/EnumUtils.h" +#include "Common/FileUtil.h" +#include "Common/JsonUtil.h" +#include "Core/ConfigManager.h" +#include "Core/System.h" + +#include "VideoCommon/AbstractGfx.h" +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/Assets/CustomAssetLoader.h" +#include "VideoCommon/Assets/CustomTextureData.h" +#include "VideoCommon/GraphicsModEditor/Controls/MeshExtractWindow.h" +#include "VideoCommon/GraphicsModEditor/EditorBackend.h" +#include "VideoCommon/GraphicsModEditor/EditorEvents.h" +#include "VideoCommon/GraphicsModEditor/EditorState.h" +#include "VideoCommon/GraphicsModEditor/EditorTypes.h" +#include "VideoCommon/GraphicsModEditor/Panels/ActiveTargetsPanel.h" +#include "VideoCommon/GraphicsModEditor/Panels/AssetBrowserPanel.h" +#include "VideoCommon/GraphicsModEditor/Panels/PropertiesPanel.h" +#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" +#include "VideoCommon/GraphicsModSystem/Runtime/Actions/ModifyLight.h" +#include "VideoCommon/GraphicsModSystem/Runtime/CustomResourceManager.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" +#include "VideoCommon/HiresTextures.h" +#include "VideoCommon/TextureCacheBase.h" +#include "VideoCommon/VideoConfig.h" + +namespace GraphicsModEditor +{ +namespace +{ +bool AddTextureToResources(const std::string& texture_path, const std::string& name, + EditorState* state) +{ + VideoCommon::CustomTextureData::ArraySlice::Level level; + if (!VideoCommon::LoadPNGTexture(&level, texture_path)) + { + return false; + } + + TextureConfig tex_config(level.width, level.height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0, + AbstractTextureType::Texture_2DArray); + auto editor_tex = g_gfx->CreateTexture(tex_config, name); + if (!editor_tex) + { + PanicAlertFmt("Failed to create editor texture '{}'", name); + return false; + } + + editor_tex->Load(0, level.width, level.height, level.width, level.data.data(), + sizeof(u32) * level.width * level.height); + state->m_editor_data.m_name_to_texture[name] = std::move(editor_tex); + + return true; +} + +bool AddTemplate(const std::string& template_path, const std::string& name, EditorState* state) +{ + std::string template_data; + if (!File::ReadFileToString(template_path, template_data)) + { + PanicAlertFmt("Failed to load editor template '{}'", name); + return false; + } + + state->m_editor_data.m_name_to_template[name] = template_data; + return true; +} +} // namespace +EditorMain::EditorMain() = default; +EditorMain::~EditorMain() = default; + +bool EditorMain::Initialize() +{ + if (!RebuildState()) + return false; + + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + + // TODO + m_enabled = true; + + return true; +} + +void EditorMain::Shutdown() +{ + m_active_targets_panel.reset(); + m_asset_browser_panel.reset(); + m_properties_panel.reset(); + + auto& system = Core::System::GetInstance(); + system.GetGraphicsModManager().SetEditorBackend(nullptr); + + m_state.reset(); + + m_has_changes = false; + m_enabled = false; + m_editor_session_in_progress = false; +} + +void EditorMain::DrawImGui() +{ + if (!m_enabled) + return; + + DrawMenu(); + + if (m_editor_session_in_progress) + { + m_active_targets_panel->DrawImGui(); + + if (!m_inspect_only) + m_asset_browser_panel->DrawImGui(); + m_properties_panel->DrawImGui(); + } + else + { + // ImGui::ShowDemoWindow(); + } +} + +bool EditorMain::RebuildState() +{ + m_state = std::make_unique(); + m_active_targets_panel = std::make_unique(*m_state); + m_asset_browser_panel = std::make_unique(*m_state); + m_properties_panel = std::make_unique(*m_state); + + m_state->m_user_data.m_asset_library = std::make_shared(); + m_state->m_editor_data.m_asset_library = + std::make_shared(); + m_state->m_editor_data.m_asset_library->Watch(File::GetSysDirectory() + GRAPHICSMODEDITOR_DIR); + m_change_occurred_event = + EditorEvents::ChangeOccurredEvent::Register([this] { OnChangeOccured(); }, "EditorMain"); + const std::string textures_path_root = + File::GetSysDirectory() + GRAPHICSMODEDITOR_DIR + "/Textures"; + if (!AddTextureToResources(fmt::format("{}/icons8-portraits-50.png", textures_path_root), + "hollow_cube", m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-document-500.png", textures_path_root), "file", + m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-folder-50.png", textures_path_root), + "tiny_folder", m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-folder-500.png", textures_path_root), "folder", + m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-image-file-500.png", textures_path_root), + "image", m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-code-file-100.png", textures_path_root), "code", + m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-cube-filled-50.png", textures_path_root), + "filled_cube", m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-search-50.png", textures_path_root), "search", + m_state.get())) + { + return false; + } + if (!AddTextureToResources(fmt::format("{}/icons8-error-50.png", textures_path_root), "error", + m_state.get())) + { + return false; + } + + const std::string templates_path_root = + File::GetSysDirectory() + GRAPHICSMODEDITOR_DIR + "/Templates"; + if (!AddTemplate(fmt::format("{}/raster_material.json", templates_path_root), "raster_material", + m_state.get())) + { + return false; + } + if (!AddTemplate(fmt::format("{}/raster_shader.json", templates_path_root), "raster_shader_meta", + m_state.get())) + { + return false; + } + if (!AddTemplate(fmt::format("{}/raster_shader.ps.glsl", templates_path_root), "raster_shader_ps", + m_state.get())) + { + return false; + } + if (!AddTemplate(fmt::format("{}/raster_shader.vs.glsl", templates_path_root), "raster_shader_vs", + m_state.get())) + { + return false; + } + + auto& system = Core::System::GetInstance(); + auto& asset_loader = system.GetCustomAssetLoader(); + auto& custom_resource_manager = system.GetCustomResourceManager(); + asset_loader.Reset(); + custom_resource_manager.Reset(); + + m_asset_reload_event = EditorEvents::AssetReloadEvent::Register( + [&custom_resource_manager, this](const VideoCommon::CustomAssetLibrary::AssetID& asset_id) { + if (asset_id != "") + { + custom_resource_manager.ReloadAsset(asset_id); + OnChangeOccured(); + } + }, + "EditorMain"); + + const std::string pipeline_path_root = + File::GetSysDirectory() + GRAPHICSMODEDITOR_DIR + "/Pipelines"; + const std::filesystem::path pipeline_path_root_fs{pipeline_path_root}; + + { + VideoCommon::Assets::AssetMap highlight_shader_asset_map; + highlight_shader_asset_map.try_emplace("metadata", pipeline_path_root_fs / "highlight" / + "color.rastershader"); + highlight_shader_asset_map.try_emplace("pixel_shader", + pipeline_path_root_fs / "highlight" / "color.ps.glsl"); + highlight_shader_asset_map.try_emplace("vertex_shader", + pipeline_path_root_fs / "highlight" / "color.vs.glsl"); + m_state->m_editor_data.m_asset_library->SetAssetIDMapData( + "highlight_shader", std::move(highlight_shader_asset_map)); + + VideoCommon::Assets::AssetMap highlight_material_asset_map; + highlight_material_asset_map.try_emplace("", pipeline_path_root_fs / "highlight" / + "highlight.rastermaterial"); + m_state->m_editor_data.m_asset_library->SetAssetIDMapData( + "editor-highlight-material", std::move(highlight_material_asset_map)); + } + + { + VideoCommon::Assets::AssetMap simple_light_visual_shader_asset_map; + simple_light_visual_shader_asset_map.try_emplace("metadata", + pipeline_path_root_fs / "light_visualization" / + "simple-light-visualization.rastershader"); + simple_light_visual_shader_asset_map.try_emplace("pixel_shader", + pipeline_path_root_fs / "light_visualization" / + "simple-light-visualization.ps.glsl"); + simple_light_visual_shader_asset_map.try_emplace("vertex_shader", + pipeline_path_root_fs / "light_visualization" / + "simple-light-visualization.vs.glsl"); + m_state->m_editor_data.m_asset_library->SetAssetIDMapData( + "simple_light_visualization_shader", std::move(simple_light_visual_shader_asset_map)); + + VideoCommon::Assets::AssetMap simple_light_visual_material_asset_map; + simple_light_visual_material_asset_map.try_emplace( + "", pipeline_path_root_fs / "light_visualization" / + "simple-light-visualization.rastermaterial"); + m_state->m_editor_data.m_asset_library->SetAssetIDMapData( + "editor-lighting-vis-material", std::move(simple_light_visual_material_asset_map)); + } + + { + VideoCommon::Assets::AssetMap normal_visual_shader_asset_map; + normal_visual_shader_asset_map.try_emplace("metadata", pipeline_path_root_fs / + "normal_visualization" / + "normal-visualization.rastershader"); + normal_visual_shader_asset_map.try_emplace("pixel_shader", pipeline_path_root_fs / + "normal_visualization" / + "normal-visualization.ps.glsl"); + normal_visual_shader_asset_map.try_emplace("vertex_shader", pipeline_path_root_fs / + "normal_visualization" / + "normal-visualization.vs.glsl"); + m_state->m_editor_data.m_asset_library->SetAssetIDMapData( + "normal_visualization_shader", std::move(normal_visual_shader_asset_map)); + + VideoCommon::Assets::AssetMap normal_visual_material_asset_map; + normal_visual_material_asset_map.try_emplace( + "", pipeline_path_root_fs / "normal_visualization" / "normal-visualization.rastermaterial"); + m_state->m_editor_data.m_asset_library->SetAssetIDMapData( + "editor-normal-vis-material", std::move(normal_visual_material_asset_map)); + } + + { + GraphicsModSystem::Config::GraphicsModTag tag; + tag.m_name = "Bloom"; + tag.m_description = "Post processing effect that blurs out the brightest areas of the screen"; + tag.m_color = Common::Vec3{0.66f, 0.63f, 0.16f}; + m_state->m_editor_data.m_tags[tag.m_name] = tag; + } + + { + GraphicsModSystem::Config::GraphicsModTag tag; + tag.m_name = "Depth of Field"; + tag.m_description = "Post processing effect that blurs distant objects"; + tag.m_color = Common::Vec3{0.8f, 0, 0}; + m_state->m_editor_data.m_tags[tag.m_name] = tag; + } + + { + GraphicsModSystem::Config::GraphicsModTag tag; + tag.m_name = "User Interface"; + tag.m_description = ""; + tag.m_color = Common::Vec3{0.01f, 0.04f, 0.99f}; + m_state->m_editor_data.m_tags[tag.m_name] = tag; + } + + return true; +} + +void EditorMain::DrawMenu() +{ + bool new_mod_popup = false; + if (ImGui::BeginMainMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::BeginMenu("New")) + { + if (ImGui::MenuItem("Project")) + { + new_mod_popup = true; + } + if (ImGui::MenuItem("Inspect Only")) + { + m_editor_session_in_progress = true; + m_inspect_only = true; + + RebuildState(); + auto& system = Core::System::GetInstance(); + system.GetGraphicsModManager().SetEditorBackend( + std::make_unique(*m_state)); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Open")) + { + const std::string& game_id = SConfig::GetInstance().GetGameID(); + const auto directories = + GetTextureDirectoriesWithGameId(File::GetUserPath(D_GRAPHICSMOD_IDX), game_id); + if (directories.empty()) + { + ImGui::Text("No available projects, create a new project instead"); + } + else + { + for (const auto& directory : directories) + { + const auto name = StringToPath(directory).filename(); + const auto name_str = PathToString(name); + if (ImGui::MenuItem(name_str.c_str())) + { + LoadMod(name_str); + } + } + } + ImGui::EndMenu(); + } + if (ImGui::MenuItem("Save", "Ctrl+S", false, + m_has_changes && m_editor_session_in_progress && !m_inspect_only)) + { + Save(); + } + if (ImGui::MenuItem("Save As..", nullptr, false, false)) + { + // TODO + } + if (ImGui::MenuItem("Close", nullptr, false, m_editor_session_in_progress)) + { + Close(); + } + + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Scene")) + { + if (ImGui::MenuItem("Export Scene As Mesh", nullptr, false, m_editor_session_in_progress)) + { + m_open_mesh_dump_export_window = true; + } + if (ImGui::MenuItem("Export Scene Metadata As JSON", nullptr, false, + m_editor_session_in_progress)) + { + picojson::array data_objects; + for (const auto& [draw_call_id, data] : m_state->m_runtime_data.m_draw_call_id_to_data) + { + picojson::object obj; + obj.emplace("draw_call_id", fmt::to_string(Common::ToUnderlying(draw_call_id))); + obj.emplace("projection type", fmt::to_string(data.draw_data.projection_type)); + // TODO: blending, depth, rasterization... + + picojson::array textures; + for (const auto& texture : data.draw_data.textures) + { + picojson::object texture_obj; + texture_obj.emplace("hash", texture.hash_name); + if (texture.texture_type == GraphicsModSystem::TextureType::Normal) + { + texture_obj.emplace("type", "Normal"); + } + else if (texture.texture_type == GraphicsModSystem::TextureType::EFB) + { + texture_obj.emplace("type", "EFB"); + } + else if (texture.texture_type == GraphicsModSystem::TextureType::XFB) + { + texture_obj.emplace("type", "XFB"); + } + + textures.push_back(picojson::value{texture_obj}); + } + obj.emplace("textures", picojson::value{textures}); + data_objects.push_back(picojson::value{obj}); + } + const std::string path = File::GetUserPath(D_DUMP_IDX) + SConfig::GetInstance().GetGameID(); + JsonToFile(PathToString(path + "_SceneMetadata.json"), picojson::value{data_objects}, true); + } + if (ImGui::MenuItem("Disable all actions", nullptr, + &m_state->m_editor_data.m_disable_all_actions)) + { + } + if (ImGui::MenuItem("View lighting", nullptr, &m_state->m_editor_data.m_view_lighting)) + { + } + if (ImGui::MenuItem("View normals", nullptr, &m_state->m_editor_data.m_view_normals)) + { + } + ImGui::EndMenu(); + } + + ImGui::EndMainMenuBar(); + } + + if (m_open_mesh_dump_export_window && m_state) + { + if (Controls::ShowMeshExtractWindow(m_state->m_scene_dumper, m_last_mesh_dump_request)) + { + m_open_mesh_dump_export_window = false; + m_last_mesh_dump_request = {}; + } + } + + const std::string_view new_graphics_mod_popup_name = "New Graphics Mod"; + if (new_mod_popup) + { + if (!ImGui::IsPopupOpen(new_graphics_mod_popup_name.data())) + { + m_editor_new_mod_name = ""; + m_editor_new_mod_author = ""; + m_editor_new_mod_description = ""; + ImGui::OpenPopup(new_graphics_mod_popup_name.data()); + } + } + + // New mod popup below + const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + if (ImGui::BeginPopupModal(new_graphics_mod_popup_name.data(), nullptr)) + { + bool is_valid = false; + + if (ImGui::BeginTable("NewModForm", 2)) + { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Name"); + ImGui::TableNextColumn(); + ImGui::InputText("##NewModName", &m_editor_new_mod_name); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Author"); + ImGui::TableNextColumn(); + ImGui::InputText("##NewModAuthor", &m_editor_new_mod_author); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Description"); + ImGui::TableNextColumn(); + ImGui::InputTextMultiline("##NewModDescription", &m_editor_new_mod_description); + ImGui::EndTable(); + } + + const std::string& graphics_mod_root = File::GetUserPath(D_GRAPHICSMOD_IDX); + if (!std::filesystem::exists(std::filesystem::path{graphics_mod_root} / m_editor_new_mod_name)) + { + is_valid = true; + } + + if (!is_valid) + ImGui::BeginDisabled(); + if (ImGui::Button("Create", ImVec2(120, 0))) + { + NewMod(m_editor_new_mod_name, m_editor_new_mod_author, m_editor_new_mod_description); + ImGui::CloseCurrentPopup(); + } + if (!is_valid) + ImGui::EndDisabled(); + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +bool EditorMain::NewMod(std::string_view name, std::string_view author, + std::string_view description) +{ + const std::string& graphics_mod_root = File::GetUserPath(D_GRAPHICSMOD_IDX); + const std::filesystem::path mod_path = std::filesystem::path{graphics_mod_root} / name; + if (std::filesystem::exists(mod_path)) + { + // TODO: error + return false; + } + + std::error_code error_code; + std::filesystem::create_directory(mod_path, error_code); + if (error_code) + { + // TODO: error + return false; + } + + const std::string& game_id = SConfig::GetInstance().GetGameID(); + if (!File::WriteStringToFile(PathToString(mod_path / fmt::format("{}.txt", game_id)), "")) + return false; + + if (!RebuildState()) + return false; + + m_state->m_user_data.m_title = name; + m_state->m_user_data.m_author = author; + m_state->m_user_data.m_description = description; + m_state->m_user_data.m_current_mod_path = mod_path; + m_has_changes = false; + m_editor_session_in_progress = true; + m_inspect_only = false; + + m_asset_browser_panel->ResetCurrentPath(); + m_state->m_user_data.m_asset_library->Watch(PathToString(mod_path)); + + auto& system = Core::System::GetInstance(); + system.GetGraphicsModManager().SetEditorBackend(std::make_unique(*m_state)); + + return true; +} + +bool EditorMain::LoadMod(std::string_view name) +{ + const std::string& graphics_mod_root = File::GetUserPath(D_GRAPHICSMOD_IDX); + const std::filesystem::path mod_path = std::filesystem::path{graphics_mod_root} / name; + if (!std::filesystem::exists(mod_path)) + { + // TODO: error + return false; + } + + if (!RebuildState()) + return false; + + const auto config = + GraphicsModSystem::Config::GraphicsMod::Create(PathToString(mod_path / "metadata.json")); + if (!config) + { + // TODO: error + return false; + } + + ReadFromGraphicsMod(&m_state->m_user_data, &m_state->m_editor_data, m_state->m_runtime_data, + *config, PathToString(mod_path)); + + auto& system = Core::System::GetInstance(); + auto& loader = system.GetCustomAssetLoader(); + for (const auto& asset : config->m_assets) + { + // TODO: generate preview for other types? + if (asset.m_map.find("texture") != asset.m_map.end()) + { + m_state->m_editor_data.m_assets_waiting_for_preview.try_emplace( + asset.m_asset_id, + loader.LoadGameTexture(asset.m_asset_id, m_state->m_user_data.m_asset_library)); + } + } + + m_has_changes = false; + m_editor_session_in_progress = true; + m_inspect_only = false; + + m_asset_browser_panel->ResetCurrentPath(); + m_state->m_user_data.m_asset_library->Watch(PathToString(mod_path)); + + system.GetGraphicsModManager().SetEditorBackend(std::make_unique(*m_state)); + + return true; +} + +void EditorMain::Save() +{ + if (!m_has_changes) + return; + + const std::string file_path = + PathToString(m_state->m_user_data.m_current_mod_path / "metadata.json"); + std::ofstream json_stream; + File::OpenFStream(json_stream, file_path, std::ios_base::out); + if (!json_stream.is_open()) + { + ERROR_LOG_FMT(VIDEO, "Failed to open graphics mod json file '{}' for writing", file_path); + return; + } + + m_state->m_user_data.m_asset_library->SaveAssetDataAsFiles(); + + GraphicsModSystem::Config::GraphicsMod mod; + WriteToGraphicsMod(m_state->m_user_data, &mod); + + picojson::object serialized_root; + mod.Serialize(serialized_root); + + const auto output = picojson::value{serialized_root}.serialize(true); + json_stream << output; + + m_has_changes = false; +} + +void EditorMain::Close() +{ + if (m_has_changes) + { + // Ask "are you sure?" + } + + m_editor_session_in_progress = false; + m_inspect_only = false; + + auto& system = Core::System::GetInstance(); + system.GetGraphicsModManager().SetEditorBackend(nullptr); +} + +void EditorMain::OnChangeOccured() +{ + m_has_changes = true; +} + +EditorState* EditorMain::GetEditorState() const +{ + return m_state.get(); +} +} // namespace GraphicsModEditor diff --git a/Source/Core/VideoCommon/GraphicsModEditor/EditorMain.h b/Source/Core/VideoCommon/GraphicsModEditor/EditorMain.h new file mode 100644 index 0000000000..cd352dbb3b --- /dev/null +++ b/Source/Core/VideoCommon/GraphicsModEditor/EditorMain.h @@ -0,0 +1,89 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "Common/HookableEvent.h" +#include "VideoCommon/GraphicsModEditor/SceneDumper.h" +#include "VideoCommon/GraphicsModSystem/Types.h" +#include "VideoCommon/XFMemory.h" + +struct FBInfo; +class GraphicsModAction; + +namespace GraphicsModEditor +{ +struct DrawCallData; +struct FBCallData; +struct LightData; +struct EditorState; + +namespace Panels +{ +class ActiveTargetsPanel; +class AssetBrowserPanel; +class PropertiesPanel; +} // namespace Panels +class EditorMain +{ +public: + EditorMain(); + ~EditorMain(); + + bool Initialize(); + void Shutdown(); + + bool IsEnabled() const { return m_enabled; } + + // Creates a new graphics mod for editing + bool NewMod(std::string_view name, std::string_view author, std::string_view description); + + // Loads an existing graphics mod for editing + bool LoadMod(std::string_view name); + + // Renders ImGui windows to the currently-bound framebuffer. + // Should be called by main UI manager + void DrawImGui(); + + EditorState* GetEditorState() const; + +private: + bool RebuildState(); + + void DrawMenu(); + + // Saves the current editor session + void Save(); + + // Closes this editor session + void Close(); + + void OnChangeOccured(); + Common::EventHook m_change_occurred_event; + Common::EventHook m_asset_reload_event; + bool m_has_changes = false; + bool m_editor_session_in_progress = false; + bool m_enabled = false; + + // Inspect mode allows the user to look and add some basic + // graphics mods but they can't create any new files or save + bool m_inspect_only = false; + + std::unique_ptr m_state; + + std::unique_ptr m_active_targets_panel; + std::unique_ptr m_asset_browser_panel; + std::unique_ptr m_properties_panel; + + std::string m_editor_new_mod_name; + std::string m_editor_new_mod_author; + std::string m_editor_new_mod_description; + + SceneDumper::RecordingRequest m_last_mesh_dump_request; + bool m_open_mesh_dump_export_window = false; +}; +} // namespace GraphicsModEditor