#pragma once #include #include #include "app.hpp" #include "assertions.hpp" #include "asset.hpp" #include "components.hpp" #include "debugpass.hpp" #include "engine.hpp" #include "file.hpp" #include "imgui_backend.hpp" #include "log.hpp" #include "math.hpp" #include "object.hpp" #include "platform.hpp" #include "renderer.hpp" #include "scene.hpp" #include "undostack.hpp" #include "utility.hpp" #include #include #include class TransformCommand : public Command { public: prism::Object transformed; Transform old_transform, new_transform; std::string fetch_name() override { return "Transform " + engine->get_scene()->get(transformed).name; } void undo() override { engine->get_scene()->get(transformed) = old_transform; } void execute() override { engine->get_scene()->get(transformed) = new_transform; } }; class RenameCommand : public Command { public: prism::Object object; std::string old_name, new_name; std::string fetch_name() override { return "Rename " + old_name + " to " + new_name; } void undo() override { engine->get_scene()->get(object).name = old_name; } void execute() override { engine->get_scene()->get(object).name = new_name; } }; class ChangeParentCommand : public Command { public: prism::Object object; prism::Object old_parent; prism::Object new_parent; std::string fetch_name() override { return "Change parent of " + engine->get_scene()->get(object).name + " to " + engine->get_scene()->get(new_parent).name; } void undo() override { engine->get_scene()->get(object).parent = old_parent; } void execute() override { engine->get_scene()->get(object).parent = new_parent; } }; enum class AssetType { Unknown, Mesh, Texture, Material, Scene }; template AssetType get_asset_type() { if constexpr (std::is_same::value) { return AssetType::Mesh; } else if constexpr (std::is_same::value) { return AssetType::Material; } else if constexpr (std::is_same::value) { return AssetType::Texture; } else if constexpr (std::is_same::value) { return AssetType::Scene; } return AssetType::Unknown; } constexpr int thumbnail_resolution = 256; class CommonEditor : public prism::app { public: explicit CommonEditor(std::string_view id); void initialize_render() override; void prepare_quit() override; bool should_quit() override; virtual void drawUI() {} void begin_frame() override; void update(float deltaTime) override; virtual void renderEditor([[maybe_unused]] GFXCommandBuffer* command_buffer) {} void render(GFXCommandBuffer* command_buffer) override { renderEditor(command_buffer); } virtual void updateEditor([[maybe_unused]] float deltaTime) {} virtual void object_selected([[maybe_unused]] prism::Object object) {} virtual void asset_selected([[maybe_unused]] const std::filesystem::path& path, [[maybe_unused]] AssetType type) {} bool wants_no_scene_rendering() override { return true; } bool is_multimodal() override { return true; } void createDockArea(); void drawViewport(Scene* scene); void drawAssets(); // options int getDefaultX() const; int getDefaultY() const; int getDefaultWidth() const; int getDefaultHeight() const; void addOpenedFile(std::string_view path); void clearOpenedFiles(); std::vector getOpenedFiles() const; void drawOutline(); void drawPropertyEditor(); void drawConsole(); void set_undo_stack(UndoStack* stack); prism::Object selected_object = prism::NullObject; GFXTexture* get_material_preview(Material& material); GFXTexture* get_mesh_preview(Mesh& mesh); GFXTexture* get_texture_preview(Texture& texture); GFXTexture* generate_common_preview(Scene& scene, prism::float3 camera_position); template GFXTexture* get_asset_thumbnail(AssetPtr& asset) { Expects(asset.handle != nullptr); if (asset_thumbnails.count(asset->path)) { return asset_thumbnails[asset->path]; } else { if constexpr (std::is_same_v) { auto texture = get_material_preview(*asset.handle); asset_thumbnails[asset->path] = texture; return texture; } else if constexpr (std::is_same_v) { auto texture = get_mesh_preview(*asset.handle); asset_thumbnails[asset->path] = texture; return texture; } else if constexpr (std::is_same_v) { auto texture = get_texture_preview(*asset.handle); asset_thumbnails[asset->path] = texture; return texture; } else { return engine->get_renderer()->dummy_texture; } } } GFXTexture* get_asset_thumbnail(const prism::path& path) { if (asset_thumbnails.count(path.string())) { return asset_thumbnails[path.string()]; } else { auto [asset, block] = assetm->load_asset_generic(path); // store as dummy texture, as to stop infinite reload because of failure (e.g. out of date model) if (asset == nullptr) { asset_thumbnails[path.string()] = engine->get_renderer()->dummy_texture; return engine->get_renderer()->dummy_texture; } if (can_load_asset(path)) { auto ptr = AssetPtr(static_cast(asset), block); return get_asset_thumbnail(ptr); } else if (can_load_asset(path)) { auto ptr = AssetPtr(static_cast(asset), block); return get_asset_thumbnail(ptr); } else if (can_load_asset(path)) { auto ptr = AssetPtr(static_cast(asset), block); return get_asset_thumbnail(ptr); } else { return engine->get_renderer()->dummy_texture; } } } bool has_asset_edit_changed = false; template bool edit_asset(const char* name, AssetPtr& asset) { ImGui::PushID(&asset); auto draw_list = ImGui::GetWindowDrawList(); const auto window_pos = ImGui::GetCursorScreenPos(); const float thumbnail_size = 35.0f; const auto line_height = ImGui::GetTextLineHeight(); const float item_width = ImGui::CalcItemWidth(); const float inner_spacing = ImGui::GetStyle().ItemInnerSpacing.x; const auto frame_color = ImGui::GetStyle().Colors[ImGuiCol_FrameBg]; const auto text_color = ImGui::GetStyle().Colors[ImGuiCol_Text]; const ImRect edit_rect = ImRect(window_pos, ImVec2(window_pos.x + thumbnail_size, window_pos.y + thumbnail_size)); if (asset) draw_list->AddImageRounded( get_asset_thumbnail(asset), edit_rect.Min, edit_rect.Max, ImVec2(0, 0), ImVec2(1, 1), IM_COL32_WHITE, 3.0f); ImRect path_rect = ImRect( ImVec2(window_pos.x + thumbnail_size + 10.0f, window_pos.y), ImVec2(window_pos.x + item_width - inner_spacing, window_pos.y + 20.0f)); draw_list->AddText( ImVec2(window_pos.x + item_width, window_pos.y + (thumbnail_size / 2.0f) - (line_height / 2.0f)), ImColor(text_color), name); draw_list->AddRectFilled(path_rect.Min, path_rect.Max, ImColor(frame_color), 3.0f); ImRect clear_rect = ImRect( ImVec2(window_pos.x + thumbnail_size + 10.0f, window_pos.y + 20.0f), ImVec2(window_pos.x + thumbnail_size + 30.0f, window_pos.y + 40.0f)); draw_list->AddRectFilled(clear_rect.Min, clear_rect.Max, ImColor(255, 0, 0, 255), 3.0f); std::string path; if (asset) path = asset->path; else path = "None"; ImGui::PushClipRect(path_rect.Min, path_rect.Max, false); draw_list->AddText( ImVec2(window_pos.x + thumbnail_size + 10.0f, window_pos.y), ImColor(text_color), path.c_str()); ImGui::Dummy(ImVec2(thumbnail_size, thumbnail_size + 10.0f)); ImGui::ItemAdd(path_rect, ImGui::GetID("path")); ImGui::PopClipRect(); if (ImGui::IsItemClicked()) { current_asset_type = get_asset_type(); open_asset_popup = true; on_asset_select = [&asset, this](auto p) { asset = assetm->get(prism::game_domain / p); has_asset_edit_changed = true; }; } ImGui::ItemAdd(edit_rect, ImGui::GetID("edit")); if (ImGui::IsItemClicked()) asset_selected(asset->path, get_asset_type()); ImGui::ItemAdd(clear_rect, ImGui::GetID("clear")); if (ImGui::IsItemClicked()) asset.clear(); ImGui::PopID(); if (has_asset_edit_changed) { has_asset_edit_changed = false; return true; } else { return false; } } template void open_asset(const AssetType type, const std::function& func_ptr) { current_asset_type = type; open_asset_popup = true; on_asset_select = func_ptr; } DebugPass* debugPass = nullptr; int viewport_width = 1, viewport_height = 1; protected: struct ViewportRenderTarget { Scene* scene = nullptr; RenderTarget* target = nullptr; }; std::unordered_map viewport_render_targets; private: void load_options(); void save_options(); void load_thumbnail_cache(); void save_thumbnail_cache(); std::string id; std::string iniFileName; std::unordered_map asset_thumbnails; bool accepting_viewport_input = false, doing_viewport_input = false; int viewport_x = 1, viewport_y = 1; bool transforming_axis = false; SelectableObject::Axis axis; prism::float3 last_object_position; bool open_asset_popup = false; AssetType current_asset_type; std::function on_asset_select; UndoStack* current_stack = nullptr; int defaultX, defaultY, defaultWidth, defaultHeight; std::vector lastOpenedFiles; void walkObject(prism::Object object, prism::Object parentObject = prism::NullObject); void editTransform(prism::Object object, Transform transform); void editRenderable(Renderable& mesh); }; inline void editPath( const char* label, std::string& path, bool editable = true, const std::function on_selected = nullptr) { ImGui::PushID(label); if (!editable) { ImGui::Text("%s: %s", label, path.c_str()); } else { ImGui::InputText(label, &path); } ImGui::SameLine(); if (ImGui::Button("...")) { engine->get_imgui().open_dialog(false, [&path, &on_selected](std::string p) { path = prism::get_relative_path(prism::domain::game, p).string(); if (on_selected != nullptr) on_selected(); }); } ImGui::PopID(); } class SelectionCommand : public Command { public: CommonEditor* editor = nullptr; prism::Object new_selection; prism::Object old_selection; std::string fetch_name() override { return "Change selection to " + engine->get_scene()->get(new_selection).name; } void undo() override { editor->selected_object = old_selection; } void execute() override { editor->selected_object = new_selection; } };