Archived
1
Fork 0
This repository has been archived on 2025-04-12. You can view files and clone it, but cannot push or open issues or pull requests.
prism/tools/common/include/commoneditor.hpp

423 lines
12 KiB
C++
Raw Normal View History

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