Fix tree model depth rendering, ImGuizmo interactions, HDR IBL format, and link stb_image_impl
This commit is contained in:
parent
27aa4547c7
commit
9e2c2a8954
@ -9,6 +9,7 @@
|
||||
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataDispatcher.h"
|
||||
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataProject.h"
|
||||
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataSource.h"
|
||||
#include "MetaCoreScene/MetaCoreRuntimeLifecycle.h"
|
||||
#include "MetaCoreScene/MetaCoreScenePackage.h"
|
||||
#include "MetaCoreScene/MetaCoreSceneSerializer.h"
|
||||
#include "MetaCoreScene/MetaCoreScene.h"
|
||||
@ -727,6 +728,21 @@ int main(int argc, char* argv[]) {
|
||||
scene = MetaCore::MetaCoreCreateDefaultScene();
|
||||
std::cout << "MetaCorePlayer: startup scene unavailable, using built-in default scene\n";
|
||||
}
|
||||
MetaCore::MetaCoreRuntimeLifecycleExecutor runtimeLifecycleExecutor;
|
||||
const std::size_t runtimeComponentCount = runtimeLifecycleExecutor.RegisterDescriptors(
|
||||
MetaCore::MetaCoreGetRuntimeLifecycleRegistry().GetDescriptors()
|
||||
);
|
||||
if (runtimeComponentCount > 0) {
|
||||
std::cout << "MetaCorePlayer: registered " << runtimeComponentCount
|
||||
<< " runtime lifecycle component(s)\n";
|
||||
}
|
||||
const auto runtimeLifecycleErrorSink = [](const MetaCore::MetaCoreRuntimeLifecycleError& error) {
|
||||
std::cerr << "MetaCorePlayer: lifecycle " << error.Phase
|
||||
<< " failed component=" << error.ComponentTypeId
|
||||
<< " object=" << error.ObjectId
|
||||
<< " error=" << error.Message << '\n';
|
||||
};
|
||||
runtimeLifecycleExecutor.EnterScene(scene, runtimeLifecycleErrorSink);
|
||||
|
||||
std::optional<MetaCore::MetaCoreUiDocument> startupUiDocument;
|
||||
if (!runtimeProjectDocument.StartupUiPath.empty()) {
|
||||
@ -892,6 +908,7 @@ int main(int argc, char* argv[]) {
|
||||
});
|
||||
runtimeUiRenderer.Resize(windowWidth, windowHeight);
|
||||
runtimeUiRenderer.BeginFrame(1.0F / 60.0F);
|
||||
runtimeLifecycleExecutor.TickScene(scene, 1.0F / 60.0F, runtimeLifecycleErrorSink);
|
||||
if (!runtimeSources.empty()) {
|
||||
std::uint64_t latestUpdateAt = 0;
|
||||
for (MetaCoreRuntimeDataSourceInstance& sourceInstance : runtimeSources) {
|
||||
@ -949,6 +966,7 @@ int main(int argc, char* argv[]) {
|
||||
window.EndFrame();
|
||||
}
|
||||
|
||||
runtimeLifecycleExecutor.ExitScene(scene, runtimeLifecycleErrorSink);
|
||||
runtimeUiRenderer.Shutdown();
|
||||
viewportRenderer.Shutdown();
|
||||
renderDevice.Shutdown();
|
||||
|
||||
@ -223,6 +223,7 @@ target_compile_definitions(MetaCorePlatform PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX
|
||||
set(METACORE_SCENE_HEADERS
|
||||
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h
|
||||
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreGameObject.h
|
||||
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreRuntimeLifecycle.h
|
||||
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreSceneDocument.h
|
||||
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreScenePackage.h
|
||||
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreScene.h
|
||||
@ -232,6 +233,7 @@ set(METACORE_SCENE_HEADERS
|
||||
|
||||
set(METACORE_SCENE_SOURCES
|
||||
Source/MetaCoreScene/Private/MetaCoreScenePackage.cpp
|
||||
Source/MetaCoreScene/Private/MetaCoreRuntimeLifecycle.cpp
|
||||
Source/MetaCoreScene/Private/MetaCoreScene.cpp
|
||||
Source/MetaCoreScene/Private/MetaCoreSceneSerializer.cpp
|
||||
Source/MetaCoreScene/Private/MetaCoreUiRmlCompiler.cpp
|
||||
@ -486,6 +488,7 @@ endif()
|
||||
|
||||
add_executable(MetaCorePlayer
|
||||
Apps/MetaCorePlayer/main.cpp
|
||||
third_party/stb/stb_image_impl.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MetaCorePlayer
|
||||
|
||||
BIN
SandboxProject/Assets/111.glb
Normal file
BIN
SandboxProject/Assets/111.glb
Normal file
Binary file not shown.
BIN
SandboxProject/Assets/111.glb.bak
Normal file
BIN
SandboxProject/Assets/111.glb.bak
Normal file
Binary file not shown.
8
SandboxProject/Assets/111.glb.bak.mcmeta
Normal file
8
SandboxProject/Assets/111.glb.bak.mcmeta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "asset",
|
||||
"guid": "305e95e7-4f41-426b-8ea5-8ba4fc1a8724",
|
||||
"importer_id": "BinaryImporter",
|
||||
"package_path": "Assets/111.glb.bak.mcasset",
|
||||
"source_hash": 17894180331051193223,
|
||||
"source_path": "Assets/111.glb.bak"
|
||||
}
|
||||
BIN
SandboxProject/Assets/111.glb.bak2
Normal file
BIN
SandboxProject/Assets/111.glb.bak2
Normal file
Binary file not shown.
8
SandboxProject/Assets/111.glb.bak2.mcmeta
Normal file
8
SandboxProject/Assets/111.glb.bak2.mcmeta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "asset",
|
||||
"guid": "b5c7953d-3806-4660-9596-043eaa9220a1",
|
||||
"importer_id": "BinaryImporter",
|
||||
"package_path": "Assets/111.glb.bak2.mcasset",
|
||||
"source_hash": 16380358792779090604,
|
||||
"source_path": "Assets/111.glb.bak2"
|
||||
}
|
||||
22
SandboxProject/Assets/111.glb.mcmeta
Normal file
22
SandboxProject/Assets/111.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "ee0fec65-bcc9-4efb-a5b4-871385648c23",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/111.glb.mcasset",
|
||||
"source_hash": 11760661239691667159,
|
||||
"source_path": "Assets/111.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "ff7c19a0-ba4c-d02a-2111-835582811855",
|
||||
"index": 0,
|
||||
"name": "111",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "030a04c3-630d-affd-1c58-fc3bc8aa9f91",
|
||||
"index": 0,
|
||||
"name": "111_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/Models/Cube.glb.mcasset
Normal file
BIN
SandboxProject/Assets/Models/Cube.glb.mcasset
Normal file
Binary file not shown.
Binary file not shown.
BIN
SandboxProject/Assets/Models/Floor4.glb
Normal file
BIN
SandboxProject/Assets/Models/Floor4.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/Models/Floor4.glb.mcmeta
Normal file
22
SandboxProject/Assets/Models/Floor4.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "7817e162-2dd7-4747-bdfb-93bb38a72ebb",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/Models/Floor4.glb.mcasset",
|
||||
"source_hash": 17521808281966876099,
|
||||
"source_path": "Assets/Models/Floor4.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "f0a46cf7-2e2c-d921-eb90-551705ce1b1e",
|
||||
"index": 0,
|
||||
"name": "Floor4",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "7424828f-d8de-69d9-8850-1cc4e467ba88",
|
||||
"index": 0,
|
||||
"name": "Floor4_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/Models/JXB.glb
Normal file
BIN
SandboxProject/Assets/Models/JXB.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/Models/JXB.glb.mcmeta
Normal file
22
SandboxProject/Assets/Models/JXB.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "f6a51ac3-fb03-42df-8836-9bc605b60b0c",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/Models/JXB.glb.mcasset",
|
||||
"source_hash": 9095552786760188715,
|
||||
"source_path": "Assets/Models/JXB.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "20593992-3c28-20dc-0734-2068a98c33dc",
|
||||
"index": 0,
|
||||
"name": "JXB",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "24102470-e146-19aa-cccb-65dc97d3c0db",
|
||||
"index": 0,
|
||||
"name": "JXB_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/Models/RFF-37-3D.glb
Normal file
BIN
SandboxProject/Assets/Models/RFF-37-3D.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/Models/RFF-37-3D.glb.mcmeta
Normal file
22
SandboxProject/Assets/Models/RFF-37-3D.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "0998aab9-9952-4b49-8723-7a1dd5903e9e",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/Models/RFF-37-3D.glb.mcasset",
|
||||
"source_hash": 7543758070289905162,
|
||||
"source_path": "Assets/Models/RFF-37-3D.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "e642444d-d75f-96ad-fedb-d226fc4669fc",
|
||||
"index": 0,
|
||||
"name": "RFF-37-3D",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "22e350d5-7fa6-c46b-0533-914588f2d8a6",
|
||||
"index": 0,
|
||||
"name": "RFF-37-3D_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/Models/box1.glb.mcasset
Normal file
BIN
SandboxProject/Assets/Models/box1.glb.mcasset
Normal file
Binary file not shown.
Binary file not shown.
BIN
SandboxProject/Assets/Models/ch_desk.glb
Normal file
BIN
SandboxProject/Assets/Models/ch_desk.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/Models/ch_desk.glb.mcmeta
Normal file
22
SandboxProject/Assets/Models/ch_desk.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "719e2e9f-4445-48f5-bf2e-eb4bacf2cac5",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/Models/ch_desk.glb.mcasset",
|
||||
"source_hash": 2230133667640719958,
|
||||
"source_path": "Assets/Models/ch_desk.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "eefc1764-5c68-39f6-74f5-2d02ce94d07a",
|
||||
"index": 0,
|
||||
"name": "ch_desk",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "a29b1867-cf52-f37c-e545-39802041e061",
|
||||
"index": 0,
|
||||
"name": "ch_desk_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/Models/cuite.glb
Normal file
BIN
SandboxProject/Assets/Models/cuite.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/Models/cuite.glb.mcmeta
Normal file
22
SandboxProject/Assets/Models/cuite.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "ad0a19f9-393b-4ed8-899c-d4e44505396f",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/Models/cuite.glb.mcasset",
|
||||
"source_hash": 16889646040474063424,
|
||||
"source_path": "Assets/Models/cuite.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "e46dfa30-317d-d3a3-bc8e-f4e2cbcb067d",
|
||||
"index": 0,
|
||||
"name": "cuite",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "e0590e30-51ba-e7cd-b784-c0a613b48da3",
|
||||
"index": 0,
|
||||
"name": "cuite_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/Models/haqijingzhu.glb
Normal file
BIN
SandboxProject/Assets/Models/haqijingzhu.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/Models/haqijingzhu.glb.mcmeta
Normal file
22
SandboxProject/Assets/Models/haqijingzhu.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "9f9aa1f2-21ea-4b17-9da7-3713c32e11ee",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/Models/haqijingzhu.glb.mcasset",
|
||||
"source_hash": 8705570643519871155,
|
||||
"source_path": "Assets/Models/haqijingzhu.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "e0e7ad5c-3363-965c-37a7-7715359229a1",
|
||||
"index": 0,
|
||||
"name": "haqijingzhu",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "7c3bb866-d2e3-de15-c287-49e822dbec2b",
|
||||
"index": 0,
|
||||
"name": "haqijingzhu_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/RFF-37-3D.glb
Normal file
BIN
SandboxProject/Assets/RFF-37-3D.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/RFF-37-3D.glb.mcmeta
Normal file
22
SandboxProject/Assets/RFF-37-3D.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "54bd5826-8c6e-4b90-be92-d77180516bf6",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/RFF-37-3D.glb.mcasset",
|
||||
"source_hash": 7543758070289905162,
|
||||
"source_path": "Assets/RFF-37-3D.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "bbb65c83-5b02-4945-f61d-657c8260dd61",
|
||||
"index": 0,
|
||||
"name": "RFF-37-3D",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "ef308213-3ea5-cb66-35a6-23bf086156eb",
|
||||
"index": 0,
|
||||
"name": "RFF-37-3D_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
SandboxProject/Assets/haqijingzhu.glb
Normal file
BIN
SandboxProject/Assets/haqijingzhu.glb
Normal file
Binary file not shown.
22
SandboxProject/Assets/haqijingzhu.glb.mcmeta
Normal file
22
SandboxProject/Assets/haqijingzhu.glb.mcmeta
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"asset_type": "model",
|
||||
"guid": "f9c69ec5-0d65-450d-80f2-636393b1d608",
|
||||
"importer_id": "GltfModelImporter",
|
||||
"package_path": "Assets/haqijingzhu.glb.mcasset",
|
||||
"source_hash": 8705570643519871155,
|
||||
"source_path": "Assets/haqijingzhu.glb",
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "61886497-7a91-4806-5681-54511c9ff052",
|
||||
"index": 0,
|
||||
"name": "haqijingzhu",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "5d2f7f6a-fa58-6d4a-ab5e-dfebc1789e24",
|
||||
"index": 0,
|
||||
"name": "haqijingzhu_Material",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -2010,7 +2010,7 @@ void MetaCoreTrackInspectorEdit(MetaCoreEditorContext& editorContext, const char
|
||||
}
|
||||
|
||||
const bool changed = editorContext.ExecuteSnapshotCommand(
|
||||
addComponent ? "添加组件" : "移除组件",
|
||||
addComponent ? "Add Component" : "移除组件",
|
||||
[&]() {
|
||||
bool anyChanged = false;
|
||||
for (const MetaCoreId selectedId : editorContext.GetSelectedObjectIds()) {
|
||||
@ -2408,7 +2408,8 @@ public:
|
||||
const auto scenePersistenceService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIScenePersistenceService>();
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
|
||||
if (ImGui::BeginMenu("文件")) {
|
||||
// 顶级菜单 1:项目 (对应 Project)
|
||||
if (ImGui::BeginMenu("项目")) {
|
||||
if (ImGui::MenuItem("新建项目...")) {
|
||||
if (assetDatabaseService != nullptr && assetDatabaseService->HasProject()) {
|
||||
const auto& project = assetDatabaseService->GetProjectDescriptor();
|
||||
@ -2437,16 +2438,41 @@ public:
|
||||
if (ImGui::MenuItem("刷新 Asset 数据库", nullptr, false, assetDatabaseService != nullptr)) {
|
||||
MetaCoreHandleReloadAssets(editorContext);
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
// 收纳 MetaCore 专属资源功能菜单项
|
||||
if (ImGui::BeginMenu("资源管理")) {
|
||||
ImGui::MenuItem("导入资源", nullptr, false, false);
|
||||
if (ImGui::MenuItem("从选中对象创建 Prefab", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreCreatePrefabFromSelection(editorContext);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
// 复制 Infernux 占位项,对齐界面一致度
|
||||
ImGui::MenuItem("偏好设置", nullptr, false, false);
|
||||
ImGui::MenuItem("构建设置", nullptr, false, false);
|
||||
ImGui::MenuItem("物理层交互矩阵", nullptr, false, false);
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("重置布局")) {
|
||||
editorContext.SetDockLayoutBuilt(false);
|
||||
}
|
||||
|
||||
// 收纳原有的 Help 菜单
|
||||
if (ImGui::BeginMenu("帮助说明")) {
|
||||
ImGui::MenuItem("MetaCore Editor", nullptr, false, false);
|
||||
ImGui::MenuItem("当前运行模式:MetaCore 编辑器", nullptr, false, false);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("退出")) {
|
||||
editorContext.GetWindow().RequestClose();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
// 新建项目弹窗中文化
|
||||
if (ImGui::BeginPopupModal("MetaCoreCreateProjectPopup", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::InputText("项目目录", CreateProjectPathBuffer_, sizeof(CreateProjectPathBuffer_));
|
||||
ImGui::InputText("项目名称", CreateProjectNameBuffer_, sizeof(CreateProjectNameBuffer_));
|
||||
@ -2462,6 +2488,7 @@ public:
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// 打开项目弹窗中文化
|
||||
if (ImGui::BeginPopupModal("MetaCoreOpenProjectPopup", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::InputText("项目路径", OpenProjectPathBuffer_, sizeof(OpenProjectPathBuffer_));
|
||||
if (ImGui::Button("打开")) {
|
||||
@ -2475,6 +2502,7 @@ public:
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// 顶级菜单 2:编辑 (对应 Edit)
|
||||
if (ImGui::BeginMenu("编辑")) {
|
||||
if (ImGui::MenuItem("撤销", "Ctrl+Z", false, editorContext.GetCommandService().CanUndo())) {
|
||||
MetaCoreHandleUndo(editorContext);
|
||||
@ -2492,52 +2520,48 @@ public:
|
||||
if (ImGui::MenuItem("重命名", "F2", false, editorContext.GetActiveObjectId() != 0)) {
|
||||
editorContext.RequestRenameActiveObject();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("资源")) {
|
||||
ImGui::MenuItem("导入资源", nullptr, false, false);
|
||||
if (ImGui::MenuItem("从选中对象创建 Prefab", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreCreatePrefabFromSelection(editorContext);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("游戏对象")) {
|
||||
if (ImGui::MenuItem("创建空对象")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "GameObject", false, std::nullopt);
|
||||
}
|
||||
if (ImGui::MenuItem("创建立方体")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Cube", true, std::nullopt);
|
||||
}
|
||||
if (ImGui::MenuItem("创建平面")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Plane", true, std::nullopt);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("应用当前 Prefab", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreApplySelectedPrefabInstance(editorContext);
|
||||
|
||||
// 收纳 MetaCore 专属游戏对象与组件创建逻辑
|
||||
if (ImGui::BeginMenu("创建游戏对象")) {
|
||||
if (ImGui::MenuItem("创建空对象")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "GameObject", false, std::nullopt);
|
||||
}
|
||||
if (ImGui::MenuItem("创建立方体")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Cube", true, std::nullopt);
|
||||
}
|
||||
if (ImGui::MenuItem("创建平面")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Plane", true, std::nullopt);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("应用当前 Prefab", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreApplySelectedPrefabInstance(editorContext);
|
||||
}
|
||||
if (ImGui::MenuItem("还原当前 Prefab", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreRevertSelectedPrefabInstance(editorContext);
|
||||
}
|
||||
if (ImGui::MenuItem("Break Prefab Instance", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreBreakSelectedPrefabInstance(editorContext);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("复制", "Ctrl+D", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDuplicateSelection(editorContext);
|
||||
}
|
||||
if (ImGui::MenuItem("删除", "Delete", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDeleteSelection(editorContext);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem("还原当前 Prefab", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreRevertSelectedPrefabInstance(editorContext);
|
||||
}
|
||||
if (ImGui::MenuItem("Break Prefab Instance", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreBreakSelectedPrefabInstance(editorContext);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("复制", "Ctrl+D", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDuplicateSelection(editorContext);
|
||||
}
|
||||
if (ImGui::MenuItem("删除", "Delete", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDeleteSelection(editorContext);
|
||||
|
||||
if (ImGui::BeginMenu("添加组件")) {
|
||||
ImGui::MenuItem("Add Component", nullptr, false, false);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("组件")) {
|
||||
ImGui::MenuItem("添加组件", nullptr, false, false);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
// 顶级菜单 3:窗口 (对应 Window)
|
||||
if (ImGui::BeginMenu("窗口")) {
|
||||
for (const auto& panelProvider : editorContext.GetModuleRegistry().GetPanelProviders()) {
|
||||
bool& panelOpen = editorContext.GetModuleRegistry().AccessPanelOpenState(panelProvider->GetPanelId());
|
||||
@ -2545,12 +2569,6 @@ public:
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("帮助")) {
|
||||
ImGui::MenuItem("MetaCore Editor", nullptr, false, false);
|
||||
ImGui::MenuItem("当前运行模式:MetaCore 编辑器", nullptr, false, false);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -2562,34 +2580,33 @@ private:
|
||||
class MetaCoreHierarchyPanelProvider final : public MetaCoreIEditorPanelProvider {
|
||||
public:
|
||||
std::string GetPanelId() const override { return "Hierarchy"; }
|
||||
std::string GetPanelTitle() const override { return "层级"; }
|
||||
std::string GetPanelTitle() const override { return "Hierarchy"; }
|
||||
bool IsOpenByDefault() const override { return true; }
|
||||
|
||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
||||
// 全局资源选择状态
|
||||
static std::filesystem::path GProjectSelectedDirectory{};
|
||||
static MetaCoreAssetGuid GProjectSelectedAssetGuid{};
|
||||
static std::filesystem::path GProjectSelectedAssetPath{};
|
||||
static std::string GProjectSelectedAssetType{};
|
||||
static MetaCoreAssetStorageKind GProjectSelectedAssetStorageKind = MetaCoreAssetStorageKind::SourcePackage;
|
||||
static MetaCoreAssetGuid GProjectSelectedGeneratedResourceGuid{};
|
||||
static std::string GProjectSelectedGeneratedResourceKind{};
|
||||
static std::string GProjectGeneratedResourceFilter{};
|
||||
static std::string GProjectGeneratedResourceKindFilter{};
|
||||
static std::string GProjectSelectedUiNodeId{};
|
||||
|
||||
// Global Asset Selection State in BuiltinEditorModule
|
||||
static std::filesystem::path GProjectSelectedDirectory{};
|
||||
static MetaCoreAssetGuid GProjectSelectedAssetGuid{};
|
||||
static std::filesystem::path GProjectSelectedAssetPath{};
|
||||
static std::string GProjectSelectedAssetType{};
|
||||
static MetaCoreAssetStorageKind GProjectSelectedAssetStorageKind = MetaCoreAssetStorageKind::SourcePackage;
|
||||
static MetaCoreAssetGuid GProjectSelectedGeneratedResourceGuid{};
|
||||
static std::string GProjectSelectedGeneratedResourceKind{};
|
||||
static std::string GProjectGeneratedResourceFilter{};
|
||||
static std::string GProjectGeneratedResourceKindFilter{};
|
||||
static std::string GProjectSelectedUiNodeId{};
|
||||
|
||||
static std::filesystem::path GProjectPendingRenamePath{};
|
||||
static std::array<char, 256> GProjectAssetFilterBuffer{};
|
||||
static std::array<char, 128> GProjectFolderNameBuffer{};
|
||||
static std::array<char, 256> GProjectRenamePathBuffer{};
|
||||
static std::filesystem::path GProjectPendingRenamePath{};
|
||||
static std::array<char, 256> GProjectAssetFilterBuffer{};
|
||||
static std::array<char, 128> GProjectFolderNameBuffer{};
|
||||
static std::array<char, 256> GProjectRenamePathBuffer{};
|
||||
const auto scenePersistenceService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIScenePersistenceService>();
|
||||
if (const MetaCoreId requestedRenameId = editorContext.ConsumeRenameRequestObjectId(); requestedRenameId != 0) {
|
||||
BeginRename(editorContext, requestedRenameId);
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 8));
|
||||
ImGui::TextColored(ImVec4(0.4F, 0.6F, 1.0F, 1.0F), " (S) ");
|
||||
ImGui::TextColored(ImVec4(0.4F, 0.6F, 1.0F, 1.0F), "Scene: ");
|
||||
ImGui::SameLine();
|
||||
const std::string sceneLabel =
|
||||
scenePersistenceService != nullptr && scenePersistenceService->HasOpenScene()
|
||||
@ -2609,7 +2626,6 @@ static std::array<char, 256> GProjectRenamePathBuffer{};
|
||||
|
||||
DrawRootDropTarget(editorContext);
|
||||
DrawWindowContextMenu(editorContext);
|
||||
DrawRenamePopup(editorContext);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -2620,6 +2636,34 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
// 就地重命名交互逻辑
|
||||
if (RenameObjectId_ == objectId) {
|
||||
if (OpenRenamePopup_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
OpenRenamePopup_ = false;
|
||||
}
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
const bool submittedByEnter = ImGui::InputText(
|
||||
"##InlineRename",
|
||||
RenameBuffer_.data(),
|
||||
RenameBuffer_.size(),
|
||||
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue
|
||||
);
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
RenameObjectId_ = 0;
|
||||
return;
|
||||
}
|
||||
if (submittedByEnter || ImGui::IsItemDeactivated()) {
|
||||
std::string newName(RenameBuffer_.data());
|
||||
if (!newName.empty()) {
|
||||
(void)MetaCoreRenameObject(editorContext, objectId, newName);
|
||||
}
|
||||
RenameObjectId_ = 0;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<MetaCoreId> childIds = scene.GetChildrenOf(objectId);
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanFullWidth;
|
||||
if (childIds.empty()) {
|
||||
@ -2635,19 +2679,12 @@ private:
|
||||
gameObject.HasComponent<MetaCoreMeshRendererComponent>() &&
|
||||
!gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelPath.empty();
|
||||
|
||||
// 1:1 对齐 Infernux 装饰样式:模型实例冠以 * 前缀 (使用 ASCII 规避缺失字体导致的问号)
|
||||
// 对齐 Infernux 装饰样式:模型实例以星号前缀标示
|
||||
std::string hierarchyLabel = gameObject.GetName();
|
||||
if (isModelInstance) {
|
||||
hierarchyLabel = "* " + hierarchyLabel;
|
||||
}
|
||||
|
||||
// 1:1 对齐选中背景色与模型实例的文字红强调色
|
||||
if (isSelected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
}
|
||||
|
||||
bool pushedTextColor = false;
|
||||
if (isModelInstance) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(235.0F / 255.0F, 87.0F / 255.0F, 87.0F / 255.0F, 1.0F));
|
||||
@ -2665,10 +2702,6 @@ private:
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
const bool leftClicked = ImGui::IsItemClicked(ImGuiMouseButton_Left);
|
||||
const bool rightClicked = ImGui::IsItemClicked(ImGuiMouseButton_Right);
|
||||
if ((leftClicked || rightClicked) && !ImGui::IsItemToggledOpen()) {
|
||||
@ -2697,7 +2730,7 @@ private:
|
||||
}
|
||||
MetaCoreId payloadId = objectId;
|
||||
ImGui::SetDragDropPayload("MC_HIERARCHY_OBJECT", &payloadId, sizeof(payloadId));
|
||||
ImGui::Text("拖拽 %zu 个对象", editorContext.GetSelectedObjectIds().size());
|
||||
ImGui::Text("Drag %zu objects", editorContext.GetSelectedObjectIds().size());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
@ -2729,13 +2762,13 @@ private:
|
||||
|
||||
void DrawNodeContextMenu(MetaCoreEditorContext& editorContext, MetaCoreId objectId) {
|
||||
const MetaCoreGameObject gameObject = editorContext.GetScene().FindGameObject(objectId);
|
||||
if (ImGui::MenuItem("创建空对象")) {
|
||||
if (ImGui::MenuItem("Create Empty")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "GameObject", false, objectId);
|
||||
}
|
||||
if (ImGui::MenuItem("创建立方体")) {
|
||||
if (ImGui::MenuItem("Create Cube")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Cube", true, objectId);
|
||||
}
|
||||
if (ImGui::MenuItem("创建平面")) {
|
||||
if (ImGui::MenuItem("Create Plane")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Plane", true, objectId);
|
||||
}
|
||||
ImGui::Separator();
|
||||
@ -2746,35 +2779,35 @@ private:
|
||||
const auto sameSourceModelObjects = FindSceneObjectsUsingSameSourceModel(editorContext, gameObject);
|
||||
const auto sameSourceNodeObjects = FindSceneObjectsUsingSameSourceNode(editorContext, gameObject);
|
||||
if (sourceAsset.has_value()) {
|
||||
ImGui::TextDisabled("模型: %s", sourceAsset->RelativePath.filename().string().c_str());
|
||||
ImGui::TextDisabled("Model: %s", sourceAsset->RelativePath.filename().string().c_str());
|
||||
} else {
|
||||
ImGui::TextDisabled("模型: %s", gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelPath.c_str());
|
||||
ImGui::TextDisabled("Model: %s", gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelPath.c_str());
|
||||
}
|
||||
ImGui::TextDisabled("节点: %d", gameObject.GetComponent<MetaCoreMeshRendererComponent>().ModelNodeIndex);
|
||||
ImGui::TextDisabled("同源模型实例: %d", static_cast<int>(sameSourceModelObjects.size()));
|
||||
ImGui::TextDisabled("同源节点实例: %d", static_cast<int>(sameSourceNodeObjects.size()));
|
||||
ImGui::TextDisabled("Node: %d", gameObject.GetComponent<MetaCoreMeshRendererComponent>().ModelNodeIndex);
|
||||
ImGui::TextDisabled("Same Source Model Instances: %d", static_cast<int>(sameSourceModelObjects.size()));
|
||||
ImGui::TextDisabled("Same Source Node Instances: %d", static_cast<int>(sameSourceNodeObjects.size()));
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("打开源模型")) {
|
||||
if (ImGui::MenuItem("Open Source Model")) {
|
||||
OpenSourceModelForGameObject(editorContext, gameObject);
|
||||
}
|
||||
if (ImGui::MenuItem("选择源节点", nullptr, false, gameObject.GetComponent<MetaCoreMeshRendererComponent>().ModelNodeIndex >= 0)) {
|
||||
if (ImGui::MenuItem("Select Source Node", nullptr, false, gameObject.GetComponent<MetaCoreMeshRendererComponent>().ModelNodeIndex >= 0)) {
|
||||
OpenSourceModelForGameObject(editorContext, gameObject);
|
||||
}
|
||||
if (ImGui::MenuItem("选择同源模型实例", nullptr, false, !sameSourceModelObjects.empty())) {
|
||||
if (ImGui::MenuItem("Select Same Source Model Instances", nullptr, false, !sameSourceModelObjects.empty())) {
|
||||
editorContext.SetSelection(sameSourceModelObjects, sameSourceModelObjects.front());
|
||||
}
|
||||
if (ImGui::MenuItem("选择同源节点实例", nullptr, false, !sameSourceNodeObjects.empty())) {
|
||||
if (ImGui::MenuItem("Select Same Source Node Instances", nullptr, false, !sameSourceNodeObjects.empty())) {
|
||||
editorContext.SetSelection(sameSourceNodeObjects, sameSourceNodeObjects.front());
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
if (ImGui::MenuItem("重命名", "F2", false, objectId != 0)) {
|
||||
if (ImGui::MenuItem("Rename", "F2", false, objectId != 0)) {
|
||||
BeginRename(editorContext, objectId);
|
||||
}
|
||||
if (ImGui::MenuItem("复制", "Ctrl+D", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
if (ImGui::MenuItem("Duplicate", "Ctrl+D", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDuplicateSelection(editorContext);
|
||||
}
|
||||
if (ImGui::MenuItem("删除", "Delete", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
if (ImGui::MenuItem("Delete", "Delete", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDeleteSelection(editorContext);
|
||||
}
|
||||
}
|
||||
@ -2784,23 +2817,23 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("创建空对象")) {
|
||||
if (ImGui::MenuItem("Create Empty")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "GameObject", false, 0);
|
||||
}
|
||||
if (ImGui::MenuItem("创建立方体")) {
|
||||
if (ImGui::MenuItem("Create Cube")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Cube", true, 0);
|
||||
}
|
||||
if (ImGui::MenuItem("创建平面")) {
|
||||
if (ImGui::MenuItem("Create Plane")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Plane", true, 0);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("重命名", "F2", false, editorContext.GetActiveObjectId() != 0)) {
|
||||
if (ImGui::MenuItem("Rename", "F2", false, editorContext.GetActiveObjectId() != 0)) {
|
||||
BeginRename(editorContext, editorContext.GetActiveObjectId());
|
||||
}
|
||||
if (ImGui::MenuItem("复制", "Ctrl+D", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
if (ImGui::MenuItem("Duplicate", "Ctrl+D", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDuplicateSelection(editorContext);
|
||||
}
|
||||
if (ImGui::MenuItem("删除", "Delete", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
if (ImGui::MenuItem("Delete", "Delete", false, !editorContext.GetSelectedObjectIds().empty())) {
|
||||
(void)MetaCoreDeleteSelection(editorContext);
|
||||
}
|
||||
|
||||
@ -2809,16 +2842,20 @@ private:
|
||||
|
||||
void DrawRootDropTarget(MetaCoreEditorContext& editorContext) {
|
||||
ImGui::Separator();
|
||||
ImGui::TextDisabled("拖拽到下方设为根节点");
|
||||
ImGui::TextDisabled("Drag here to reparent to root");
|
||||
const ImVec2 dropAreaMin = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 dropAreaSize = ImVec2(ImGui::GetContentRegionAvail().x, 24.0F);
|
||||
ImGui::InvisibleButton("MetaCoreHierarchyRootDrop", dropAreaSize);
|
||||
const ImVec2 dropAreaMax = ImVec2(dropAreaMin.x + dropAreaSize.x, dropAreaMin.y + dropAreaSize.y);
|
||||
|
||||
// 绘制 Notion 虚线直角框
|
||||
ImGui::GetWindowDrawList()->AddRect(
|
||||
dropAreaMin,
|
||||
dropAreaMax,
|
||||
IM_COL32(96, 106, 122, 180),
|
||||
2.0F
|
||||
0.0F,
|
||||
0,
|
||||
1.0F
|
||||
);
|
||||
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
@ -2843,53 +2880,11 @@ private:
|
||||
}
|
||||
|
||||
RenameObjectId_ = objectId;
|
||||
RenameBuffer_.fill('\0');
|
||||
RenameBuffer_.fill(0);
|
||||
std::snprintf(RenameBuffer_.data(), RenameBuffer_.size(), "%s", object.GetName().c_str());
|
||||
OpenRenamePopup_ = true;
|
||||
}
|
||||
|
||||
void DrawRenamePopup(MetaCoreEditorContext& editorContext) {
|
||||
if (RenameObjectId_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (OpenRenamePopup_) {
|
||||
ImGui::OpenPopup("重命名对象##MetaCoreHierarchy");
|
||||
OpenRenamePopup_ = false;
|
||||
}
|
||||
|
||||
if (!ImGui::BeginPopupModal("重命名对象##MetaCoreHierarchy", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
const bool submittedByEnter = ImGui::InputText(
|
||||
"名称",
|
||||
RenameBuffer_.data(),
|
||||
RenameBuffer_.size(),
|
||||
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue
|
||||
);
|
||||
|
||||
bool closePopup = false;
|
||||
if (submittedByEnter || ImGui::Button("确定", ImVec2(90.0F, 0.0F))) {
|
||||
closePopup = MetaCoreRenameObject(editorContext, RenameObjectId_, RenameBuffer_.data());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("取消", ImVec2(90.0F, 0.0F))) {
|
||||
closePopup = true;
|
||||
}
|
||||
|
||||
if (closePopup) {
|
||||
RenameObjectId_ = 0;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
MetaCoreId RenameObjectId_ = 0;
|
||||
std::array<char, 256> RenameBuffer_{};
|
||||
bool OpenRenamePopup_ = false;
|
||||
@ -2898,7 +2893,7 @@ private:
|
||||
class MetaCoreScenePanelProvider final : public MetaCoreIEditorPanelProvider {
|
||||
public:
|
||||
std::string GetPanelId() const override { return "Scene"; }
|
||||
std::string GetPanelTitle() const override { return "场景"; }
|
||||
std::string GetPanelTitle() const override { return "Scene"; }
|
||||
bool IsOpenByDefault() const override { return true; }
|
||||
|
||||
void DrawPanel(MetaCoreEditorContext&) override {
|
||||
@ -4530,18 +4525,8 @@ void DrawDirectoryTree(
|
||||
if (!MatchesAssetFilter(label) && !HasVisibleChildren(assetDatabaseService, dir)) return;
|
||||
const bool isSelectedDirectory = editorContext.GetSelectedProjectDirectory() == dir;
|
||||
|
||||
if (isSelectedDirectory) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
}
|
||||
|
||||
const bool nodeOpened = ImGui::TreeNodeEx(dir.generic_string().c_str(), ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanFullWidth | (isSelectedDirectory ? ImGuiTreeNodeFlags_Selected : 0), "%s", label.c_str());
|
||||
|
||||
if (isSelectedDirectory) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
if (nodeOpened) {
|
||||
if (ImGui::IsItemClicked()) editorContext.SetSelectedProjectDirectory(dir);
|
||||
for (const auto& d : assetDatabaseService.GetDirectoriesUnder(dir)) {
|
||||
@ -4556,7 +4541,7 @@ void DrawDirectoryTree(
|
||||
class MetaCoreProjectPanelProvider final : public MetaCoreIEditorPanelProvider {
|
||||
public:
|
||||
std::string GetPanelId() const override { return "Project"; }
|
||||
std::string GetPanelTitle() const override { return "项目"; }
|
||||
std::string GetPanelTitle() const override { return "Project"; }
|
||||
bool IsOpenByDefault() const override { return true; }
|
||||
|
||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
||||
@ -4569,8 +4554,8 @@ public:
|
||||
bool openRenamePopup = false;
|
||||
|
||||
ImGui::TextDisabled("%s | %s", assetDatabaseService->GetProjectDescriptor().Name.c_str(), assetDatabaseService->GetProjectDescriptor().RootPath.string().c_str());
|
||||
if (ImGui::Button("刷新")) { MetaCoreHandleReloadAssets(editorContext); assetDatabaseService->Refresh(); }
|
||||
ImGui::InputTextWithHint("##Filter", "过滤资源...", AssetFilterBuffer_.data(), AssetFilterBuffer_.size());
|
||||
|
||||
ImGui::InputTextWithHint("##Filter", "Filter Assets...", AssetFilterBuffer_.data(), AssetFilterBuffer_.size());
|
||||
|
||||
ImGui::Columns(2, "ProjectCols", true);
|
||||
DrawDirectoryTree(editorContext, *assetDatabaseService, "Assets", "Assets");
|
||||
@ -4584,7 +4569,7 @@ public:
|
||||
// 1. 绘制面包屑导航 (Breadcrumbs)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 2));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("路径:");
|
||||
ImGui::TextDisabled("Path:");
|
||||
ImGui::SameLine();
|
||||
|
||||
std::vector<std::filesystem::path> pathSegments;
|
||||
@ -4622,7 +4607,7 @@ public:
|
||||
ImGui::PushItemWidth(100.0f);
|
||||
ImGui::SliderFloat("##IconSize", &IconSize_, 16.0f, 128.0f, "");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("图标大小: %.0f px", IconSize_);
|
||||
ImGui::SetTooltip("Icon Size: %.0f px", IconSize_);
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::PopStyleVar();
|
||||
@ -4684,17 +4669,8 @@ public:
|
||||
isSelected = (editorContext.GetSelectedAsset().Guid == item.Guid);
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
}
|
||||
|
||||
ImGui::PushID(item.IsDirectory ? item.Path.string().c_str() : item.Guid.ToString().c_str());
|
||||
const bool clicked = ImGui::Selectable(labelText.c_str(), isSelected);
|
||||
if (isSelected) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
// 2. 用 ImDrawList 在该行的左侧空白处绘制 10px 精美矢量微型图标 (避免缺失显示为问号)
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
@ -4707,8 +4683,9 @@ public:
|
||||
float h = miniSize * 0.75f;
|
||||
ImVec2 topLeft(miniIconCenter.x - w * 0.5f, miniIconCenter.y - h * 0.5f + h * 0.1f);
|
||||
ImVec2 bottomRight(miniIconCenter.x + w * 0.5f, miniIconCenter.y + h * 0.5f);
|
||||
drawList->AddRectFilled(topLeft, bottomRight, IM_COL32(230, 180, 45, 255), 1.0f);
|
||||
drawList->AddRectFilled(ImVec2(topLeft.x, topLeft.y - h * 0.2f), ImVec2(topLeft.x + w * 0.4f, topLeft.y + h * 0.1f), IM_COL32(190, 145, 30, 255), 0.5f);
|
||||
// 替换亮黄色为 Notion 冷灰色
|
||||
drawList->AddRectFilled(topLeft, bottomRight, IM_COL32(110, 110, 110, 255), 1.0f);
|
||||
drawList->AddRectFilled(ImVec2(topLeft.x, topLeft.y - h * 0.2f), ImVec2(topLeft.x + w * 0.4f, topLeft.y + h * 0.1f), IM_COL32(80, 80, 80, 255), 0.5f);
|
||||
} else if (item.Type == "material") {
|
||||
drawList->AddCircleFilled(miniIconCenter, miniSize * 0.45f, IM_COL32(140, 80, 210, 255), 12);
|
||||
ImVec2 highlightCenter(miniIconCenter.x - miniSize * 0.15f, miniIconCenter.y - miniSize * 0.15f);
|
||||
@ -4847,15 +4824,7 @@ public:
|
||||
const std::string subId = MetaCoreBuildGeneratedResourceSubId(generatedEntry.Kind, generatedEntry.AssetGuid);
|
||||
const bool generatedSelected = editorContext.GetSelectedAsset().Guid == a.Guid && editorContext.GetSelectedAssetSubId() == subId;
|
||||
const std::string generatedLabel = "↳ " + generatedEntry.Name + " [" + generatedEntry.SecondaryText + "]";
|
||||
if (generatedSelected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.173F, 0.365F, 0.529F, 1.0F));
|
||||
}
|
||||
const bool generatedClicked = ImGui::Selectable(generatedLabel.c_str(), generatedSelected);
|
||||
if (generatedSelected) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
if (generatedClicked) {
|
||||
editorContext.SelectAsset(MetaCoreSelectedAssetState{
|
||||
a.Guid,
|
||||
@ -4903,9 +4872,9 @@ public:
|
||||
ImGui::SetCursorPos(startPos);
|
||||
|
||||
if (isSelected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173F, 0.365F, 0.529F, 0.40F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.173F, 0.365F, 0.529F, 0.60F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.173F, 0.365F, 0.529F, 0.80F));
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.200F, 0.200F, 0.200F, 0.50F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.220F, 0.165F, 0.165F, 0.60F));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.270F, 0.180F, 0.180F, 0.80F));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.08f));
|
||||
@ -4922,20 +4891,20 @@ public:
|
||||
float largeSize = IconSize_;
|
||||
|
||||
if (item.IsDirectory) {
|
||||
// 绘制带有浅黄色前盖和深黄色后盖层叠效果的双矩形立体文件夹
|
||||
// 绘制带有浅灰色前盖和深灰色后盖层叠效果的双矩形立体文件夹
|
||||
float w = largeSize * 0.85f;
|
||||
float h = largeSize * 0.65f;
|
||||
ImVec2 topLeft(gridIconCenter.x - w * 0.5f, gridIconCenter.y - h * 0.5f + h * 0.1f);
|
||||
ImVec2 bottomRight(gridIconCenter.x + w * 0.5f, gridIconCenter.y + h * 0.5f);
|
||||
// 绘制文件夹后背板 (略暗)
|
||||
drawList->AddRectFilled(topLeft, bottomRight, IM_COL32(190, 145, 30, 255), 3.0f);
|
||||
// 绘制文件夹后背板 (略暗,改冷灰)
|
||||
drawList->AddRectFilled(topLeft, bottomRight, IM_COL32(80, 80, 80, 255), 3.0f);
|
||||
// 绘制文件夹上方标签页
|
||||
ImVec2 tabTopLeft(topLeft.x, topLeft.y - h * 0.2f);
|
||||
ImVec2 tabBottomRight(topLeft.x + w * 0.4f, topLeft.y + h * 0.1f);
|
||||
drawList->AddRectFilled(tabTopLeft, tabBottomRight, IM_COL32(190, 145, 30, 255), 2.0f);
|
||||
// 绘制文件夹前挡板 (略亮)
|
||||
drawList->AddRectFilled(tabTopLeft, tabBottomRight, IM_COL32(80, 80, 80, 255), 2.0f);
|
||||
// 绘制文件夹前挡板 (略亮,改冷灰)
|
||||
ImVec2 frontTopLeft(topLeft.x, topLeft.y + h * 0.15f);
|
||||
drawList->AddRectFilled(frontTopLeft, bottomRight, IM_COL32(235, 185, 45, 255), 3.0f);
|
||||
drawList->AddRectFilled(frontTopLeft, bottomRight, IM_COL32(110, 110, 110, 255), 3.0f);
|
||||
} else if (item.Type == "material") {
|
||||
// 绘制具有左上角高光球体感的紫色材质圆球
|
||||
float radius = largeSize * 0.4f;
|
||||
@ -5145,19 +5114,19 @@ public:
|
||||
ImGui::Columns(1);
|
||||
|
||||
if (openRenamePopup) {
|
||||
ImGui::OpenPopup("重命名资源");
|
||||
ImGui::OpenPopup("Rename Asset");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("ProjectBlankContextMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||
if (ImGui::MenuItem("刷新")) {
|
||||
if (ImGui::MenuItem("Refresh")) {
|
||||
MetaCoreHandleReloadAssets(editorContext);
|
||||
assetDatabaseService->Refresh();
|
||||
}
|
||||
if (ImGui::MenuItem("新建文件夹")) {
|
||||
if (ImGui::MenuItem("New Folder")) {
|
||||
std::snprintf(NewFolderBuffer_.data(), NewFolderBuffer_.size(), "NewFolder");
|
||||
ImGui::OpenPopup("新建文件夹");
|
||||
ImGui::OpenPopup("New Folder");
|
||||
}
|
||||
if (ImGui::MenuItem("新建材质")) {
|
||||
if (ImGui::MenuItem("New Material")) {
|
||||
CreateNewMaterial(editorContext, *assetDatabaseService);
|
||||
}
|
||||
if (ImGui::MenuItem("New UI Document")) {
|
||||
@ -5185,10 +5154,10 @@ public:
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal("重命名资源", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("请输入新名称:");
|
||||
if (ImGui::BeginPopupModal("Rename Asset", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Enter new name:");
|
||||
ImGui::InputText("##NewName", RenameBuffer_.data(), RenameBuffer_.size());
|
||||
if (ImGui::Button("确定", ImVec2(120, 0))) {
|
||||
if (ImGui::Button("OK", ImVec2(120, 0))) {
|
||||
std::string newName(RenameBuffer_.data());
|
||||
if (!newName.empty()) {
|
||||
(void)assetDatabaseService->RenamePath(PendingRenamePath_, newName);
|
||||
@ -5196,16 +5165,16 @@ public:
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("取消", ImVec2(120, 0))) {
|
||||
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal("新建文件夹", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("请输入文件夹名称:");
|
||||
if (ImGui::BeginPopupModal("New Folder", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Enter folder name:");
|
||||
ImGui::InputText("##FolderName", NewFolderBuffer_.data(), NewFolderBuffer_.size());
|
||||
if (ImGui::Button("确定", ImVec2(120, 0))) {
|
||||
if (ImGui::Button("OK", ImVec2(120, 0))) {
|
||||
std::string folderName(NewFolderBuffer_.data());
|
||||
if (!folderName.empty()) {
|
||||
std::filesystem::path newDirPath = assetDatabaseService->GetProjectDescriptor().RootPath / selectedDirectory / folderName;
|
||||
@ -5216,7 +5185,7 @@ public:
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("取消", ImVec2(120, 0))) {
|
||||
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
@ -5227,7 +5196,7 @@ public:
|
||||
class MetaCoreConsolePanelProvider final : public MetaCoreIEditorPanelProvider {
|
||||
public:
|
||||
std::string GetPanelId() const override { return "Console"; }
|
||||
std::string GetPanelTitle() const override { return "控制台"; }
|
||||
std::string GetPanelTitle() const override { return "Console"; }
|
||||
bool IsOpenByDefault() const override { return true; }
|
||||
|
||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
||||
@ -5290,13 +5259,15 @@ public:
|
||||
ImGui::Separator();
|
||||
|
||||
// 2. 日志列表区域
|
||||
// 为下方详细文本保留 85px 高度
|
||||
float availHeight = ImGui::GetContentRegionAvail().y - 85.0f;
|
||||
if (availHeight < 50.0f) {
|
||||
availHeight = 50.0f;
|
||||
static float ConsoleDetailHeight = 100.0f;
|
||||
float totalHeight = ImGui::GetContentRegionAvail().y;
|
||||
float splitterHeight = 4.0f;
|
||||
float listHeight = totalHeight - ConsoleDetailHeight - splitterHeight;
|
||||
if (listHeight < 50.0f) {
|
||||
listHeight = 50.0f;
|
||||
}
|
||||
|
||||
if (ImGui::BeginChild("LogList", ImVec2(0, availHeight), true)) {
|
||||
if (ImGui::BeginChild("LogList", ImVec2(0, listHeight), true)) {
|
||||
struct DisplayEntry {
|
||||
size_t OriginalIndex = 0;
|
||||
MetaCoreLogLevel Level = MetaCoreLogLevel::Info;
|
||||
@ -5394,9 +5365,22 @@ public:
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
// 可拖动高度调节栏 Splitter
|
||||
ImGui::Separator();
|
||||
ImGui::InvisibleButton("##ConsoleSplitter", ImVec2(-1, splitterHeight));
|
||||
if (ImGui::IsItemActive()) {
|
||||
float dy = ImGui::GetMouseDragDelta(0).y;
|
||||
if (std::abs(dy) > 0.5f) {
|
||||
ConsoleDetailHeight = std::clamp(ConsoleDetailHeight - dy, 40.0f, totalHeight - 60.0f);
|
||||
ImGui::ResetMouseDragDelta(0);
|
||||
}
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
// 3. 详细展示区
|
||||
// 3. 详细展示区 (只读 InputTextMultiline 支持 Ctrl+C 复制)
|
||||
if (ImGui::BeginChild("LogDetail", ImVec2(0, 0), true)) {
|
||||
if (ConsoleSelectedEntryIdx_ >= 0 && ConsoleSelectedEntryIdx_ < static_cast<int>(logService.GetEntries().size())) {
|
||||
const auto& entry = logService.GetEntries()[ConsoleSelectedEntryIdx_];
|
||||
@ -5407,12 +5391,24 @@ public:
|
||||
txtColor = ImVec4(0.95f, 0.35f, 0.30f, 1.0f);
|
||||
}
|
||||
|
||||
ImGui::TextColored(txtColor, "[%s]", entry.Category.c_str());
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.85f, 0.85f, 0.85f, 1.0f));
|
||||
ImGui::TextWrapped("%s", entry.Message.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
std::string logDetailText = "[" + entry.Category + "] " + entry.Message;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, txtColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
|
||||
|
||||
ImGui::InputTextMultiline(
|
||||
"##ConsoleDetailText",
|
||||
const_cast<char*>(logDetailText.c_str()),
|
||||
logDetailText.size() + 1,
|
||||
ImVec2(-1, -1),
|
||||
ImGuiInputTextFlags_ReadOnly
|
||||
);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor(3);
|
||||
} else {
|
||||
ImGui::TextDisabled("选择任意日志项以查看详细信息。");
|
||||
ImGui::TextDisabled("Select a log entry to view details.");
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
@ -6131,10 +6127,7 @@ public:
|
||||
|
||||
ImGui::Text("Platform");
|
||||
if (ImGui::BeginChild("PlatformList", ImVec2(0, 180.0f), true)) {
|
||||
// Unity 风格的高亮单选列表
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173f, 0.365f, 0.529f, 0.8f));
|
||||
ImGui::Selectable("💻 PC, Mac & Linux", true);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Selectable("📱 Android", false);
|
||||
ImGui::Selectable("🍏 iOS", false);
|
||||
@ -6644,7 +6637,7 @@ bool MetaCoreTransformInspectorDrawer::GScaleLocked = true;
|
||||
class MetaCoreInspectorPanelProvider final : public MetaCoreIEditorPanelProvider {
|
||||
public:
|
||||
std::string GetPanelId() const override { return "Inspector"; }
|
||||
std::string GetPanelTitle() const override { return "检查器"; }
|
||||
std::string GetPanelTitle() const override { return "Inspector"; }
|
||||
bool IsOpenByDefault() const override { return true; }
|
||||
|
||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
||||
@ -6660,14 +6653,14 @@ public:
|
||||
MetaCoreDrawAssetInspector(editorContext, *assetDatabaseService, *packageService, *reflectionRegistry);
|
||||
}
|
||||
} else {
|
||||
ImGui::TextUnformatted("当前没有选中对象。");
|
||||
ImGui::TextUnformatted("No object selected.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
|
||||
if (!selectedObject) {
|
||||
ImGui::TextUnformatted("当前没有可编辑的主对象。");
|
||||
ImGui::TextUnformatted("No active object editable.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6702,7 +6695,7 @@ public:
|
||||
if (!hasActiveProp) {
|
||||
defaultActiveState = activeState;
|
||||
}
|
||||
(void)editorContext.ExecuteSnapshotCommand("修改对象激活状态", [&]() {
|
||||
(void)editorContext.ExecuteSnapshotCommand("Modify Active State", [&]() {
|
||||
bool anyChanged = false;
|
||||
for (const MetaCoreId selectedId : editorContext.GetSelectedObjectIds()) {
|
||||
MetaCoreGameObject obj = editorContext.GetScene().FindGameObject(selectedId);
|
||||
@ -6779,7 +6772,7 @@ public:
|
||||
ImGui::SameLine();
|
||||
|
||||
// 扁平化名称输入框样式,移除突兀的蓝色边框,并与 Static 控件完美对齐,使用全局 Infernux Theme 配色
|
||||
float staticControlWidth = 85.0F; // "静态的" 文字及 checkbox 的总估算宽度
|
||||
float staticControlWidth = 85.0F; // "Static" 文字及 checkbox 的总估算宽度
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.165F, 0.165F, 0.165F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);
|
||||
@ -6794,7 +6787,7 @@ public:
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - staticControlWidth);
|
||||
static bool isStatic = false;
|
||||
ImGui::TextDisabled("静态的"); ImGui::SameLine();
|
||||
ImGui::TextDisabled("Static"); ImGui::SameLine();
|
||||
ImGui::Checkbox("##Static", &isStatic);
|
||||
|
||||
// Tag and Layer
|
||||
@ -6810,12 +6803,12 @@ public:
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0F, 0.5F)); // 文本左对齐
|
||||
|
||||
ImGui::Text("标签"); ImGui::SameLine();
|
||||
ImGui::Text("Tag"); ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::Button("Untagged ▾##Tag", ImVec2(-1, 0))) { /* Menu */ }
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("图层"); ImGui::SameLine();
|
||||
ImGui::Text("Layer"); ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::Button("Default ▾##Layer", ImVec2(-1, 0))) { /* Menu */ }
|
||||
|
||||
|
||||
@ -478,66 +478,96 @@ void MetaCoreApplyEditorStyle() {
|
||||
style.WindowBorderSize = 1.0F;
|
||||
style.ChildBorderSize = 1.0F;
|
||||
style.PopupBorderSize = 1.0F;
|
||||
style.FrameBorderSize = 0.0F;
|
||||
style.TabBorderSize = 1.0F;
|
||||
style.FrameBorderSize = 1.0F;
|
||||
style.TabBorderSize = 0.0F;
|
||||
style.TabBarBorderSize = 1.0F;
|
||||
|
||||
style.WindowPadding = ImVec2(4.0F, 4.0F);
|
||||
style.FramePadding = ImVec2(4.0F, 2.0F);
|
||||
style.ItemSpacing = ImVec2(4.0F, 4.0F);
|
||||
style.ItemInnerSpacing = ImVec2(4.0F, 4.0F);
|
||||
style.WindowPadding = ImVec2(10.0F, 10.0F);
|
||||
style.FramePadding = ImVec2(8.0F, 3.0F);
|
||||
style.CellPadding = ImVec2(4.0F, 4.0F);
|
||||
style.ItemSpacing = ImVec2(8.0F, 6.0F);
|
||||
style.ItemInnerSpacing = ImVec2(6.0F, 4.0F);
|
||||
style.IndentSpacing = 18.0F;
|
||||
style.ScrollbarSize = 8.0F;
|
||||
style.GrabMinSize = 6.0F;
|
||||
|
||||
// 1:1 对齐 Infernux Unity 风格暗色调与暗红高亮主题
|
||||
style.Colors[ImGuiCol_Text] = ImVec4(0.84F, 0.84F, 0.84F, 1.0F); // 主文本色
|
||||
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.40F, 0.40F, 0.40F, 1.0F); // 禁用文本色
|
||||
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.22F, 0.22F, 0.22F, 1.0F); // 窗口背景 (Unity #383838)
|
||||
style.Colors[ImGuiCol_ChildBg] = ImVec4(0.00F, 0.00F, 0.00F, 0.0F); // 子窗口背景 (透明)
|
||||
style.Colors[ImGuiCol_PopupBg] = ImVec4(0.24F, 0.24F, 0.24F, 0.96F); // 弹出菜单背景 (Unity #3E3E3E)
|
||||
style.Colors[ImGuiCol_Border] = ImVec4(0.10F, 0.10F, 0.10F, 1.0F); // 边框颜色 (Unity #1A1A1A)
|
||||
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00F, 0.00F, 0.00F, 0.0F); // 边框阴影 (无)
|
||||
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.165F, 0.165F, 0.165F, 1.0F); // 输入框/滑动条背景 (Unity #2A2A2A)
|
||||
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.20F, 0.20F, 0.20F, 1.0F); // 输入框悬停
|
||||
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.24F, 0.17F, 0.17F, 1.0F); // 输入框激活 (带微弱红调)
|
||||
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.16F, 0.16F, 0.16F, 1.0F); // 标题栏背景
|
||||
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.16F, 0.16F, 0.16F, 1.0F); // 活动标题栏背景
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.16F, 0.16F, 0.16F, 0.5F); // 折叠标题栏背景
|
||||
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.16F, 0.16F, 0.16F, 1.0F); // 菜单栏背景
|
||||
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.13F, 0.13F, 0.13F, 1.0F); // 滚动条背景
|
||||
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.25F, 0.25F, 0.25F, 1.0F); // 滚动条滑块 (Unity #404040)
|
||||
style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.30F, 0.25F, 0.25F, 1.0F); // 滚动条悬停 (微红)
|
||||
style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.35F, 0.22F, 0.22F, 1.0F); // 滚动条激活
|
||||
style.Colors[ImGuiCol_CheckMark] = ImVec4(0.922F, 0.341F, 0.341F, 1.0F); // 勾选标记 (强调红 #EB5757)
|
||||
style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.25F, 0.25F, 0.25F, 1.0F); // 滑动条滑块
|
||||
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.35F, 0.22F, 0.22F, 1.0F); // 滑动条激活
|
||||
style.Colors[ImGuiCol_Button] = ImVec4(0.25F, 0.25F, 0.25F, 1.0F); // 普通按钮背景
|
||||
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.30F, 0.25F, 0.25F, 1.0F); // 按钮悬停 (微红调)
|
||||
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.35F, 0.22F, 0.22F, 1.0F); // 按钮激活 (深红调)
|
||||
style.Colors[ImGuiCol_Header] = ImVec4(0.235F, 0.235F, 0.235F, 1.0F); // 折叠头背景 (Unity #3C3C3C)
|
||||
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.28F, 0.24F, 0.24F, 1.0F); // 悬停 (微红调)
|
||||
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.32F, 0.25F, 0.25F, 1.0F); // 激活 (深红调)
|
||||
style.Colors[ImGuiCol_Separator] = ImVec4(0.10F, 0.10F, 0.10F, 1.0F); // 分割线 (同边框)
|
||||
style.Colors[ImGuiCol_SeparatorHovered] = ImVec4(0.35F, 0.25F, 0.25F, 0.6F); // 悬停分割线 (红暖色)
|
||||
style.Colors[ImGuiCol_SeparatorActive] = ImVec4(0.40F, 0.28F, 0.28F, 0.8F); // 激活分割线 (深红)
|
||||
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(0.16F, 0.16F, 0.16F, 0.5F); // 调整手柄
|
||||
style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.28F, 0.24F, 0.24F, 0.6F);
|
||||
style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.32F, 0.25F, 0.25F, 0.9F);
|
||||
style.Colors[ImGuiCol_Tab] = ImVec4(0.18F, 0.18F, 0.18F, 1.0F); // 标签页未选中 (暗灰色)
|
||||
style.Colors[ImGuiCol_TabHovered] = ImVec4(0.28F, 0.24F, 0.24F, 1.0F); // 标签悬停 (微红调)
|
||||
style.Colors[ImGuiCol_TabActive] = ImVec4(0.22F, 0.22F, 0.22F, 1.0F); // 活动标签 (同窗口底色)
|
||||
style.Colors[ImGuiCol_TabUnfocused] = ImVec4(0.18F, 0.18F, 0.18F, 1.0F);
|
||||
style.Colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.22F, 0.22F, 0.22F, 1.0F);
|
||||
style.Colors[ImGuiCol_DockingPreview] = ImVec4(0.173F, 0.365F, 0.529F, 0.7F); // 停靠预览区 (Unity 蓝色半透明)
|
||||
style.Colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.18F, 0.18F, 0.18F, 1.0F);
|
||||
style.Colors[ImGuiCol_TableHeaderBg] = ImVec4(0.16F, 0.16F, 0.16F, 1.0F); // 表格头背景
|
||||
style.Colors[ImGuiCol_TableBorderStrong] = ImVec4(0.10F, 0.10F, 0.10F, 1.0F); // 强边框
|
||||
style.Colors[ImGuiCol_TableBorderLight] = ImVec4(0.15F, 0.15F, 0.15F, 1.0F); // 弱边框
|
||||
style.Colors[ImGuiCol_TableRowBg] = ImVec4(0.00F, 0.00F, 0.00F, 0.0F);
|
||||
style.Colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00F, 1.00F, 1.00F, 0.03F); // 斑马纹交替底色
|
||||
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.173F, 0.365F, 0.529F, 1.0F); // 选中文本背景 (Unity 经典选色 #2C5D87)
|
||||
style.Colors[ImGuiCol_DragDropTarget] = ImVec4(0.173F, 0.365F, 0.529F, 0.9F); // 拖放目标高亮 (带选蓝)
|
||||
style.Colors[ImGuiCol_NavHighlight] = ImVec4(0.922F, 0.341F, 0.341F, 1.0F); // 导航高亮
|
||||
style.Colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00F, 1.00F, 1.00F, 0.7F);
|
||||
style.Colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80F, 0.80F, 0.80F, 0.2F);
|
||||
style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00F, 0.00F, 0.00F, 0.6F); // 模态遮罩
|
||||
// Anti-aliasing
|
||||
style.AntiAliasedLines = true;
|
||||
style.AntiAliasedFill = true;
|
||||
|
||||
// 1:1 对齐 Infernux Notion 风格暗色调与亮红高亮主题
|
||||
constexpr float AR = 0.922F; // #EB5757 Accent color R
|
||||
constexpr float AG = 0.341F; // #EB5757 Accent color G
|
||||
constexpr float AB = 0.341F; // #EB5757 Accent color B
|
||||
|
||||
style.Colors[ImGuiCol_Text] = ImVec4(0.812F, 0.812F, 0.812F, 1.00F); // #CFCFCF
|
||||
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.333F, 0.333F, 0.333F, 1.00F); // #555555
|
||||
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.200F, 0.200F, 0.200F, 0.60F); // #333333
|
||||
|
||||
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.098F, 0.098F, 0.098F, 1.00F); // #191919
|
||||
style.Colors[ImGuiCol_ChildBg] = ImVec4(0.125F, 0.125F, 0.125F, 1.00F); // #202020
|
||||
style.Colors[ImGuiCol_PopupBg] = ImVec4(0.125F, 0.125F, 0.125F, 0.98F); // #202020
|
||||
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.125F, 0.125F, 0.125F, 1.00F); // #202020
|
||||
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.165F, 0.165F, 0.165F, 1.00F); // #2A2A2A
|
||||
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.240F, 0.170F, 0.170F, 1.00F); // 激活时偏红
|
||||
|
||||
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.098F, 0.098F, 0.098F, 1.00F);
|
||||
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.098F, 0.098F, 0.098F, 1.00F);
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.098F, 0.098F, 0.098F, 0.75F);
|
||||
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.098F, 0.098F, 0.098F, 1.00F);
|
||||
|
||||
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.098F, 0.098F, 0.098F, 0.00F);
|
||||
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.184F, 0.184F, 0.184F, 1.00F);
|
||||
style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.333F, 0.333F, 0.333F, 1.00F);
|
||||
style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.439F, 0.439F, 0.439F, 1.00F);
|
||||
|
||||
style.Colors[ImGuiCol_CheckMark] = ImVec4(AR, AG, AB, 1.00F); // #EB5757
|
||||
style.Colors[ImGuiCol_SliderGrab] = ImVec4(AR, AG, AB, 0.88F); // #EB5757
|
||||
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(AR, AG, AB, 1.00F); // #EB5757
|
||||
style.Colors[ImGuiCol_NavHighlight] = ImVec4(0.00F, 0.00F, 0.00F, 0.00F);
|
||||
|
||||
style.Colors[ImGuiCol_Button] = ImVec4(0.165F, 0.165F, 0.165F, 1.00F); // #2A2A2A
|
||||
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.220F, 0.165F, 0.165F, 1.00F);
|
||||
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.270F, 0.180F, 0.180F, 1.00F);
|
||||
|
||||
style.Colors[ImGuiCol_Header] = ImVec4(0.200F, 0.200F, 0.200F, 1.00F); // #333333
|
||||
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.200F, 0.160F, 0.160F, 1.00F);
|
||||
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.240F, 0.170F, 0.170F, 1.00F);
|
||||
|
||||
style.Colors[ImGuiCol_Border] = ImVec4(0.184F, 0.184F, 0.184F, 1.00F);
|
||||
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00F, 0.00F, 0.00F, 0.00F);
|
||||
style.Colors[ImGuiCol_Separator] = ImVec4(0.184F, 0.184F, 0.184F, 1.00F);
|
||||
style.Colors[ImGuiCol_SeparatorHovered] = ImVec4(AR, AG, AB, 0.60F); // #EB5757
|
||||
style.Colors[ImGuiCol_SeparatorActive] = ImVec4(AR, AG, AB, 0.80F); // #EB5757
|
||||
|
||||
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(0.00F, 0.00F, 0.00F, 0.00F);
|
||||
style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(AR, AG, AB, 0.30F);
|
||||
style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(AR, AG, AB, 0.50F);
|
||||
|
||||
style.Colors[ImGuiCol_Tab] = ImVec4(0.098F, 0.098F, 0.098F, 1.00F); // #191919
|
||||
style.Colors[ImGuiCol_TabHovered] = ImVec4(0.165F, 0.165F, 0.165F, 1.00F); // #2A2A2A
|
||||
style.Colors[ImGuiCol_TabActive] = ImVec4(0.125F, 0.125F, 0.125F, 1.00F); // #202020
|
||||
style.Colors[ImGuiCol_TabUnfocused] = ImVec4(0.098F, 0.098F, 0.098F, 1.00F);
|
||||
style.Colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.125F, 0.125F, 0.125F, 1.00F);
|
||||
|
||||
#ifdef ImGuiCol_TabSelectedOverline
|
||||
style.Colors[ImGuiCol_TabSelectedOverline] = ImVec4(AR, AG, AB, 1.00F); // #EB5757
|
||||
#endif
|
||||
#ifdef ImGuiCol_TabDimmedSelectedOverline
|
||||
style.Colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(AR, AG, AB, 0.60F);
|
||||
#endif
|
||||
|
||||
style.Colors[ImGuiCol_DockingPreview] = ImVec4(AR, AG, AB, 0.25F); // #EB5757
|
||||
style.Colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.060F, 0.060F, 0.060F, 1.00F);
|
||||
|
||||
style.Colors[ImGuiCol_TableHeaderBg] = ImVec4(0.125F, 0.125F, 0.125F, 1.00F);
|
||||
style.Colors[ImGuiCol_TableBorderStrong] = ImVec4(0.184F, 0.184F, 0.184F, 1.00F);
|
||||
style.Colors[ImGuiCol_TableBorderLight] = ImVec4(0.149F, 0.149F, 0.149F, 1.00F);
|
||||
style.Colors[ImGuiCol_TableRowBg] = ImVec4(0.00F, 0.00F, 0.00F, 0.00F);
|
||||
style.Colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00F, 1.00F, 1.00F, 0.02F);
|
||||
|
||||
style.Colors[ImGuiCol_DragDropTarget] = ImVec4(1.00F, 1.00F, 1.00F, 1.00F);
|
||||
style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00F, 0.00F, 0.00F, 0.56F);
|
||||
|
||||
// 优化 ImGuizmo 属性,使其看起来更高端并移除了舱口剖面线(虚线黑影)
|
||||
ImGuizmo::Style& gizmoStyle = ImGuizmo::GetStyle();
|
||||
@ -1009,9 +1039,9 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
|
||||
DrawSceneViewWindow();
|
||||
DrawGameViewWindow();
|
||||
DrawPlaceholderDockWindow("UI Editor###ui_editor", "UI Editor");
|
||||
DrawPlaceholderDockWindow("Anim Clip 2D###animclip2d_editor", "Animation Clip 2D");
|
||||
DrawPlaceholderDockWindow("Anim FSM###animfsm_editor", "Animation FSM");
|
||||
DrawPlaceholderDockWindow("UI编辑器###ui_editor", "UI编辑器");
|
||||
DrawPlaceholderDockWindow("2D动画片段编辑器###animclip2d_editor", "2D动画片段编辑器");
|
||||
DrawPlaceholderDockWindow("动画状态机编辑器###animfsm_editor", "动画状态机编辑器");
|
||||
ApplyPendingDockTabSelections();
|
||||
DrawEditorStatusBar();
|
||||
}
|
||||
@ -1024,31 +1054,66 @@ void MetaCoreEditorApp::DrawEditorToolbar() {
|
||||
ImGuiWindowFlags_NoScrollWithMouse |
|
||||
ImGuiWindowFlags_NoSavedSettings;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0F, 4.0F));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8.0F, 4.0F));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.0F, 4.0F)); // TOOLBAR_WIN_PAD
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0F, 4.0F)); // TOOLBAR_FRAME_PAD
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0F, 4.0F)); // TOOLBAR_ITEM_SPC
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0F); // TOOLBAR_FRAME_RND
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F); // TOOLBAR_FRAME_BRD
|
||||
|
||||
if (ImGui::Begin("Toolbar###toolbar", nullptr, toolbarWindowFlags)) {
|
||||
auto drawButton = [](const char* label, bool selected, bool enabled = true) {
|
||||
if (selected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.20F, 0.34F, 0.58F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25F, 0.40F, 0.68F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.16F, 0.29F, 0.50F, 1.0F));
|
||||
} else if (!enabled) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.16F, 0.16F, 0.16F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.16F, 0.16F, 0.16F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.16F, 0.16F, 0.16F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.45F, 0.45F, 0.45F, 1.0F));
|
||||
}
|
||||
const bool clicked = enabled && ImGui::Button(label, ImVec2(0.0F, 24.0F));
|
||||
if (selected) {
|
||||
ImGui::PopStyleColor(3);
|
||||
} else if (!enabled) {
|
||||
ImGui::PopStyleColor(4);
|
||||
enum ButtonStyle {
|
||||
Idle = 0,
|
||||
PlayActive,
|
||||
PauseActive,
|
||||
Disabled
|
||||
};
|
||||
|
||||
auto drawButton = [](const char* label, ButtonStyle style) {
|
||||
ImVec4 baseColor;
|
||||
ImVec4 hoverColor;
|
||||
ImVec4 activeColor;
|
||||
ImVec4 textColor = ImVec4(0.812F, 0.812F, 0.812F, 1.00F);
|
||||
bool enabled = true;
|
||||
|
||||
switch (style) {
|
||||
case PlayActive:
|
||||
baseColor = ImVec4(0.20F, 0.45F, 0.30F, 1.00F); // PLAY_ACTIVE
|
||||
hoverColor = ImVec4(0.26F, 0.51F, 0.36F, 1.00F);
|
||||
activeColor = ImVec4(0.32F, 0.57F, 0.42F, 1.00F);
|
||||
break;
|
||||
case PauseActive:
|
||||
baseColor = ImVec4(0.50F, 0.40F, 0.15F, 1.00F); // PAUSE_ACTIVE
|
||||
hoverColor = ImVec4(0.56F, 0.46F, 0.21F, 1.00F);
|
||||
activeColor = ImVec4(0.62F, 0.52F, 0.27F, 1.00F);
|
||||
break;
|
||||
case Disabled:
|
||||
baseColor = ImVec4(0.15F, 0.15F, 0.15F, 0.40F); // BTN_DISABLED
|
||||
hoverColor = ImVec4(0.15F, 0.15F, 0.15F, 0.40F);
|
||||
activeColor = ImVec4(0.15F, 0.15F, 0.15F, 0.40F);
|
||||
textColor = ImVec4(0.40F, 0.40F, 0.40F, 1.00F);
|
||||
enabled = false;
|
||||
break;
|
||||
case Idle:
|
||||
default:
|
||||
baseColor = ImVec4(0.18F, 0.18F, 0.18F, 1.00F); // BTN_IDLE
|
||||
hoverColor = ImVec4(0.24F, 0.24F, 0.24F, 1.00F);
|
||||
activeColor = ImVec4(0.30F, 0.30F, 0.30F, 1.00F);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, baseColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, hoverColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, activeColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, textColor);
|
||||
|
||||
const bool clicked = enabled && ImGui::Button(label, ImVec2(52.0F, 24.0F));
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
return clicked;
|
||||
};
|
||||
|
||||
const float windowWidth = ImGui::GetWindowWidth();
|
||||
const float playControlsWidth = 300.0F;
|
||||
const float playControlsWidth = 160.0F; // 52 * 3 + 2 * 2 = 160
|
||||
ImGui::SetCursorPosX(std::max(8.0F, (windowWidth - playControlsWidth) * 0.5F));
|
||||
|
||||
if (const auto playModeService = ModuleRegistry_.ResolveService<MetaCoreIPlayModeService>();
|
||||
@ -1056,109 +1121,170 @@ void MetaCoreEditorApp::DrawEditorToolbar() {
|
||||
const MetaCorePlayModeState playState = playModeService->GetState();
|
||||
const bool isPlaying = playState == MetaCorePlayModeState::Playing || playState == MetaCorePlayModeState::Paused;
|
||||
const bool isPaused = playState == MetaCorePlayModeState::Paused;
|
||||
if (playState == MetaCorePlayModeState::Edit) {
|
||||
if (drawButton("Play", false)) {
|
||||
(void)playModeService->EnterPlayMode(*EditorContext_);
|
||||
}
|
||||
} else {
|
||||
if (drawButton("Stop", true)) {
|
||||
|
||||
// 1. Play / Stop
|
||||
if (isPlaying) {
|
||||
if (drawButton("停止", PlayActive)) {
|
||||
(void)playModeService->ExitPlayMode(*EditorContext_);
|
||||
}
|
||||
} else {
|
||||
if (drawButton("播放", Idle)) {
|
||||
(void)playModeService->EnterPlayMode(*EditorContext_);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine(0.0F, 4.0F);
|
||||
if (isPaused) {
|
||||
if (drawButton("Resume", true)) {
|
||||
|
||||
ImGui::SameLine(0.0F, 2.0F);
|
||||
|
||||
// 2. Pause / Resume
|
||||
if (!isPlaying) {
|
||||
(void)drawButton("暂停", Disabled);
|
||||
} else if (isPaused) {
|
||||
if (drawButton("继续", PauseActive)) {
|
||||
(void)playModeService->ResumePlayMode(*EditorContext_);
|
||||
}
|
||||
} else if (drawButton("Pause", false, isPlaying)) {
|
||||
(void)playModeService->PausePlayMode(*EditorContext_);
|
||||
} else {
|
||||
if (drawButton("暂停", Idle)) {
|
||||
(void)playModeService->PausePlayMode(*EditorContext_);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine(0.0F, 4.0F);
|
||||
if (drawButton("Step", false, isPaused)) {
|
||||
(void)playModeService->StepPlayMode(*EditorContext_, 1.0F / 60.0F);
|
||||
|
||||
ImGui::SameLine(0.0F, 2.0F);
|
||||
|
||||
// 3. Step
|
||||
if (isPaused) {
|
||||
if (drawButton("步进", Idle)) {
|
||||
(void)playModeService->StepPlayMode(*EditorContext_, 1.0F / 60.0F);
|
||||
}
|
||||
} else {
|
||||
(void)drawButton("步进", Disabled);
|
||||
}
|
||||
|
||||
if (isPlaying) {
|
||||
ImGui::SameLine(0.0F, 8.0F);
|
||||
ImGui::Text("Time %.2fs", playModeService->GetElapsedPlayTimeSeconds());
|
||||
ImGui::Text("已运行 %.2fs", playModeService->GetElapsedPlayTimeSeconds());
|
||||
}
|
||||
} else {
|
||||
(void)drawButton("Play", false, false);
|
||||
ImGui::SameLine(0.0F, 4.0F);
|
||||
(void)drawButton("Pause", false, false);
|
||||
ImGui::SameLine(0.0F, 4.0F);
|
||||
(void)drawButton("Step", false, false);
|
||||
(void)drawButton("播放", Disabled);
|
||||
ImGui::SameLine(0.0F, 2.0F);
|
||||
(void)drawButton("暂停", Disabled);
|
||||
ImGui::SameLine(0.0F, 2.0F);
|
||||
(void)drawButton("步进", Disabled);
|
||||
}
|
||||
|
||||
constexpr float rightControlsWidth = 180.0F;
|
||||
ImGui::SameLine(std::max(8.0F, windowWidth - rightControlsWidth));
|
||||
if (ImGui::Button("Gizmos", ImVec2(78.0F, 24.0F))) {
|
||||
// 右对齐下拉菜单
|
||||
const float rightX = windowWidth - 160.0F;
|
||||
if (rightX > 250.0F) {
|
||||
ImGui::SameLine(rightX);
|
||||
}
|
||||
|
||||
// Gizmos 下拉菜单 (对齐 Infernux "Gizmos v")
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0F, 0.0F, 0.0F, 0.0F)); // 幽灵按钮风格
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0F, 1.0F, 1.0F, 0.06F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0F, 1.0F, 1.0F, 0.10F));
|
||||
if (ImGui::Button("Gizmos v", ImVec2(72.0F, 24.0F))) {
|
||||
ImGui::OpenPopup("MetaCoreToolbarGizmosPopup");
|
||||
}
|
||||
ImGui::SameLine(0.0F, 4.0F);
|
||||
if (ImGui::Button("Camera", ImVec2(78.0F, 24.0F))) {
|
||||
if (ImGui::Button("Camera v", ImVec2(72.0F, 24.0F))) {
|
||||
ImGui::OpenPopup("MetaCoreToolbarCameraPopup");
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (ImGui::BeginPopup("MetaCoreToolbarGizmosPopup")) {
|
||||
if (ImGui::MenuItem("View", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::None)) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0F, 8.0F)); // POPUP_WIN_PAD
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0F, 4.0F)); // POPUP_ITEM_SPC
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 3.0F)); // POPUP_FRAME_PAD
|
||||
|
||||
ImGui::Dummy(ImVec2(180.0F, 0.0F));
|
||||
ImGui::TextUnformatted("Gizmos 设置");
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0.0F, 4.0F));
|
||||
|
||||
if (ImGui::MenuItem("视图", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::None)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::None);
|
||||
}
|
||||
if (ImGui::MenuItem("Move", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Translate)) {
|
||||
if (ImGui::MenuItem("位移", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Translate)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Translate);
|
||||
}
|
||||
if (ImGui::MenuItem("Rotate", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Rotate)) {
|
||||
if (ImGui::MenuItem("旋转", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Rotate)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Rotate);
|
||||
}
|
||||
if (ImGui::MenuItem("Scale", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Scale)) {
|
||||
if (ImGui::MenuItem("缩放", nullptr, EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Scale)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Scale);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Local", nullptr, EditorContext_->GetGizmoMode() == MetaCoreGizmoMode::Local)) {
|
||||
if (ImGui::MenuItem("局部", nullptr, EditorContext_->GetGizmoMode() == MetaCoreGizmoMode::Local)) {
|
||||
EditorContext_->SetGizmoMode(MetaCoreGizmoMode::Local);
|
||||
}
|
||||
if (ImGui::MenuItem("Global", nullptr, EditorContext_->GetGizmoMode() == MetaCoreGizmoMode::Global)) {
|
||||
if (ImGui::MenuItem("全局", nullptr, EditorContext_->GetGizmoMode() == MetaCoreGizmoMode::Global)) {
|
||||
EditorContext_->SetGizmoMode(MetaCoreGizmoMode::Global);
|
||||
}
|
||||
ImGui::Separator();
|
||||
bool snapEnabled = EditorContext_->GetGizmoSnapSettings().Enabled;
|
||||
if (ImGui::Checkbox("Snap", &snapEnabled)) {
|
||||
if (ImGui::Checkbox("吸附", &snapEnabled)) {
|
||||
EditorContext_->SetGizmoSnapEnabled(snapEnabled);
|
||||
}
|
||||
bool showGrid = EditorContext_->GetShowViewportGrid();
|
||||
if (ImGui::Checkbox("Show Grid", &showGrid)) {
|
||||
if (ImGui::Checkbox("显示网格", &showGrid)) {
|
||||
EditorContext_->SetShowViewportGrid(showGrid);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("MetaCoreToolbarCameraPopup")) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0F, 8.0F)); // POPUP_WIN_PAD
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0F, 4.0F)); // POPUP_ITEM_SPC
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 3.0F)); // POPUP_FRAME_PAD
|
||||
|
||||
MetaCoreSceneView sceneView = EditorContext_->GetCameraController().BuildSceneView();
|
||||
ImGui::TextUnformatted("Scene Camera");
|
||||
ImGui::Dummy(ImVec2(340.0F, 0.0F));
|
||||
ImGui::TextUnformatted("场景相机");
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0.0F, 4.0F));
|
||||
|
||||
bool cameraChanged = false;
|
||||
float fieldOfViewDegrees = sceneView.VerticalFieldOfViewDegrees;
|
||||
ImGui::SetNextItemWidth(180.0F);
|
||||
if (ImGui::SliderFloat("FOV", &fieldOfViewDegrees, 20.0F, 110.0F, "%.1f")) {
|
||||
ImGui::TextUnformatted("视野角度");
|
||||
ImGui::SameLine(130.0F);
|
||||
ImGui::SetNextItemWidth(120.0F);
|
||||
if (ImGui::SliderFloat("##fov_slider", &fieldOfViewDegrees, 10.0F, 120.0F, "%.1F")) {
|
||||
sceneView.VerticalFieldOfViewDegrees = fieldOfViewDegrees;
|
||||
cameraChanged = true;
|
||||
}
|
||||
ImGui::SetNextItemWidth(220.0F);
|
||||
if (ImGui::DragFloat3("Position", glm::value_ptr(sceneView.CameraPosition), 0.05F)) {
|
||||
ImGui::SameLine(0.0F, 6.0F);
|
||||
ImGui::SetNextItemWidth(72.0F);
|
||||
if (ImGui::InputFloat("##fov_input", &fieldOfViewDegrees, 1.0F, 10.0F, "%.1f")) {
|
||||
sceneView.VerticalFieldOfViewDegrees = std::clamp(fieldOfViewDegrees, 10.0F, 120.0F);
|
||||
cameraChanged = true;
|
||||
}
|
||||
ImGui::SetNextItemWidth(220.0F);
|
||||
if (ImGui::DragFloat3("Target", glm::value_ptr(sceneView.CameraTarget), 0.05F)) {
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0F, 4.0F));
|
||||
ImGui::TextUnformatted("相机坐标");
|
||||
ImGui::SetNextItemWidth(260.0F);
|
||||
if (ImGui::DragFloat3("##pos_drag", glm::value_ptr(sceneView.CameraPosition), 0.05F)) {
|
||||
cameraChanged = true;
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0F, 4.0F));
|
||||
ImGui::TextUnformatted("相机目标点");
|
||||
ImGui::SetNextItemWidth(260.0F);
|
||||
if (ImGui::DragFloat3("##target_drag", glm::value_ptr(sceneView.CameraTarget), 0.05F)) {
|
||||
cameraChanged = true;
|
||||
}
|
||||
|
||||
if (cameraChanged) {
|
||||
EditorContext_->GetCameraController().ApplySceneView(sceneView);
|
||||
}
|
||||
if (ImGui::Button("Focus Selection", ImVec2(-1.0F, 0.0F))) {
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0F, 4.0F));
|
||||
if (ImGui::Button("聚焦选中物体", ImVec2(-1.0F, 24.0F))) {
|
||||
if (MetaCoreGameObject selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject) {
|
||||
EditorContext_->GetCameraController().FocusGameObject(selectedObject);
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Reset Camera", ImVec2(-1.0F, 0.0F))) {
|
||||
if (ImGui::Button("重置相机设置", ImVec2(-1.0F, 24.0F))) {
|
||||
MetaCoreSceneView defaultSceneView{};
|
||||
defaultSceneView.CameraPosition = {0.0F, 2.5F, 6.5F};
|
||||
defaultSceneView.CameraTarget = {0.0F, 0.7F, 0.0F};
|
||||
@ -1166,11 +1292,13 @@ void MetaCoreEditorApp::DrawEditorToolbar() {
|
||||
defaultSceneView.VerticalFieldOfViewDegrees = 60.0F;
|
||||
EditorContext_->GetCameraController().ApplySceneView(defaultSceneView);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleVar(5);
|
||||
}
|
||||
|
||||
void MetaCoreEditorApp::DrawEditorStatusBar() {
|
||||
@ -1188,25 +1316,118 @@ void MetaCoreEditorApp::DrawEditorStatusBar() {
|
||||
ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoSavedSettings;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0F, 3.0F));
|
||||
ImGui::Begin("MetaCoreStatusBar", nullptr, statusWindowFlags);
|
||||
ImGui::TextUnformatted("MetaCore");
|
||||
ImGui::SameLine(0.0F, 16.0F);
|
||||
if (const auto playModeService = ModuleRegistry_.ResolveService<MetaCoreIPlayModeService>();
|
||||
playModeService != nullptr) {
|
||||
const MetaCorePlayModeState playState = playModeService->GetState();
|
||||
const char* stateText = playState == MetaCorePlayModeState::Edit ? "Edit" :
|
||||
(playState == MetaCorePlayModeState::Playing ? "Playing" : "Paused");
|
||||
ImGui::Text("Mode: %s", stateText);
|
||||
}
|
||||
if (const auto assetDatabaseService = ModuleRegistry_.ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
assetDatabaseService != nullptr && assetDatabaseService->HasProject()) {
|
||||
ImGui::SameLine(0.0F, 16.0F);
|
||||
const std::string projectRoot = assetDatabaseService->GetProjectDescriptor().RootPath.string();
|
||||
ImGui::Text("Project: %s", projectRoot.c_str());
|
||||
// Background color: STATUS_BAR_BG #131313
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.13F, 0.13F, 0.13F, 1.00F));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6.0F, 4.0F)); // STATUS_BAR_WIN_PAD
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0F, 0.0F)); // STATUS_BAR_ITEM_SPC
|
||||
|
||||
if (ImGui::Begin("##MetaCoreStatusBar", nullptr, statusWindowFlags)) {
|
||||
float dispW = ImGui::GetWindowWidth();
|
||||
float leftZoneW = dispW - 160.0F;
|
||||
|
||||
// Retrieve log entries
|
||||
const auto& entries = EditorContext_->GetLogService().GetEntries();
|
||||
std::string latestMsg = "";
|
||||
MetaCoreLogLevel latestLevel = MetaCoreLogLevel::Info;
|
||||
int warnCount = 0;
|
||||
int errorCount = 0;
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
if (entry.Level == MetaCoreLogLevel::Warning) {
|
||||
warnCount++;
|
||||
} else if (entry.Level == MetaCoreLogLevel::Error) {
|
||||
errorCount++;
|
||||
}
|
||||
latestMsg = entry.Message;
|
||||
latestLevel = entry.Level;
|
||||
}
|
||||
|
||||
// 1. Clickable area for latest message -> opens/selects Console panel
|
||||
float clickW = std::max(leftZoneW - 8.0F, 100.0F);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0F, 0.0F, 0.0F, 0.0F)); // Ghost
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.20F, 0.20F, 0.20F, 0.60F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.25F, 0.25F, 0.25F, 0.80F));
|
||||
|
||||
if (ImGui::InvisibleButton("##StatusBarClick", ImVec2(clickW, 16.0F))) {
|
||||
ModuleRegistry_.AccessPanelOpenState("Console") = true;
|
||||
ImGui::SetWindowFocus("Console###console");
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
// Overlay text on the invisible button
|
||||
ImGui::SameLine(6.0F);
|
||||
|
||||
// Truncate message
|
||||
int maxChars = std::max(10, static_cast<int>((leftZoneW - 160.0F) / 8.0F));
|
||||
std::string msgDisplay = latestMsg;
|
||||
if (static_cast<int>(msgDisplay.size()) > maxChars) {
|
||||
msgDisplay = msgDisplay.substr(0, maxChars - 1) + "...";
|
||||
}
|
||||
|
||||
// Color and icon for level
|
||||
ImVec4 levelClr = ImVec4(0.82F, 0.82F, 0.85F, 1.00F); // Info color
|
||||
std::string icon = "";
|
||||
if (latestLevel == MetaCoreLogLevel::Error) {
|
||||
levelClr = ImVec4(0.922F, 0.341F, 0.341F, 1.00F); // Accent red
|
||||
icon = "* "; // Character for error
|
||||
} else if (latestLevel == MetaCoreLogLevel::Warning) {
|
||||
levelClr = ImVec4(0.890F, 0.710F, 0.300F, 1.00F); // Yellow
|
||||
icon = "! "; // Warning icon character
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, levelClr);
|
||||
if (latestMsg.empty()) {
|
||||
ImGui::TextUnformatted("MetaCore Ready");
|
||||
} else {
|
||||
ImGui::TextUnformatted((icon + msgDisplay).c_str());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// 2. Right counters: [W] N [E] N
|
||||
float counterX = leftZoneW - 130.0F;
|
||||
if (counterX > 0.0F) {
|
||||
ImGui::SameLine(counterX);
|
||||
|
||||
// Warning count
|
||||
if (warnCount > 0) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.890F, 0.710F, 0.300F, 1.00F));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.133F, 0.133F, 0.133F, 0.60F)); // Dimmed
|
||||
}
|
||||
ImGui::Text("! %d", warnCount);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SameLine(0.0F, 12.0F);
|
||||
|
||||
// Error count
|
||||
if (errorCount > 0) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.922F, 0.341F, 0.341F, 1.00F));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.133F, 0.133F, 0.133F, 0.60F)); // Dimmed
|
||||
}
|
||||
ImGui::Text("* %d", errorCount);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// 3. Engine status (right zone)
|
||||
float statusX = leftZoneW + 8.0F;
|
||||
ImGui::SameLine(statusX);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.55F, 0.55F, 0.55F, 1.00F)); // Dim
|
||||
if (const auto playModeService = ModuleRegistry_.ResolveService<MetaCoreIPlayModeService>();
|
||||
playModeService != nullptr) {
|
||||
const MetaCorePlayModeState playState = playModeService->GetState();
|
||||
const char* stateText = playState == MetaCorePlayModeState::Edit ? "Edit" :
|
||||
(playState == MetaCorePlayModeState::Playing ? "Playing" : "Paused");
|
||||
ImGui::TextUnformatted(stateText);
|
||||
} else {
|
||||
ImGui::TextUnformatted("Edit");
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void MetaCoreEditorApp::DrawSceneViewWindow() {
|
||||
@ -1225,7 +1446,7 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
|
||||
ImGuiWindowFlags_NoScrollWithMouse;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
|
||||
if (ImGui::Begin("\u573A\u666F###scene_view", &sceneOpen, sceneWindowFlags)) {
|
||||
if (ImGui::Begin("场景###scene_view", &sceneOpen, sceneWindowFlags)) {
|
||||
MetaCoreSceneViewportState& viewportState = EditorContext_->GetSceneViewportState();
|
||||
ImVec2 viewportPos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
|
||||
@ -1276,22 +1497,22 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
|
||||
ImGui::Dummy(viewportSize);
|
||||
}
|
||||
|
||||
// 场景视口浮动快捷工具条 (Toolbar Overlay)
|
||||
// 浮动在视口左上角,采用半透明暗色背景,1:1 复刻 Unity 视口功能控制体验
|
||||
// 场景视口浮动快捷工具条 (ViewportToolbar)
|
||||
ImGui::SetCursorScreenPos(ImVec2(viewportPos.x + 10.0f, viewportPos.y + 10.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.12f, 0.12f, 0.85f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 4.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6.0f, 4.0f));
|
||||
|
||||
if (ImGui::BeginChild("ViewportToolbar", ImVec2(410.0f, 28.0f), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
if (ImGui::BeginChild("ViewportToolbar", ImVec2(370.0f, 28.0f), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
// A. Gizmo 轴操作切换 (T: 位移, R: 旋转, S: 缩放)
|
||||
auto currentOp = EditorContext_->GetGizmoOperation();
|
||||
|
||||
auto drawToolbarButton = [](const char* label, bool active) -> bool {
|
||||
if (active) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.173f, 0.365f, 0.529f, 1.0f)); // 选中亮蓝色
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.22f, 0.45f, 0.65f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.12f, 0.28f, 0.42f, 1.0f));
|
||||
// 替换亮蓝色为 Notion 红灰风格高亮
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.270F, 0.180F, 0.180F, 1.00F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.320f, 0.220f, 0.220f, 1.00F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.200f, 0.140f, 0.140f, 1.00F));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); // 平时透明
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.12f));
|
||||
@ -1323,10 +1544,10 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
|
||||
ImGui::TextDisabled("|");
|
||||
ImGui::SameLine(0, 6);
|
||||
|
||||
// B. 坐标系切换 (Local本地 / Global世界)
|
||||
// B. 坐标系切换 (局部 / 全局)
|
||||
auto currentMode = EditorContext_->GetGizmoMode();
|
||||
const char* modeLabel = (currentMode == MetaCoreGizmoMode::Local) ? "Local" : "Global";
|
||||
if (ImGui::Button(modeLabel, ImVec2(50.0f, 20.0f))) {
|
||||
const char* modeLabel = (currentMode == MetaCoreGizmoMode::Local) ? "局部" : "全局";
|
||||
if (ImGui::Button(modeLabel, ImVec2(40.0f, 20.0f))) {
|
||||
EditorContext_->SetGizmoMode(currentMode == MetaCoreGizmoMode::Local ? MetaCoreGizmoMode::Global : MetaCoreGizmoMode::Local);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("切换本地/全局坐标系");
|
||||
@ -1335,10 +1556,10 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
|
||||
ImGui::TextDisabled("|");
|
||||
ImGui::SameLine(0, 6);
|
||||
|
||||
// C. 枢轴模式切换 (Pivot枢轴 / Center中心)
|
||||
// C. 枢轴模式切换 (枢轴 / 中心)
|
||||
auto currentPivot = EditorContext_->GetGizmoPivotMode();
|
||||
const char* pivotLabel = (currentPivot == MetaCoreGizmoPivotMode::Pivot) ? "Pivot" : "Center";
|
||||
if (ImGui::Button(pivotLabel, ImVec2(55.0f, 20.0f))) {
|
||||
const char* pivotLabel = (currentPivot == MetaCoreGizmoPivotMode::Pivot) ? "枢轴" : "中心";
|
||||
if (ImGui::Button(pivotLabel, ImVec2(40.0f, 20.0f))) {
|
||||
EditorContext_->SetGizmoPivotMode(currentPivot == MetaCoreGizmoPivotMode::Pivot ? MetaCoreGizmoPivotMode::Center : MetaCoreGizmoPivotMode::Pivot);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("切换 Pivot/Center 枢轴中心");
|
||||
@ -1367,7 +1588,7 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
|
||||
ImGui::SameLine(0, 6);
|
||||
ImGui::TextDisabled("|");
|
||||
ImGui::SameLine(0, 6);
|
||||
if (ImGui::Button("Reset Cam", ImVec2(75.0f, 20.0f))) {
|
||||
if (ImGui::Button("复位", ImVec2(40.0f, 20.0f))) {
|
||||
EditorContext_->GetCameraController().FocusGameObject({}); // 传入空 GameObject 表示复位摄像机到初始中心位置
|
||||
}
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("复位视口摄像机");
|
||||
@ -1454,7 +1675,7 @@ void MetaCoreEditorApp::DrawGameViewWindow() {
|
||||
ImGuiWindowFlags_NoScrollWithMouse;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
|
||||
if (ImGui::Begin("\u6E38\u620F###game_view", nullptr, gameWindowFlags)) {
|
||||
if (ImGui::Begin("游戏###game_view", nullptr, gameWindowFlags)) {
|
||||
const ImVec2 origin = ImGui::GetCursorScreenPos();
|
||||
ImVec2 size = ImGui::GetContentRegionAvail();
|
||||
if (size.x < 1.0F) {
|
||||
|
||||
@ -18,35 +18,68 @@ void MetaCoreEditorCameraController::Update(const MetaCoreSceneViewportState& vi
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.WantCaptureMouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// We remove the strict WantCaptureMouse check here because the entire Scene Viewport
|
||||
// is an ImGui window, and WantCaptureMouse is always true.
|
||||
// We rely on viewportState.Hovered instead to determine if we should process input.
|
||||
|
||||
const bool altDown = input.IsKeyDown(MetaCoreInputKey::LeftAlt) || input.IsKeyDown(MetaCoreInputKey::RightAlt);
|
||||
|
||||
// Zoom: Alt + Right Drag or Mouse Wheel
|
||||
if (viewportState.Hovered && altDown && ImGui::IsMouseDragging(ImGuiMouseButton_Right)) {
|
||||
Distance_ *= (1.0F - io.MouseDelta.y * 0.01F);
|
||||
Distance_ = std::clamp(Distance_, 0.5F, 100.0F);
|
||||
} else if (viewportState.Hovered && std::abs(io.MouseWheel) > 0.001F) {
|
||||
Distance_ *= (io.MouseWheel > 0.0F) ? 0.88F : 1.12F;
|
||||
Distance_ = std::clamp(Distance_, 0.5F, 100.0F);
|
||||
Distance_ = std::max(0.01F, Distance_);
|
||||
} else if (viewportState.Hovered && !ImGui::IsMouseDown(ImGuiMouseButton_Right) && std::abs(io.MouseWheel) > 0.001F) {
|
||||
// [Unity Style] 滚轮直接带动整个相机坐标系朝正前方无限推进/后退
|
||||
float scrollSpeed = std::max(1.0F, Distance_ * 0.1F) * CameraSpeed_ * 0.1F;
|
||||
Target_ += GetForwardDirection() * io.MouseWheel * scrollSpeed;
|
||||
}
|
||||
|
||||
// Orbit: Alt + Left Drag
|
||||
if (viewportState.Hovered && altDown && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
YawDegrees_ -= io.MouseDelta.x * 0.35F; // 逆时针
|
||||
YawDegrees_ += io.MouseDelta.x * 0.35F; // [Unity Style] 右拖逆时针
|
||||
PitchDegrees_ -= io.MouseDelta.y * 0.35F;
|
||||
PitchDegrees_ = std::clamp(PitchDegrees_, -85.0F, 85.0F);
|
||||
}
|
||||
// Look Around: Right Drag (without Alt)
|
||||
else if (viewportState.Hovered && !altDown && ImGui::IsMouseDragging(ImGuiMouseButton_Right)) {
|
||||
// Fly Mode / Look Around: Right Hold (without Alt)
|
||||
else if (viewportState.Hovered && !altDown && ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
|
||||
// --- 0. 滚轮动态调速 ---
|
||||
if (std::abs(io.MouseWheel) > 0.001F) {
|
||||
CameraSpeed_ *= (io.MouseWheel > 0.0F) ? 1.25F : 0.8F;
|
||||
CameraSpeed_ = std::clamp(CameraSpeed_, 0.1F, 2000.0F);
|
||||
}
|
||||
|
||||
// --- 1. 原地张望 (Free Look) ---
|
||||
const glm::vec3 oldPosition = GetCameraPosition();
|
||||
YawDegrees_ -= io.MouseDelta.x * 0.35F;
|
||||
PitchDegrees_ -= io.MouseDelta.y * 0.35F;
|
||||
|
||||
// [Unity Style] 鼠标右移=视角右转(Yaw++),鼠标下移=视角下看(Pitch--)
|
||||
YawDegrees_ += io.MouseDelta.x * 0.18F;
|
||||
PitchDegrees_ -= io.MouseDelta.y * 0.18F;
|
||||
PitchDegrees_ = std::clamp(PitchDegrees_, -85.0F, 85.0F);
|
||||
|
||||
Target_ = oldPosition + GetForwardDirection() * Distance_;
|
||||
|
||||
// --- 2. 自由漫游飞行 (FPS Flight) ---
|
||||
glm::vec3 movement(0.0F);
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_W)) movement += GetForwardDirection();
|
||||
if (ImGui::IsKeyDown(ImGuiKey_S)) movement -= GetForwardDirection();
|
||||
if (ImGui::IsKeyDown(ImGuiKey_D)) movement += GetRightDirection();
|
||||
if (ImGui::IsKeyDown(ImGuiKey_A)) movement -= GetRightDirection();
|
||||
if (ImGui::IsKeyDown(ImGuiKey_E)) movement += glm::vec3(0.0F, 0.0F, 1.0F);
|
||||
if (ImGui::IsKeyDown(ImGuiKey_Q)) movement -= glm::vec3(0.0F, 0.0F, 1.0F);
|
||||
|
||||
if (glm::length(movement) > 0.001F) {
|
||||
movement = glm::normalize(movement);
|
||||
|
||||
float speed = CameraSpeed_ * io.DeltaTime;
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) {
|
||||
speed *= 3.0F;
|
||||
}
|
||||
|
||||
Target_ += movement * speed;
|
||||
}
|
||||
}
|
||||
|
||||
// Pan: Middle Drag
|
||||
|
||||
@ -26,6 +26,7 @@ private:
|
||||
|
||||
glm::vec3 Target_{0.0F, 0.0F, 0.0F};
|
||||
float Distance_ = 10.0F;
|
||||
float CameraSpeed_ = 10.0F; // [MetaCore] Dynamic camera flight speed
|
||||
float YawDegrees_ = 45.0F;
|
||||
float PitchDegrees_ = -35.0F;
|
||||
float FieldOfViewDegrees_ = 60.0F;
|
||||
|
||||
@ -203,6 +203,7 @@ void MetaCoreExpandFocusBounds(
|
||||
void MetaCoreSceneInteractionService::ResetFrameState() {
|
||||
GizmoWasUsing_ = false;
|
||||
HasGizmoBeforeSnapshot_ = false;
|
||||
GizmoDragStarted_ = false;
|
||||
}
|
||||
|
||||
MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
|
||||
@ -229,11 +230,11 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
|
||||
sceneView.CameraTarget,
|
||||
sceneView.CameraUp
|
||||
);
|
||||
const glm::mat4 projectionMatrix = glm::perspective(
|
||||
const glm::mat4 projectionMatrix = glm::perspectiveZO(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
viewportState.Width / viewportState.Height,
|
||||
0.05F,
|
||||
500.0F
|
||||
0.1F,
|
||||
1000.0F
|
||||
);
|
||||
|
||||
const float normalizedX = (localCursor.x / viewportState.Width) * 2.0F - 1.0F;
|
||||
@ -509,13 +510,19 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
|
||||
const int h = static_cast<int>(viewportState.Height);
|
||||
const float gizmoAspect = (h > 0) ? (static_cast<float>(w) / static_cast<float>(h)) : 1.0f;
|
||||
const glm::mat4 gizmoViewMatrix = glm::lookAt(sceneView.CameraPosition, sceneView.CameraTarget, sceneView.CameraUp);
|
||||
const glm::mat4 gizmoProjectionMatrix = glm::perspective(
|
||||
const glm::mat4 gizmoProjectionMatrix = glm::perspectiveZO(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
gizmoAspect, 0.1F, 1000.0F
|
||||
);
|
||||
|
||||
// 获取当前物体的真实世界矩阵
|
||||
glm::mat4 worldMatrixMetaCore = MetaCoreBuildSelectionGizmoWorldMatrix(editorContext, selectedObject.GetId());
|
||||
const glm::mat4 originalWorldMatrixMetaCore = worldMatrixMetaCore;
|
||||
|
||||
// 若当前未进行拖拽,保持每一帧更新初始矩阵为物体的最新矩阵
|
||||
if (!GizmoDragStarted_) {
|
||||
GizmoStartMatrix_ = worldMatrixMetaCore;
|
||||
GizmoCurrentMatrix_ = worldMatrixMetaCore;
|
||||
}
|
||||
|
||||
ImGuizmo::SetOrthographic(false);
|
||||
ImGuizmo::Enable(true);
|
||||
@ -538,32 +545,29 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
|
||||
|
||||
ImGuizmo::PushID(reinterpret_cast<void*>(selectedObject.GetId()));
|
||||
glm::mat4 gizmoMatrix = worldMatrixMetaCore;
|
||||
const MetaCoreGizmoSnapSettings& snapSettings = editorContext.GetGizmoSnapSettings();
|
||||
const float snapStep = snapSettings.StepForOperation(currentOp);
|
||||
const float snapValues[3] = {snapStep, snapStep, snapStep};
|
||||
ImGuizmo::Manipulate(
|
||||
glm::value_ptr(gizmoViewMatrix),
|
||||
glm::value_ptr(gizmoProjectionMatrix),
|
||||
MetaCoreToImGuizmoOperation(currentOp),
|
||||
effectiveMode,
|
||||
glm::value_ptr(gizmoMatrix),
|
||||
nullptr,
|
||||
snapSettings.Enabled ? snapValues : nullptr
|
||||
glm::value_ptr(gizmoMatrix)
|
||||
);
|
||||
|
||||
const bool gizmoUsing = ImGuizmo::IsUsing();
|
||||
ImGuizmo::PopID();
|
||||
|
||||
HandleGizmoBeginUse(editorContext, gizmoUsing);
|
||||
if (gizmoUsing && gizmoMatrix != originalWorldMatrixMetaCore) {
|
||||
(void)ApplyWorldTransformDeltaToSelection(
|
||||
editorContext,
|
||||
selectedObject.GetId(),
|
||||
originalWorldMatrixMetaCore,
|
||||
gizmoMatrix,
|
||||
editorContext.GetGizmoPivotMode()
|
||||
);
|
||||
|
||||
if (gizmoUsing && gizmoMatrix != GizmoStartMatrix_) {
|
||||
glm::mat4 newLocalMatrix = gizmoMatrix;
|
||||
if (selectedObject.GetParentId() != 0) {
|
||||
const glm::mat4 parentWorldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject.GetParentId());
|
||||
newLocalMatrix = glm::inverse(parentWorldMatrixMetaCore) * gizmoMatrix;
|
||||
}
|
||||
|
||||
MetaCoreApplyMatrixToTransform(newLocalMatrix, selectedObject.GetComponent<MetaCoreTransformComponent>());
|
||||
}
|
||||
|
||||
HandleGizmoEndUse(editorContext, gizmoUsing);
|
||||
}
|
||||
|
||||
@ -776,9 +780,10 @@ void MetaCoreSceneInteractionService::HandleShortcuts(MetaCoreEditorContext& edi
|
||||
FocusSelectedObject(editorContext);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
||||
FocusVisibleScene(editorContext);
|
||||
}
|
||||
// [MetaCore] Removed 'A' hotkey for FocusVisibleScene because it conflicts with WASD FPS flight
|
||||
// if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
||||
// FocusVisibleScene(editorContext);
|
||||
// }
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_G)) {
|
||||
editorContext.SetShowViewportGrid(!editorContext.GetShowViewportGrid());
|
||||
|
||||
@ -46,6 +46,11 @@ private:
|
||||
bool GizmoWasUsing_ = false;
|
||||
bool HasGizmoBeforeSnapshot_ = false;
|
||||
MetaCoreEditorStateSnapshot GizmoBeforeSnapshot_{};
|
||||
|
||||
// 记录 Gizmo 拖拽状态以防止单轴拖拽卡顿并保持视觉轴实时跟随
|
||||
bool GizmoDragStarted_ = false;
|
||||
glm::mat4 GizmoStartMatrix_{1.0F};
|
||||
glm::mat4 GizmoCurrentMatrix_{1.0F};
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -22,10 +22,12 @@
|
||||
#include <filament/LightManager.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/IndirectLight.h>
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
#include <gltfio/TextureProvider.h>
|
||||
#include <gltfio/MaterialProvider.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
@ -94,12 +96,18 @@ public:
|
||||
Scene_ = Engine_->createScene();
|
||||
|
||||
// 创建默认灯光
|
||||
filament::LightManager::ShadowOptions shadowOptions;
|
||||
shadowOptions.mapSize = 2048;
|
||||
shadowOptions.shadowCascades = 3;
|
||||
|
||||
// 主光 (Sun)
|
||||
Light_ = utils::EntityManager::get().create();
|
||||
filament::LightManager::Builder(filament::LightManager::Type::DIRECTIONAL)
|
||||
.color(filament::Color::toLinear<filament::ACCURATE>({ 1.0f, 1.0f, 1.0f }))
|
||||
.color(filament::Color::toLinear<filament::ACCURATE>({ 1.0f, 0.95f, 0.9f }))
|
||||
.intensity(100000.0f)
|
||||
.direction({ 0.5f, 0.5f, -1.0f }) // Z-up 下 Z 向下
|
||||
.direction({ 0.5f, 0.5f, -1.0f })
|
||||
.castShadows(true)
|
||||
.shadowOptions(shadowOptions)
|
||||
.build(*Engine_, Light_);
|
||||
Scene_->addEntity(Light_);
|
||||
EntitiesInScene_.insert(Light_);
|
||||
@ -165,6 +173,63 @@ public:
|
||||
MaterialProvider_ = filament::gltfio::createJitShaderProvider(Engine_);
|
||||
NameManager_ = new utils::NameComponentManager(utils::EntityManager::get());
|
||||
AssetLoader_ = filament::gltfio::AssetLoader::create({ Engine_, MaterialProvider_, NameManager_ });
|
||||
ResourceLoader_ = new filament::gltfio::ResourceLoader({ Engine_ });
|
||||
|
||||
// 创建 STB 图像解码器以支持 JPG/PNG 等内部贴图
|
||||
StbDecoder_ = filament::gltfio::createStbProvider(Engine_);
|
||||
ResourceLoader_->addTextureProvider("image/png", StbDecoder_);
|
||||
ResourceLoader_->addTextureProvider("image/jpeg", StbDecoder_);
|
||||
|
||||
// 构造一个 1x1 的浅灰色 Texture 充当 IBL 反射天空盒
|
||||
|
||||
|
||||
// 生成标准的 256x256 全级 Mipmap IBL 反射贴图
|
||||
// Filament 要求 IBL 至少 256 且具有完整的 Mipmap,否则带有 Roughness 的 PBR 材质(尤其是金属)会因为采样不到而死黑!
|
||||
// 生成标准的 256x256 全级 Mipmap HDR(RGBA16F) IBL 反射贴图
|
||||
// Filament 的 IndirectLight::reflections 严格要求必须是 HDR 格式(如 RGBA16F),如果给 LDR (RGBA8) 则会直接被丢弃!
|
||||
// 必须使用 FLOAT 数据类型提交!
|
||||
uint32_t texSize = 256;
|
||||
IblTexture_ = filament::Texture::Builder()
|
||||
.width(texSize).height(texSize).levels(9) // 256 共有 9 级 mipmap
|
||||
.usage(filament::Texture::Usage::DEFAULT)
|
||||
.format(filament::Texture::InternalFormat::RGBA16F)
|
||||
.sampler(filament::Texture::Sampler::SAMPLER_CUBEMAP)
|
||||
.build(*Engine_);
|
||||
|
||||
for (uint8_t level = 0; level < 9; ++level) {
|
||||
uint32_t shifted = texSize >> level;
|
||||
uint32_t levelSize = shifted > 0 ? shifted : 1;
|
||||
size_t facePixels = levelSize * levelSize;
|
||||
|
||||
float* colors = new float[facePixels * 6 * 4];
|
||||
for (size_t i = 0; i < facePixels * 6 * 4; ++i) colors[i] = 0.5f; // HDR 环境光强度 0.5
|
||||
|
||||
filament::Texture::PixelBufferDescriptor bufferDesc(
|
||||
colors, facePixels * 6 * 4 * sizeof(float),
|
||||
filament::Texture::Format::RGBA,
|
||||
filament::Texture::Type::FLOAT,
|
||||
[](void* buffer, size_t size, void* user) { delete[] static_cast<float*>(buffer); }
|
||||
);
|
||||
|
||||
filament::Texture::FaceOffsets faceOffsets;
|
||||
for (int i = 0; i < 6; ++i) faceOffsets.offsets[i] = i * facePixels * 4 * sizeof(float);
|
||||
|
||||
IblTexture_->setImage(*Engine_, level, std::move(bufferDesc), faceOffsets);
|
||||
}
|
||||
|
||||
// 生成默认全局环境漫反射与镜面反射 (IBL)
|
||||
filament::math::float3 sh[9] = {};
|
||||
sh[0] = { 1.0f, 1.0f, 1.0f }; // 基础环境光亮度
|
||||
IndirectLight_ = filament::IndirectLight::Builder()
|
||||
.irradiance(3, sh)
|
||||
.reflections(IblTexture_) // 恢复物理正确的镜面反射
|
||||
.intensity(30000.0f) // 正常物理光强
|
||||
.build(*Engine_);
|
||||
Scene_->setIndirectLight(IndirectLight_);
|
||||
|
||||
// 开启高级后处理
|
||||
View_->setAntiAliasing(filament::View::AntiAliasing::FXAA);
|
||||
View_->setBloomOptions({ .strength = 0.15f, .resolution = 384, .levels = 6, .blendMode = filament::View::BloomOptions::BlendMode::ADD, .threshold = true });
|
||||
|
||||
std::cout << "FilamentSceneBridge: Initialized with Offscreen=" << (offscreen ? "true" : "false") << std::endl;
|
||||
return true;
|
||||
@ -184,6 +249,7 @@ public:
|
||||
std::cout << "[DEBUG] Destroyed asset for ID: " << id << std::endl;
|
||||
}
|
||||
LoadedAssets_.clear();
|
||||
AssetBuffers_.clear();
|
||||
|
||||
// 2. 销毁所有 pivotEntity 中间辅助坐标变换实体
|
||||
for (auto& [objectId, assetEntity] : ObjectToFilamentEntity_) {
|
||||
@ -211,16 +277,9 @@ public:
|
||||
}
|
||||
ObjectToFilamentEntity_.clear();
|
||||
ObjectWorldMatrices_.clear();
|
||||
AssetBuffers_.clear();
|
||||
|
||||
|
||||
// 3. 销毁所有克隆的材质实例,由于引用的实体都已被销毁,此时可安全释放
|
||||
for (auto& [id, mats] : AssetDuplicatedMaterials_) {
|
||||
for (auto* mat : mats) {
|
||||
if (mat) {
|
||||
Engine_->destroy(mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
AssetDuplicatedMaterials_.clear();
|
||||
|
||||
for (auto& [id, entity] : SceneLightEntities_) {
|
||||
if (Scene_) {
|
||||
@ -240,6 +299,22 @@ public:
|
||||
filament::gltfio::AssetLoader::destroy(&AssetLoader_);
|
||||
AssetLoader_ = nullptr;
|
||||
}
|
||||
if (ResourceLoader_) {
|
||||
delete ResourceLoader_;
|
||||
ResourceLoader_ = nullptr;
|
||||
}
|
||||
if (StbDecoder_) {
|
||||
delete StbDecoder_;
|
||||
StbDecoder_ = nullptr;
|
||||
}
|
||||
if (IndirectLight_) {
|
||||
Engine_->destroy(IndirectLight_);
|
||||
IndirectLight_ = nullptr;
|
||||
}
|
||||
if (IblTexture_) {
|
||||
Engine_->destroy(IblTexture_);
|
||||
IblTexture_ = nullptr;
|
||||
}
|
||||
if (MaterialProvider_) {
|
||||
MaterialProvider_->destroyMaterials();
|
||||
delete MaterialProvider_;
|
||||
@ -525,18 +600,10 @@ public:
|
||||
// 确保顶级父 ID 的变换缓存也被清除
|
||||
ObjectWorldMatrices_.erase(hostRootId);
|
||||
|
||||
// 销毁克隆的材质
|
||||
auto matIt = AssetDuplicatedMaterials_.find(hostRootId);
|
||||
if (matIt != AssetDuplicatedMaterials_.end()) {
|
||||
for (auto* mat : matIt->second) {
|
||||
if (mat) {
|
||||
Engine_->destroy(mat);
|
||||
}
|
||||
}
|
||||
AssetDuplicatedMaterials_.erase(matIt);
|
||||
}
|
||||
|
||||
|
||||
// 从 LoadedAssets_ 中移除
|
||||
AssetBuffers_.erase(hostRootId);
|
||||
it = LoadedAssets_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
@ -599,8 +666,10 @@ public:
|
||||
}
|
||||
|
||||
// 加载贴图等资源
|
||||
filament::gltfio::ResourceLoader resourceLoader({ Engine_ });
|
||||
resourceLoader.loadResources(asset);
|
||||
if (ResourceLoader_) {
|
||||
ResourceLoader_->loadResources(asset);
|
||||
ResourceLoader_->asyncUpdateLoad(); // 触发底层的纹理同步
|
||||
}
|
||||
|
||||
// 添加到场景
|
||||
Scene_->addEntities(asset->getEntities(), asset->getEntityCount());
|
||||
@ -613,32 +682,9 @@ public:
|
||||
}
|
||||
|
||||
LoadedAssets_[hostRootId] = asset;
|
||||
AssetBuffers_[hostRootId] = std::move(buffer);
|
||||
|
||||
|
||||
// 复制材质实例防止共享干扰,放在加载好资源后
|
||||
{
|
||||
auto& rm = Engine_->getRenderableManager();
|
||||
std::vector<filament::MaterialInstance*>& dupList = AssetDuplicatedMaterials_[hostRootId];
|
||||
const utils::Entity* assetEntities = asset->getEntities();
|
||||
size_t assetEntityCount = asset->getEntityCount();
|
||||
for (size_t i = 0; i < assetEntityCount; ++i) {
|
||||
utils::Entity ent = assetEntities[i];
|
||||
if (!ent) continue;
|
||||
auto rmInst = rm.getInstance(ent);
|
||||
if (rmInst) {
|
||||
size_t primCount = rm.getPrimitiveCount(rmInst);
|
||||
for (size_t primIndex = 0; primIndex < primCount; ++primIndex) {
|
||||
filament::MaterialInstance* originalMat = rm.getMaterialInstanceAt(rmInst, primIndex);
|
||||
if (originalMat) {
|
||||
filament::MaterialInstance* dupMat = filament::MaterialInstance::duplicate(originalMat);
|
||||
if (dupMat) {
|
||||
rm.setMaterialInstanceAt(rmInst, primIndex, dupMat);
|
||||
dupList.push_back(dupMat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scene != nullptr) {
|
||||
// 有 ECS 实例模式下同步层级结构并补挂 Tag
|
||||
@ -928,6 +974,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// 持续推进后台的纹理解码任务!
|
||||
if (ResourceLoader_) {
|
||||
ResourceLoader_->asyncUpdateLoad();
|
||||
}
|
||||
|
||||
bool renderRuntimeUi = false;
|
||||
if (!RenderTarget_ && RuntimeUIView_ && RuntimeUiHelper_) {
|
||||
const auto viewport = View_->getViewport();
|
||||
@ -1263,12 +1314,17 @@ private:
|
||||
activeLightIds.insert(light.ObjectId);
|
||||
auto lightIt = SceneLightEntities_.find(light.ObjectId);
|
||||
if (lightIt == SceneLightEntities_.end()) {
|
||||
filament::LightManager::ShadowOptions shadowOptions;
|
||||
shadowOptions.mapSize = 2048;
|
||||
shadowOptions.shadowCascades = 3;
|
||||
|
||||
utils::Entity lightEntity = utils::EntityManager::get().create();
|
||||
filament::LightManager::Builder(filament::LightManager::Type::DIRECTIONAL)
|
||||
.color(BuildFilamentLightColor(light.Color))
|
||||
.intensity(BuildFilamentLightIntensity(light.Intensity))
|
||||
.direction(BuildFilamentLightDirection(light.WorldMatrix))
|
||||
.castShadows(true)
|
||||
.shadowOptions(shadowOptions)
|
||||
.build(*Engine_, lightEntity);
|
||||
Scene_->addEntity(lightEntity);
|
||||
EntitiesInScene_.insert(lightEntity);
|
||||
@ -1331,14 +1387,19 @@ private:
|
||||
|
||||
filament::gltfio::AssetLoader* AssetLoader_ = nullptr;
|
||||
filament::gltfio::MaterialProvider* MaterialProvider_ = nullptr;
|
||||
filament::gltfio::ResourceLoader* ResourceLoader_ = nullptr;
|
||||
utils::NameComponentManager* NameManager_ = nullptr;
|
||||
filament::IndirectLight* IndirectLight_ = nullptr;
|
||||
filament::Texture* IblTexture_ = nullptr;
|
||||
filament::gltfio::TextureProvider* StbDecoder_ = nullptr;
|
||||
|
||||
MetaCoreSceneRenderSync RenderSync_{};
|
||||
MetaCoreSceneRenderSyncSnapshot LastSyncSnapshot_{};
|
||||
std::unordered_map<MetaCoreId, glm::mat4> ObjectWorldMatrices_{};
|
||||
std::unordered_map<MetaCoreId, std::pair<filament::gltfio::FilamentAsset*, utils::Entity>> ObjectToFilamentEntity_;
|
||||
std::unordered_map<MetaCoreId, filament::gltfio::FilamentAsset*> LoadedAssets_;
|
||||
std::unordered_map<MetaCoreId, std::vector<filament::MaterialInstance*>> AssetDuplicatedMaterials_;
|
||||
std::unordered_map<MetaCoreId, std::vector<char>> AssetBuffers_;
|
||||
|
||||
std::unordered_map<MetaCoreId, utils::Entity> SceneLightEntities_;
|
||||
std::unordered_set<utils::Entity, utils::Entity::Hasher> EntitiesInScene_;
|
||||
|
||||
|
||||
@ -89,7 +89,10 @@ MetaCoreImGuiHelper::MetaCoreImGuiHelper(Engine* engine, filament::View* view, c
|
||||
mRenderable = em.create();
|
||||
mScene->addEntity(mRenderable);
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
if (mOwnsImGuiContext) {
|
||||
// 只有在拥有私有 ImGui 上下文时才调用 StyleColorsDark()
|
||||
ImGui::StyleColorsDark();
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreImGuiHelper::createAtlasTexture(Engine* engine) {
|
||||
|
||||
37
third_party/ImGuizmo/ImGuizmo.cpp
vendored
37
third_party/ImGuizmo/ImGuizmo.cpp
vendored
@ -1098,19 +1098,24 @@ namespace IMGUIZMO_NAMESPACE
|
||||
|
||||
// projection reverse
|
||||
vec_t nearPos, farPos;
|
||||
nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat);
|
||||
farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat);
|
||||
nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat);
|
||||
farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat);
|
||||
|
||||
gContext.mReversed = (nearPos.z/nearPos.w) > (farPos.z / farPos.w);
|
||||
// FIXED: Force mReversed to false. The automatic detection fails for Right-Handed ZO projection matrices,
|
||||
// causing rays to be cast backwards from the far plane, which breaks horizon detection.
|
||||
gContext.mReversed = false;
|
||||
|
||||
// compute scale from the size of camera right vector projected on screen at the matrix position
|
||||
const float zNear = gContext.mReversed ? (1.f - FLT_EPSILON) : 0.f;// compute scale from the size of camera right vector projected on screen at the matrix position
|
||||
vec_t pointRight = viewInverse.v.right;
|
||||
pointRight.TransformPoint(gContext.mViewProjection);
|
||||
|
||||
vec_t rightViewInverse = viewInverse.v.right;
|
||||
rightViewInverse.TransformVector(gContext.mModelInverse);
|
||||
float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse);
|
||||
gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / rightLength;
|
||||
if (!gContext.mbUsing)
|
||||
{
|
||||
gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / rightLength;
|
||||
}
|
||||
|
||||
ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP);
|
||||
gContext.mScreenSquareCenter = centerSSpace;
|
||||
@ -2169,7 +2174,12 @@ namespace IMGUIZMO_NAMESPACE
|
||||
ImGui::CaptureMouseFromApp();
|
||||
#endif
|
||||
const float signedLength = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
|
||||
const float len = fabsf(signedLength); // near plan
|
||||
if (signedLength < 0.0f) {
|
||||
// FIXED: If intersection is behind the camera (mouse points at the sky above the horizon),
|
||||
// ignore the movement to prevent the object from shooting to infinity.
|
||||
return false;
|
||||
}
|
||||
const float len = signedLength; // FIXED: Removed fabsf to allow correct projection when ray diverges
|
||||
const vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
|
||||
|
||||
// compute delta
|
||||
@ -2258,8 +2268,15 @@ namespace IMGUIZMO_NAMESPACE
|
||||
for (unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized);
|
||||
movePlanNormal[i].Cross(orthoVector);
|
||||
movePlanNormal[i].Normalize();
|
||||
if (orthoVector.Length() < 0.1f) // FIXED: If axis is almost parallel to camera, use view plane to avoid singularity
|
||||
{
|
||||
movePlanNormal[i] = -gContext.mCameraDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
movePlanNormal[i].Cross(orthoVector);
|
||||
movePlanNormal[i].Normalize();
|
||||
}
|
||||
}
|
||||
// pickup plan
|
||||
gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_MOVE_X]);
|
||||
@ -2323,6 +2340,10 @@ namespace IMGUIZMO_NAMESPACE
|
||||
ImGui::CaptureMouseFromApp();
|
||||
#endif
|
||||
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
|
||||
if (len < 0.0f) {
|
||||
// FIXED: Prevent sky/horizon explosion
|
||||
return false;
|
||||
}
|
||||
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
|
||||
vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
|
||||
vec_t delta = newOrigin - gContext.mModelLocal.v.position;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user