684 lines
25 KiB
C++
684 lines
25 KiB
C++
#include "MetaEditor/DearImGuiEditorShell.h"
|
|
|
|
#include "MetaCore/foundation/FileSystem.h"
|
|
|
|
#if METACORE_HAS_IMGUI
|
|
#include <imgui.h>
|
|
#include <imgui_internal.h>
|
|
#endif
|
|
|
|
#if METACORE_HAS_IMGUIZMO
|
|
#include <ImGuizmo.h>
|
|
#endif
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <string_view>
|
|
#include <sstream>
|
|
|
|
namespace metacore::editor {
|
|
|
|
namespace {
|
|
|
|
#if METACORE_HAS_IMGUIZMO
|
|
ImGuizmo::OPERATION to_imguizmo_operation(const std::string& operation) {
|
|
if (operation == "Rotate") {
|
|
return ImGuizmo::ROTATE;
|
|
}
|
|
if (operation == "Scale") {
|
|
return ImGuizmo::SCALE;
|
|
}
|
|
return ImGuizmo::TRANSLATE;
|
|
}
|
|
|
|
ImGuizmo::MODE to_imguizmo_mode(const std::string& space) {
|
|
return space == "World" ? ImGuizmo::WORLD : ImGuizmo::LOCAL;
|
|
}
|
|
|
|
std::array<float, 16> make_transform_matrix(const EditorFrameSnapshot::ViewportSnapshot& viewport) {
|
|
const float rx = viewport.object_rotation[0] * 0.01745329252F;
|
|
const float ry = viewport.object_rotation[1] * 0.01745329252F;
|
|
const float rz = viewport.object_rotation[2] * 0.01745329252F;
|
|
|
|
const float cx = std::cos(rx);
|
|
const float sx = std::sin(rx);
|
|
const float cy = std::cos(ry);
|
|
const float sy = std::sin(ry);
|
|
const float cz = std::cos(rz);
|
|
const float sz = std::sin(rz);
|
|
|
|
const float r00 = cz * cy;
|
|
const float r01 = cz * sy * sx - sz * cx;
|
|
const float r02 = cz * sy * cx + sz * sx;
|
|
const float r10 = sz * cy;
|
|
const float r11 = sz * sy * sx + cz * cx;
|
|
const float r12 = sz * sy * cx - cz * sx;
|
|
const float r20 = -sy;
|
|
const float r21 = cy * sx;
|
|
const float r22 = cy * cx;
|
|
|
|
return {
|
|
r00 * viewport.object_scale[0], r10 * viewport.object_scale[0], r20 * viewport.object_scale[0], 0.0F,
|
|
r01 * viewport.object_scale[1], r11 * viewport.object_scale[1], r21 * viewport.object_scale[1], 0.0F,
|
|
r02 * viewport.object_scale[2], r12 * viewport.object_scale[2], r22 * viewport.object_scale[2], 0.0F,
|
|
viewport.object_position[0], viewport.object_position[1], viewport.object_position[2], 1.0F
|
|
};
|
|
}
|
|
|
|
std::array<float, 16> make_view_matrix(const std::array<float, 3>& camera_position) {
|
|
return {
|
|
1.0F, 0.0F, 0.0F, 0.0F,
|
|
0.0F, 1.0F, 0.0F, 0.0F,
|
|
0.0F, 0.0F, 1.0F, 0.0F,
|
|
-camera_position[0], -camera_position[1], -camera_position[2], 1.0F
|
|
};
|
|
}
|
|
|
|
std::array<float, 16> make_projection_matrix(float width, float height) {
|
|
const float aspect = height > 0.0F ? width / height : 1.0F;
|
|
const float fov = 60.0F * 0.01745329252F;
|
|
const float f = 1.0F / std::tan(fov * 0.5F);
|
|
const float near_plane = 0.1F;
|
|
const float far_plane = 1000.0F;
|
|
return {
|
|
f / aspect, 0.0F, 0.0F, 0.0F,
|
|
0.0F, f, 0.0F, 0.0F,
|
|
0.0F, 0.0F, far_plane / (far_plane - near_plane), 1.0F,
|
|
0.0F, 0.0F, (-near_plane * far_plane) / (far_plane - near_plane), 0.0F
|
|
};
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace
|
|
|
|
bool DearImGuiEditorShell::initialize() {
|
|
const char* headless_frame_env = std::getenv("METACORE_ENABLE_HEADLESS_IMGUI");
|
|
headless_frame_enabled_ = headless_frame_env != nullptr && std::string(headless_frame_env) == "1";
|
|
|
|
#if METACORE_HAS_IMGUI
|
|
if (headless_frame_enabled_) {
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
|
io.DisplaySize = ImVec2(1600.0F, 900.0F);
|
|
io.DeltaTime = 1.0F / 60.0F;
|
|
unsigned char* font_pixels = nullptr;
|
|
int font_width = 0;
|
|
int font_height = 0;
|
|
io.Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height);
|
|
#if METACORE_HAS_IMGUIZMO
|
|
ImGuizmo::SetImGuiContext(ImGui::GetCurrentContext());
|
|
#endif
|
|
ImGui::StyleColorsDark();
|
|
}
|
|
#endif
|
|
initialized_ = true;
|
|
return true;
|
|
}
|
|
|
|
void DearImGuiEditorShell::shutdown() {
|
|
#if METACORE_HAS_IMGUI
|
|
if (headless_frame_enabled_ && ImGui::GetCurrentContext() != nullptr) {
|
|
ImGui::DestroyContext();
|
|
}
|
|
#endif
|
|
initialized_ = false;
|
|
last_snapshot_ = {};
|
|
last_draw_command_count_ = 0;
|
|
last_vertex_count_ = 0;
|
|
last_index_count_ = 0;
|
|
last_window_count_ = 0;
|
|
headless_frame_enabled_ = false;
|
|
dock_layout_initialized_ = false;
|
|
headless_transform_override_consumed_ = false;
|
|
}
|
|
|
|
void DearImGuiEditorShell::consume_snapshot(const EditorFrameSnapshot& snapshot) {
|
|
last_snapshot_ = snapshot;
|
|
enqueue_headless_transform_edit(snapshot);
|
|
|
|
#if METACORE_HAS_IMGUI
|
|
if (headless_frame_enabled_ && ImGui::GetCurrentContext() != nullptr) {
|
|
render_headless_frame(snapshot);
|
|
if (const ImDrawData* draw_data = ImGui::GetDrawData(); draw_data != nullptr) {
|
|
last_draw_command_count_ = 0;
|
|
last_vertex_count_ = draw_data->TotalVtxCount;
|
|
last_index_count_ = draw_data->TotalIdxCount;
|
|
last_window_count_ = draw_data->CmdListsCount;
|
|
for (int index = 0; index < draw_data->CmdListsCount; ++index) {
|
|
last_draw_command_count_ += draw_data->CmdLists[index]->CmdBuffer.Size;
|
|
}
|
|
} else {
|
|
last_draw_command_count_ = 0;
|
|
last_vertex_count_ = 0;
|
|
last_index_count_ = 0;
|
|
last_window_count_ = 0;
|
|
}
|
|
} else {
|
|
last_draw_command_count_ = 0;
|
|
last_vertex_count_ = 0;
|
|
last_index_count_ = 0;
|
|
last_window_count_ = 0;
|
|
}
|
|
#endif
|
|
|
|
export_debug_snapshot();
|
|
}
|
|
|
|
void DearImGuiEditorShell::enqueue_headless_transform_edit(const EditorFrameSnapshot& snapshot) {
|
|
if (!headless_frame_enabled_ || !snapshot.viewport.has_selection || headless_transform_override_consumed_) {
|
|
return;
|
|
}
|
|
|
|
const char* translate_env = std::getenv("METACORE_HEADLESS_GIZMO_TRANSLATE");
|
|
if (translate_env == nullptr) {
|
|
return;
|
|
}
|
|
|
|
std::istringstream stream(translate_env);
|
|
float x = snapshot.viewport.object_position[0];
|
|
float y = snapshot.viewport.object_position[1];
|
|
float z = snapshot.viewport.object_position[2];
|
|
if (!(stream >> x >> y >> z)) {
|
|
return;
|
|
}
|
|
|
|
PendingShellEdits::TransformEdit edit;
|
|
edit.entity_id = snapshot.viewport.selected_object_id;
|
|
edit.position = {x, y, z};
|
|
edit.rotation = snapshot.viewport.object_rotation;
|
|
edit.scale = snapshot.viewport.object_scale;
|
|
pending_edits_.transform_edit = edit;
|
|
headless_transform_override_consumed_ = true;
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_headless_frame(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
io.DisplaySize = ImVec2(1600.0F, 900.0F);
|
|
io.DeltaTime = 1.0F / 60.0F;
|
|
|
|
ImGui::NewFrame();
|
|
render_dockspace_frame(snapshot);
|
|
ImGui::Render();
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_dockspace_frame(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
ImGui::SetNextWindowPos(viewport->WorkPos);
|
|
ImGui::SetNextWindowSize(viewport->WorkSize);
|
|
ImGui::SetNextWindowViewport(viewport->ID);
|
|
|
|
constexpr ImGuiWindowFlags host_window_flags =
|
|
ImGuiWindowFlags_NoDocking |
|
|
ImGuiWindowFlags_NoTitleBar |
|
|
ImGuiWindowFlags_NoCollapse |
|
|
ImGuiWindowFlags_NoResize |
|
|
ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
|
ImGuiWindowFlags_NoNavFocus |
|
|
ImGuiWindowFlags_MenuBar;
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
|
|
ImGui::Begin("MetaCore DockSpace", nullptr, host_window_flags);
|
|
ImGui::PopStyleVar(3);
|
|
|
|
if (ImGui::BeginMenuBar()) {
|
|
for (const auto& menu : snapshot.shell.menus) {
|
|
if (ImGui::BeginMenu(menu.menu_name.c_str())) {
|
|
for (const auto& action : menu.actions) {
|
|
ImGui::MenuItem(action.label.c_str(), action.shortcut.empty() ? nullptr : action.shortcut.c_str(), false, action.enabled);
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
}
|
|
ImGui::EndMenuBar();
|
|
}
|
|
|
|
ImGuiID dockspace_id = ImGui::GetID("MetaCoreEditorDockspace");
|
|
ImGui::DockSpace(dockspace_id, ImVec2(0.0F, 0.0F), ImGuiDockNodeFlags_PassthruCentralNode);
|
|
ImGui::End();
|
|
|
|
ensure_default_dock_layout(snapshot, dockspace_id);
|
|
|
|
render_toolbar(snapshot);
|
|
for (const auto& section : snapshot.shell.sections) {
|
|
for (const auto& panel : section.panels) {
|
|
render_panel(panel, snapshot);
|
|
}
|
|
}
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::ensure_default_dock_layout(const EditorFrameSnapshot& snapshot, unsigned int dockspace_id) {
|
|
#if METACORE_HAS_IMGUI
|
|
if (dock_layout_initialized_) {
|
|
return;
|
|
}
|
|
|
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
ImGui::DockBuilderRemoveNode(dockspace_id);
|
|
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
|
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->WorkSize);
|
|
|
|
ImGuiID left_id = 0;
|
|
ImGuiID right_id = 0;
|
|
ImGuiID bottom_id = 0;
|
|
ImGuiID center_id = dockspace_id;
|
|
ImGuiID preview_id = 0;
|
|
|
|
ImGui::DockBuilderSplitNode(center_id, ImGuiDir_Left, 0.22F, &left_id, ¢er_id);
|
|
ImGui::DockBuilderSplitNode(center_id, ImGuiDir_Right, 0.24F, &right_id, ¢er_id);
|
|
ImGui::DockBuilderSplitNode(center_id, ImGuiDir_Down, 0.26F, &bottom_id, ¢er_id);
|
|
ImGui::DockBuilderSplitNode(bottom_id, ImGuiDir_Down, 0.45F, &preview_id, &bottom_id);
|
|
|
|
for (const auto& section : snapshot.shell.sections) {
|
|
for (const auto& panel : section.panels) {
|
|
if (!panel.visible) {
|
|
continue;
|
|
}
|
|
|
|
ImGuiID target_id = center_id;
|
|
switch (panel.region) {
|
|
case metacore::engine::editor_core::DockRegion::LeftSidebar:
|
|
target_id = left_id;
|
|
break;
|
|
case metacore::engine::editor_core::DockRegion::RightSidebar:
|
|
target_id = right_id;
|
|
break;
|
|
case metacore::engine::editor_core::DockRegion::BottomConsole:
|
|
target_id = bottom_id;
|
|
break;
|
|
case metacore::engine::editor_core::DockRegion::BottomRuntimePreview:
|
|
target_id = preview_id;
|
|
break;
|
|
case metacore::engine::editor_core::DockRegion::CenterViewport:
|
|
target_id = center_id;
|
|
break;
|
|
case metacore::engine::editor_core::DockRegion::TopMenuBar:
|
|
case metacore::engine::editor_core::DockRegion::TopToolbar:
|
|
continue;
|
|
}
|
|
ImGui::DockBuilderDockWindow(panel.title.c_str(), target_id);
|
|
}
|
|
}
|
|
|
|
ImGui::DockBuilderFinish(dockspace_id);
|
|
dock_layout_initialized_ = true;
|
|
#else
|
|
(void)snapshot;
|
|
(void)dockspace_id;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_toolbar(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
if (!snapshot.toolbar_visible) {
|
|
return;
|
|
}
|
|
|
|
if (ImGui::Begin("Toolbar", nullptr, ImGuiWindowFlags_NoCollapse)) {
|
|
for (std::size_t group_index = 0; group_index < snapshot.shell.toolbar_groups.size(); ++group_index) {
|
|
const auto& group = snapshot.shell.toolbar_groups[group_index];
|
|
if (group_index > 0) {
|
|
ImGui::Separator();
|
|
}
|
|
ImGui::TextUnformatted(group.name.c_str());
|
|
for (const auto& action : group.actions) {
|
|
ImGui::SameLine();
|
|
ImGui::BeginDisabled(!action.enabled);
|
|
ImGui::Button(action.label.c_str());
|
|
ImGui::EndDisabled();
|
|
}
|
|
}
|
|
}
|
|
ImGui::End();
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_panel(const metacore::engine::editor_core::DockPanel& panel, const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
if (!panel.visible) {
|
|
return;
|
|
}
|
|
|
|
if (panel.id == "hierarchy" && !snapshot.hierarchy_visible) {
|
|
return;
|
|
}
|
|
if (panel.id == "project" && !snapshot.project_visible) {
|
|
return;
|
|
}
|
|
if (panel.id == "inspector" && !snapshot.inspector_visible) {
|
|
return;
|
|
}
|
|
if (panel.id == "console" && !snapshot.console_visible) {
|
|
return;
|
|
}
|
|
if (panel.id == "runtime_preview" && !snapshot.runtime_preview_visible) {
|
|
return;
|
|
}
|
|
|
|
const bool began = ImGui::Begin(panel.title.c_str(), nullptr, ImGuiWindowFlags_NoCollapse);
|
|
if (began) {
|
|
if (panel.id == "hierarchy") {
|
|
render_hierarchy_panel(snapshot);
|
|
} else if (panel.id == "project") {
|
|
render_project_panel(snapshot);
|
|
} else if (panel.id == "viewport") {
|
|
render_viewport_panel(snapshot);
|
|
} else if (panel.id == "inspector") {
|
|
render_inspector_panel(snapshot);
|
|
} else if (panel.id == "console") {
|
|
render_console_panel(snapshot);
|
|
} else if (panel.id == "runtime_preview") {
|
|
render_runtime_preview_panel(snapshot);
|
|
} else if (panel.id == "main_menu") {
|
|
ImGui::TextUnformatted("MetaCore menu host");
|
|
} else if (panel.id == "toolbar") {
|
|
ImGui::TextUnformatted("MetaCore toolbar host");
|
|
} else {
|
|
ImGui::Text("Panel: %s", panel.id.c_str());
|
|
}
|
|
}
|
|
ImGui::End();
|
|
#else
|
|
(void)panel;
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_hierarchy_panel(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
for (const auto& item : snapshot.hierarchy) {
|
|
ImGui::BulletText("%s", item.name.c_str());
|
|
}
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_project_panel(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
for (const auto& asset : snapshot.assets) {
|
|
ImGui::BulletText("%s", asset.relative_path.c_str());
|
|
}
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_viewport_panel(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
ImGui::Text("Scene Objects: %zu", snapshot.scene_object_count);
|
|
ImGui::Text(
|
|
"Camera: (%.1f, %.1f, %.1f)",
|
|
snapshot.viewport.camera_position[0],
|
|
snapshot.viewport.camera_position[1],
|
|
snapshot.viewport.camera_position[2]
|
|
);
|
|
if (!snapshot.viewport.focus_target_id.empty()) {
|
|
ImGui::Text("Focus Target: %s", snapshot.viewport.focus_target_id.c_str());
|
|
}
|
|
ImGui::Separator();
|
|
ImGui::TextUnformatted(snapshot.status_text.c_str());
|
|
ImGui::Spacing();
|
|
ImGui::TextUnformatted("MetaCore Scene View");
|
|
|
|
if (snapshot.viewport.has_selection) {
|
|
ImGui::Text("Selection: %s", snapshot.viewport.selected_object_name.c_str());
|
|
ImGui::Text("Operation: %s", snapshot.viewport.gizmo_operation.c_str());
|
|
ImGui::SameLine();
|
|
ImGui::Text("Space: %s", snapshot.viewport.gizmo_space.c_str());
|
|
if (snapshot.viewport.snap_enabled) {
|
|
ImGui::Text(
|
|
"Snap: %.2f / %.2f / %.2f",
|
|
snapshot.viewport.snap_values[0],
|
|
snapshot.viewport.snap_values[1],
|
|
snapshot.viewport.snap_values[2]
|
|
);
|
|
} else {
|
|
ImGui::TextUnformatted("Snap: Off");
|
|
}
|
|
ImGui::Text(
|
|
"Transform P(%.2f, %.2f, %.2f) R(%.2f, %.2f, %.2f) S(%.2f, %.2f, %.2f)",
|
|
snapshot.viewport.object_position[0],
|
|
snapshot.viewport.object_position[1],
|
|
snapshot.viewport.object_position[2],
|
|
snapshot.viewport.object_rotation[0],
|
|
snapshot.viewport.object_rotation[1],
|
|
snapshot.viewport.object_rotation[2],
|
|
snapshot.viewport.object_scale[0],
|
|
snapshot.viewport.object_scale[1],
|
|
snapshot.viewport.object_scale[2]
|
|
);
|
|
} else {
|
|
ImGui::TextUnformatted("Selection: none");
|
|
}
|
|
|
|
const ImVec2 viewport_min = ImGui::GetCursorScreenPos();
|
|
const ImVec2 viewport_size = ImVec2(
|
|
ImGui::GetContentRegionAvail().x,
|
|
(ImGui::GetContentRegionAvail().y > 240.0F) ? ImGui::GetContentRegionAvail().y : 240.0F
|
|
);
|
|
ImGui::InvisibleButton("MetaCoreViewportCanvas", viewport_size);
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
draw_list->AddRectFilled(viewport_min, ImVec2(viewport_min.x + viewport_size.x, viewport_min.y + viewport_size.y), IM_COL32(26, 29, 36, 255));
|
|
draw_list->AddRect(ImVec2(viewport_min.x, viewport_min.y), ImVec2(viewport_min.x + viewport_size.x, viewport_min.y + viewport_size.y), IM_COL32(82, 91, 107, 255));
|
|
draw_list->AddText(ImVec2(viewport_min.x + 14.0F, viewport_min.y + 14.0F), IM_COL32(220, 226, 235, 255), "Viewport backend handoff surface");
|
|
|
|
#if METACORE_HAS_IMGUIZMO
|
|
if (snapshot.viewport.has_selection) {
|
|
auto model = make_transform_matrix(snapshot.viewport);
|
|
const auto view = make_view_matrix(snapshot.viewport.camera_position);
|
|
const auto projection = make_projection_matrix(viewport_size.x, viewport_size.y);
|
|
ImGuizmo::SetDrawlist(draw_list);
|
|
ImGuizmo::SetRect(viewport_min.x, viewport_min.y, viewport_size.x, viewport_size.y);
|
|
ImGuizmo::Manipulate(
|
|
view.data(),
|
|
projection.data(),
|
|
to_imguizmo_operation(snapshot.viewport.gizmo_operation),
|
|
to_imguizmo_mode(snapshot.viewport.gizmo_space),
|
|
model.data(),
|
|
nullptr,
|
|
snapshot.viewport.snap_enabled ? snapshot.viewport.snap_values.data() : nullptr
|
|
);
|
|
if (ImGuizmo::IsUsing()) {
|
|
float translation[3]{};
|
|
float rotation[3]{};
|
|
float scale[3]{};
|
|
ImGuizmo::DecomposeMatrixToComponents(model.data(), translation, rotation, scale);
|
|
PendingShellEdits::TransformEdit edit;
|
|
edit.entity_id = snapshot.viewport.selected_object_id;
|
|
edit.position = {translation[0], translation[1], translation[2]};
|
|
edit.rotation = {rotation[0], rotation[1], rotation[2]};
|
|
edit.scale = {scale[0], scale[1], scale[2]};
|
|
pending_edits_.transform_edit = edit;
|
|
}
|
|
ImGui::TextUnformatted("ImGuizmo bridge active");
|
|
} else {
|
|
ImGui::TextUnformatted("ImGuizmo bridge ready");
|
|
}
|
|
#endif
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_inspector_panel(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
ImGui::TextUnformatted(snapshot.inspector.object_name.c_str());
|
|
ImGui::Separator();
|
|
for (const auto& field : snapshot.inspector.fields) {
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::TextUnformatted(field.label.c_str());
|
|
ImGui::SameLine(180.0F);
|
|
ImGui::TextUnformatted(field.value.c_str());
|
|
}
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_console_panel(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
for (const auto& message : snapshot.console) {
|
|
ImGui::Text("[%s] %s", message.category.c_str(), message.text.c_str());
|
|
}
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
void DearImGuiEditorShell::render_runtime_preview_panel(const EditorFrameSnapshot& snapshot) {
|
|
#if METACORE_HAS_IMGUI
|
|
std::string_view document_path = "None";
|
|
for (const auto& field : snapshot.inspector.fields) {
|
|
if (field.label == "Document") {
|
|
document_path = field.value;
|
|
break;
|
|
}
|
|
}
|
|
ImGui::TextUnformatted("MetaCore Runtime UI Preview");
|
|
ImGui::Separator();
|
|
ImGui::Text("Document: %.*s", static_cast<int>(document_path.size()), document_path.data());
|
|
ImGui::Text("Console messages: %zu", snapshot.console.size());
|
|
#else
|
|
(void)snapshot;
|
|
#endif
|
|
}
|
|
|
|
bool DearImGuiEditorShell::initialized() const {
|
|
return initialized_;
|
|
}
|
|
|
|
const EditorFrameSnapshot& DearImGuiEditorShell::last_snapshot() const {
|
|
return last_snapshot_;
|
|
}
|
|
|
|
std::string DearImGuiEditorShell::summarize_snapshot() const {
|
|
std::ostringstream stream;
|
|
stream << "Shell=" << shell_name()
|
|
<< ", Menus=" << last_snapshot_.shell.menus.size()
|
|
<< ", ToolbarGroups=" << last_snapshot_.shell.toolbar_groups.size()
|
|
<< ", Sections=" << last_snapshot_.shell.sections.size()
|
|
<< ", Hierarchy=" << last_snapshot_.hierarchy.size()
|
|
<< ", Assets=" << last_snapshot_.assets.size()
|
|
<< ", Inspector=" << last_snapshot_.inspector.object_name
|
|
<< ", DrawCommands=" << last_draw_command_count_
|
|
<< ", DrawVertices=" << last_vertex_count_
|
|
<< ", DrawIndices=" << last_index_count_
|
|
<< ", DrawLists=" << last_window_count_;
|
|
return stream.str();
|
|
}
|
|
|
|
std::string DearImGuiEditorShell::describe_workspace() const {
|
|
std::ostringstream stream;
|
|
stream << "DearImGuiEditorShell composition";
|
|
for (const auto& menu : last_snapshot_.shell.menus) {
|
|
stream << "\n [Menu] " << menu.menu_name << " actions=" << menu.actions.size();
|
|
for (const auto& action : menu.actions) {
|
|
stream << "\n - " << action.label;
|
|
if (!action.shortcut.empty()) {
|
|
stream << " [" << action.shortcut << "]";
|
|
}
|
|
}
|
|
}
|
|
for (const auto& group : last_snapshot_.shell.toolbar_groups) {
|
|
stream << "\n [Toolbar] " << group.name << " actions=" << group.actions.size();
|
|
for (const auto& action : group.actions) {
|
|
stream << "\n - " << action.label;
|
|
}
|
|
}
|
|
for (const auto& section : last_snapshot_.shell.sections) {
|
|
stream << "\n [Section] panels=" << section.panels.size();
|
|
for (const auto& panel : section.panels) {
|
|
stream << "\n - " << panel.title << " (" << panel.id << ")";
|
|
}
|
|
}
|
|
return stream.str();
|
|
}
|
|
|
|
std::string DearImGuiEditorShell::shell_name() const {
|
|
return "DearImGuiEditorShell";
|
|
}
|
|
|
|
PendingShellEdits DearImGuiEditorShell::consume_pending_edits() {
|
|
PendingShellEdits edits = pending_edits_;
|
|
pending_edits_ = {};
|
|
return edits;
|
|
}
|
|
|
|
void DearImGuiEditorShell::set_debug_export_path(std::filesystem::path path) {
|
|
debug_export_path_ = std::move(path);
|
|
}
|
|
|
|
void DearImGuiEditorShell::export_debug_snapshot() const {
|
|
if (debug_export_path_.empty()) {
|
|
return;
|
|
}
|
|
|
|
std::ostringstream stream;
|
|
stream << summarize_snapshot() << "\n";
|
|
stream << describe_workspace() << "\n";
|
|
stream << "[Inspector] " << last_snapshot_.inspector.object_name << "\n";
|
|
for (const auto& field : last_snapshot_.inspector.fields) {
|
|
stream << " - " << field.label << ": " << field.value << "\n";
|
|
}
|
|
stream << "[Console]\n";
|
|
for (const auto& message : last_snapshot_.console) {
|
|
stream << " - [" << message.category << "] " << message.text << "\n";
|
|
}
|
|
export_viewport_snapshot(stream);
|
|
stream << "[DrawData]\n";
|
|
stream << " - CommandCount: " << last_draw_command_count_ << "\n";
|
|
stream << " - VertexCount: " << last_vertex_count_ << "\n";
|
|
stream << " - IndexCount: " << last_index_count_ << "\n";
|
|
stream << " - DrawLists: " << last_window_count_ << "\n";
|
|
foundation::write_text_file(debug_export_path_, stream.str());
|
|
}
|
|
|
|
void DearImGuiEditorShell::export_viewport_snapshot(std::ostringstream& stream) const {
|
|
stream << "[Viewport]\n";
|
|
stream << " - Selection: "
|
|
<< (last_snapshot_.viewport.has_selection ? last_snapshot_.viewport.selected_object_name : std::string("None")) << "\n";
|
|
stream << " - SelectionId: " << last_snapshot_.viewport.selected_object_id << "\n";
|
|
stream << " - Operation: " << last_snapshot_.viewport.gizmo_operation << "\n";
|
|
stream << " - Space: " << last_snapshot_.viewport.gizmo_space << "\n";
|
|
stream << " - SnapEnabled: " << (last_snapshot_.viewport.snap_enabled ? "true" : "false") << "\n";
|
|
stream << " - SnapValues: "
|
|
<< last_snapshot_.viewport.snap_values[0] << ", "
|
|
<< last_snapshot_.viewport.snap_values[1] << ", "
|
|
<< last_snapshot_.viewport.snap_values[2] << "\n";
|
|
stream << " - Camera: "
|
|
<< last_snapshot_.viewport.camera_position[0] << ", "
|
|
<< last_snapshot_.viewport.camera_position[1] << ", "
|
|
<< last_snapshot_.viewport.camera_position[2] << "\n";
|
|
stream << " - Position: "
|
|
<< last_snapshot_.viewport.object_position[0] << ", "
|
|
<< last_snapshot_.viewport.object_position[1] << ", "
|
|
<< last_snapshot_.viewport.object_position[2] << "\n";
|
|
stream << " - Rotation: "
|
|
<< last_snapshot_.viewport.object_rotation[0] << ", "
|
|
<< last_snapshot_.viewport.object_rotation[1] << ", "
|
|
<< last_snapshot_.viewport.object_rotation[2] << "\n";
|
|
stream << " - Scale: "
|
|
<< last_snapshot_.viewport.object_scale[0] << ", "
|
|
<< last_snapshot_.viewport.object_scale[1] << ", "
|
|
<< last_snapshot_.viewport.object_scale[2] << "\n";
|
|
stream << " - FocusTarget: " << last_snapshot_.viewport.focus_target_id << "\n";
|
|
}
|
|
|
|
} // namespace metacore::editor
|