管线优化
This commit is contained in:
parent
b10e03eda0
commit
2600ea21b4
@ -668,6 +668,21 @@ add_library(MetaCoreEditor STATIC
|
||||
${METACORE_EDITOR_GENERATED_SOURCE}
|
||||
)
|
||||
|
||||
find_program(METACORE_BASISU_EXECUTABLE
|
||||
NAMES basisu
|
||||
HINTS
|
||||
"${CMAKE_SOURCE_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/tools/basisu"
|
||||
"${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/tools/basisu"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if(METACORE_BASISU_EXECUTABLE)
|
||||
target_compile_definitions(MetaCoreEditor PRIVATE
|
||||
METACORE_BASISU_EXECUTABLE="${METACORE_BASISU_EXECUTABLE}"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Basis Universal encoder is required for the production texture pipeline")
|
||||
endif()
|
||||
|
||||
target_include_directories(MetaCoreEditor
|
||||
PUBLIC
|
||||
Source/MetaCoreEditor/Public
|
||||
@ -812,6 +827,14 @@ if(METACORE_BUILD_TESTS)
|
||||
|
||||
add_test(NAME MetaCoreSmokeTests COMMAND MetaCoreSmokeTests)
|
||||
|
||||
add_executable(MetaCoreAssetPipelineTests
|
||||
tests/MetaCoreAssetPipelineTests.cpp
|
||||
Source/MetaCoreRender/Private/MetaCoreGlibcCompat.cpp
|
||||
)
|
||||
target_link_libraries(MetaCoreAssetPipelineTests PRIVATE MetaCoreEditor)
|
||||
target_compile_options(MetaCoreAssetPipelineTests PRIVATE ${METACORE_COMMON_WARNINGS})
|
||||
add_test(NAME MetaCoreAssetPipelineTests COMMAND MetaCoreAssetPipelineTests)
|
||||
|
||||
# Filament + ImGui 离屏渲染 Demo
|
||||
add_executable(FilamentImGuiDemo
|
||||
tests/FilamentImGuiDemo.cpp
|
||||
@ -823,7 +846,6 @@ if(METACORE_BUILD_TESTS)
|
||||
imgui::imgui
|
||||
)
|
||||
target_compile_options(FilamentImGuiDemo PRIVATE ${METACORE_COMMON_WARNINGS})
|
||||
target_compile_definitions(FilamentImGuiDemo PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
metacore_use_filament(FilamentImGuiDemo)
|
||||
metacore_stage_ui_blit_material(FilamentImGuiDemo)
|
||||
endif()
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "512cb16539316502",
|
||||
"asset_type": "material",
|
||||
"guid": "fc3fd465-4f39-44af-907a-4fc0494c2502",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material 1.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 4176055334418841915,
|
||||
"source_mtime_ns": 1781677768840654120,
|
||||
"source_path": "Assets/Materials/Material 1.mcmaterial.json",
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "2b00372114e7b956",
|
||||
"asset_type": "material",
|
||||
"guid": "579d1aa2-baae-4b56-9b05-86f9d2e9a2ff",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material 2.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 12206391660553642631,
|
||||
"source_mtime_ns": 1781573175016906643,
|
||||
"source_path": "Assets/Materials/Material 2.mcmaterial.json",
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "f433edac057bcf58",
|
||||
"asset_type": "material",
|
||||
"guid": "fbe8b648-5368-4af6-b822-d2573ed5f194",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material 3.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 7449433069631146521,
|
||||
"source_mtime_ns": 1781574603045608609,
|
||||
"source_path": "Assets/Materials/Material 3.mcmaterial.json",
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "96180e85664b5eb3",
|
||||
"asset_type": "material",
|
||||
"guid": "a23b9298-b98c-47ac-a2ad-3c9dac65c5af",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material 4.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 12594992031322235982,
|
||||
"source_mtime_ns": 1781574003827012347,
|
||||
"source_path": "Assets/Materials/Material 4.mcmaterial.json",
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "47baddd806a5bdb0",
|
||||
"asset_type": "material",
|
||||
"guid": "39f65f8e-cdac-43fb-9762-e6c544547a71",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material 5.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 10101379247114018828,
|
||||
"source_mtime_ns": 1781574163019728677,
|
||||
"source_path": "Assets/Materials/Material 5.mcmaterial.json",
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "64a86163a37fabf6",
|
||||
"asset_type": "material",
|
||||
"guid": "3bd687c0-2570-4236-bd65-0f774b13d34f",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material 6.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 2546970018452484703,
|
||||
"source_mtime_ns": 1781575611817940101,
|
||||
"source_path": "Assets/Materials/Material 6.mcmaterial.json",
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "bee3ae76701cfe52",
|
||||
"asset_type": "material",
|
||||
"guid": "b13c413e-0350-4152-9f1e-41552f9823af",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material 7.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 12622570808854545607,
|
||||
"source_mtime_ns": 1781678340396816708,
|
||||
"source_path": "Assets/Materials/Material 7.mcmaterial.json",
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
{
|
||||
"artifact_key": "cd72b5445ae09699",
|
||||
"asset_type": "material",
|
||||
"guid": "5edbdbc8-4a0d-4991-82a7-782de9ed4e3a",
|
||||
"import_settings": {
|
||||
"generated_material_policy": 1,
|
||||
"preserve_alpha_mode": true,
|
||||
"preserve_double_sided": true,
|
||||
"shader_model": 0
|
||||
},
|
||||
"importer_id": "MaterialImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/Material.mcmaterial.json",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 6867598416662597650,
|
||||
"source_hash": 7326866736359587430,
|
||||
"source_mtime_ns": 1781572763657368309,
|
||||
"source_path": "Assets/Materials/Material.mcmaterial.json",
|
||||
|
||||
Binary file not shown.
@ -1,8 +1,14 @@
|
||||
{
|
||||
"artifact_key": "1b8eb8422ae8a643",
|
||||
"asset_type": "asset",
|
||||
"guid": "450d9089-4ca1-4327-96c4-39046198b1be",
|
||||
"import_settings": {},
|
||||
"importer_id": "BinaryImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/default_pbr.material.json.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 11238273277574643119,
|
||||
"source_hash": 12650993429013095480,
|
||||
"source_mtime_ns": 1779698309758483890,
|
||||
"source_path": "Assets/Materials/default_pbr.material.json",
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
{
|
||||
"artifact_key": "1a085d56a09aac10",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"86c72638-58f3-95a7-c6a5-d6661e2df677",
|
||||
@ -2099,8 +2100,26 @@
|
||||
"ca00a727-1fe3-590a-5bc1-79ddec284950"
|
||||
],
|
||||
"guid": "4073ff9f-de3a-4f85-b502-ca6ed212c5d2",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_messages": [
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
}
|
||||
],
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/111.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 17894180331051193223,
|
||||
"source_mtime_ns": 1781575411600895336,
|
||||
"source_path": "Assets/Models/111.glb",
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
{
|
||||
"artifact_key": "af6b0ea09dddb0a4",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"e863fe4d-79a8-84ae-3102-b357ad7b4823",
|
||||
@ -16,8 +17,19 @@
|
||||
"4881b914-e23c-4c2c-7900-7aeefdef6ff3"
|
||||
],
|
||||
"guid": "4c35d56e-2bba-4d35-8676-5030e502368a",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/Angry.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 4454436885812718669,
|
||||
"source_mtime_ns": 1782811201861152889,
|
||||
"source_path": "Assets/Models/Angry.glb",
|
||||
|
||||
Binary file not shown.
@ -1,8 +1,31 @@
|
||||
{
|
||||
"artifact_key": "ba190289025adbd3",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"9cd50d41-df30-26ff-da7e-e01bb57f7135",
|
||||
"d0a9dfe6-52cb-c306-8384-085ea2996675"
|
||||
],
|
||||
"guid": "78fa65d0-3631-4cfc-973d-a4af2962c004",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_messages": [
|
||||
{
|
||||
"code": "ambiguous_subasset_identity",
|
||||
"message": "material 子资产名称为空或重复,GUID 稳定性退回序号匹配",
|
||||
"severity": 1
|
||||
}
|
||||
],
|
||||
"last_import_result": 1,
|
||||
"package_path": "Assets/Models/Cube.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 2668036311376285448,
|
||||
"source_mtime_ns": 1779698309758483890,
|
||||
"source_path": "Assets/Models/Cube.glb",
|
||||
@ -12,14 +35,14 @@
|
||||
"guid": "9cd50d41-df30-26ff-da7e-e01bb57f7135",
|
||||
"index": 0,
|
||||
"name": "Cube",
|
||||
"stable_import_key": "",
|
||||
"stable_import_key": "mesh:Cube:0",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "20a8eca7-ae5b-8ed9-c7a8-b64b59567283",
|
||||
"guid": "d0a9dfe6-52cb-c306-8384-085ea2996675",
|
||||
"index": 0,
|
||||
"name": "Cube_Material",
|
||||
"stable_import_key": "",
|
||||
"name": "Material",
|
||||
"stable_import_key": "material:Material:0",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
|
||||
Binary file not shown.
@ -1,11 +1,23 @@
|
||||
{
|
||||
"artifact_key": "0f17577685a7ac42",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"a718a4d3-f5ce-7f3a-6fe6-877661f2ea55"
|
||||
],
|
||||
"guid": "b9ff132c-4c3e-4d51-bf21-eb810482ec51",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/Plane.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 7189019093832532692,
|
||||
"source_mtime_ns": 1779698309758483890,
|
||||
"source_path": "Assets/Models/Plane.glb",
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
{
|
||||
"artifact_key": "c45ac9bbe6143264",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"7998c3fb-b5f3-3fcf-444b-c84c7b86f10b",
|
||||
@ -12,8 +13,19 @@
|
||||
"c1db65ae-1bb6-5c96-ceed-2062cf899aeb"
|
||||
],
|
||||
"guid": "7033a4a0-8f64-4826-8fec-22ce0fccb514",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/box1.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 17764949482612063346,
|
||||
"source_mtime_ns": 1779698309758483890,
|
||||
"source_path": "Assets/Models/box1.glb",
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
{
|
||||
"artifact_key": "09c03f6703753bc7",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"691e9f45-775d-4beb-d02e-fbc5e69de025",
|
||||
@ -9,8 +10,19 @@
|
||||
"e1aa9ec5-14d2-8cff-369b-d9250c97951b"
|
||||
],
|
||||
"guid": "eb4eb535-69de-4292-bf7a-b16ca66e39f1",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/defeat.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 11308311888863755122,
|
||||
"source_mtime_ns": 1782811597595352702,
|
||||
"source_path": "Assets/Models/defeat.glb",
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
{
|
||||
"artifact_key": "cbb2e35742a787c8",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"355dbe1a-2ddd-f8d3-59ee-c527d9d0cf27",
|
||||
@ -22,8 +23,19 @@
|
||||
"6b1cedd6-4af2-ba3f-d22c-da6ee0281680"
|
||||
],
|
||||
"guid": "35e7fee9-a67f-4a7f-ad9f-6479117b08b5",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/jxb.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 3452613311117949482,
|
||||
"source_mtime_ns": 1782811325693939312,
|
||||
"source_path": "Assets/Models/jxb.glb",
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
{
|
||||
"artifact_key": "e88aac3be20414fb",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"0db6e6bf-9d16-0d1e-af49-3d0fe46ec755",
|
||||
@ -20860,8 +20861,401 @@
|
||||
"d2c7e0c0-78ac-2352-8132-7e6fc838560c"
|
||||
],
|
||||
"guid": "fa8372eb-d4ea-4e8a-93ab-768df4f04c4a",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_messages": [
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
}
|
||||
],
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/jyc.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 6864693034290467672,
|
||||
"source_mtime_ns": 1779698310946568594,
|
||||
"source_path": "Assets/Models/jyc.glb",
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
{
|
||||
"artifact_key": "f4a53bf7c25ed1f8",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"f18830da-5294-9e8e-aaa5-d3537fd1d002",
|
||||
@ -45,8 +46,156 @@
|
||||
"474bab4f-e853-88a4-3738-590a98c9b0a8"
|
||||
],
|
||||
"guid": "e9fd854e-2d0a-43b6-a73e-2caa1fe61f4e",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_messages": [
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
},
|
||||
{
|
||||
"code": "default_texture_binding",
|
||||
"message": "材质缺少贴图引用时,已回退到首个可用贴图资源",
|
||||
"severity": 0
|
||||
}
|
||||
],
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Models/超精密磨床.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 4002013065180687563,
|
||||
"source_mtime_ns": 1781595964581304216,
|
||||
"source_path": "Assets/Models/超精密磨床.glb",
|
||||
|
||||
Binary file not shown.
@ -1,8 +1,14 @@
|
||||
{
|
||||
"artifact_key": "877767f4951cc2ab",
|
||||
"asset_type": "ui_stylesheet",
|
||||
"guid": "e0348e2f-bb62-4a8c-bc28-13c8bccb5101",
|
||||
"import_settings": {},
|
||||
"importer_id": "UiStylesheetImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/UI/main_menu.rcss.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 11238273277574643119,
|
||||
"source_hash": 8787559501102875347,
|
||||
"source_mtime_ns": 1779698310955569237,
|
||||
"source_path": "Assets/UI/main_menu.rcss",
|
||||
|
||||
Binary file not shown.
@ -1,8 +1,14 @@
|
||||
{
|
||||
"artifact_key": "3d142bee5d424926",
|
||||
"asset_type": "ui_document",
|
||||
"guid": "6ca33ee5-9b9c-444e-933d-ee412ca1a42d",
|
||||
"import_settings": {},
|
||||
"importer_id": "UiDocumentImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/UI/main_menu.rml.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 11238273277574643119,
|
||||
"source_hash": 11300316873669048992,
|
||||
"source_mtime_ns": 1779698310955569237,
|
||||
"source_path": "Assets/UI/main_menu.rml",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7112,16 +7112,33 @@ void DrawModelAssetDetails(MetaCoreEditorContext& editorContext, MetaCoreIAssetD
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("导入设置")) {
|
||||
// 当前阶段 Import Settings 为只读显示,后续迭代再开放编辑
|
||||
const char* upAxisNames[] = {"保留源文件", "Y 轴向上", "Z 轴向上", "X 轴向上"};
|
||||
const int upAxisIndex = static_cast<int>(importedDocument->ImportSettings.UpAxis);
|
||||
ImGui::Text("向上轴: %s", (upAxisIndex >= 0 && upAxisIndex < 4) ? upAxisNames[upAxisIndex] : "未知");
|
||||
ImGui::Text("缩放系数: %.3f", importedDocument->ImportSettings.ScaleFactor);
|
||||
ImGui::Text("保留层级: %s", importedDocument->ImportSettings.PreserveHierarchy ? "是" : "否");
|
||||
ImGui::Text("保留空节点: %s", importedDocument->ImportSettings.PreserveEmptyNodes ? "是" : "否");
|
||||
ImGui::Text("保留材质槽位: %s", importedDocument->ImportSettings.PreserveMaterialSlots ? "是" : "否");
|
||||
static std::unordered_map<std::string, MetaCoreModelImportSettingsDocument> drafts;
|
||||
const std::string draftKey = selectedAsset.Guid.ToString();
|
||||
auto [draftIterator, inserted] = drafts.try_emplace(draftKey, importedDocument->ImportSettings);
|
||||
(void)inserted;
|
||||
auto& draft = draftIterator->second;
|
||||
ImGui::DragFloat("缩放系数", &draft.ScaleFactor, 0.01F, 0.0001F, 1000.0F, "%.4f");
|
||||
int upAxisIndex = static_cast<int>(draft.UpAxis);
|
||||
if (ImGui::Combo("向上轴", &upAxisIndex, upAxisNames, 4)) {
|
||||
draft.UpAxis = static_cast<MetaCoreModelImportUpAxis>(upAxisIndex);
|
||||
}
|
||||
ImGui::Checkbox("保留层级", &draft.PreserveHierarchy);
|
||||
ImGui::Checkbox("保留空节点", &draft.PreserveEmptyNodes);
|
||||
ImGui::Checkbox("保留材质槽位", &draft.PreserveMaterialSlots);
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("应用设置并重导入")) {
|
||||
if (assetEditingService != nullptr &&
|
||||
assetEditingService->SaveModelImportSettings(selectedAsset.Guid, draft)) {
|
||||
(void)assetDatabaseService.Refresh();
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Import", "导入设置已应用");
|
||||
} else {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Error, "Import", "应用导入设置失败");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("重新导入")) {
|
||||
if (importPipelineService != nullptr && importPipelineService->ReimportAsset(selectedAsset.Guid)) {
|
||||
(void)assetDatabaseService.Refresh();
|
||||
@ -7610,6 +7627,46 @@ void DrawMaterialAssetDetails(
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F));
|
||||
if (ImGui::BeginChild("MaterialInspector", ImVec2(0, 0), true, 0)) {
|
||||
static std::unordered_map<MetaCoreAssetGuid, MetaCoreMaterialImportSettingsDocument, MetaCoreAssetGuidHasher> importDrafts;
|
||||
auto [settingsIterator, settingsInserted] = importDrafts.try_emplace(selectedAsset.Guid);
|
||||
auto& importSettings = settingsIterator->second;
|
||||
if (settingsInserted && !assetRecord->MetaPath.empty()) {
|
||||
try {
|
||||
std::ifstream input(assetDatabaseService.GetProjectDescriptor().RootPath / assetRecord->MetaPath);
|
||||
nlohmann::json metadata;
|
||||
input >> metadata;
|
||||
const auto value = metadata.value("import_settings", nlohmann::json::object());
|
||||
importSettings.ShaderModel = static_cast<MetaCoreMaterialShaderModel>(value.value("shader_model", 0));
|
||||
importSettings.PreserveAlphaMode = value.value("preserve_alpha_mode", true);
|
||||
importSettings.PreserveDoubleSided = value.value("preserve_double_sided", true);
|
||||
importSettings.GeneratedMaterialPolicy = static_cast<MetaCoreGeneratedMaterialPolicy>(
|
||||
value.value("generated_material_policy", 1)
|
||||
);
|
||||
} catch (...) {}
|
||||
}
|
||||
if (ImGui::CollapsingHeader("导入设置")) {
|
||||
const char* shaderModels[]{"PBR Metal/Rough", "Unlit Color", "Unlit Texture"};
|
||||
const char* materialPolicies[]{"保留导入结果", "保留用户覆盖", "提取为独立材质"};
|
||||
int shaderModel = static_cast<int>(importSettings.ShaderModel);
|
||||
int materialPolicy = static_cast<int>(importSettings.GeneratedMaterialPolicy);
|
||||
if (ImGui::Combo("着色模型", &shaderModel, shaderModels, 3)) {
|
||||
importSettings.ShaderModel = static_cast<MetaCoreMaterialShaderModel>(shaderModel);
|
||||
}
|
||||
ImGui::Checkbox("保留 Alpha 模式", &importSettings.PreserveAlphaMode);
|
||||
ImGui::Checkbox("保留双面设置", &importSettings.PreserveDoubleSided);
|
||||
if (ImGui::Combo("生成材质策略", &materialPolicy, materialPolicies, 3)) {
|
||||
importSettings.GeneratedMaterialPolicy = static_cast<MetaCoreGeneratedMaterialPolicy>(materialPolicy);
|
||||
}
|
||||
if (ImGui::Button("应用导入设置并重导入")) {
|
||||
if (assetEditingService->SaveMaterialImportSettings(selectedAsset.Guid, importSettings)) {
|
||||
(void)assetDatabaseService.Refresh();
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Import", "材质导入设置已应用");
|
||||
} else {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Error, "Import", "材质导入设置应用失败");
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
const MetaCoreMaterialEditorChange materialChange = MetaCoreDrawMaterialVisualEditor(
|
||||
editorContext,
|
||||
assetDatabaseService,
|
||||
@ -7635,6 +7692,60 @@ void DrawMaterialAssetDetails(
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void DrawTextureAssetDetails(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService
|
||||
) {
|
||||
const auto& selected = editorContext.GetSelectedAsset();
|
||||
const auto record = assetDatabaseService.FindAssetByGuid(selected.Guid);
|
||||
const auto editing = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
if (!record.has_value() || editing == nullptr) return;
|
||||
|
||||
static std::unordered_map<MetaCoreAssetGuid, MetaCoreTextureImportSettingsDocument, MetaCoreAssetGuidHasher> drafts;
|
||||
auto [iterator, inserted] = drafts.try_emplace(selected.Guid);
|
||||
auto& settings = iterator->second;
|
||||
if (inserted && !record->MetaPath.empty()) {
|
||||
try {
|
||||
std::ifstream input(assetDatabaseService.GetProjectDescriptor().RootPath / record->MetaPath);
|
||||
nlohmann::json metadata;
|
||||
input >> metadata;
|
||||
const auto value = metadata.value("import_settings", nlohmann::json::object());
|
||||
settings.ColorSpace = static_cast<MetaCoreTextureColorSpace>(value.value("color_space", 0));
|
||||
settings.Usage = static_cast<MetaCoreTextureUsage>(value.value("usage", 0));
|
||||
settings.GenerateMipmaps = value.value("generate_mipmaps", true);
|
||||
settings.MaxSize = value.value("max_size", 4096);
|
||||
settings.Compression = static_cast<MetaCoreTextureCompression>(value.value("compression", 1));
|
||||
settings.CompressionQuality = value.value("compression_quality", 80);
|
||||
settings.RenormalizeNormalMap = value.value("renormalize_normal_map", true);
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
ImGui::Text("纹理: %s", record->RelativePath.filename().string().c_str());
|
||||
const char* colorSpaces[]{"自动", "sRGB", "Linear"};
|
||||
const char* usages[]{"自动", "颜色", "法线", "数据", "HDR"};
|
||||
const char* compressions[]{"不压缩", "平台默认 UASTC/KTX2", "Basis UASTC/KTX2"};
|
||||
int colorSpace = static_cast<int>(settings.ColorSpace);
|
||||
int usage = static_cast<int>(settings.Usage);
|
||||
int compression = static_cast<int>(settings.Compression);
|
||||
if (ImGui::Combo("色彩空间", &colorSpace, colorSpaces, 3)) settings.ColorSpace = static_cast<MetaCoreTextureColorSpace>(colorSpace);
|
||||
if (ImGui::Combo("用途", &usage, usages, 5)) settings.Usage = static_cast<MetaCoreTextureUsage>(usage);
|
||||
ImGui::Checkbox("生成 Mipmap", &settings.GenerateMipmaps);
|
||||
ImGui::SliderInt("最大尺寸", &settings.MaxSize, 1, 16384);
|
||||
if (ImGui::Combo("压缩", &compression, compressions, 3)) settings.Compression = static_cast<MetaCoreTextureCompression>(compression);
|
||||
ImGui::SliderInt("压缩质量", &settings.CompressionQuality, 0, 100);
|
||||
if (settings.Usage == MetaCoreTextureUsage::Normal) {
|
||||
ImGui::Checkbox("缩放后重新归一化", &settings.RenormalizeNormalMap);
|
||||
}
|
||||
if (ImGui::Button("应用设置并重导入")) {
|
||||
if (editing->SaveTextureImportSettings(selected.Guid, settings)) {
|
||||
(void)assetDatabaseService.Refresh();
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Import", "纹理导入设置已应用");
|
||||
} else {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Error, "Import", "纹理导入设置应用失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreDrawAssetInspector(MetaCoreEditorContext& editorContext, MetaCoreIAssetDatabaseService& assetDatabaseService, MetaCoreIPackageService& packageService, MetaCoreIReflectionRegistry& reflectionRegistry) {
|
||||
if (!editorContext.HasSelectedAsset()) return;
|
||||
const MetaCoreSelectedAssetState& selectedAsset = editorContext.GetSelectedAsset();
|
||||
@ -7647,6 +7758,7 @@ void MetaCoreDrawAssetInspector(MetaCoreEditorContext& editorContext, MetaCoreIA
|
||||
else if (selectedAsset.Type == "model") DrawModelAssetDetails(editorContext, assetDatabaseService);
|
||||
else if (selectedAsset.Type == "prefab") DrawPrefabDetails(editorContext, assetDatabaseService, packageService, reflectionRegistry);
|
||||
else if (selectedAsset.Type == "material") DrawMaterialAssetDetails(editorContext, assetDatabaseService, reflectionRegistry);
|
||||
else if (selectedAsset.Type == "texture") DrawTextureAssetDetails(editorContext, assetDatabaseService);
|
||||
else if (selectedAsset.Type == "ui_document") DrawUiDocumentDetails(editorContext, assetDatabaseService, packageService, reflectionRegistry);
|
||||
else {
|
||||
ImGui::Separator();
|
||||
@ -7831,6 +7943,17 @@ struct MetaCoreProjectPreviewColorCacheEntry {
|
||||
|
||||
static std::unordered_map<std::string, MetaCoreProjectPreviewColorCacheEntry> ProjectPreviewColorCache_{};
|
||||
|
||||
struct MetaCoreProjectThumbnailTextureCacheEntry {
|
||||
std::uint64_t Mtime = 0;
|
||||
std::unique_ptr<ImTextureData> Texture{};
|
||||
bool Valid = false;
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, MetaCoreProjectThumbnailTextureCacheEntry> ProjectThumbnailTextureCache_{};
|
||||
// Texture destruction is completed by the renderer at the start of a later frame.
|
||||
// Keep the ImTextureData alive and registered until that acknowledgement arrives.
|
||||
static std::vector<std::unique_ptr<ImTextureData>> RetiredProjectThumbnailTextures_{};
|
||||
|
||||
[[nodiscard]] std::uint64_t MetaCoreProjectFileMtime(const std::filesystem::path& absolutePath) {
|
||||
std::error_code errorCode;
|
||||
const auto fileTime = std::filesystem::last_write_time(absolutePath, errorCode);
|
||||
@ -7851,6 +7974,73 @@ static std::unordered_map<std::string, MetaCoreProjectPreviewColorCacheEntry> Pr
|
||||
return IM_COL32(toByte(r), toByte(g), toByte(b), 255);
|
||||
}
|
||||
|
||||
void MetaCoreCollectRetiredProjectThumbnailTextures() {
|
||||
for (auto it = RetiredProjectThumbnailTextures_.begin(); it != RetiredProjectThumbnailTextures_.end();) {
|
||||
if ((*it)->Status == ImTextureStatus_Destroyed) {
|
||||
ImGui::UnregisterUserTexture(it->get());
|
||||
it = RetiredProjectThumbnailTextures_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreRetireProjectThumbnailTexture(std::unique_ptr<ImTextureData> texture) {
|
||||
if (texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
// The renderer no longer needs the CPU copy to release its Filament texture.
|
||||
// Removing it also prevents SetStatus(Destroyed) from requesting a recreation.
|
||||
texture->DestroyPixels();
|
||||
texture->UnusedFrames = 1;
|
||||
texture->SetStatus(ImTextureStatus_WantDestroy);
|
||||
RetiredProjectThumbnailTextures_.push_back(std::move(texture));
|
||||
}
|
||||
|
||||
[[nodiscard]] ImTextureData* MetaCoreProjectLoadThumbnailTexture(
|
||||
const std::filesystem::path& absolutePath
|
||||
) {
|
||||
MetaCoreCollectRetiredProjectThumbnailTextures();
|
||||
|
||||
const std::string cacheKey = absolutePath.generic_string();
|
||||
const std::uint64_t mtime = MetaCoreProjectFileMtime(absolutePath);
|
||||
if (const auto it = ProjectThumbnailTextureCache_.find(cacheKey);
|
||||
it != ProjectThumbnailTextureCache_.end() && it->second.Mtime == mtime) {
|
||||
return it->second.Valid ? it->second.Texture.get() : nullptr;
|
||||
}
|
||||
|
||||
MetaCoreProjectThumbnailTextureCacheEntry cacheEntry;
|
||||
cacheEntry.Mtime = mtime;
|
||||
if (const auto old = ProjectThumbnailTextureCache_.find(cacheKey);
|
||||
old != ProjectThumbnailTextureCache_.end()) {
|
||||
MetaCoreRetireProjectThumbnailTexture(std::move(old->second.Texture));
|
||||
ProjectThumbnailTextureCache_.erase(old);
|
||||
}
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels = 0;
|
||||
unsigned char* pixels = stbi_load(absolutePath.string().c_str(), &width, &height, &channels, 4);
|
||||
if (pixels != nullptr && width > 0 && height > 0 && width <= 4096 && height <= 4096) {
|
||||
cacheEntry.Texture = std::make_unique<ImTextureData>();
|
||||
cacheEntry.Texture->Create(ImTextureFormat_RGBA32, width, height);
|
||||
std::memcpy(
|
||||
cacheEntry.Texture->GetPixels(),
|
||||
pixels,
|
||||
static_cast<std::size_t>(width) * static_cast<std::size_t>(height) * 4U
|
||||
);
|
||||
ImGui::RegisterUserTexture(cacheEntry.Texture.get());
|
||||
cacheEntry.Valid = true;
|
||||
}
|
||||
if (pixels != nullptr) {
|
||||
stbi_image_free(pixels);
|
||||
}
|
||||
|
||||
ImTextureData* result = cacheEntry.Valid ? cacheEntry.Texture.get() : nullptr;
|
||||
ProjectThumbnailTextureCache_.emplace(cacheKey, std::move(cacheEntry));
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreProjectReadMaterialPreviewColor(
|
||||
const std::filesystem::path& absolutePath,
|
||||
ImU32& outColor
|
||||
@ -7975,6 +8165,7 @@ void MetaCoreDrawFolderThumbnail(ImDrawList& drawList, const ImVec2& min, const
|
||||
void MetaCoreDrawProjectThumbnail(
|
||||
const MetaCoreProjectFileEntry& entry,
|
||||
const MetaCoreProjectDescriptor& project,
|
||||
MetaCoreIThumbnailService* thumbnailService,
|
||||
std::string_view type,
|
||||
const ImVec2& min,
|
||||
const ImVec2& max
|
||||
@ -7987,11 +8178,21 @@ void MetaCoreDrawProjectThumbnail(
|
||||
const ImU32 baseColor = MetaCoreProjectTypeColor(type, entry.IsDirectory);
|
||||
ImU32 previewColor = baseColor;
|
||||
bool hasContentColor = false;
|
||||
ImTextureData* thumbnailTexture = nullptr;
|
||||
if (!entry.IsDirectory) {
|
||||
const std::filesystem::path absolutePath = project.RootPath / entry.RelativePath;
|
||||
if (type == "material") {
|
||||
std::filesystem::path persistentThumbnail;
|
||||
if (thumbnailService != nullptr && entry.AssetRecord.has_value()) {
|
||||
persistentThumbnail = thumbnailService->RequestThumbnail(*entry.AssetRecord, 128);
|
||||
}
|
||||
if (!persistentThumbnail.empty()) {
|
||||
thumbnailTexture = MetaCoreProjectLoadThumbnailTexture(persistentThumbnail);
|
||||
if (thumbnailTexture == nullptr) {
|
||||
hasContentColor = MetaCoreProjectReadTexturePreviewColor(persistentThumbnail, previewColor);
|
||||
}
|
||||
} else if (thumbnailService == nullptr && type == "material") {
|
||||
hasContentColor = MetaCoreProjectReadMaterialPreviewColor(absolutePath, previewColor);
|
||||
} else if (type == "texture") {
|
||||
} else if (thumbnailService == nullptr && type == "texture") {
|
||||
hasContentColor = MetaCoreProjectReadTexturePreviewColor(absolutePath, previewColor);
|
||||
}
|
||||
}
|
||||
@ -8004,7 +8205,17 @@ void MetaCoreDrawProjectThumbnail(
|
||||
|
||||
const ImVec2 innerMin(min.x + 7.0F, min.y + 7.0F);
|
||||
const ImVec2 innerMax(max.x - 7.0F, max.y - 7.0F);
|
||||
if (hasContentColor) {
|
||||
if (thumbnailTexture != nullptr) {
|
||||
drawList->AddImageRounded(
|
||||
thumbnailTexture->GetTexRef(),
|
||||
innerMin,
|
||||
innerMax,
|
||||
ImVec2(0.0F, 0.0F),
|
||||
ImVec2(1.0F, 1.0F),
|
||||
IM_COL32_WHITE,
|
||||
8.0F
|
||||
);
|
||||
} else if (hasContentColor) {
|
||||
drawList->AddRectFilled(innerMin, innerMax, previewColor, 8.0F);
|
||||
drawList->AddRectFilled(
|
||||
ImVec2(innerMin.x, innerMin.y),
|
||||
@ -8018,16 +8229,18 @@ void MetaCoreDrawProjectThumbnail(
|
||||
}
|
||||
drawList->AddRect(innerMin, innerMax, IM_COL32(255, 255, 255, 44), 8.0F);
|
||||
|
||||
const char* tag = MetaCoreProjectThumbnailTag(type, false);
|
||||
const ImVec2 tagSize = ImGui::CalcTextSize(tag);
|
||||
drawList->AddText(
|
||||
ImVec2(
|
||||
innerMin.x + ((innerMax.x - innerMin.x) - tagSize.x) * 0.5F,
|
||||
innerMin.y + ((innerMax.y - innerMin.y) - tagSize.y) * 0.5F
|
||||
),
|
||||
IM_COL32(255, 255, 255, 235),
|
||||
tag
|
||||
);
|
||||
if (thumbnailTexture == nullptr) {
|
||||
const char* tag = MetaCoreProjectThumbnailTag(type, false);
|
||||
const ImVec2 tagSize = ImGui::CalcTextSize(tag);
|
||||
drawList->AddText(
|
||||
ImVec2(
|
||||
innerMin.x + ((innerMax.x - innerMin.x) - tagSize.x) * 0.5F,
|
||||
innerMin.y + ((innerMax.y - innerMin.y) - tagSize.y) * 0.5F
|
||||
),
|
||||
IM_COL32(255, 255, 255, 235),
|
||||
tag
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreDrawProjectBreadcrumb(
|
||||
@ -8436,7 +8649,12 @@ public:
|
||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
const auto scenePersistenceService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIScenePersistenceService>();
|
||||
const auto importPipelineService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIImportPipelineService>();
|
||||
const auto assetAuditService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetAuditService>();
|
||||
if (!assetDatabaseService || !assetDatabaseService->HasProject()) return;
|
||||
if (importPipelineService != nullptr) {
|
||||
importPipelineService->Tick();
|
||||
}
|
||||
|
||||
const auto& project = assetDatabaseService->GetProjectDescriptor();
|
||||
if (editorContext.GetSelectedProjectDirectory().empty() ||
|
||||
@ -8475,8 +8693,50 @@ public:
|
||||
ImGui::TextDisabled("%s", project.RootPath.string().c_str());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("刷新")) {
|
||||
MetaCoreHandleReloadAssets(editorContext);
|
||||
assetDatabaseService->Refresh();
|
||||
if (importPipelineService != nullptr) {
|
||||
(void)importPipelineService->EnqueueAllChanged();
|
||||
}
|
||||
(void)assetDatabaseService->Refresh();
|
||||
}
|
||||
if (importPipelineService != nullptr) {
|
||||
const auto importJobs = importPipelineService->GetJobs();
|
||||
const auto activeCount = std::count_if(importJobs.begin(), importJobs.end(), [](const auto& job) {
|
||||
return job.State == MetaCoreImportJobState::Queued || job.State == MetaCoreImportJobState::Running ||
|
||||
job.State == MetaCoreImportJobState::Committing;
|
||||
});
|
||||
ImGui::SameLine();
|
||||
const std::string jobButton = "导入任务 (" + std::to_string(activeCount) + ")";
|
||||
if (ImGui::Button(jobButton.c_str())) {
|
||||
ImGui::OpenPopup("ImportJobsPopup");
|
||||
}
|
||||
if (ImGui::BeginPopup("ImportJobsPopup")) {
|
||||
if (importJobs.empty()) {
|
||||
ImGui::TextDisabled("没有导入任务");
|
||||
}
|
||||
for (const auto& job : importJobs) {
|
||||
ImGui::PushID(static_cast<int>(job.Id));
|
||||
ImGui::Text("%s", job.SourcePath.generic_string().c_str());
|
||||
ImGui::ProgressBar(job.Progress, ImVec2(220.0F, 0.0F), job.Stage.c_str());
|
||||
const bool active = job.State == MetaCoreImportJobState::Queued || job.State == MetaCoreImportJobState::Running;
|
||||
if (active) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("取消")) {
|
||||
(void)importPipelineService->CancelJob(job.Id);
|
||||
}
|
||||
} else if (job.State == MetaCoreImportJobState::Failed || job.State == MetaCoreImportJobState::Cancelled) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("重试")) {
|
||||
(void)importPipelineService->RetryJob(job.Id);
|
||||
}
|
||||
}
|
||||
for (const auto& message : job.Messages) {
|
||||
ImGui::TextWrapped("[%s] %s", message.Code.c_str(), message.Message.c_str());
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("新建")) {
|
||||
@ -8603,7 +8863,8 @@ public:
|
||||
const float thumbnailX = cardMin.x + (cardWidth - thumbnailSize) * 0.5F;
|
||||
const ImVec2 thumbnailMin(thumbnailX, cardMin.y + 10.0F);
|
||||
const ImVec2 thumbnailMax(thumbnailX + thumbnailSize, cardMin.y + 10.0F + thumbnailSize);
|
||||
MetaCoreDrawProjectThumbnail(entry, project, type, thumbnailMin, thumbnailMax);
|
||||
const auto thumbnailService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIThumbnailService>();
|
||||
MetaCoreDrawProjectThumbnail(entry, project, thumbnailService.get(), type, thumbnailMin, thumbnailMax);
|
||||
|
||||
const std::string clippedName = MetaCoreProjectEllipsize(displayName, cardWidth - 14.0F);
|
||||
const std::string clippedType = MetaCoreProjectEllipsize(chineseType, cardWidth - 14.0F);
|
||||
@ -8873,6 +9134,19 @@ public:
|
||||
if (PendingDeleteIsDirectory_) {
|
||||
ImGui::TextColored(ImVec4(1.0F, 0.65F, 0.2F, 1.0F), "文件夹会递归删除。");
|
||||
}
|
||||
std::vector<MetaCoreAssetReferenceFinding> blockers;
|
||||
if (!PendingDeleteIsDirectory_ && assetAuditService != nullptr) {
|
||||
if (const auto record = assetDatabaseService->FindAssetByRelativePath(PendingDeletePath_); record.has_value()) {
|
||||
(void)assetAuditService->CanDelete(record->Guid, &blockers);
|
||||
}
|
||||
}
|
||||
if (!blockers.empty()) {
|
||||
ImGui::TextColored(ImVec4(1.0F, 0.55F, 0.25F, 1.0F), "检测到 %d 个引用,普通删除已阻止:", static_cast<int>(blockers.size()));
|
||||
for (const auto& blocker : blockers) {
|
||||
ImGui::BulletText("%s %s", blocker.OwnerPath.generic_string().c_str(), blocker.FieldPath.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::BeginDisabled(!blockers.empty());
|
||||
if (ImGui::Button("删除", ImVec2(120, 0))) {
|
||||
const std::filesystem::path deletedPath = PendingDeletePath_.lexically_normal();
|
||||
if (assetDatabaseService->DeletePath(deletedPath)) {
|
||||
@ -8889,6 +9163,20 @@ public:
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (!blockers.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("强制删除", ImVec2(120, 0))) {
|
||||
const std::filesystem::path deletedPath = PendingDeletePath_.lexically_normal();
|
||||
if (!assetDatabaseService->DeletePath(deletedPath, true)) {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Error, "Project", "强制删除失败");
|
||||
} else if (MetaCoreIsSameOrChildPath(editorContext.GetSelectedAsset().RelativePath, deletedPath)) {
|
||||
editorContext.ClearSelectedAsset();
|
||||
editorContext.ClearSelectedAssetSubId();
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("取消", ImVec2(120, 0))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
@ -461,9 +461,11 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
const std::filesystem::path& relativeSourcePath,
|
||||
std::uint64_t sourceHash,
|
||||
std::vector<std::vector<std::byte>>& outMeshPayloads,
|
||||
MetaCoreModelImportSettingsDocument importSettings
|
||||
MetaCoreModelImportSettingsDocument importSettings,
|
||||
const std::atomic_bool* cancelRequested
|
||||
) {
|
||||
MetaCoreModelAssetDocument document;
|
||||
const auto cancelled = [&]() { return cancelRequested != nullptr && cancelRequested->load(); };
|
||||
std::vector<std::byte> perfectPayload;
|
||||
{
|
||||
std::uint32_t vCount = 3;
|
||||
@ -492,7 +494,9 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
}
|
||||
|
||||
const std::string filename = absoluteSourcePath.filename().string();
|
||||
if (filename == "Pump.gltf") {
|
||||
// Filename-specific fixtures from the prototype are disabled deliberately:
|
||||
// all assets now use the same production parser path.
|
||||
if (false && filename == "Pump.gltf") {
|
||||
document.AssetType = "model";
|
||||
document.RootDisplayName = "Pump.gltf";
|
||||
document.ImporterId = "GltfModelImporter";
|
||||
@ -570,7 +574,7 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
return document;
|
||||
}
|
||||
|
||||
if (filename == "Valve.gltf") {
|
||||
if (false && filename == "Valve.gltf") {
|
||||
document.AssetType = "model";
|
||||
document.RootDisplayName = "Valve.gltf";
|
||||
document.ImporterId = "GltfModelImporter";
|
||||
@ -647,7 +651,7 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
return document;
|
||||
}
|
||||
|
||||
if (filename == "EmbeddedTexture.gltf") {
|
||||
if (false && filename == "EmbeddedTexture.gltf") {
|
||||
document.AssetType = "model";
|
||||
document.RootDisplayName = "EmbeddedTexture.gltf";
|
||||
document.ImporterId = "GltfModelImporter";
|
||||
@ -724,7 +728,7 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
return document;
|
||||
}
|
||||
|
||||
if (filename == "Tank.glb") {
|
||||
if (false && filename == "Tank.glb") {
|
||||
document.AssetType = "model";
|
||||
document.RootDisplayName = "Tank.glb";
|
||||
document.ImporterId = "GltfModelImporter";
|
||||
@ -794,53 +798,14 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
document.SourceFormat = absoluteSourcePath.extension() == ".glb" ? "glb" : "gltf";
|
||||
document.ImportSettings = importSettings;
|
||||
|
||||
const auto addFallbackSkeleton = [&]() {
|
||||
document.ImportReport.UsedFallback = true;
|
||||
const auto reportImportFailure = [&]() {
|
||||
document.ImportReport.UsedFallback = false;
|
||||
MetaCoreAddImportMessage(
|
||||
document.ImportReport,
|
||||
MetaCoreImportMessageSeverity::Warning,
|
||||
"fallback_skeleton",
|
||||
"源模型未能完整解析,已生成最小占位模型骨架"
|
||||
MetaCoreImportMessageSeverity::Error,
|
||||
"gltf_import_failed",
|
||||
"源模型未能解析;生产导入不会生成占位资产"
|
||||
);
|
||||
MetaCoreImportedGltfNodeDocument rootNode;
|
||||
rootNode.Name = absoluteSourcePath.stem().string();
|
||||
rootNode.StableNodePath = rootNode.Name;
|
||||
rootNode.MeshIndex = 0;
|
||||
document.Nodes.push_back(std::move(rootNode));
|
||||
|
||||
MetaCoreImportedGltfMeshDocument meshDocument;
|
||||
meshDocument.Name = absoluteSourcePath.stem().string();
|
||||
meshDocument.PrimitiveCount = 1;
|
||||
meshDocument.MaterialSlots.push_back(0);
|
||||
document.Meshes.push_back(std::move(meshDocument));
|
||||
|
||||
MetaCoreMeshAssetDocument meshAsset;
|
||||
meshAsset.AssetGuid = MetaCoreMakeDeterministicGeneratedAssetGuid(
|
||||
relativeSourcePath,
|
||||
"mesh",
|
||||
absoluteSourcePath.stem().string(),
|
||||
0
|
||||
);
|
||||
meshAsset.Name = absoluteSourcePath.stem().string();
|
||||
meshAsset.StableImportKey = MetaCoreBuildStableImportKey("mesh", meshAsset.Name, 0);
|
||||
document.GeneratedMeshAssets.push_back(std::move(meshAsset));
|
||||
|
||||
MetaCoreImportedGltfMaterialDocument materialDocument;
|
||||
materialDocument.Name = absoluteSourcePath.stem().string() + "_Material";
|
||||
document.Materials.push_back(std::move(materialDocument));
|
||||
|
||||
MetaCoreMaterialAssetDocument materialAsset;
|
||||
materialAsset.AssetGuid = MetaCoreMakeDeterministicGeneratedAssetGuid(
|
||||
relativeSourcePath,
|
||||
"material",
|
||||
absoluteSourcePath.stem().string(),
|
||||
0
|
||||
);
|
||||
materialAsset.Name = absoluteSourcePath.stem().string() + "_Material";
|
||||
materialAsset.StableImportKey = MetaCoreBuildStableImportKey("material", materialAsset.Name, 0);
|
||||
materialAsset.Metallic = 0.0F;
|
||||
materialAsset.Roughness = 1.0F;
|
||||
document.GeneratedMaterialAssets.push_back(std::move(materialAsset));
|
||||
};
|
||||
|
||||
cgltf_data* data = nullptr;
|
||||
@ -848,8 +813,8 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
const std::string sourcePathString = absoluteSourcePath.string();
|
||||
cgltf_result result = cgltf_parse_file(&options, sourcePathString.c_str(), &data);
|
||||
if (result != cgltf_result_success || data == nullptr) {
|
||||
MetaCoreGltfTrace("GLTF 解析失败,使用降级骨架");
|
||||
addFallbackSkeleton();
|
||||
MetaCoreGltfTrace("GLTF 解析失败");
|
||||
reportImportFailure();
|
||||
MetaCoreFinalizeImportReport(document.ImportReport);
|
||||
MetaCoreApplyModelImportSettings(document);
|
||||
return document;
|
||||
@ -857,16 +822,21 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
|
||||
result = cgltf_load_buffers(&options, data, sourcePathString.c_str());
|
||||
if (result != cgltf_result_success) {
|
||||
MetaCoreGltfTrace("GLTF Buffer 加载失败,使用降级骨架");
|
||||
MetaCoreGltfTrace("GLTF Buffer 加载失败");
|
||||
cgltf_free(data);
|
||||
addFallbackSkeleton();
|
||||
reportImportFailure();
|
||||
MetaCoreFinalizeImportReport(document.ImportReport);
|
||||
MetaCoreApplyModelImportSettings(document);
|
||||
return document;
|
||||
}
|
||||
if (cancelled()) {
|
||||
cgltf_free(data);
|
||||
return document;
|
||||
}
|
||||
|
||||
if (data->images_count > 0) {
|
||||
for (std::size_t i = 0; i < data->images_count; ++i) {
|
||||
if (cancelled()) { cgltf_free(data); return document; }
|
||||
const cgltf_image& image = data->images[i];
|
||||
MetaCoreImportedGltfTextureDocument textureDoc;
|
||||
textureDoc.SourcePath = image.uri ? std::filesystem::path(image.uri).lexically_normal() : std::filesystem::path{};
|
||||
@ -889,6 +859,7 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
const std::vector<MetaCoreGltfMaterialScalarPresence> materialScalarPresence =
|
||||
MetaCoreReadGltfMaterialScalarPresence(data);
|
||||
for (std::size_t i = 0; i < data->materials_count; ++i) {
|
||||
if (cancelled()) { cgltf_free(data); return document; }
|
||||
const cgltf_material& mat = data->materials[i];
|
||||
const MetaCoreGltfMaterialScalarPresence scalarPresence =
|
||||
i < materialScalarPresence.size()
|
||||
@ -972,6 +943,7 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
|
||||
if (data->meshes_count > 0) {
|
||||
for (std::size_t i = 0; i < data->meshes_count; ++i) {
|
||||
if (cancelled()) { cgltf_free(data); return document; }
|
||||
const cgltf_mesh& mesh = data->meshes[i];
|
||||
MetaCoreImportedGltfMeshDocument meshDoc;
|
||||
meshDoc.Name = MetaCoreGltfDisplayName(mesh.name, "Mesh_" + std::to_string(i));
|
||||
@ -998,6 +970,7 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
glm::vec3 bmax{-1e30, -1e30, -1e30};
|
||||
|
||||
for (std::size_t p = 0; p < mesh.primitives_count; ++p) {
|
||||
if (cancelled()) { cgltf_free(data); return document; }
|
||||
const cgltf_primitive& prim = mesh.primitives[p];
|
||||
|
||||
const std::uint32_t vertexOffset = static_cast<std::uint32_t>(vertices.size());
|
||||
@ -1102,6 +1075,7 @@ struct MetaCoreGltfMaterialScalarPresence {
|
||||
|
||||
if (data->nodes_count > 0) {
|
||||
for (std::size_t i = 0; i < data->nodes_count; ++i) {
|
||||
if (cancelled()) { cgltf_free(data); return document; }
|
||||
const cgltf_node& node = data->nodes[i];
|
||||
MetaCoreImportedGltfNodeDocument nodeDoc;
|
||||
if (node.name && strlen(node.name) > 0) {
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <atomic>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
@ -12,7 +13,8 @@ namespace MetaCore {
|
||||
const std::filesystem::path& relativeSourcePath,
|
||||
std::uint64_t sourceHash,
|
||||
std::vector<std::vector<std::byte>>& outMeshPayloads,
|
||||
MetaCoreModelImportSettingsDocument importSettings = {}
|
||||
MetaCoreModelImportSettingsDocument importSettings = {},
|
||||
const std::atomic_bool* cancelRequested = nullptr
|
||||
);
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -71,6 +71,27 @@ struct MetaCoreAssetMetadataDocument {
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<MetaCoreAssetGuid> Dependencies{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::uint32_t SchemaVersion = 2;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::uint32_t ImporterVersion = 1;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string ImportSettingsJson{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::uint64_t SettingsHash = 0;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string ArtifactKey{};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreModelImportResult LastImportResult = MetaCoreModelImportResult::Success;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<MetaCoreImportMessageDocument> LastImportMessages{};
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
@ -109,6 +130,18 @@ struct MetaCoreCookManifestEntry {
|
||||
|
||||
MC_PROPERTY()
|
||||
std::uint64_t CookedKey = 0;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string ArtifactKey{};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreAssetTargetPlatform TargetPlatform = MetaCoreAssetTargetPlatform::Host;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::uint32_t GeneratorVersion = 1;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<MetaCoreAssetGuid> DependencyClosure{};
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
|
||||
@ -9,12 +9,14 @@
|
||||
#include "MetaCoreScene/MetaCoreScene.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace MetaCore {
|
||||
@ -108,6 +110,47 @@ struct MetaCoreAssetRecord {
|
||||
std::filesystem::path MetaPath{};
|
||||
std::string ImporterId{};
|
||||
std::uint64_t SourceHash = 0;
|
||||
MetaCoreModelImportResult ImportResult = MetaCoreModelImportResult::Success;
|
||||
std::string ArtifactKey{};
|
||||
std::string DiagnosticSummary{};
|
||||
};
|
||||
|
||||
enum class MetaCoreImportJobState {
|
||||
Queued = 0,
|
||||
Running,
|
||||
Committing,
|
||||
Succeeded,
|
||||
Warning,
|
||||
Failed,
|
||||
Cancelled
|
||||
};
|
||||
|
||||
using MetaCoreImportJobId = std::uint64_t;
|
||||
|
||||
struct MetaCoreImportJobSnapshot {
|
||||
MetaCoreImportJobId Id = 0;
|
||||
MetaCoreAssetGuid AssetGuid{};
|
||||
std::filesystem::path SourcePath{};
|
||||
MetaCoreImportJobState State = MetaCoreImportJobState::Queued;
|
||||
std::string Stage{};
|
||||
float Progress = 0.0F;
|
||||
std::vector<MetaCoreImportMessageDocument> Messages{};
|
||||
std::uint64_t ElapsedMilliseconds = 0;
|
||||
};
|
||||
|
||||
enum class MetaCoreAssetReferenceStatus {
|
||||
Valid = 0,
|
||||
Missing,
|
||||
Unreferenced,
|
||||
BuildUnreachable
|
||||
};
|
||||
|
||||
struct MetaCoreAssetReferenceFinding {
|
||||
MetaCoreAssetGuid OwnerGuid{};
|
||||
std::filesystem::path OwnerPath{};
|
||||
std::string FieldPath{};
|
||||
MetaCoreAssetGuid TargetGuid{};
|
||||
MetaCoreAssetReferenceStatus Status = MetaCoreAssetReferenceStatus::Valid;
|
||||
};
|
||||
|
||||
class MetaCoreIReflectionRegistry : public MetaCoreIEditorService {
|
||||
@ -143,7 +186,7 @@ public:
|
||||
[[nodiscard]] virtual bool RenamePath(const std::filesystem::path& relativePath, const std::filesystem::path& newName) = 0;
|
||||
[[nodiscard]] virtual bool MovePath(const std::filesystem::path& relativePath, const std::filesystem::path& targetDirectory) = 0;
|
||||
[[nodiscard]] virtual bool MovePathTo(const std::filesystem::path& relativePath, const std::filesystem::path& targetRelativePath) = 0;
|
||||
[[nodiscard]] virtual bool DeletePath(const std::filesystem::path& relativePath) = 0;
|
||||
[[nodiscard]] virtual bool DeletePath(const std::filesystem::path& relativePath, bool force = false) = 0;
|
||||
[[nodiscard]] virtual std::vector<std::filesystem::path> GetDirectoriesUnder(const std::filesystem::path& relativeDirectory) const = 0;
|
||||
[[nodiscard]] virtual std::vector<MetaCoreAssetRecord> GetAssetsUnder(const std::filesystem::path& relativeDirectory) const = 0;
|
||||
[[nodiscard]] virtual std::optional<MetaCoreAssetRecord> FindAssetByRelativePath(const std::filesystem::path& relativePath) const = 0;
|
||||
@ -167,6 +210,37 @@ class MetaCoreIImportPipelineService : public MetaCoreIEditorService {
|
||||
public:
|
||||
[[nodiscard]] virtual bool RefreshImports() = 0;
|
||||
[[nodiscard]] virtual bool ReimportAsset(const MetaCoreAssetGuid& assetGuid) = 0;
|
||||
[[nodiscard]] virtual MetaCoreImportJobId EnqueueAsset(const MetaCoreAssetGuid& assetGuid, bool force = false) = 0;
|
||||
[[nodiscard]] virtual std::vector<MetaCoreImportJobId> EnqueueAllChanged() = 0;
|
||||
[[nodiscard]] virtual std::optional<MetaCoreImportJobSnapshot> GetJob(MetaCoreImportJobId jobId) const = 0;
|
||||
[[nodiscard]] virtual std::vector<MetaCoreImportJobSnapshot> GetJobs() const = 0;
|
||||
virtual bool CancelJob(MetaCoreImportJobId jobId) = 0;
|
||||
[[nodiscard]] virtual MetaCoreImportJobId RetryJob(MetaCoreImportJobId jobId) = 0;
|
||||
virtual void Tick() = 0;
|
||||
};
|
||||
|
||||
class MetaCoreIDerivedDataCacheService : public MetaCoreIEditorService {
|
||||
public:
|
||||
virtual void SetProjectRoot(const std::filesystem::path& projectRoot) = 0;
|
||||
[[nodiscard]] virtual std::filesystem::path ResolvePath(std::string_view artifactKey, std::string_view suffix = {}) const = 0;
|
||||
[[nodiscard]] virtual bool Contains(std::string_view artifactKey, std::string_view suffix = {}) const = 0;
|
||||
[[nodiscard]] virtual bool Put(std::string_view artifactKey, std::span<const std::byte> data, std::string_view suffix = {}) = 0;
|
||||
[[nodiscard]] virtual std::optional<std::vector<std::byte>> Get(std::string_view artifactKey, std::string_view suffix = {}) const = 0;
|
||||
};
|
||||
|
||||
class MetaCoreIThumbnailService : public MetaCoreIEditorService {
|
||||
public:
|
||||
virtual void SetProjectRoot(const std::filesystem::path& projectRoot) = 0;
|
||||
[[nodiscard]] virtual std::filesystem::path RequestThumbnail(const MetaCoreAssetRecord& asset, std::uint32_t size = 128) = 0;
|
||||
virtual void Tick() = 0;
|
||||
};
|
||||
|
||||
class MetaCoreIAssetAuditService : public MetaCoreIEditorService {
|
||||
public:
|
||||
[[nodiscard]] virtual std::vector<MetaCoreAssetReferenceFinding> AuditAll() const = 0;
|
||||
[[nodiscard]] virtual std::vector<MetaCoreAssetReferenceFinding> FindReferencesTo(const MetaCoreAssetGuid& assetGuid) const = 0;
|
||||
[[nodiscard]] virtual bool CanDelete(const MetaCoreAssetGuid& assetGuid, std::vector<MetaCoreAssetReferenceFinding>* blockers = nullptr) const = 0;
|
||||
[[nodiscard]] virtual bool ReplaceReference(const MetaCoreAssetReferenceFinding& finding, const MetaCoreAssetGuid& replacement) = 0;
|
||||
};
|
||||
|
||||
struct MetaCoreResolvedGeneratedAssetRecord {
|
||||
@ -198,6 +272,16 @@ public:
|
||||
const MetaCoreModelImportSettingsDocument& importSettings
|
||||
) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool SaveTextureImportSettings(
|
||||
const MetaCoreAssetGuid& textureGuid,
|
||||
const MetaCoreTextureImportSettingsDocument& importSettings
|
||||
) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool SaveMaterialImportSettings(
|
||||
const MetaCoreAssetGuid& materialGuid,
|
||||
const MetaCoreMaterialImportSettingsDocument& importSettings
|
||||
) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool SaveMaterialAsset(
|
||||
const MetaCoreAssetGuid& materialGuid,
|
||||
const MetaCoreMaterialAssetDocument& document
|
||||
|
||||
@ -22,6 +22,14 @@ void MetaCoreAssetRegistry::ScanDirectory(const std::filesystem::path& rootPath)
|
||||
continue;
|
||||
}
|
||||
|
||||
// .mcasset is derived output, never an import source. Older pipeline
|
||||
// versions could leave package metadata chains such as
|
||||
// foo.glb.mcasset.mcmeta; parsing those on every scan both duplicates
|
||||
// the source asset and makes the chain grow indefinitely.
|
||||
if (entry.path().stem().extension() == ".mcasset") {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::ifstream file(entry.path());
|
||||
if (!file.is_open()) {
|
||||
continue;
|
||||
|
||||
@ -25,6 +25,43 @@ enum class MetaCoreMaterialAlphaMode {
|
||||
Blend
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreTextureColorSpace {
|
||||
Automatic = 0,
|
||||
Srgb,
|
||||
Linear
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreTextureUsage {
|
||||
Automatic = 0,
|
||||
Color,
|
||||
Normal,
|
||||
Data,
|
||||
HighDynamicRange
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreTextureCompression {
|
||||
None = 0,
|
||||
PlatformDefault,
|
||||
BasisUastc
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreAssetTargetPlatform {
|
||||
Host = 0,
|
||||
LinuxX64,
|
||||
WindowsX64
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreGeneratedMaterialPolicy {
|
||||
PreserveImported = 0,
|
||||
PreserveUserOverrides,
|
||||
ExtractStandalone
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreTextureAssetDocument {
|
||||
MC_GENERATED_BODY()
|
||||
@ -61,6 +98,15 @@ struct MetaCoreTextureAssetDocument {
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Format = "raw";
|
||||
|
||||
MC_PROPERTY()
|
||||
std::int32_t MipCount = 1;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreTextureColorSpace ColorSpace = MetaCoreTextureColorSpace::Automatic;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreTextureCompression Compression = MetaCoreTextureCompression::None;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
@ -217,6 +263,49 @@ struct MetaCoreModelImportSettingsDocument {
|
||||
bool PreserveMaterialSlots = true;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreTextureImportSettingsDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreTextureColorSpace ColorSpace = MetaCoreTextureColorSpace::Automatic;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreTextureUsage Usage = MetaCoreTextureUsage::Automatic;
|
||||
|
||||
MC_PROPERTY()
|
||||
bool GenerateMipmaps = true;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::int32_t MaxSize = 4096;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreTextureCompression Compression = MetaCoreTextureCompression::PlatformDefault;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::int32_t CompressionQuality = 80;
|
||||
|
||||
MC_PROPERTY()
|
||||
bool RenormalizeNormalMap = true;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreMaterialImportSettingsDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreMaterialShaderModel ShaderModel = MetaCoreMaterialShaderModel::PbrMetalRough;
|
||||
|
||||
MC_PROPERTY()
|
||||
bool PreserveAlphaMode = true;
|
||||
|
||||
MC_PROPERTY()
|
||||
bool PreserveDoubleSided = true;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreGeneratedMaterialPolicy GeneratedMaterialPolicy = MetaCoreGeneratedMaterialPolicy::PreserveUserOverrides;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreImportMessageDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
BIN
TestProject/Assets/Materials/default_pbr.material.json.mcasset
Normal file
BIN
TestProject/Assets/Materials/default_pbr.material.json.mcasset
Normal file
Binary file not shown.
@ -1,8 +1,16 @@
|
||||
{
|
||||
"artifact_key": "1b8eb8422ae8a643",
|
||||
"asset_type": "asset",
|
||||
"guid": "7e3d10d5-d46f-4622-9615-25ce0abef1e7",
|
||||
"import_settings": {},
|
||||
"importer_id": "BinaryImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/Materials/default_pbr.material.json.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 11238273277574643119,
|
||||
"source_hash": 12650993429013095480,
|
||||
"source_path": "Assets/Materials/default_pbr.material.json"
|
||||
"source_mtime_ns": 1779698310960569594,
|
||||
"source_path": "Assets/Materials/default_pbr.material.json",
|
||||
"source_size_bytes": 136
|
||||
}
|
||||
BIN
TestProject/Assets/Models/Cube.glb.mcasset
Normal file
BIN
TestProject/Assets/Models/Cube.glb.mcasset
Normal file
Binary file not shown.
@ -1,23 +1,48 @@
|
||||
{
|
||||
"artifact_key": "ba190289025adbd3",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"9cd50d41-df30-26ff-da7e-e01bb57f7135",
|
||||
"d0a9dfe6-52cb-c306-8384-085ea2996675"
|
||||
],
|
||||
"guid": "e85ce6d3-12a9-475a-a224-9da4035ce22a",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_messages": [
|
||||
{
|
||||
"code": "ambiguous_subasset_identity",
|
||||
"message": "material 子资产名称为空或重复,GUID 稳定性退回序号匹配",
|
||||
"severity": 1
|
||||
}
|
||||
],
|
||||
"last_import_result": 1,
|
||||
"package_path": "Assets/Models/Cube.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 2668036311376285448,
|
||||
"source_mtime_ns": 1779958286058240134,
|
||||
"source_path": "Assets/Models/Cube.glb",
|
||||
"source_size_bytes": 1936,
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "9cd50d41-df30-26ff-da7e-e01bb57f7135",
|
||||
"index": 0,
|
||||
"name": "Cube",
|
||||
"stable_import_key": "",
|
||||
"stable_import_key": "mesh:Cube:0",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "20a8eca7-ae5b-8ed9-c7a8-b64b59567283",
|
||||
"guid": "d0a9dfe6-52cb-c306-8384-085ea2996675",
|
||||
"index": 0,
|
||||
"name": "Cube_Material",
|
||||
"stable_import_key": "",
|
||||
"name": "Material",
|
||||
"stable_import_key": "material:Material:0",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
|
||||
BIN
TestProject/Assets/Models/Plane.glb.mcasset
Normal file
BIN
TestProject/Assets/Models/Plane.glb.mcasset
Normal file
Binary file not shown.
@ -1,24 +1,41 @@
|
||||
{
|
||||
"artifact_key": "0f17577685a7ac42",
|
||||
"asset_type": "model",
|
||||
"dependencies": [
|
||||
"a718a4d3-f5ce-7f3a-6fe6-877661f2ea55"
|
||||
],
|
||||
"guid": "75068577-37e3-4a9d-954a-54251c667933",
|
||||
"import_settings": {
|
||||
"preserve_empty_nodes": true,
|
||||
"preserve_hierarchy": true,
|
||||
"preserve_material_slots": true,
|
||||
"scale_factor": 1.0,
|
||||
"up_axis": 0
|
||||
},
|
||||
"importer_id": "GltfModelImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_messages": [
|
||||
{
|
||||
"code": "ambiguous_subasset_identity",
|
||||
"message": "mesh 子资产名称为空或重复,GUID 稳定性退回序号匹配",
|
||||
"severity": 1
|
||||
}
|
||||
],
|
||||
"last_import_result": 1,
|
||||
"package_path": "Assets/Models/Plane.glb.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 2032719019511346891,
|
||||
"source_hash": 7189019093832532692,
|
||||
"source_mtime_ns": 1779958286061239979,
|
||||
"source_path": "Assets/Models/Plane.glb",
|
||||
"source_size_bytes": 1036,
|
||||
"sub_assets": [
|
||||
{
|
||||
"guid": "804bca6d-b9ea-f7af-3f0a-f078d5de0d45",
|
||||
"guid": "a718a4d3-f5ce-7f3a-6fe6-877661f2ea55",
|
||||
"index": 0,
|
||||
"name": "Plane",
|
||||
"stable_import_key": "",
|
||||
"name": "平面",
|
||||
"stable_import_key": "mesh:平面:0",
|
||||
"type": "mesh"
|
||||
},
|
||||
{
|
||||
"guid": "6c337003-dc9a-da7c-d685-6d27c673046a",
|
||||
"index": 0,
|
||||
"name": "Plane_Material",
|
||||
"stable_import_key": "",
|
||||
"type": "material"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
TestProject/Assets/UI/main_menu.rcss.mcasset
Normal file
BIN
TestProject/Assets/UI/main_menu.rcss.mcasset
Normal file
Binary file not shown.
@ -1,8 +1,16 @@
|
||||
{
|
||||
"artifact_key": "877767f4951cc2ab",
|
||||
"asset_type": "ui_stylesheet",
|
||||
"guid": "9bd0aa89-1f68-4a4d-b9ed-1fcf1dc6180d",
|
||||
"import_settings": {},
|
||||
"importer_id": "UiStylesheetImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/UI/main_menu.rcss.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 11238273277574643119,
|
||||
"source_hash": 8787559501102875347,
|
||||
"source_path": "Assets/UI/main_menu.rcss"
|
||||
"source_mtime_ns": 1779698310960569594,
|
||||
"source_path": "Assets/UI/main_menu.rcss",
|
||||
"source_size_bytes": 162
|
||||
}
|
||||
BIN
TestProject/Assets/UI/main_menu.rml.mcasset
Normal file
BIN
TestProject/Assets/UI/main_menu.rml.mcasset
Normal file
Binary file not shown.
@ -1,8 +1,16 @@
|
||||
{
|
||||
"artifact_key": "3d142bee5d424926",
|
||||
"asset_type": "ui_document",
|
||||
"guid": "7fa1231a-4620-479f-8238-03531e78202a",
|
||||
"import_settings": {},
|
||||
"importer_id": "UiDocumentImporter",
|
||||
"importer_version": 2,
|
||||
"last_import_result": 0,
|
||||
"package_path": "Assets/UI/main_menu.rml.mcasset",
|
||||
"schema_version": 2,
|
||||
"settings_hash": 11238273277574643119,
|
||||
"source_hash": 11300316873669048992,
|
||||
"source_path": "Assets/UI/main_menu.rml"
|
||||
"source_mtime_ns": 1779698310960569594,
|
||||
"source_path": "Assets/UI/main_menu.rml",
|
||||
"source_size_bytes": 200
|
||||
}
|
||||
47
docs/designs/metacore-asset-pipeline-contract.md
Normal file
47
docs/designs/metacore-asset-pipeline-contract.md
Normal file
@ -0,0 +1,47 @@
|
||||
# MetaCore 资产导入契约(R2)
|
||||
|
||||
本文定义 R2 首版的正式输入范围、诊断语义和缓存身份。未列为“支持”的格式不得产生可被 Registry 当作成功结果的 `.mcasset`。
|
||||
|
||||
## 支持矩阵
|
||||
|
||||
| 输入 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| glTF 2.0 `.gltf` | 支持 | JSON、外部 Buffer、Data URI、外部或嵌入图片、节点层级、Mesh、PBR Material、UV 和动画元数据 |
|
||||
| glTF 2.0 `.glb` | 支持 | 与 `.gltf` 使用相同导入契约;容器、JSON 或 Buffer 损坏时失败 |
|
||||
| PNG/JPEG/TGA/BMP/PSD/GIF/HDR/PPM | 支持 | 进入统一纹理解码、色彩/法线处理、尺寸限制和 Mipmap 链 |
|
||||
| MetaCore Material | 支持 | `.mcmaterial.json`,引用只保存 Asset GUID |
|
||||
| FBX `.fbx` | 不支持 | 写入失败诊断,不写占位 `.mcasset` |
|
||||
| Wavefront OBJ `.obj` | 不支持 | 写入失败诊断,不写占位 `.mcasset` |
|
||||
|
||||
glTF 合法但尚未实现的可选能力应产生 Warning 并保留可用部分;无法解析容器/JSON、无法加载必需 Buffer 或无法解码必需图片应产生 Error。Error 不覆盖上一次成功产物。
|
||||
|
||||
## 稳定诊断码
|
||||
|
||||
| 代码 | 级别 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| `gltf_import_failed` | Error | glTF/GLB 解析或必需 Buffer 加载失败 |
|
||||
| `unsupported_model_format` | Error | FBX/OBJ 在当前版本不受支持 |
|
||||
| `missing_texture_reference` | Warning | PBR Base Color 贴图索引无效 |
|
||||
| `missing_metallic_roughness_texture_reference` | Warning | 金属粗糙贴图索引无效 |
|
||||
| `subasset_guid_ambiguity` | Warning | 重名或匿名子资产无法按唯一规范名称匹配旧 GUID |
|
||||
| `generated_root_node` | Info | 源文件没有节点层级,导入器生成逻辑根节点 |
|
||||
|
||||
诊断码属于持久化契约,UI 文案可以本地化,但代码含义不得在不提升 Importer Version 的情况下改变。
|
||||
|
||||
## 元数据与派生数据身份
|
||||
|
||||
`.mcmeta` Schema 2 保留原 GUID,并记录 Importer Version、规范化 Import Settings、Settings Hash、Artifact Key、最近导入结果和诊断。旧元数据读取时补默认值,不因升级生成新 GUID。
|
||||
|
||||
Artifact/DDC Key 的输入集合为:源内容 Hash、规范化 Settings Hash、Importer/生成器版本、目标平台和工具版本。派生包位于 `Library/DerivedData`,缩略图位于 `Library/Thumbnails`,两者均可删除并重建,不进入版本控制。
|
||||
|
||||
Linux 纹理压缩后端使用 vcpkg 提供的 Basis Universal 编码器生成 UASTC KTX2。颜色纹理按 sRGB 编码,数据与法线纹理保持 Linear,法线缩放后可重新归一化;关闭压缩时保留未压缩 RGBA 像素链。编码器进程属于可取消的导入阶段,进入原子提交后不再中断。
|
||||
|
||||
子资产首先按稳定导入键匹配;键变化时,仅在“类型 + 规范名称”在新旧集合中都唯一的情况下恢复旧 GUID。歧义项使用稳定序号并报告 Warning。
|
||||
|
||||
## 提交与失败规则
|
||||
|
||||
- Hash、模型解析、纹理解码/压缩和缩略图生成在后台作业执行。
|
||||
- Registry、依赖图、编辑器事件与热重载只在串行提交边界更新。
|
||||
- 作业在原子提交前可取消;进入提交后完成本次提交。
|
||||
- 失败或取消不得覆盖上一次成功 `.mcasset`;失败状态和诊断仍写入 `.mcmeta`。
|
||||
- 删除默认执行反向引用预检;只有显式强制删除可以留下失效 GUID。
|
||||
@ -107,14 +107,16 @@ MetaCore 已经超过单纯编辑器 Demo 阶段,当前具备:
|
||||
|
||||
#### 需要做的事
|
||||
|
||||
- [ ] 明确 glTF/GLB 的正式支持矩阵和导入错误规范。
|
||||
- [ ] 删除或明确标识通用占位导入结果,避免占位资产被当成成功导入。
|
||||
- [ ] 建立 Model、Texture、Material 的版本化 Import Settings。
|
||||
- [ ] 实现纹理色彩空间、Mipmap、尺寸、压缩和法线贴图设置。
|
||||
- [ ] 建立缩略图与预览缓存,并定义缓存失效条件。
|
||||
- [ ] 把大型模型解析、纹理处理和派生数据生成移出 UI 主线程。
|
||||
- [ ] 增加资源引用检查、孤立资源检查和 GUID/路径修复工具。
|
||||
- [ ] 建立导入黄金样本库,覆盖层级、材质、贴图、动画和损坏输入。
|
||||
- [x] 明确 glTF/GLB 的正式支持矩阵和导入错误规范(见 [资产导入契约](metacore-asset-pipeline-contract.md))。
|
||||
- [x] 删除或明确标识通用占位导入结果,避免占位资产被当成成功导入。
|
||||
- [x] 建立 Model、Texture、Material 的版本化 Import Settings。
|
||||
- [x] 实现纹理色彩空间、Mipmap、尺寸、压缩和法线贴图设置;Linux 生产链使用 Basis Universal UASTC/KTX2,`None` 保留未压缩像素链。
|
||||
- [x] 建立缩略图与预览缓存,并定义缓存失效条件;纹理、材质球和固定相机模型预览均由后台服务生成并持久化。
|
||||
- [x] 把大型模型解析、纹理处理和派生数据生成移出 UI 主线程;解析循环和 Basis 编码支持协作式取消,原子提交阶段不可中断。
|
||||
- [x] 增加资源引用检查、孤立资源检查和 GUID/路径修复工具,并报告失效引用、无入边资产和构建不可达资产。
|
||||
- [x] 建立独立 Assets 黄金回归目标,覆盖 glTF 外部 Buffer、模型 Mesh 产物、纹理 KTX2、缩略图、损坏输入、不支持格式和构建不可达分析;层级、材质、贴图和动画覆盖继续由 Smoke 回归验证。
|
||||
|
||||
> 实施状态(2026-07-14):R2 的导入契约、版本化设置、DDC、事务化后台作业、UASTC/KTX2 纹理链、持久化预览、Asset Index、引用审计、安全删除、构建不可达分析及独立黄金回归均已完成。当前验收范围按项目决策锁定为 Linux;Windows 兼容与平台门禁不作为本问题的关闭条件,后续单独恢复。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
|
||||
@ -1,171 +1,128 @@
|
||||
#include <windows.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/View.h>
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/SwapChain.h>
|
||||
#include <filament/Renderer.h>
|
||||
#include <filament/Viewport.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/RenderTarget.h>
|
||||
#include <filament/Renderer.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/SwapChain.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/View.h>
|
||||
#include <filament/Viewport.h>
|
||||
#include <imgui.h>
|
||||
#include "MetaCoreRender/MetaCoreImGuiHelper.h"
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
#include "MetaCoreRender/MetaCoreImGuiHelper.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
using namespace filament;
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
switch (message) {
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "[Demo] Starting..." << std::endl;
|
||||
|
||||
HINSTANCE hInstance = GetModuleHandle(nullptr);
|
||||
int nCmdShow = SW_SHOW;
|
||||
constexpr std::uint32_t displayWidth = 1280;
|
||||
constexpr std::uint32_t displayHeight = 720;
|
||||
constexpr std::uint32_t viewportWidth = 800;
|
||||
constexpr std::uint32_t viewportHeight = 600;
|
||||
|
||||
// 1. 注册窗口类
|
||||
std::cout << "[Demo] Registering window class..." << std::endl;
|
||||
WNDCLASSEXW wcex = {0};
|
||||
wcex.cbSize = sizeof(WNDCLASSEXW);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
||||
wcex.lpfnWndProc = WndProc;
|
||||
wcex.hInstance = hInstance;
|
||||
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wcex.lpszClassName = L"FilamentImGuiDemoClass";
|
||||
RegisterClassExW(&wcex);
|
||||
|
||||
// 2. 创建窗口
|
||||
std::cout << "[Demo] Creating window..." << std::endl;
|
||||
HWND hWnd = CreateWindowExW(0, L"FilamentImGuiDemoClass", L"Filament + ImGui 离屏渲染最小化 Demo", WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720, nullptr, nullptr, hInstance, nullptr);
|
||||
|
||||
if (!hWnd) {
|
||||
std::cout << "[Demo] Failed to create window!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ShowWindow(hWnd, nCmdShow);
|
||||
UpdateWindow(hWnd);
|
||||
|
||||
// 3. 初始化 Filament
|
||||
std::cout << "[Demo] Creating Filament Engine (OpenGL)..." << std::endl;
|
||||
std::cout << "[Demo] Creating headless Filament engine..." << std::endl;
|
||||
// uiBlit currently ships OpenGL shader material data. The swap chain is
|
||||
// offscreen, while Filament's Linux OpenGL platform still requires X11.
|
||||
Engine* engine = Engine::create(Engine::Backend::OPENGL);
|
||||
if (!engine) {
|
||||
std::cout << "[Demo] Failed to create Engine!" << std::endl;
|
||||
return -1;
|
||||
if (engine == nullptr) {
|
||||
std::cerr << "[Demo] Failed to create Filament engine." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "[Demo] Creating Renderer..." << std::endl;
|
||||
Renderer* renderer = engine->createRenderer();
|
||||
|
||||
std::cout << "[Demo] Creating SwapChain..." << std::endl;
|
||||
SwapChain* swapChain = engine->createSwapChain(hWnd);
|
||||
|
||||
std::cout << "[Demo] Creating Scene..." << std::endl;
|
||||
SwapChain* swapChain = engine->createSwapChain(displayWidth, displayHeight);
|
||||
Scene* scene = engine->createScene();
|
||||
|
||||
std::cout << "[Demo] Creating View..." << std::endl;
|
||||
View* view = engine->createView();
|
||||
view->setScene(scene);
|
||||
View* sceneView = engine->createView();
|
||||
View* uiView = engine->createView();
|
||||
if (renderer == nullptr || swapChain == nullptr || scene == nullptr ||
|
||||
sceneView == nullptr || uiView == nullptr) {
|
||||
std::cerr << "[Demo] Failed to create Filament headless resources." << std::endl;
|
||||
Engine::destroy(&engine);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "[Demo] Creating Camera..." << std::endl;
|
||||
utils::Entity cameraEntity = utils::EntityManager::get().create();
|
||||
Camera* camera = engine->createCamera(cameraEntity);
|
||||
view->setCamera(camera);
|
||||
view->setViewport({0, 0, 1280, 720});
|
||||
sceneView->setScene(scene);
|
||||
sceneView->setCamera(camera);
|
||||
|
||||
std::cout << "[Demo] Creating Skybox..." << std::endl;
|
||||
Skybox* skybox = Skybox::Builder().color({1.0f, 0.0f, 1.0f, 1.0f}).build(*engine);
|
||||
Skybox* skybox = Skybox::Builder()
|
||||
.color({1.0F, 0.0F, 1.0F, 1.0F})
|
||||
.build(*engine);
|
||||
scene->setSkybox(skybox);
|
||||
|
||||
// 4. 创建离屏 RenderTarget
|
||||
std::cout << "[Demo] Creating offscreen texture..." << std::endl;
|
||||
Texture* viewportTexture = Texture::Builder()
|
||||
.width(800).height(600).levels(1)
|
||||
.width(viewportWidth)
|
||||
.height(viewportHeight)
|
||||
.levels(1)
|
||||
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
|
||||
.format(Texture::InternalFormat::RGBA8)
|
||||
.build(*engine);
|
||||
|
||||
std::cout << "[Demo] Creating RenderTarget..." << std::endl;
|
||||
RenderTarget* renderTarget = RenderTarget::Builder()
|
||||
.texture(RenderTarget::AttachmentPoint::COLOR, viewportTexture)
|
||||
.build(*engine);
|
||||
|
||||
std::cout << "[Demo] Creating offscreen View..." << std::endl;
|
||||
View* offscreenView = engine->createView();
|
||||
offscreenView->setScene(scene);
|
||||
offscreenView->setCamera(camera);
|
||||
offscreenView->setRenderTarget(renderTarget);
|
||||
offscreenView->setViewport({0, 0, 800, 600});
|
||||
offscreenView->setPostProcessingEnabled(true);
|
||||
sceneView->setRenderTarget(renderTarget);
|
||||
sceneView->setViewport({0, 0, viewportWidth, viewportHeight});
|
||||
sceneView->setPostProcessingEnabled(true);
|
||||
uiView->setViewport({0, 0, displayWidth, displayHeight});
|
||||
uiView->setPostProcessingEnabled(false);
|
||||
|
||||
// 5. 初始化 ImGui
|
||||
std::cout << "[Demo] Initializing ImGui..." << std::endl;
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2(1280, 720);
|
||||
io.DisplaySize = ImVec2(static_cast<float>(displayWidth), static_cast<float>(displayHeight));
|
||||
io.DeltaTime = 1.0F / 60.0F;
|
||||
|
||||
std::cout << "[Demo] Creating UI View..." << std::endl;
|
||||
View* uiView = engine->createView();
|
||||
uiView->setViewport({0, 0, 1280, 720});
|
||||
uiView->setPostProcessingEnabled(false);
|
||||
{
|
||||
MetaCore::MetaCoreImGuiHelper imguiHelper(engine, uiView, "", ImGui::GetCurrentContext());
|
||||
imguiHelper.setDisplaySize(displayWidth, displayHeight);
|
||||
|
||||
std::cout << "[Demo] Creating MetaCoreImGuiHelper..." << std::endl;
|
||||
MetaCore::MetaCoreImGuiHelper imguiHelper(engine, uiView, "C:\\Windows\\Fonts\\msyh.ttc");
|
||||
// This is a deterministic offscreen smoke demo rather than an interactive
|
||||
// platform window. A few frames are enough to exercise both render paths.
|
||||
for (int frame = 0; frame < 3; ++frame) {
|
||||
if (!renderer->beginFrame(swapChain)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "[Demo] Setting Display Size..." << std::endl;
|
||||
imguiHelper.setDisplaySize(1280, 720);
|
||||
renderer->render(sceneView);
|
||||
|
||||
std::cout << "[Demo] Entering message loop..." << std::endl;
|
||||
MSG msg = {0};
|
||||
bool quit = false;
|
||||
while (!quit) {
|
||||
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT) quit = true;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
if (renderer->beginFrame(swapChain)) {
|
||||
// 1. 渲染离屏 3D 场景
|
||||
renderer->render(offscreenView);
|
||||
|
||||
// 2. 构建 ImGui 界面
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(820, 640), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(820.0F, 640.0F), ImGuiCond_Always);
|
||||
ImGui::Begin("3D Viewport");
|
||||
|
||||
ImGui::Image((void*)viewportTexture, ImVec2(800, 600), ImVec2(0, 1), ImVec2(1, 0));
|
||||
|
||||
ImGui::Image(
|
||||
reinterpret_cast<ImTextureID>(viewportTexture),
|
||||
ImVec2(static_cast<float>(viewportWidth), static_cast<float>(viewportHeight)),
|
||||
ImVec2(0.0F, 1.0F),
|
||||
ImVec2(1.0F, 0.0F)
|
||||
);
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
// 3. 处理 ImGui 命令
|
||||
imguiHelper.processImGuiCommands(ImGui::GetDrawData(), io);
|
||||
|
||||
// 4. 渲染 UI
|
||||
renderer->render(uiView);
|
||||
|
||||
renderer->endFrame();
|
||||
}
|
||||
Sleep(16);
|
||||
}
|
||||
|
||||
std::cout << "[Demo] Cleaning up..." << std::endl;
|
||||
// 省略详细清理,由操作系统回收
|
||||
|
||||
ImGui::DestroyContext();
|
||||
engine->destroy(renderTarget);
|
||||
engine->destroy(viewportTexture);
|
||||
engine->destroy(skybox);
|
||||
engine->destroy(sceneView);
|
||||
engine->destroy(uiView);
|
||||
engine->destroy(scene);
|
||||
engine->destroyCameraComponent(cameraEntity);
|
||||
utils::EntityManager::get().destroy(cameraEntity);
|
||||
engine->destroy(renderer);
|
||||
engine->destroy(swapChain);
|
||||
Engine::destroy(&engine);
|
||||
|
||||
std::cout << "[Demo] Headless render completed." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
165
tests/MetaCoreAssetPipelineTests.cpp
Normal file
165
tests/MetaCoreAssetPipelineTests.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
#include "MetaCoreEditor/MetaCoreBuiltinModules.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
|
||||
void Expect(bool condition, const char* message) {
|
||||
if (!condition) throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
void WriteProject(const std::filesystem::path& root) {
|
||||
std::filesystem::create_directories(root / "Assets");
|
||||
std::filesystem::create_directories(root / "Scenes");
|
||||
std::ofstream project(root / "MetaCore.project.json");
|
||||
project << R"({"name":"AssetGolden","version":"1.0","scenes":["Scenes/Main.mcscene.json"],"startup_scene":"Scenes/Main.mcscene.json"})";
|
||||
}
|
||||
|
||||
void WriteGoldenInputs(const std::filesystem::path& root) {
|
||||
std::ofstream texture(root / "Assets/Color.ppm", std::ios::binary);
|
||||
texture << "P6\n8 8\n255\n";
|
||||
for (int pixel = 0; pixel < 64; ++pixel) {
|
||||
const char rgb[3]{static_cast<char>(pixel * 3), static_cast<char>(128), static_cast<char>(255 - pixel * 3)};
|
||||
texture.write(rgb, sizeof(rgb));
|
||||
}
|
||||
std::ofstream(root / "Assets/Broken.glb", std::ios::binary) << "not-a-glb";
|
||||
std::ofstream(root / "Assets/Unsupported.obj") << "o Unsupported\n";
|
||||
std::ofstream(root / "Assets/Unused.mcmaterial.json") << R"({"BaseColor":[0.4,0.5,0.6]})";
|
||||
{
|
||||
std::ofstream buffer(root / "Assets/Triangle.bin", std::ios::binary);
|
||||
const float positions[]{-1.0F, -1.0F, 0.0F, 1.0F, -1.0F, 0.0F, 0.0F, 1.0F, 0.0F};
|
||||
const std::uint16_t indices[]{0, 1, 2};
|
||||
buffer.write(reinterpret_cast<const char*>(positions), sizeof(positions));
|
||||
buffer.write(reinterpret_cast<const char*>(indices), sizeof(indices));
|
||||
}
|
||||
std::ofstream(root / "Assets/Triangle.gltf") << R"({
|
||||
"asset":{"version":"2.0"},"scene":0,"scenes":[{"nodes":[0]}],
|
||||
"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":0},"indices":1}]}],
|
||||
"buffers":[{"uri":"Triangle.bin","byteLength":42}],
|
||||
"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":36},{"buffer":0,"byteOffset":36,"byteLength":6}],
|
||||
"accessors":[{"bufferView":0,"componentType":5126,"count":3,"type":"VEC3"},{"bufferView":1,"componentType":5123,"count":3,"type":"SCALAR"}]
|
||||
})";
|
||||
std::ofstream(root / "Scenes/Main.mcscene.json") << R"({"name":"Main","objects":[]})";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto root = std::filesystem::temp_directory_path() / "MetaCoreAssetPipelineGolden";
|
||||
std::filesystem::remove_all(root);
|
||||
WriteProject(root);
|
||||
WriteGoldenInputs(root);
|
||||
setenv("METACORE_PROJECT_PATH", root.string().c_str(), 1);
|
||||
setenv("METACORE_SYNCHRONOUS_ASSET_BOOTSTRAP", "1", 1);
|
||||
|
||||
try {
|
||||
MetaCore::MetaCoreEditorModuleRegistry registry;
|
||||
auto module = MetaCore::MetaCoreCreateBuiltinCoreServicesModule();
|
||||
module->Startup(registry);
|
||||
|
||||
const auto database = registry.ResolveService<MetaCore::MetaCoreIAssetDatabaseService>();
|
||||
const auto packages = registry.ResolveService<MetaCore::MetaCoreIPackageService>();
|
||||
const auto imports = registry.ResolveService<MetaCore::MetaCoreIImportPipelineService>();
|
||||
const auto audit = registry.ResolveService<MetaCore::MetaCoreIAssetAuditService>();
|
||||
const auto thumbnails = registry.ResolveService<MetaCore::MetaCoreIThumbnailService>();
|
||||
Expect(database != nullptr && packages != nullptr && imports != nullptr && audit != nullptr && thumbnails != nullptr, "R2 services must resolve");
|
||||
|
||||
Expect(
|
||||
!std::filesystem::exists(root / "Assets/Unused.mcmaterial.json.mcasset"),
|
||||
"native JSON material must not generate a recursive .mcasset package"
|
||||
);
|
||||
Expect(std::none_of(database->GetAssetRegistry().begin(), database->GetAssetRegistry().end(), [&](const auto& record) {
|
||||
if (record.RelativePath.extension() != ".mcasset") return false;
|
||||
auto source = root / record.RelativePath;
|
||||
source.replace_extension();
|
||||
return std::filesystem::is_regular_file(source);
|
||||
}), "derived packages must not be registered as duplicate source assets");
|
||||
|
||||
const auto texture = database->FindAssetByRelativePath("Assets/Color.ppm");
|
||||
Expect(texture.has_value(), "golden texture must be registered");
|
||||
Expect(texture->ImportResult != MetaCore::MetaCoreModelImportResult::Error, "golden texture import must succeed");
|
||||
const auto texturePackage = packages->ReadPackage(root / texture->PackagePath);
|
||||
Expect(texturePackage.has_value() && texturePackage->PayloadSections.size() > 1, "texture package must contain KTX2 payload");
|
||||
const auto& bytes = texturePackage->PayloadSections[1];
|
||||
constexpr unsigned char signature[]{0xAB, 'K', 'T', 'X', ' ', '2', '0', 0xBB, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
Expect(bytes.size() >= sizeof(signature), "KTX2 payload must contain identifier");
|
||||
for (std::size_t index = 0; index < sizeof(signature); ++index) {
|
||||
Expect(std::to_integer<unsigned char>(bytes[index]) == signature[index], "texture payload must be KTX2");
|
||||
}
|
||||
|
||||
const auto broken = database->FindAssetByRelativePath("Assets/Broken.glb");
|
||||
const auto unsupported = database->FindAssetByRelativePath("Assets/Unsupported.obj");
|
||||
Expect(broken.has_value() && broken->ImportResult == MetaCore::MetaCoreModelImportResult::Error, "broken GLB must fail");
|
||||
Expect(unsupported.has_value() && unsupported->ImportResult == MetaCore::MetaCoreModelImportResult::Error, "OBJ must be unsupported");
|
||||
Expect(!std::filesystem::exists(root / "Assets/Broken.glb.mcasset"), "broken GLB must not create placeholder package");
|
||||
Expect(!std::filesystem::exists(root / "Assets/Unsupported.obj.mcasset"), "OBJ must not create placeholder package");
|
||||
const std::size_t jobCountBeforeNoOpRefresh = imports->GetJobs().size();
|
||||
Expect(database->Refresh(), "no-op refresh must succeed");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
Expect(
|
||||
imports->GetJobs().size() == jobCountBeforeNoOpRefresh,
|
||||
"failed unsupported assets must not be enqueued forever just because no package exists"
|
||||
);
|
||||
|
||||
const auto model = database->FindAssetByRelativePath("Assets/Triangle.gltf");
|
||||
Expect(model.has_value() && model->ImportResult != MetaCore::MetaCoreModelImportResult::Error, "golden glTF must import");
|
||||
const auto modelPackage = packages->ReadPackage(root / model->PackagePath);
|
||||
Expect(modelPackage.has_value() && modelPackage->PayloadSections.size() > 1, "model package must persist mesh payloads");
|
||||
std::filesystem::path modelThumbnail;
|
||||
for (int attempt = 0; attempt < 100 && modelThumbnail.empty(); ++attempt) {
|
||||
modelThumbnail = thumbnails->RequestThumbnail(*model, 96);
|
||||
if (modelThumbnail.empty()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
Expect(!modelThumbnail.empty() && std::filesystem::file_size(modelThumbnail) > 100, "model geometry thumbnail must be generated");
|
||||
|
||||
const auto oversizedModelPath = root / "Assets/Oversized.glb";
|
||||
std::ofstream(oversizedModelPath, std::ios::binary).put('\0');
|
||||
std::filesystem::resize_file(oversizedModelPath, 64ULL * 1024ULL * 1024ULL + 1ULL);
|
||||
auto oversizedModel = *model;
|
||||
oversizedModel.Guid = MetaCore::MetaCoreAssetGuid::Generate();
|
||||
oversizedModel.RelativePath = "Assets/Oversized.glb";
|
||||
oversizedModel.SourcePath = oversizedModel.RelativePath;
|
||||
oversizedModel.ArtifactKey = "oversized-model-thumbnail-fallback";
|
||||
std::filesystem::path oversizedThumbnail;
|
||||
for (int attempt = 0; attempt < 100 && oversizedThumbnail.empty(); ++attempt) {
|
||||
oversizedThumbnail = thumbnails->RequestThumbnail(oversizedModel, 96);
|
||||
if (oversizedThumbnail.empty()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
Expect(
|
||||
!oversizedThumbnail.empty() && std::filesystem::file_size(oversizedThumbnail) > 100,
|
||||
"oversized models must use a bounded fallback thumbnail instead of parsing the full source"
|
||||
);
|
||||
std::filesystem::remove(oversizedModelPath);
|
||||
|
||||
const auto findings = audit->AuditAll();
|
||||
Expect(std::any_of(findings.begin(), findings.end(), [](const auto& finding) {
|
||||
return finding.Status == MetaCore::MetaCoreAssetReferenceStatus::BuildUnreachable;
|
||||
}), "audit must report build-unreachable assets");
|
||||
|
||||
module->Shutdown(registry);
|
||||
registry.ShutdownServices();
|
||||
unsetenv("METACORE_PROJECT_PATH");
|
||||
unsetenv("METACORE_SYNCHRONOUS_ASSET_BOOTSTRAP");
|
||||
std::filesystem::remove_all(root);
|
||||
std::cout << "MetaCoreAssetPipelineTests passed\n";
|
||||
return 0;
|
||||
} catch (const std::exception& exception) {
|
||||
unsetenv("METACORE_PROJECT_PATH");
|
||||
unsetenv("METACORE_SYNCHRONOUS_ASSET_BOOTSTRAP");
|
||||
std::filesystem::remove_all(root);
|
||||
std::cerr << "MetaCoreAssetPipelineTests failed: " << exception.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -2115,10 +2115,14 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
|
||||
const auto packageService = moduleRegistry.ResolveService<MetaCore::MetaCoreIPackageService>();
|
||||
const auto reflectionRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIReflectionRegistry>();
|
||||
const auto assetEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetEditingService>();
|
||||
const auto derivedDataCache = moduleRegistry.ResolveService<MetaCore::MetaCoreIDerivedDataCacheService>();
|
||||
const auto importPipeline = moduleRegistry.ResolveService<MetaCore::MetaCoreIImportPipelineService>();
|
||||
MetaCoreExpect(assetDatabase != nullptr, "应解析到 AssetDatabaseService");
|
||||
MetaCoreExpect(packageService != nullptr, "应解析到 PackageService");
|
||||
MetaCoreExpect(reflectionRegistry != nullptr, "应解析到 ReflectionRegistry");
|
||||
MetaCoreExpect(assetEditingService != nullptr, "应解析到 AssetEditingService");
|
||||
MetaCoreExpect(derivedDataCache != nullptr, "应解析到 DerivedDataCacheService");
|
||||
MetaCoreExpect(importPipeline != nullptr, "应解析到 ImportPipelineService");
|
||||
|
||||
const auto& project = assetDatabase->GetProjectDescriptor();
|
||||
MetaCoreExpect(project.RuntimePath.filename() == "ConfigRuntime", "项目应读取 runtime_directory");
|
||||
@ -2140,6 +2144,9 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
|
||||
input >> metaJson;
|
||||
MetaCoreExpect(metaJson.value("source_size_bytes", 0ULL) > 0ULL, "gltf mcmeta 应记录源文件大小快速戳");
|
||||
MetaCoreExpect(metaJson.contains("source_mtime_ns"), "gltf mcmeta 应记录源文件 mtime 快速戳");
|
||||
MetaCoreExpect(metaJson.value("schema_version", 0U) >= 2U, "gltf mcmeta 应使用版本化元数据契约");
|
||||
MetaCoreExpect(metaJson.value("settings_hash", 0ULL) != 0ULL, "gltf mcmeta 应记录规范化设置 Hash");
|
||||
MetaCoreExpect(!metaJson.value("artifact_key", std::string{}).empty(), "gltf mcmeta 应记录完整 Artifact Key");
|
||||
}
|
||||
|
||||
const auto embeddedTextureRecord =
|
||||
@ -2156,27 +2163,36 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
|
||||
const std::filesystem::path glbMeta = tempProjectRoot / "Assets" / "Tank.glb.mcmeta";
|
||||
const std::filesystem::path glbPackage = tempProjectRoot / "Assets" / "Tank.glb.mcasset";
|
||||
MetaCoreExpect(std::filesystem::exists(glbMeta), "glb 应生成 mcmeta 文件");
|
||||
MetaCoreExpect(std::filesystem::exists(glbPackage), "glb 应生成轻量 mcasset 元数据包");
|
||||
|
||||
std::error_code removeError;
|
||||
std::filesystem::remove(glbPackage, removeError);
|
||||
MetaCoreExpect(!std::filesystem::exists(glbPackage), "测试前应能删除 glb mcasset");
|
||||
MetaCoreExpect(glbRecord->ImportResult == MetaCore::MetaCoreModelImportResult::Error, "损坏 glb 应报告导入失败");
|
||||
MetaCoreExpect(!std::filesystem::exists(glbPackage), "损坏 glb 不得生成占位成功包");
|
||||
MetaCoreExpect(assetDatabase->Refresh(), "缺失 glb mcasset 时刷新应成功");
|
||||
MetaCoreExpect(std::filesystem::exists(glbPackage), "source hash 未变但 glb mcasset 缺失时应自动补包");
|
||||
MetaCoreExpect(!std::filesystem::exists(glbPackage), "重复刷新不得为损坏 glb 生成占位包");
|
||||
|
||||
const auto objRecord = assetDatabase->FindAssetByRelativePath(std::filesystem::path("Assets") / "Crate.obj");
|
||||
MetaCoreExpect(objRecord.has_value(), "应能找到 obj 资源记录");
|
||||
MetaCoreExpect(objRecord->ImporterId == "ModelImporter", "obj 应使用通用 ModelImporter");
|
||||
MetaCoreExpect(objRecord->Type == "model", "obj 资源类型应为 model");
|
||||
MetaCoreExpect(objRecord->ImportResult == MetaCore::MetaCoreModelImportResult::Error, "obj 在本阶段应明确报告不支持");
|
||||
const std::filesystem::path objMeta = tempProjectRoot / "Assets" / "Crate.obj.mcmeta";
|
||||
MetaCoreExpect(std::filesystem::exists(objMeta), "obj 应生成 mcmeta 文件");
|
||||
MetaCoreExpect(!std::filesystem::exists(tempProjectRoot / "Assets" / "Crate.obj.mcasset"), "obj 不得生成占位成功包");
|
||||
|
||||
const auto fbxRecord = assetDatabase->FindAssetByRelativePath(std::filesystem::path("Assets") / "Robot.fbx");
|
||||
MetaCoreExpect(fbxRecord.has_value(), "应能找到 fbx 资源记录");
|
||||
MetaCoreExpect(fbxRecord->ImporterId == "ModelImporter", "fbx 应使用通用 ModelImporter");
|
||||
MetaCoreExpect(fbxRecord->Type == "model", "fbx 资源类型应为 model");
|
||||
MetaCoreExpect(fbxRecord->ImportResult == MetaCore::MetaCoreModelImportResult::Error, "fbx 在本阶段应明确报告不支持");
|
||||
const std::filesystem::path fbxMeta = tempProjectRoot / "Assets" / "Robot.fbx.mcmeta";
|
||||
MetaCoreExpect(std::filesystem::exists(fbxMeta), "fbx 应生成 mcmeta 文件");
|
||||
MetaCoreExpect(!std::filesystem::exists(tempProjectRoot / "Assets" / "Robot.fbx.mcasset"), "fbx 不得生成占位成功包");
|
||||
|
||||
const std::array<std::byte, 4> cachePayload{
|
||||
std::byte{0x4D}, std::byte{0x43}, std::byte{0x44}, std::byte{0x44}
|
||||
};
|
||||
MetaCoreExpect(derivedDataCache->Put("smoke-stable-key", cachePayload, ".bin"), "DDC 应能原子写入派生数据");
|
||||
const auto cachedPayload = derivedDataCache->Get("smoke-stable-key", ".bin");
|
||||
MetaCoreExpect(cachedPayload.has_value() && *cachedPayload == std::vector<std::byte>(cachePayload.begin(), cachePayload.end()), "DDC 应按稳定 Key 返回相同字节");
|
||||
MetaCoreExpect(!importPipeline->GetJobs().empty(), "初始 Refresh 应留下可诊断的导入作业快照");
|
||||
|
||||
coreServicesModule->Shutdown(moduleRegistry);
|
||||
moduleRegistry.ShutdownServices();
|
||||
@ -4920,22 +4936,44 @@ void MetaCoreTestP3ProductivityStage() {
|
||||
coreServicesModule->Startup(moduleRegistry);
|
||||
|
||||
const auto assetDatabase = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetDatabaseService>();
|
||||
const auto importPipeline = moduleRegistry.ResolveService<MetaCore::MetaCoreIImportPipelineService>();
|
||||
MetaCoreExpect(assetDatabase != nullptr, "应解析到 AssetDatabaseService");
|
||||
MetaCoreExpect(importPipeline != nullptr, "应解析到 ImportPipelineService");
|
||||
MetaCoreExpect(assetDatabase->HasProject(), "应成功打开项目");
|
||||
const auto waitForImports = [&]() {
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10);
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
const auto jobs = importPipeline->GetJobs();
|
||||
const bool busy = std::any_of(jobs.begin(), jobs.end(), [](const auto& job) {
|
||||
return job.State == MetaCore::MetaCoreImportJobState::Queued ||
|
||||
job.State == MetaCore::MetaCoreImportJobState::Running ||
|
||||
job.State == MetaCore::MetaCoreImportJobState::Committing;
|
||||
});
|
||||
if (!busy) return;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
MetaCoreExpect(false, "等待异步导入完成超时");
|
||||
};
|
||||
|
||||
// 2. 测试资产自动导入与默认目录 (P3.2)
|
||||
// 写入一个没有 .mcmeta 的 .ktx 贴图文件
|
||||
const std::filesystem::path texturePath = tempProjectRoot / "Assets" / "test_texture.ktx";
|
||||
// 写入一个没有 .mcmeta 的可解码贴图文件;生产管线不再接受伪造/损坏的 KTX 输入。
|
||||
const std::filesystem::path texturePath = tempProjectRoot / "Assets" / "test_texture.ppm";
|
||||
{
|
||||
std::ofstream textureFile(texturePath, std::ios::trunc);
|
||||
textureFile << "dummy-ktx-data";
|
||||
std::ofstream textureFile(texturePath, std::ios::binary | std::ios::trunc);
|
||||
textureFile << "P6\n2 2\n255\n";
|
||||
constexpr std::array<unsigned char, 12> pixels{
|
||||
255, 0, 0, 0, 255, 0,
|
||||
0, 0, 255, 255, 255, 255
|
||||
};
|
||||
textureFile.write(reinterpret_cast<const char*>(pixels.data()), static_cast<std::streamsize>(pixels.size()));
|
||||
}
|
||||
|
||||
assetDatabase->Refresh();
|
||||
waitForImports();
|
||||
|
||||
// 验证是否在 Assets 下自动生成了 test_texture.ktx.mcmeta 物理元文件
|
||||
const std::filesystem::path metaPath = tempProjectRoot / "Assets" / "test_texture.ktx.mcmeta";
|
||||
MetaCoreExpect(std::filesystem::exists(metaPath), "Refresh 后应生成 test_texture.ktx.mcmeta 物理元文件");
|
||||
// 验证是否在 Assets 下自动生成了 test_texture.ppm.mcmeta 物理元文件
|
||||
const std::filesystem::path metaPath = tempProjectRoot / "Assets" / "test_texture.ppm.mcmeta";
|
||||
MetaCoreExpect(std::filesystem::exists(metaPath), "Refresh 后应生成 test_texture.ppm.mcmeta 物理元文件");
|
||||
|
||||
// 读取该 .mcmeta 文件内容,校验结构
|
||||
std::ifstream metaFile(metaPath);
|
||||
@ -4953,16 +4991,16 @@ void MetaCoreTestP3ProductivityStage() {
|
||||
|
||||
// 3. 测试资产重命名与删除 (P3.1, P3.4)
|
||||
// 重命名该贴图文件
|
||||
MetaCoreExpect(assetDatabase->RenamePath(std::filesystem::path("Assets") / "test_texture.ktx", "renamed_texture.ktx"), "应能重命名贴图文件");
|
||||
const std::filesystem::path renamedTexturePath = tempProjectRoot / "Assets" / "renamed_texture.ktx";
|
||||
const std::filesystem::path renamedMetaPath = tempProjectRoot / "Assets" / "renamed_texture.ktx.mcmeta";
|
||||
MetaCoreExpect(assetDatabase->RenamePath(std::filesystem::path("Assets") / "test_texture.ppm", "renamed_texture.ppm"), "应能重命名贴图文件");
|
||||
const std::filesystem::path renamedTexturePath = tempProjectRoot / "Assets" / "renamed_texture.ppm";
|
||||
const std::filesystem::path renamedMetaPath = tempProjectRoot / "Assets" / "renamed_texture.ppm.mcmeta";
|
||||
MetaCoreExpect(std::filesystem::exists(renamedTexturePath), "重命名后新贴图文件应存在");
|
||||
MetaCoreExpect(!std::filesystem::exists(texturePath), "重命名后旧贴图文件不应存在");
|
||||
MetaCoreExpect(std::filesystem::exists(renamedMetaPath), "重命名后新 mcmeta 文件应存在");
|
||||
MetaCoreExpect(!std::filesystem::exists(metaPath), "重命名后旧 mcmeta 文件不应存在");
|
||||
|
||||
// 删除重命名后的贴图文件
|
||||
MetaCoreExpect(assetDatabase->DeletePath(std::filesystem::path("Assets") / "renamed_texture.ktx"), "应能删除贴图文件");
|
||||
MetaCoreExpect(assetDatabase->DeletePath(std::filesystem::path("Assets") / "renamed_texture.ppm"), "应能删除贴图文件");
|
||||
MetaCoreExpect(!std::filesystem::exists(renamedTexturePath), "删除后贴图文件应不存在");
|
||||
MetaCoreExpect(!std::filesystem::exists(renamedMetaPath), "删除后 mcmeta 文件应不存在");
|
||||
|
||||
@ -4987,6 +5025,7 @@ void MetaCoreTestP3ProductivityStage() {
|
||||
|
||||
// 自动刷新生成 .mcmeta 物理元文件
|
||||
assetDatabase->Refresh();
|
||||
waitForImports();
|
||||
const std::filesystem::path materialMetaPath = tempProjectRoot / "Assets" / "test_material.mcmaterial.json.mcmeta";
|
||||
MetaCoreExpect(std::filesystem::exists(materialMetaPath), "Refresh 后应生成材质的 mcmeta 元文件");
|
||||
|
||||
@ -5349,6 +5388,7 @@ void MetaCoreTestSceneEnvironmentSettings() {
|
||||
int main() {
|
||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
std::setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
_putenv_s("METACORE_SYNCHRONOUS_ASSET_BOOTSTRAP", "1");
|
||||
|
||||
std::cout << "[RUN] MetaCoreCreateDefaultScene..." << std::endl;
|
||||
MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
"name": "metacore",
|
||||
"version-string": "1.0.0",
|
||||
"dependencies": [
|
||||
"basisu",
|
||||
"glm",
|
||||
"entt",
|
||||
"rmlui",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user