587 lines
24 KiB
C++
587 lines
24 KiB
C++
#include "MetaEditor/EditorApplication.h"
|
|
|
|
#include "MetaEditor/DearImGuiEditorShell.h"
|
|
|
|
#include "MetaCore/foundation/Log.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <windows.h>
|
|
|
|
namespace metacore::editor {
|
|
|
|
namespace {
|
|
|
|
using TransformGizmoService = metacore::engine::editor_core::TransformGizmoService;
|
|
|
|
std::optional<TransformGizmoService::Operation> parse_operation(const std::string& operation) {
|
|
if (operation == "TRANSLATE") {
|
|
return TransformGizmoService::Operation::Translate;
|
|
}
|
|
if (operation == "ROTATE") {
|
|
return TransformGizmoService::Operation::Rotate;
|
|
}
|
|
if (operation == "SCALE") {
|
|
return TransformGizmoService::Operation::Scale;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<TransformGizmoService::Space> parse_space(const std::string& space) {
|
|
if (space == "LOCAL") {
|
|
return TransformGizmoService::Space::Local;
|
|
}
|
|
if (space == "WORLD") {
|
|
return TransformGizmoService::Space::World;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::string operation_label(const TransformGizmoService::Operation operation) {
|
|
switch (operation) {
|
|
case TransformGizmoService::Operation::Translate:
|
|
return "Translate";
|
|
case TransformGizmoService::Operation::Rotate:
|
|
return "Rotate";
|
|
case TransformGizmoService::Operation::Scale:
|
|
return "Scale";
|
|
}
|
|
return "Translate";
|
|
}
|
|
|
|
std::string space_label(const TransformGizmoService::Space space) {
|
|
return space == TransformGizmoService::Space::World ? "World" : "Local";
|
|
}
|
|
|
|
int parse_positive_int_env(const char* value, const int fallback) {
|
|
if (value == nullptr) {
|
|
return fallback;
|
|
}
|
|
try {
|
|
const int parsed = std::stoi(value);
|
|
return parsed > 0 ? parsed : fallback;
|
|
} catch (...) {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
bool has_explicit_env(const char* value) {
|
|
return value != nullptr && value[0] != '\0';
|
|
}
|
|
|
|
bool env_enabled(const char* name) {
|
|
const char* value = std::getenv(name);
|
|
return value != nullptr && std::string(value) == "1";
|
|
}
|
|
|
|
std::string build_panda_hud_text(const EditorFrameSnapshot& snapshot, const int ui_frame_submissions) {
|
|
std::ostringstream stream;
|
|
stream << "MetaEditor\n";
|
|
stream << "Selection: "
|
|
<< (snapshot.viewport.has_selection ? snapshot.viewport.selected_object_name : "None") << "\n";
|
|
stream << "Gizmo: " << snapshot.viewport.gizmo_operation
|
|
<< " / " << snapshot.viewport.gizmo_space
|
|
<< " / Snap " << (snapshot.viewport.snap_enabled ? "On" : "Off") << "\n";
|
|
stream << "Position: "
|
|
<< snapshot.viewport.object_position[0] << ", "
|
|
<< snapshot.viewport.object_position[1] << ", "
|
|
<< snapshot.viewport.object_position[2] << "\n";
|
|
stream << "Panels: Hierarchy | Viewport | Inspector | Console\n";
|
|
stream << "Controls: W/E/R mode, Q space, Shift+S snap\n";
|
|
stream << "Move: Arrows + PageUp/PageDown | Rotate: Arrows | Scale: +/-\n";
|
|
stream << "UI Packets: " << ui_frame_submissions;
|
|
return stream.str();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
EditorApplication::EditorApplication() = default;
|
|
|
|
bool EditorApplication::initialize(const std::filesystem::path& project_root) {
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize enter\n";
|
|
project_root_ = project_root;
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after project_root\n";
|
|
editor_shell_ = create_default_editor_shell();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after shell create\n";
|
|
const auto shell_debug_path = project_root_ / "Library" / "Layout" / (editor_shell_->shell_name() + std::string("_snapshot.txt"));
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after shell debug path\n";
|
|
editor_shell_->set_debug_export_path(shell_debug_path);
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize before platform\n";
|
|
if (!platform_.initialize("MetaEditor")) {
|
|
return false;
|
|
}
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after platform\n";
|
|
render_backend_.initialize();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after render backend\n";
|
|
render_backend_.bind_window_framework(platform_.window_framework_handle());
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after callback bind gate\n";
|
|
if (auto* dearimgui_shell = dynamic_cast<DearImGuiEditorShell*>(editor_shell_.get())) {
|
|
dearimgui_shell->configure_platform_window(
|
|
platform_.native_window_handle(),
|
|
platform_.window_width(),
|
|
platform_.window_height()
|
|
);
|
|
}
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after shell window configure\n";
|
|
editor_shell_->initialize();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::initialize after shell initialize\n";
|
|
|
|
if (!std::filesystem::exists(project_root_ / "MetaCore.project.json")) {
|
|
project_service_.create_project(project_root_, "MetaCoreSample");
|
|
}
|
|
|
|
project_service_.load_project(project_root_ / "MetaCore.project.json", project_);
|
|
scene_ = scene_service_.create_default_scene();
|
|
scene_serializer_.load_scene(project_root_ / "Scenes" / "Main.mcscene.json", scene_);
|
|
ensure_demo_scene_content();
|
|
asset_database_.load(project_root_ / "Library" / "AssetDB.json");
|
|
layout_service_.load(project_root_ / "Library" / "Layout" / "editor_layout.json", layout_state_);
|
|
session_.apply_layout_state(layout_state_);
|
|
|
|
if (!scene_.objects.empty()) {
|
|
selection_service_.select(scene_.objects.front().id);
|
|
}
|
|
ensure_demo_scene_content();
|
|
|
|
session_.set_status_text("Editor initialized");
|
|
session_.add_console_message("System", "MetaEditor initialized");
|
|
gizmo_service_.set_operation(metacore::engine::editor_core::TransformGizmoService::Operation::Translate);
|
|
gizmo_service_.set_space(metacore::engine::editor_core::TransformGizmoService::Space::Local);
|
|
gizmo_service_.set_snap_enabled(false);
|
|
|
|
foundation::log(foundation::LogLevel::Info, "MetaEditor initialized");
|
|
foundation::log(foundation::LogLevel::Info, "Editor shell export path: " + shell_debug_path.generic_string());
|
|
foundation::log(foundation::LogLevel::Info, "Platform backend: " + platform_.backend_name().generic_string());
|
|
if (!platform_.panda_version().empty()) {
|
|
foundation::log(
|
|
foundation::LogLevel::Info,
|
|
"Panda3D runtime: " + platform_.panda_version() + " | platform=" + platform_.panda_platform() + " | build=" + platform_.panda_build_date()
|
|
);
|
|
}
|
|
foundation::log(
|
|
foundation::LogLevel::Info,
|
|
"Panda window bootstrap: enabled=" + std::string(platform_.window_enabled() ? "true" : "false") +
|
|
" opened=" + std::string(platform_.window_opened() ? "true" : "false")
|
|
);
|
|
if (platform_.window_opened()) {
|
|
foundation::log(
|
|
foundation::LogLevel::Info,
|
|
"Panda native window: hwnd=0x" +
|
|
[&]() {
|
|
std::ostringstream stream;
|
|
stream << std::hex << platform_.native_window_handle();
|
|
return stream.str();
|
|
}() +
|
|
" size=" + std::to_string(platform_.window_width()) + "x" + std::to_string(platform_.window_height())
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int EditorApplication::run() {
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run enter\n";
|
|
foundation::log(foundation::LogLevel::Info, "MetaEditor skeleton run loop started");
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after start log\n";
|
|
ensure_runtime_preview_root();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after runtime preview root\n";
|
|
metacore::engine::editor_core::SceneCommands::focus_selected_object(session_, selection_service_, viewport_service_);
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after focus selected\n";
|
|
|
|
if (const auto selected = inspector_service_.current_selection(scene_, selection_service_)) {
|
|
foundation::log(foundation::LogLevel::Info, "Current selection: " + selected->get().name);
|
|
}
|
|
|
|
const int frame_budget = resolve_frame_budget();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after frame budget\n";
|
|
for (int frame_index = 0; should_continue_running(frame_index, frame_budget); ++frame_index) {
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run frame begin\n";
|
|
if (env_enabled("METACORE_ENABLE_PANDA_UI_DRAW_CALLBACK") && !render_backend_.draw_callback_bound() && frame_index > 0) {
|
|
render_backend_.bind_primary_display_region(platform_.primary_display_region_handle());
|
|
foundation::log(foundation::LogLevel::Info, "Panda UI draw callback binding requested");
|
|
}
|
|
poll_window_shortcuts();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after shortcuts\n";
|
|
if (auto* dearimgui_shell = dynamic_cast<DearImGuiEditorShell*>(editor_shell_.get())) {
|
|
dearimgui_shell->refresh_platform_window_metrics(platform_.window_width(), platform_.window_height());
|
|
}
|
|
auto snapshot = presenter_.build_snapshot(scene_, asset_database_, selection_service_, gizmo_service_, viewport_service_, session_);
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after snapshot A\n";
|
|
editor_shell_->consume_snapshot(snapshot);
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after shell consume A\n";
|
|
render_backend_.set_ui_draw_frame(editor_shell_->consume_render_frame());
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after ui frame A\n";
|
|
render_backend_.set_demo_selection_transform(
|
|
snapshot.viewport.has_selection,
|
|
snapshot.viewport.object_position,
|
|
snapshot.viewport.object_rotation,
|
|
snapshot.viewport.object_scale
|
|
);
|
|
render_backend_.set_demo_camera(snapshot.viewport.camera_position, snapshot.viewport.object_position);
|
|
render_backend_.set_demo_hud_text(build_panda_hud_text(snapshot, render_backend_.submitted_ui_frame_count()));
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after backend demo sync A\n";
|
|
last_snapshot_ = snapshot;
|
|
apply_shell_edits();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after shell edits\n";
|
|
snapshot = presenter_.build_snapshot(scene_, asset_database_, selection_service_, gizmo_service_, viewport_service_, session_);
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after snapshot B\n";
|
|
editor_shell_->consume_snapshot(snapshot);
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after shell consume B\n";
|
|
render_backend_.set_ui_draw_frame(editor_shell_->consume_render_frame());
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after ui frame B\n";
|
|
render_backend_.set_demo_selection_transform(
|
|
snapshot.viewport.has_selection,
|
|
snapshot.viewport.object_position,
|
|
snapshot.viewport.object_rotation,
|
|
snapshot.viewport.object_scale
|
|
);
|
|
render_backend_.set_demo_camera(snapshot.viewport.camera_position, snapshot.viewport.object_position);
|
|
render_backend_.set_demo_hud_text(build_panda_hud_text(snapshot, render_backend_.submitted_ui_frame_count()));
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after backend demo sync B\n";
|
|
last_snapshot_ = snapshot;
|
|
platform_.pump_frame();
|
|
std::cerr << "[MetaCore][Trace] EditorApplication::run after pump\n";
|
|
}
|
|
persist_editor_state();
|
|
if (last_snapshot_.has_value()) {
|
|
log_frame_summary(*last_snapshot_);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void EditorApplication::shutdown() {
|
|
if (editor_shell_) {
|
|
editor_shell_->shutdown();
|
|
}
|
|
render_backend_.shutdown();
|
|
platform_.shutdown();
|
|
foundation::log(foundation::LogLevel::Info, "MetaEditor shutdown complete");
|
|
}
|
|
|
|
void EditorApplication::ensure_runtime_preview_root() {
|
|
const bool has_runtime_ui_object = std::any_of(
|
|
scene_.objects.begin(),
|
|
scene_.objects.end(),
|
|
[](const metacore::engine::scene::SceneObject& object) {
|
|
return object.has_ui_document;
|
|
}
|
|
);
|
|
if (has_runtime_ui_object) {
|
|
return;
|
|
}
|
|
|
|
metacore::engine::editor_core::SceneCommands::create_empty_object(
|
|
command_service_,
|
|
session_,
|
|
selection_service_,
|
|
scene_service_,
|
|
scene_,
|
|
"UI Root"
|
|
);
|
|
|
|
if (selection_service_.selection().has_value()) {
|
|
if (const auto object = scene_service_.find_object(scene_, *selection_service_.selection())) {
|
|
object->get().has_ui_document = true;
|
|
object->get().ui_document.document_path = "Assets/UI/main_menu.rml";
|
|
object->get().ui_document.stylesheet_path = "Assets/UI/main_menu.rcss";
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorApplication::ensure_demo_scene_content() {
|
|
const auto has_named_object = [&](const std::string& name) {
|
|
return std::any_of(scene_.objects.begin(), scene_.objects.end(), [&](const auto& object) {
|
|
return object.name == name;
|
|
});
|
|
};
|
|
const auto has_primary_camera = std::any_of(scene_.objects.begin(), scene_.objects.end(), [](const auto& object) {
|
|
return object.has_camera && object.camera.primary;
|
|
});
|
|
const auto has_light = std::any_of(scene_.objects.begin(), scene_.objects.end(), [](const auto& object) {
|
|
return object.has_light;
|
|
});
|
|
const auto has_mesh = std::any_of(scene_.objects.begin(), scene_.objects.end(), [](const auto& object) {
|
|
return object.has_mesh_renderer;
|
|
});
|
|
|
|
if (!has_primary_camera) {
|
|
auto& camera = scene_service_.create_object(scene_, "Main Camera");
|
|
camera.has_camera = true;
|
|
camera.camera.primary = true;
|
|
camera.transform.position = {0.0F, -10.0F, 3.0F};
|
|
}
|
|
if (!has_light) {
|
|
auto& light = scene_service_.create_object(scene_, "Directional Light");
|
|
light.has_light = true;
|
|
light.light.type = metacore::engine::scene::LightComponent::Type::Directional;
|
|
}
|
|
if (!has_mesh) {
|
|
auto& cube = scene_service_.create_object(scene_, "Demo Cube");
|
|
cube.has_mesh_renderer = true;
|
|
cube.mesh_renderer.mesh_asset_id = "builtin://wire-cube";
|
|
cube.mesh_renderer.material_asset_id = "builtin://editor-default";
|
|
cube.transform.position = {0.0F, 0.0F, 0.0F};
|
|
cube.transform.scale = {1.0F, 1.0F, 1.0F};
|
|
}
|
|
if (!has_named_object("UI Root")) {
|
|
auto& ui_root = scene_service_.create_object(scene_, "UI Root");
|
|
ui_root.has_ui_document = true;
|
|
ui_root.ui_document.document_path = "Assets/UI/main_menu.rml";
|
|
ui_root.ui_document.stylesheet_path = "Assets/UI/main_menu.rcss";
|
|
}
|
|
|
|
for (const auto& object : scene_.objects) {
|
|
if (object.has_mesh_renderer) {
|
|
selection_service_.select(object.id);
|
|
viewport_service_.focus_selection(object.id);
|
|
viewport_service_.set_camera_position({0.0F, -10.0F, 3.0F});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorApplication::apply_shell_edits() {
|
|
for (int pass = 0; pass < 2; ++pass) {
|
|
const PendingShellEdits shell_edits = editor_shell_->consume_pending_edits();
|
|
bool changed = false;
|
|
|
|
if (shell_edits.gizmo_state_edit.has_value()) {
|
|
const auto& edit = *shell_edits.gizmo_state_edit;
|
|
if (edit.has_operation) {
|
|
if (const auto operation = parse_operation(edit.operation)) {
|
|
gizmo_service_.set_operation(*operation);
|
|
session_.set_status_text("Gizmo operation: " + operation_label(*operation));
|
|
session_.add_console_message("Viewport", "Gizmo operation set to " + operation_label(*operation));
|
|
changed = true;
|
|
}
|
|
}
|
|
if (edit.has_space) {
|
|
if (const auto space = parse_space(edit.space)) {
|
|
gizmo_service_.set_space(*space);
|
|
session_.set_status_text("Gizmo space: " + space_label(*space));
|
|
session_.add_console_message("Viewport", "Gizmo space set to " + space_label(*space));
|
|
changed = true;
|
|
}
|
|
}
|
|
if (edit.has_snap_enabled) {
|
|
gizmo_service_.set_snap_enabled(edit.snap_enabled);
|
|
session_.set_status_text(std::string("Gizmo snap: ") + (edit.snap_enabled ? "On" : "Off"));
|
|
session_.add_console_message("Viewport", std::string("Gizmo snap ") + (edit.snap_enabled ? "enabled" : "disabled"));
|
|
changed = true;
|
|
}
|
|
if (edit.has_snap_values) {
|
|
gizmo_service_.set_snap_values(edit.snap_values);
|
|
session_.add_console_message(
|
|
"Viewport",
|
|
"Gizmo snap values updated to " +
|
|
std::to_string(edit.snap_values[0]) + ", " +
|
|
std::to_string(edit.snap_values[1]) + ", " +
|
|
std::to_string(edit.snap_values[2])
|
|
);
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (last_snapshot_.has_value() &&
|
|
shell_edits.transform_edit.has_value() &&
|
|
shell_edits.transform_edit->entity_id == last_snapshot_->viewport.selected_object_id) {
|
|
const auto requested_transform = metacore::engine::scene::TransformComponent{
|
|
shell_edits.transform_edit->position,
|
|
shell_edits.transform_edit->rotation,
|
|
shell_edits.transform_edit->scale
|
|
};
|
|
metacore::engine::editor_core::SceneCommands::apply_transform_to_selected_object(
|
|
command_service_,
|
|
session_,
|
|
selection_service_,
|
|
scene_service_,
|
|
scene_,
|
|
requested_transform
|
|
);
|
|
changed = true;
|
|
}
|
|
|
|
if (!changed) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorApplication::poll_window_shortcuts() {
|
|
if (!platform_.window_opened()) {
|
|
return;
|
|
}
|
|
|
|
const auto pressed = [&](const int virtual_key) {
|
|
const bool is_down = (GetAsyncKeyState(virtual_key) & 0x8000) != 0;
|
|
const bool was_down = key_down_[virtual_key];
|
|
key_down_[virtual_key] = is_down;
|
|
return is_down && !was_down;
|
|
};
|
|
const auto down = [&](const int virtual_key) {
|
|
const bool is_down = (GetAsyncKeyState(virtual_key) & 0x8000) != 0;
|
|
key_down_[virtual_key] = is_down;
|
|
return is_down;
|
|
};
|
|
|
|
if (pressed('W')) {
|
|
gizmo_service_.set_operation(TransformGizmoService::Operation::Translate);
|
|
}
|
|
if (pressed('E')) {
|
|
gizmo_service_.set_operation(TransformGizmoService::Operation::Rotate);
|
|
}
|
|
if (pressed('R')) {
|
|
gizmo_service_.set_operation(TransformGizmoService::Operation::Scale);
|
|
}
|
|
if (pressed('Q')) {
|
|
gizmo_service_.set_space(
|
|
gizmo_service_.space() == TransformGizmoService::Space::Local
|
|
? TransformGizmoService::Space::World
|
|
: TransformGizmoService::Space::Local
|
|
);
|
|
}
|
|
if (down(VK_SHIFT) && pressed('S')) {
|
|
gizmo_service_.set_snap_enabled(!gizmo_service_.snap_enabled());
|
|
}
|
|
|
|
const auto selected_id = selection_service_.selection();
|
|
if (!selected_id.has_value()) {
|
|
return;
|
|
}
|
|
const auto selected_object = scene_service_.find_object(scene_, *selected_id);
|
|
if (!selected_object.has_value()) {
|
|
return;
|
|
}
|
|
|
|
auto transform = selected_object->get().transform;
|
|
bool changed = false;
|
|
const auto step = gizmo_service_.snap_enabled() ? gizmo_service_.snap_values()[0] : 0.1F;
|
|
const auto rotate_step = gizmo_service_.snap_enabled() ? gizmo_service_.snap_values()[0] : 5.0F;
|
|
const auto scale_step = gizmo_service_.snap_enabled() ? 0.25F : 0.05F;
|
|
|
|
switch (gizmo_service_.operation()) {
|
|
case TransformGizmoService::Operation::Translate:
|
|
if (down(VK_LEFT)) {
|
|
transform.position[0] -= step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_RIGHT)) {
|
|
transform.position[0] += step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_UP)) {
|
|
transform.position[1] += step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_DOWN)) {
|
|
transform.position[1] -= step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_PRIOR)) {
|
|
transform.position[2] += step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_NEXT)) {
|
|
transform.position[2] -= step;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case TransformGizmoService::Operation::Rotate:
|
|
if (down(VK_LEFT)) {
|
|
transform.rotation[2] -= rotate_step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_RIGHT)) {
|
|
transform.rotation[2] += rotate_step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_UP)) {
|
|
transform.rotation[0] += rotate_step;
|
|
changed = true;
|
|
}
|
|
if (down(VK_DOWN)) {
|
|
transform.rotation[0] -= rotate_step;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case TransformGizmoService::Operation::Scale:
|
|
if (down(VK_OEM_PLUS) || down(VK_ADD)) {
|
|
for (float& axis : transform.scale) {
|
|
axis += scale_step;
|
|
}
|
|
changed = true;
|
|
}
|
|
if (down(VK_OEM_MINUS) || down(VK_SUBTRACT)) {
|
|
for (float& axis : transform.scale) {
|
|
axis = (std::max)(0.1F, axis - scale_step);
|
|
}
|
|
changed = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (changed) {
|
|
metacore::engine::editor_core::SceneCommands::apply_transform_to_selected_object(
|
|
command_service_,
|
|
session_,
|
|
selection_service_,
|
|
scene_service_,
|
|
scene_,
|
|
transform
|
|
);
|
|
}
|
|
}
|
|
|
|
void EditorApplication::log_frame_summary(const EditorFrameSnapshot& snapshot) const {
|
|
foundation::log(foundation::LogLevel::Info, "Hierarchy items: " + std::to_string(snapshot.hierarchy.size()));
|
|
foundation::log(foundation::LogLevel::Info, "Project assets: " + std::to_string(snapshot.assets.size()));
|
|
foundation::log(foundation::LogLevel::Info, "Inspector target: " + snapshot.inspector.object_name);
|
|
foundation::log(foundation::LogLevel::Info, "Scene object count: " + std::to_string(snapshot.scene_object_count));
|
|
foundation::log(foundation::LogLevel::Info, "Session status: " + snapshot.status_text);
|
|
foundation::log(foundation::LogLevel::Info, "Editor shell: " + editor_shell_->shell_name());
|
|
foundation::log(foundation::LogLevel::Info, "Editor shell summary: " + editor_shell_->summarize_snapshot());
|
|
foundation::log(foundation::LogLevel::Info, "Platform frames: " + std::to_string(platform_.frame_count()));
|
|
foundation::log(
|
|
foundation::LogLevel::Info,
|
|
"UI render handoff: submitted=" + std::to_string(render_backend_.submitted_ui_frame_count()) +
|
|
" callbackBound=" + std::string(render_backend_.draw_callback_bound() ? "true" : "false") +
|
|
" drawCallbacks=" + std::to_string(render_backend_.observed_draw_callback_count())
|
|
);
|
|
foundation::log(foundation::LogLevel::Info, editor_shell_->describe_workspace());
|
|
|
|
for (const auto& message : snapshot.console) {
|
|
foundation::log(foundation::LogLevel::Info, "[" + message.category + "] " + message.text);
|
|
}
|
|
}
|
|
|
|
int EditorApplication::resolve_frame_budget() const {
|
|
const char* frame_budget_env = std::getenv("METACORE_EDITOR_FRAME_COUNT");
|
|
if (platform_.window_opened()) {
|
|
if (!has_explicit_env(frame_budget_env)) {
|
|
return -1;
|
|
}
|
|
return parse_positive_int_env(frame_budget_env, -1);
|
|
}
|
|
return parse_positive_int_env(frame_budget_env, 1);
|
|
}
|
|
|
|
bool EditorApplication::should_continue_running(const int frame_index, const int frame_budget) const {
|
|
if (platform_.should_close()) {
|
|
return false;
|
|
}
|
|
if (frame_budget < 0) {
|
|
return true;
|
|
}
|
|
return frame_index < frame_budget;
|
|
}
|
|
|
|
void EditorApplication::persist_editor_state() const {
|
|
layout_service_.save(project_root_ / "Library" / "Layout" / "editor_layout.json", session_.to_layout_state());
|
|
scene_serializer_.save_scene(project_root_ / "Scenes" / "Main.mcscene.json", scene_);
|
|
asset_database_.save(project_root_ / "Library" / "AssetDB.json");
|
|
}
|
|
|
|
} // namespace metacore::editor
|