启动器更新和材质优化
This commit is contained in:
parent
a8739a9423
commit
7729a21fe9
1055
Apps/MetaCoreLauncher/main.cpp
Normal file
1055
Apps/MetaCoreLauncher/main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -548,6 +548,34 @@ target_link_libraries(MetaCoreEditorApp
|
||||
metacore_stage_ui_blit_material(MetaCoreEditorApp)
|
||||
|
||||
|
||||
set(METACORE_LAUNCHER_SOURCES
|
||||
Apps/MetaCoreLauncher/main.cpp
|
||||
Source/MetaCoreRender/Private/MetaCoreGlibcCompat.cpp
|
||||
)
|
||||
|
||||
if(NOT WIN32 AND METACORE_IMGUI_USES_PKG_CONFIG AND EXISTS "${METACORE_IMGUI_GLFW_BACKEND_SOURCE}")
|
||||
list(APPEND METACORE_LAUNCHER_SOURCES "${METACORE_IMGUI_GLFW_BACKEND_SOURCE}")
|
||||
endif()
|
||||
|
||||
add_executable(MetaCoreLauncher
|
||||
${METACORE_LAUNCHER_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(MetaCoreLauncher
|
||||
PRIVATE
|
||||
MetaCoreFoundation
|
||||
MetaCorePlatform
|
||||
MetaCoreRender
|
||||
imgui::imgui
|
||||
)
|
||||
|
||||
target_compile_options(MetaCoreLauncher PRIVATE ${METACORE_COMMON_WARNINGS})
|
||||
if(WIN32)
|
||||
target_compile_definitions(MetaCoreLauncher PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
endif()
|
||||
metacore_stage_ui_blit_material(MetaCoreLauncher)
|
||||
|
||||
|
||||
|
||||
add_executable(MetaCorePlayer
|
||||
Apps/MetaCorePlayer/main.cpp
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "fc3fd465-4f39-44af-907a-4fc0494c2502",
|
||||
"BaseColor": [
|
||||
1.0,
|
||||
0.9686274528503418,
|
||||
0.46666666865348816,
|
||||
0.9529411792755127,
|
||||
0.16862745583057404
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
"guid": "fc3fd465-4f39-44af-907a-4fc0494c2502",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material 1.mcmaterial.json",
|
||||
"source_hash": 12648564269874715657,
|
||||
"source_hash": 4176055334418841915,
|
||||
"source_path": "Assets/Materials/Material 1.mcmaterial.json"
|
||||
}
|
||||
26
SandboxProject/Assets/Materials/Material 7.mcmaterial.json
Normal file
26
SandboxProject/Assets/Materials/Material 7.mcmaterial.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "b13c413e-0350-4152-9f1e-41552f9823af",
|
||||
"BaseColor": [
|
||||
0.27450981736183167,
|
||||
0.19607843458652496,
|
||||
1.0
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
"DoubleSided": false,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTexture": "",
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTexture": "",
|
||||
"Name": "Material 7",
|
||||
"NormalTexture": "",
|
||||
"Roughness": 1.0,
|
||||
"ShaderModel": 0,
|
||||
"StableImportKey": ""
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "material",
|
||||
"guid": "b13c413e-0350-4152-9f1e-41552f9823af",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material 7.mcmaterial.json",
|
||||
"source_hash": 12622570808854545607,
|
||||
"source_path": "Assets/Materials/Material 7.mcmaterial.json"
|
||||
}
|
||||
BIN
SandboxProject/Assets/Models/Cube.glb.mcasset
Normal file
BIN
SandboxProject/Assets/Models/Cube.glb.mcasset
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -70,6 +70,28 @@ void MetaCoreDebugLog(const char* category, const std::string& message) {
|
||||
std::cerr << "[MetaCoreDebug][" << category << "] " << message << std::endl;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreMaterialDebugLogEnabled() {
|
||||
const char* value = std::getenv("METACORE_DEBUG_MATERIAL");
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return std::strcmp(value, "1") == 0 ||
|
||||
std::strcmp(value, "true") == 0 ||
|
||||
std::strcmp(value, "TRUE") == 0;
|
||||
}
|
||||
|
||||
void MetaCoreDebugMaterialLog(const std::string& message) {
|
||||
if (MetaCoreMaterialDebugLogEnabled()) {
|
||||
MetaCoreDebugLog("Material", message);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MetaCoreDebugVec3String(const glm::vec3& value) {
|
||||
std::ostringstream stream;
|
||||
stream << "(" << value.x << "," << value.y << "," << value.z << ")";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MetaCoreDebugSceneSummary(MetaCoreEditorContext& editorContext) {
|
||||
const auto objects = editorContext.GetScene().GetGameObjects();
|
||||
std::size_t scriptComponentCount = 0;
|
||||
@ -1112,12 +1134,6 @@ void MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(
|
||||
return std::filesystem::path(path).lexically_normal().generic_string();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreMaterialGuidListIsEmpty(const std::vector<MetaCoreAssetGuid>& materialGuids) {
|
||||
return std::all_of(materialGuids.begin(), materialGuids.end(), [](const MetaCoreAssetGuid& guid) {
|
||||
return !guid.IsValid();
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MetaCoreResolveSceneObjectModelPath(
|
||||
const MetaCoreScene& scene,
|
||||
MetaCoreGameObject object
|
||||
@ -1162,6 +1178,34 @@ void MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreMaterialGuidResolves(
|
||||
const MetaCoreModelAssetDocument* modelDocument,
|
||||
const MetaCoreIAssetEditingService* assetEditingService,
|
||||
const MetaCoreAssetGuid& materialGuid
|
||||
) {
|
||||
if (!materialGuid.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modelDocument != nullptr &&
|
||||
MetaCoreFindGeneratedMaterialIndexByGuid(*modelDocument, materialGuid).has_value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return assetEditingService != nullptr &&
|
||||
assetEditingService->LoadMaterialAsset(materialGuid).has_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreMaterialGuidListHasResolvableMaterial(
|
||||
const std::vector<MetaCoreAssetGuid>& materialGuids,
|
||||
const MetaCoreModelAssetDocument* modelDocument,
|
||||
const MetaCoreIAssetEditingService* assetEditingService
|
||||
) {
|
||||
return std::any_of(materialGuids.begin(), materialGuids.end(), [&](const MetaCoreAssetGuid& guid) {
|
||||
return MetaCoreMaterialGuidResolves(modelDocument, assetEditingService, guid);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::size_t> MetaCoreResolveFirstGeneratedMaterialIndexForMeshRenderer(
|
||||
const MetaCoreModelAssetDocument& document,
|
||||
const MetaCoreMeshRendererComponent& meshRenderer
|
||||
@ -1185,64 +1229,73 @@ void MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void MetaCoreRepairLegacyModelMaterialBinding(
|
||||
[[nodiscard]] bool MetaCoreRepairLegacyModelMaterialBinding(
|
||||
MetaCoreMeshRendererComponent& meshRenderer,
|
||||
const MetaCoreModelAssetDocument& document,
|
||||
const MetaCoreAssetGuid& modelGuid
|
||||
const MetaCoreAssetGuid& modelGuid,
|
||||
const MetaCoreIAssetEditingService* assetEditingService = nullptr
|
||||
) {
|
||||
bool changed = false;
|
||||
if (meshRenderer.SourceModelPath.empty()) {
|
||||
meshRenderer.SourceModelPath = document.SourcePath.generic_string();
|
||||
changed = true;
|
||||
}
|
||||
if (!meshRenderer.SourceModelAssetGuid.IsValid() && modelGuid.IsValid()) {
|
||||
meshRenderer.SourceModelAssetGuid = modelGuid;
|
||||
changed = true;
|
||||
}
|
||||
if (!MetaCoreMaterialGuidListIsEmpty(meshRenderer.MaterialAssetGuids)) {
|
||||
return;
|
||||
if (MetaCoreMaterialGuidListHasResolvableMaterial(
|
||||
meshRenderer.MaterialAssetGuids,
|
||||
&document,
|
||||
assetEditingService
|
||||
)) {
|
||||
return changed;
|
||||
}
|
||||
if (meshRenderer.ModelNodeIndex < 0 ||
|
||||
static_cast<std::size_t>(meshRenderer.ModelNodeIndex) >= document.Nodes.size()) {
|
||||
return;
|
||||
return changed;
|
||||
}
|
||||
|
||||
const MetaCoreImportedGltfNodeDocument& node = document.Nodes[static_cast<std::size_t>(meshRenderer.ModelNodeIndex)];
|
||||
if (node.MeshIndex < 0 || static_cast<std::size_t>(node.MeshIndex) >= document.Meshes.size()) {
|
||||
return;
|
||||
return changed;
|
||||
}
|
||||
|
||||
const auto& materialSlots = document.Meshes[static_cast<std::size_t>(node.MeshIndex)].MaterialSlots;
|
||||
meshRenderer.MaterialAssetGuids.clear();
|
||||
std::vector<MetaCoreAssetGuid> repairedMaterialGuids;
|
||||
repairedMaterialGuids.reserve(materialSlots.size());
|
||||
for (const std::int32_t materialIndex : materialSlots) {
|
||||
if (materialIndex >= 0 && static_cast<std::size_t>(materialIndex) < document.GeneratedMaterialAssets.size()) {
|
||||
meshRenderer.MaterialAssetGuids.push_back(document.GeneratedMaterialAssets[static_cast<std::size_t>(materialIndex)].AssetGuid);
|
||||
repairedMaterialGuids.push_back(document.GeneratedMaterialAssets[static_cast<std::size_t>(materialIndex)].AssetGuid);
|
||||
} else {
|
||||
meshRenderer.MaterialAssetGuids.push_back(MetaCoreAssetGuid{});
|
||||
repairedMaterialGuids.push_back(MetaCoreAssetGuid{});
|
||||
}
|
||||
}
|
||||
|
||||
if (meshRenderer.MaterialAssetGuids != repairedMaterialGuids) {
|
||||
meshRenderer.MaterialAssetGuids = std::move(repairedMaterialGuids);
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void MetaCoreApplyGeneratedMaterialPreviewToScene(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreModelAssetDocument& document
|
||||
) {
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
const std::string documentSourcePath = MetaCoreNormalizeModelRelativePath(document.SourcePath.generic_string());
|
||||
const MetaCoreAssetGuid modelGuid = documentSourcePath.empty()
|
||||
? MetaCoreAssetGuid{}
|
||||
: MetaCoreAssetRegistry::Get().ResolvePathToGuid(documentSourcePath);
|
||||
bool changed = false;
|
||||
std::size_t matchedObjects = 0;
|
||||
for (MetaCoreGameObject& sceneObject : editorContext.GetScene().GetGameObjects()) {
|
||||
if (!sceneObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& meshRenderer = sceneObject.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
std::optional<std::size_t> materialIndex;
|
||||
for (const MetaCoreAssetGuid& materialGuid : meshRenderer.MaterialAssetGuids) {
|
||||
materialIndex = MetaCoreFindGeneratedMaterialIndexByGuid(document, materialGuid);
|
||||
if (materialIndex.has_value()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const bool sameModelByGuid =
|
||||
modelGuid.IsValid() &&
|
||||
meshRenderer.SourceModelAssetGuid.IsValid() &&
|
||||
@ -1251,7 +1304,24 @@ void MetaCoreApplyGeneratedMaterialPreviewToScene(
|
||||
const bool sameModelByPath = !documentSourcePath.empty() && sceneObjectModelPath == documentSourcePath;
|
||||
const bool sameModel = sameModelByGuid || sameModelByPath;
|
||||
|
||||
if (!materialIndex.has_value() && sameModel && MetaCoreMaterialGuidListIsEmpty(meshRenderer.MaterialAssetGuids)) {
|
||||
if (sameModel) {
|
||||
changed = MetaCoreRepairLegacyModelMaterialBinding(
|
||||
meshRenderer,
|
||||
document,
|
||||
modelGuid,
|
||||
assetEditingService.get()
|
||||
) || changed;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> materialIndex;
|
||||
for (const MetaCoreAssetGuid& materialGuid : meshRenderer.MaterialAssetGuids) {
|
||||
materialIndex = MetaCoreFindGeneratedMaterialIndexByGuid(document, materialGuid);
|
||||
if (materialIndex.has_value()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!materialIndex.has_value() && sameModel) {
|
||||
materialIndex = MetaCoreResolveFirstGeneratedMaterialIndexForMeshRenderer(document, meshRenderer);
|
||||
}
|
||||
|
||||
@ -1259,13 +1329,26 @@ void MetaCoreApplyGeneratedMaterialPreviewToScene(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sameModel) {
|
||||
MetaCoreRepairLegacyModelMaterialBinding(meshRenderer, document, modelGuid);
|
||||
}
|
||||
MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(meshRenderer, document, *materialIndex);
|
||||
++matchedObjects;
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply generated material preview objectId=" + std::to_string(sceneObject.GetId()) +
|
||||
" name=\"" + sceneObject.GetName() + "\"" +
|
||||
" modelGuid=" + modelGuid.ToString() +
|
||||
" materialIndex=" + std::to_string(*materialIndex) +
|
||||
" baseColor=" + MetaCoreDebugVec3String(meshRenderer.BaseColor) +
|
||||
" metallic=" + std::to_string(meshRenderer.Metallic) +
|
||||
" roughness=" + std::to_string(meshRenderer.Roughness)
|
||||
);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply generated material preview model=\"" + documentSourcePath +
|
||||
"\" matchedObjects=" + std::to_string(matchedObjects)
|
||||
);
|
||||
}
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
@ -1328,6 +1411,7 @@ void MetaCoreApplyMaterialPreviewToSceneUsers(
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
bool changed = false;
|
||||
std::size_t matchedObjects = 0;
|
||||
for (MetaCoreGameObject& sceneObject : editorContext.GetScene().GetGameObjects()) {
|
||||
if (!sceneObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
continue;
|
||||
@ -1342,14 +1426,54 @@ void MetaCoreApplyMaterialPreviewToSceneUsers(
|
||||
meshRenderer,
|
||||
materialAsset
|
||||
);
|
||||
++matchedObjects;
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply material resource preview objectId=" + std::to_string(sceneObject.GetId()) +
|
||||
" name=\"" + sceneObject.GetName() + "\"" +
|
||||
" materialGuid=" + materialGuid.ToString() +
|
||||
" baseColor=" + MetaCoreDebugVec3String(meshRenderer.BaseColor) +
|
||||
" metallic=" + std::to_string(meshRenderer.Metallic) +
|
||||
" roughness=" + std::to_string(meshRenderer.Roughness)
|
||||
);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply material resource preview materialGuid=" + materialGuid.ToString() +
|
||||
" matchedObjects=" + std::to_string(matchedObjects)
|
||||
);
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unordered_map<MetaCoreAssetGuid, MetaCoreMaterialAssetDocument, MetaCoreAssetGuidHasher>&
|
||||
MetaCoreGetInlineMaterialEditDrafts() {
|
||||
static std::unordered_map<MetaCoreAssetGuid, MetaCoreMaterialAssetDocument, MetaCoreAssetGuidHasher> drafts;
|
||||
return drafts;
|
||||
}
|
||||
|
||||
void MetaCoreApplyMaterialAssetPreviewToScene(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreIAssetEditingService& assetEditingService,
|
||||
const MetaCoreAssetGuid& materialGuid,
|
||||
const MetaCoreMaterialAssetDocument& materialAsset
|
||||
) {
|
||||
const auto resolvedMaterial = assetEditingService.ResolveGeneratedAsset(materialGuid);
|
||||
if (resolvedMaterial.has_value() && resolvedMaterial->GeneratedKind == "material") {
|
||||
auto modelDocument = assetEditingService.LoadModelAsset(resolvedMaterial->SourceAsset.Guid);
|
||||
if (modelDocument.has_value() &&
|
||||
resolvedMaterial->GeneratedIndex < modelDocument->GeneratedMaterialAssets.size()) {
|
||||
modelDocument->GeneratedMaterialAssets[resolvedMaterial->GeneratedIndex] = materialAsset;
|
||||
modelDocument->GeneratedMaterialAssets[resolvedMaterial->GeneratedIndex].AssetGuid = materialGuid;
|
||||
MetaCoreApplyGeneratedMaterialPreviewToScene(editorContext, *modelDocument);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MetaCoreApplyMaterialPreviewToSceneUsers(editorContext, materialGuid, materialAsset);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreDrawGeneratedAssetCombo(
|
||||
const char* label,
|
||||
const std::vector<MetaCoreGeneratedAssetChoice>& choices,
|
||||
@ -1394,8 +1518,19 @@ void MetaCoreForEachSelectedGameObject(MetaCoreEditorContext& editorContext, TAc
|
||||
|
||||
auto materialAsset = assetEditingService->LoadMaterialAsset(materialGuid);
|
||||
if (!materialAsset.has_value()) {
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply material failed: materialGuid=" + materialGuid.ToString() + " could not be loaded"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply material begin slot=" + std::to_string(materialSlotIndex) +
|
||||
" materialGuid=" + materialGuid.ToString() +
|
||||
" name=\"" + materialAsset->Name + "\"" +
|
||||
" baseColor=" + MetaCoreDebugVec3String(materialAsset->BaseColor) +
|
||||
" metallic=" + std::to_string(materialAsset->Metallic) +
|
||||
" roughness=" + std::to_string(materialAsset->Roughness)
|
||||
);
|
||||
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
const auto resolvedMaterial = assetEditingService->ResolveGeneratedAsset(materialGuid);
|
||||
@ -1431,10 +1566,24 @@ void MetaCoreForEachSelectedGameObject(MetaCoreEditorContext& editorContext, TAc
|
||||
*materialAsset
|
||||
);
|
||||
}
|
||||
const auto& meshRenderer = selectedObject.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply material objectId=" + std::to_string(selectedObject.GetId()) +
|
||||
" name=\"" + selectedObject.GetName() + "\"" +
|
||||
" slot=" + std::to_string(materialSlotIndex) +
|
||||
" materialGuid=" + materialGuid.ToString() +
|
||||
" meshBaseColor=" + MetaCoreDebugVec3String(meshRenderer.BaseColor) +
|
||||
" meshMetallic=" + std::to_string(meshRenderer.Metallic) +
|
||||
" meshRoughness=" + std::to_string(meshRenderer.Roughness) +
|
||||
" sceneRevisionBeforeIncrement=" + std::to_string(editorContext.GetScene().GetRevision())
|
||||
);
|
||||
changed = true;
|
||||
});
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Apply material scene revision=" + std::to_string(editorContext.GetScene().GetRevision())
|
||||
);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
@ -1574,6 +1723,63 @@ void MetaCoreClearMaterialSlotOnSelectedObjects(
|
||||
return modelDocument->GeneratedMaterialAssets[static_cast<std::size_t>(materialIndex)].AssetGuid;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreRepairMeshRendererMaterialBindingsFromSourceModel(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreMeshRendererComponent& meshRenderer
|
||||
) {
|
||||
if (meshRenderer.MeshSource != MetaCoreMeshSourceKind::Asset || meshRenderer.ModelNodeIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
if (assetEditingService == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MetaCoreAssetGuid modelGuid = meshRenderer.SourceModelAssetGuid;
|
||||
std::optional<MetaCoreModelAssetDocument> modelDocument;
|
||||
if (modelGuid.IsValid()) {
|
||||
modelDocument = assetEditingService->LoadModelAsset(modelGuid);
|
||||
}
|
||||
|
||||
if (!modelDocument.has_value()) {
|
||||
const auto sourceAsset = MetaCoreFindSourceModelAssetForMeshRenderer(editorContext, meshRenderer);
|
||||
if (sourceAsset.has_value()) {
|
||||
modelGuid = sourceAsset->Guid;
|
||||
modelDocument = assetEditingService->LoadModelAsset(sourceAsset->Guid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!modelDocument.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool repaired = MetaCoreRepairLegacyModelMaterialBinding(
|
||||
meshRenderer,
|
||||
*modelDocument,
|
||||
modelGuid,
|
||||
assetEditingService.get()
|
||||
);
|
||||
if (!repaired) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> materialIndex;
|
||||
for (const MetaCoreAssetGuid& materialGuid : meshRenderer.MaterialAssetGuids) {
|
||||
materialIndex = MetaCoreFindGeneratedMaterialIndexByGuid(*modelDocument, materialGuid);
|
||||
if (materialIndex.has_value()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!materialIndex.has_value()) {
|
||||
materialIndex = MetaCoreResolveFirstGeneratedMaterialIndexForMeshRenderer(*modelDocument, meshRenderer);
|
||||
}
|
||||
if (materialIndex.has_value()) {
|
||||
MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(meshRenderer, *modelDocument, *materialIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreRestoreDefaultMaterialSlotOnSelectedObjects(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::size_t materialSlotIndex
|
||||
@ -2495,6 +2701,10 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
}
|
||||
|
||||
MetaCoreMeshRendererComponent& meshRenderer = gameObject.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
if (MetaCoreRepairMeshRendererMaterialBindingsFromSourceModel(editorContext, meshRenderer)) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
|
||||
const MetaCoreGameObjectData* prefabObject = nullptr;
|
||||
if (const auto prefabDocument = MetaCoreLoadPrefabDocumentForInstance(editorContext, gameObject); prefabDocument.has_value() &&
|
||||
gameObject.HasComponent<MetaCorePrefabInstanceMetadata>()) {
|
||||
@ -3045,9 +3255,6 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
|
||||
std::string materialName = "Default-Material";
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
const auto resolvedMaterial = assetEditingService != nullptr
|
||||
? assetEditingService->ResolveGeneratedAsset(materialGuid)
|
||||
: std::nullopt;
|
||||
const auto materialDocument = assetEditingService != nullptr
|
||||
? assetEditingService->LoadMaterialAsset(materialGuid)
|
||||
: std::nullopt;
|
||||
@ -3093,35 +3300,65 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
MetaCoreClearMaterialSlotOnSelectedObjects(editorContext, materialIndex);
|
||||
}
|
||||
} else {
|
||||
MetaCoreMaterialAssetDocument materialAsset = *materialDocument;
|
||||
bool modified = false;
|
||||
auto& materialDrafts = MetaCoreGetInlineMaterialEditDrafts();
|
||||
const auto draftIterator = materialDrafts.find(materialGuid);
|
||||
MetaCoreMaterialAssetDocument materialAsset =
|
||||
draftIterator != materialDrafts.end() ? draftIterator->second : *materialDocument;
|
||||
bool previewModified = false;
|
||||
bool commitModified = false;
|
||||
|
||||
const auto markContinuousEdit = [&](bool changed) {
|
||||
if (changed) {
|
||||
previewModified = true;
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
commitModified = true;
|
||||
}
|
||||
};
|
||||
|
||||
const auto markDiscreteEdit = [&](bool changed) {
|
||||
if (changed) {
|
||||
previewModified = true;
|
||||
commitModified = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 材质基本属性编辑
|
||||
float baseColorRes[3] = {materialAsset.BaseColor.x, materialAsset.BaseColor.y, materialAsset.BaseColor.z};
|
||||
if (ImGui::ColorEdit3(("Base Color##Res" + std::to_string(materialIndex)).c_str(), baseColorRes)) {
|
||||
const bool baseColorChanged = ImGui::ColorEdit3(("Base Color##Res" + std::to_string(materialIndex)).c_str(), baseColorRes);
|
||||
if (baseColorChanged) {
|
||||
materialAsset.BaseColor = {baseColorRes[0], baseColorRes[1], baseColorRes[2]};
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::SliderFloat(("Metallic##Res" + std::to_string(materialIndex)).c_str(), &materialAsset.Metallic, 0.0F, 1.0F)) {
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::SliderFloat(("Roughness##Res" + std::to_string(materialIndex)).c_str(), &materialAsset.Roughness, 0.0F, 1.0F)) {
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::Checkbox(("Double Sided##Res" + std::to_string(materialIndex)).c_str(), &materialAsset.DoubleSided)) {
|
||||
modified = true;
|
||||
}
|
||||
markContinuousEdit(baseColorChanged);
|
||||
|
||||
markContinuousEdit(ImGui::SliderFloat(
|
||||
("Metallic##Res" + std::to_string(materialIndex)).c_str(),
|
||||
&materialAsset.Metallic,
|
||||
0.0F,
|
||||
1.0F
|
||||
));
|
||||
markContinuousEdit(ImGui::SliderFloat(
|
||||
("Roughness##Res" + std::to_string(materialIndex)).c_str(),
|
||||
&materialAsset.Roughness,
|
||||
0.0F,
|
||||
1.0F
|
||||
));
|
||||
markDiscreteEdit(ImGui::Checkbox(("Double Sided##Res" + std::to_string(materialIndex)).c_str(), &materialAsset.DoubleSided));
|
||||
|
||||
const char* alphaModeItems[] = {"Opaque", "Mask", "Blend"};
|
||||
int alphaModeIndex = static_cast<int>(materialAsset.AlphaMode);
|
||||
if (ImGui::Combo(("Alpha Mode##Res" + std::to_string(materialIndex)).c_str(), &alphaModeIndex, alphaModeItems, IM_ARRAYSIZE(alphaModeItems))) {
|
||||
materialAsset.AlphaMode = static_cast<MetaCoreMaterialAlphaMode>(alphaModeIndex);
|
||||
modified = true;
|
||||
previewModified = true;
|
||||
commitModified = true;
|
||||
}
|
||||
if (materialAsset.AlphaMode == MetaCoreMaterialAlphaMode::Mask) {
|
||||
if (ImGui::SliderFloat(("Alpha Cutoff##Res" + std::to_string(materialIndex)).c_str(), &materialAsset.AlphaCutoff, 0.0F, 1.0F)) {
|
||||
modified = true;
|
||||
}
|
||||
markContinuousEdit(ImGui::SliderFloat(
|
||||
("Alpha Cutoff##Res" + std::to_string(materialIndex)).c_str(),
|
||||
&materialAsset.AlphaCutoff,
|
||||
0.0F,
|
||||
1.0F
|
||||
));
|
||||
}
|
||||
|
||||
// 贴图通道配置
|
||||
@ -3129,46 +3366,70 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
MetaCoreAssetGuid baseColorTexture = materialAsset.BaseColorTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo(("Base Color Texture##Res" + std::to_string(materialIndex)).c_str(), textureChoices, baseColorTexture)) {
|
||||
materialAsset.BaseColorTexture = baseColorTexture;
|
||||
modified = true;
|
||||
previewModified = true;
|
||||
commitModified = true;
|
||||
}
|
||||
MetaCoreAssetGuid metalRoughTexture = materialAsset.MetallicRoughnessTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo(("MetalRough Texture##Res" + std::to_string(materialIndex)).c_str(), textureChoices, metalRoughTexture)) {
|
||||
materialAsset.MetallicRoughnessTexture = metalRoughTexture;
|
||||
modified = true;
|
||||
previewModified = true;
|
||||
commitModified = true;
|
||||
}
|
||||
MetaCoreAssetGuid normalTexture = materialAsset.NormalTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo(("Normal Texture##Res" + std::to_string(materialIndex)).c_str(), textureChoices, normalTexture)) {
|
||||
materialAsset.NormalTexture = normalTexture;
|
||||
modified = true;
|
||||
previewModified = true;
|
||||
commitModified = true;
|
||||
}
|
||||
float emissiveColor[3] = {materialAsset.EmissiveColor.x, materialAsset.EmissiveColor.y, materialAsset.EmissiveColor.z};
|
||||
if (ImGui::ColorEdit3(("Emissive Color##Res" + std::to_string(materialIndex)).c_str(), emissiveColor)) {
|
||||
const bool emissiveColorChanged = ImGui::ColorEdit3(("Emissive Color##Res" + std::to_string(materialIndex)).c_str(), emissiveColor);
|
||||
if (emissiveColorChanged) {
|
||||
materialAsset.EmissiveColor = {emissiveColor[0], emissiveColor[1], emissiveColor[2]};
|
||||
modified = true;
|
||||
}
|
||||
markContinuousEdit(emissiveColorChanged);
|
||||
MetaCoreAssetGuid emissiveTexture = materialAsset.EmissiveTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo(("Emissive Texture##Res" + std::to_string(materialIndex)).c_str(), textureChoices, emissiveTexture)) {
|
||||
materialAsset.EmissiveTexture = emissiveTexture;
|
||||
modified = true;
|
||||
previewModified = true;
|
||||
commitModified = true;
|
||||
}
|
||||
MetaCoreAssetGuid aoTexture = materialAsset.AoTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo(("AO Texture##Res" + std::to_string(materialIndex)).c_str(), textureChoices, aoTexture)) {
|
||||
materialAsset.AoTexture = aoTexture;
|
||||
modified = true;
|
||||
previewModified = true;
|
||||
commitModified = true;
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
if (previewModified) {
|
||||
materialDrafts[materialGuid] = materialAsset;
|
||||
MetaCoreApplyMaterialAssetPreviewToScene(
|
||||
editorContext,
|
||||
*assetEditingService,
|
||||
materialGuid,
|
||||
materialAsset
|
||||
);
|
||||
}
|
||||
|
||||
if (commitModified) {
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Save material edit begin slot=" + std::to_string(materialIndex) +
|
||||
" materialGuid=" + materialGuid.ToString() +
|
||||
" name=\"" + materialAsset.Name + "\"" +
|
||||
" baseColor=" + MetaCoreDebugVec3String(materialAsset.BaseColor) +
|
||||
" metallic=" + std::to_string(materialAsset.Metallic) +
|
||||
" roughness=" + std::to_string(materialAsset.Roughness)
|
||||
);
|
||||
if (assetEditingService->SaveMaterialAsset(materialGuid, materialAsset)) {
|
||||
if (resolvedMaterial.has_value() && resolvedMaterial->GeneratedKind == "material") {
|
||||
if (const auto modelDocument = assetEditingService->LoadModelAsset(resolvedMaterial->SourceAsset.Guid);
|
||||
modelDocument.has_value()) {
|
||||
MetaCoreApplyGeneratedMaterialPreviewToScene(editorContext, *modelDocument);
|
||||
}
|
||||
} else {
|
||||
MetaCoreApplyMaterialPreviewToSceneUsers(editorContext, materialGuid, materialAsset);
|
||||
}
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Save material edit success materialGuid=" + materialGuid.ToString() +
|
||||
" sceneRevision=" + std::to_string(editorContext.GetScene().GetRevision())
|
||||
);
|
||||
materialDrafts.erase(materialGuid);
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已保存材质资源修改");
|
||||
} else {
|
||||
MetaCoreDebugMaterialLog(
|
||||
"Save material edit failed materialGuid=" + materialGuid.ToString()
|
||||
);
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Error, "Material", "材质修改保存失败");
|
||||
}
|
||||
}
|
||||
@ -4964,37 +5225,16 @@ void MetaCoreBuiltinAssetDatabaseService::LoadProjectDescriptor() {
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetDatabaseService::CreateProject(const std::filesystem::path& projectRoot, std::string_view projectName) {
|
||||
if (projectRoot.empty()) {
|
||||
if (!MetaCoreCreateProjectSkeleton(projectRoot, projectName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
const std::filesystem::path normalizedRoot = std::filesystem::weakly_canonical(projectRoot.parent_path(), errorCode).empty()
|
||||
? projectRoot.lexically_normal()
|
||||
: (std::filesystem::weakly_canonical(projectRoot.parent_path(), errorCode) / projectRoot.filename()).lexically_normal();
|
||||
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Assets");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Scenes");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Runtime");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Ui");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Library");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Build");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Assets" / "Models");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Assets" / "Materials");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Assets" / "Textures");
|
||||
(void)std::filesystem::create_directories(normalizedRoot / "Assets" / "Prefabs");
|
||||
|
||||
MetaCoreProjectFileDocument document;
|
||||
if (!projectName.empty()) {
|
||||
document.Name = std::string(projectName);
|
||||
}
|
||||
document.StartupScenePath = std::filesystem::path("Scenes") / "Main.mcscene.json";
|
||||
|
||||
if (!MetaCoreWriteProjectFile(MetaCoreGetProjectFilePath(normalizedRoot), document)) {
|
||||
const auto normalizedRoot = MetaCoreResolveProjectRootCandidate(projectRoot);
|
||||
if (!normalizedRoot.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return OpenProject(normalizedRoot);
|
||||
return OpenProject(*normalizedRoot);
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetDatabaseService::OpenProject(const std::filesystem::path& projectRoot) {
|
||||
@ -5698,6 +5938,7 @@ public:
|
||||
[[nodiscard]] std::string GetServiceId() const override { return "MetaCore.AssetEditing"; }
|
||||
|
||||
void Startup(MetaCoreEditorModuleRegistry& moduleRegistry) override {
|
||||
ModuleRegistry_ = &moduleRegistry;
|
||||
AssetDatabaseService_ = moduleRegistry.ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
PackageService_ = moduleRegistry.ResolveService<MetaCoreIPackageService>();
|
||||
ReflectionRegistry_ = moduleRegistry.ResolveService<MetaCoreIReflectionRegistry>();
|
||||
@ -5705,6 +5946,7 @@ public:
|
||||
|
||||
void Shutdown(MetaCoreEditorModuleRegistry& moduleRegistry) override {
|
||||
(void)moduleRegistry;
|
||||
ModuleRegistry_ = nullptr;
|
||||
AssetDatabaseService_.reset();
|
||||
PackageService_.reset();
|
||||
ReflectionRegistry_.reset();
|
||||
@ -5742,6 +5984,9 @@ public:
|
||||
) const override;
|
||||
|
||||
private:
|
||||
void RescanHotReloadBaseline() const;
|
||||
|
||||
MetaCoreEditorModuleRegistry* ModuleRegistry_ = nullptr;
|
||||
std::shared_ptr<MetaCoreIAssetDatabaseService> AssetDatabaseService_{};
|
||||
std::shared_ptr<MetaCoreIPackageService> PackageService_{};
|
||||
std::shared_ptr<MetaCoreIReflectionRegistry> ReflectionRegistry_{};
|
||||
@ -6027,6 +6272,16 @@ std::optional<MetaCoreTextureAssetDocument> MetaCoreBuiltinAssetEditingService::
|
||||
return resolved->Document.GeneratedTextureAssets[resolved->GeneratedIndex];
|
||||
}
|
||||
|
||||
void MetaCoreBuiltinAssetEditingService::RescanHotReloadBaseline() const {
|
||||
if (ModuleRegistry_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (const auto hotReloadService = ModuleRegistry_->ResolveService<MetaCoreIAssetHotReloadService>();
|
||||
hotReloadService != nullptr) {
|
||||
hotReloadService->RescanBaseline();
|
||||
}
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetEditingService::SaveModelImportSettings(
|
||||
const MetaCoreAssetGuid& modelGuid,
|
||||
const MetaCoreModelImportSettingsDocument& importSettings
|
||||
@ -6046,13 +6301,17 @@ bool MetaCoreBuiltinAssetEditingService::SaveModelImportSettings(
|
||||
}
|
||||
|
||||
modelDocument->ImportSettings = importSettings;
|
||||
return MetaCoreWriteImportedGltfAssetDocument(
|
||||
const bool saved = MetaCoreWriteImportedGltfAssetDocument(
|
||||
*AssetDatabaseService_,
|
||||
*PackageService_,
|
||||
*ReflectionRegistry_,
|
||||
*assetRecord,
|
||||
*modelDocument
|
||||
);
|
||||
if (saved) {
|
||||
RescanHotReloadBaseline();
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetEditingService::SaveMaterialAsset(
|
||||
@ -6077,6 +6336,7 @@ bool MetaCoreBuiltinAssetEditingService::SaveMaterialAsset(
|
||||
metadata.SourceHash = MetaCoreHashFile(absolutePath).value_or(0);
|
||||
(void)MetaCoreWriteMetaJson(metaPath, metadata);
|
||||
}
|
||||
RescanHotReloadBaseline();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -6103,6 +6363,7 @@ bool MetaCoreBuiltinAssetEditingService::SaveMaterialAsset(
|
||||
);
|
||||
if (saved) {
|
||||
ModelCache_[resolved->AssetRecord.Guid] = editableDocument;
|
||||
RescanHotReloadBaseline();
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
@ -2896,6 +2896,17 @@ struct MetaCoreMaterialTextureBindingState {
|
||||
std::string LocateSubId{};
|
||||
};
|
||||
|
||||
struct MetaCoreMaterialEditorChange {
|
||||
bool PreviewModified = false;
|
||||
bool CommitModified = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unordered_map<MetaCoreAssetGuid, MetaCoreMaterialAssetDocument, MetaCoreAssetGuidHasher>&
|
||||
MetaCoreGetMaterialEditorDrafts() {
|
||||
static std::unordered_map<MetaCoreAssetGuid, MetaCoreMaterialAssetDocument, MetaCoreAssetGuidHasher> drafts;
|
||||
return drafts;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<MetaCoreMaterialTextureChoice> MetaCoreCollectMaterialTextureChoices(
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService
|
||||
) {
|
||||
@ -3128,7 +3139,7 @@ void MetaCoreDrawMaterialPreviewCard(const MetaCoreMaterialAssetDocument& materi
|
||||
return changed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreDrawMaterialVisualEditor(
|
||||
[[nodiscard]] MetaCoreMaterialEditorChange MetaCoreDrawMaterialVisualEditor(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService,
|
||||
MetaCoreMaterialAssetDocument& materialDocument,
|
||||
@ -3136,7 +3147,22 @@ void MetaCoreDrawMaterialPreviewCard(const MetaCoreMaterialAssetDocument& materi
|
||||
const std::filesystem::path& displayPath,
|
||||
std::string_view idSuffix
|
||||
) {
|
||||
bool changed = false;
|
||||
MetaCoreMaterialEditorChange change;
|
||||
const auto markContinuousEdit = [&](bool changed) {
|
||||
if (changed) {
|
||||
change.PreviewModified = true;
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
change.CommitModified = true;
|
||||
}
|
||||
};
|
||||
const auto markDiscreteEdit = [&](bool changed) {
|
||||
if (changed) {
|
||||
change.PreviewModified = true;
|
||||
change.CommitModified = true;
|
||||
}
|
||||
};
|
||||
|
||||
const std::string suffix(idSuffix);
|
||||
const auto textureChoices = MetaCoreCollectMaterialTextureChoices(assetDatabaseService);
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
@ -3166,10 +3192,11 @@ void MetaCoreDrawMaterialPreviewCard(const MetaCoreMaterialAssetDocument& materi
|
||||
std::array<char, 256> nameBuffer{};
|
||||
std::snprintf(nameBuffer.data(), nameBuffer.size(), "%s", materialDocument.Name.c_str());
|
||||
ImGui::SetNextItemWidth(-1.0F);
|
||||
if (ImGui::InputText("名称", nameBuffer.data(), nameBuffer.size())) {
|
||||
const bool nameChanged = ImGui::InputText("名称", nameBuffer.data(), nameBuffer.size());
|
||||
if (nameChanged) {
|
||||
materialDocument.Name = nameBuffer.data();
|
||||
changed = true;
|
||||
}
|
||||
markContinuousEdit(nameChanged);
|
||||
|
||||
const char* shaderItems[] = {"PBR 金属粗糙度"};
|
||||
int shaderIndex = 0;
|
||||
@ -3178,46 +3205,41 @@ void MetaCoreDrawMaterialPreviewCard(const MetaCoreMaterialAssetDocument& materi
|
||||
ImGui::EndDisabled();
|
||||
|
||||
float baseColor[3] = {materialDocument.BaseColor.r, materialDocument.BaseColor.g, materialDocument.BaseColor.b};
|
||||
if (ImGui::ColorEdit3("基础颜色", baseColor)) {
|
||||
const bool baseColorChanged = ImGui::ColorEdit3("基础颜色", baseColor);
|
||||
if (baseColorChanged) {
|
||||
materialDocument.BaseColor = {baseColor[0], baseColor[1], baseColor[2]};
|
||||
changed = true;
|
||||
}
|
||||
if (ImGui::SliderFloat("金属度", &materialDocument.Metallic, 0.0F, 1.0F, "%.3f")) {
|
||||
changed = true;
|
||||
}
|
||||
if (ImGui::SliderFloat("粗糙度", &materialDocument.Roughness, 0.0F, 1.0F, "%.3f")) {
|
||||
changed = true;
|
||||
}
|
||||
markContinuousEdit(baseColorChanged);
|
||||
markContinuousEdit(ImGui::SliderFloat("金属度", &materialDocument.Metallic, 0.0F, 1.0F, "%.3f"));
|
||||
markContinuousEdit(ImGui::SliderFloat("粗糙度", &materialDocument.Roughness, 0.0F, 1.0F, "%.3f"));
|
||||
float emissiveColor[3] = {materialDocument.EmissiveColor.r, materialDocument.EmissiveColor.g, materialDocument.EmissiveColor.b};
|
||||
if (ImGui::ColorEdit3("发光颜色", emissiveColor)) {
|
||||
const bool emissiveColorChanged = ImGui::ColorEdit3("发光颜色", emissiveColor);
|
||||
if (emissiveColorChanged) {
|
||||
materialDocument.EmissiveColor = {emissiveColor[0], emissiveColor[1], emissiveColor[2]};
|
||||
changed = true;
|
||||
}
|
||||
markContinuousEdit(emissiveColorChanged);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("贴图", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
changed |= MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "基础颜色", "BaseColorTexture", materialDocument.BaseColorTexture, textureChoices);
|
||||
changed |= MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "法线", "NormalTexture", materialDocument.NormalTexture, textureChoices);
|
||||
changed |= MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "金属粗糙度", "MetallicRoughnessTexture", materialDocument.MetallicRoughnessTexture, textureChoices);
|
||||
changed |= MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "遮蔽 AO", "AoTexture", materialDocument.AoTexture, textureChoices);
|
||||
changed |= MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "发光", "EmissiveTexture", materialDocument.EmissiveTexture, textureChoices);
|
||||
markDiscreteEdit(MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "基础颜色", "BaseColorTexture", materialDocument.BaseColorTexture, textureChoices));
|
||||
markDiscreteEdit(MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "法线", "NormalTexture", materialDocument.NormalTexture, textureChoices));
|
||||
markDiscreteEdit(MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "金属粗糙度", "MetallicRoughnessTexture", materialDocument.MetallicRoughnessTexture, textureChoices));
|
||||
markDiscreteEdit(MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "遮蔽 AO", "AoTexture", materialDocument.AoTexture, textureChoices));
|
||||
markDiscreteEdit(MetaCoreDrawMaterialTextureSlot(editorContext, assetDatabaseService, assetEditingService.get(), "发光", "EmissiveTexture", materialDocument.EmissiveTexture, textureChoices));
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("透明与渲染选项", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
const char* alphaItems[] = {"不透明", "镂空裁剪", "透明混合"};
|
||||
int alphaIndex = static_cast<int>(materialDocument.AlphaMode);
|
||||
if (ImGui::Combo("透明模式", &alphaIndex, alphaItems, IM_ARRAYSIZE(alphaItems))) {
|
||||
const bool alphaModeChanged = ImGui::Combo("透明模式", &alphaIndex, alphaItems, IM_ARRAYSIZE(alphaItems));
|
||||
if (alphaModeChanged) {
|
||||
materialDocument.AlphaMode = static_cast<MetaCoreMaterialAlphaMode>(std::clamp(alphaIndex, 0, 2));
|
||||
changed = true;
|
||||
}
|
||||
markDiscreteEdit(alphaModeChanged);
|
||||
if (materialDocument.AlphaMode == MetaCoreMaterialAlphaMode::Mask) {
|
||||
if (ImGui::SliderFloat("裁剪阈值", &materialDocument.AlphaCutoff, 0.0F, 1.0F, "%.3f")) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (ImGui::Checkbox("双面渲染", &materialDocument.DoubleSided)) {
|
||||
changed = true;
|
||||
markContinuousEdit(ImGui::SliderFloat("裁剪阈值", &materialDocument.AlphaCutoff, 0.0F, 1.0F, "%.3f"));
|
||||
}
|
||||
markDiscreteEdit(ImGui::Checkbox("双面渲染", &materialDocument.DoubleSided));
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("高级信息")) {
|
||||
@ -3237,7 +3259,24 @@ void MetaCoreDrawMaterialPreviewCard(const MetaCoreMaterialAssetDocument& materi
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopID();
|
||||
return changed;
|
||||
return change;
|
||||
}
|
||||
|
||||
void MetaCoreApplyGeneratedMaterialDraftPreviewToScene(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreIAssetEditingService& assetEditingService,
|
||||
const MetaCoreResolvedGeneratedAssetRecord& resolvedMaterial,
|
||||
const MetaCoreMaterialAssetDocument& materialAsset
|
||||
) {
|
||||
auto modelDocument = assetEditingService.LoadModelAsset(resolvedMaterial.SourceAsset.Guid);
|
||||
if (!modelDocument.has_value() ||
|
||||
resolvedMaterial.GeneratedIndex >= modelDocument->GeneratedMaterialAssets.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
modelDocument->GeneratedMaterialAssets[resolvedMaterial.GeneratedIndex] = materialAsset;
|
||||
modelDocument->GeneratedMaterialAssets[resolvedMaterial.GeneratedIndex].AssetGuid = materialAsset.AssetGuid;
|
||||
MetaCoreApplyMaterialPreviewToScene(editorContext, *modelDocument);
|
||||
}
|
||||
|
||||
void DrawGeneratedMaterialDetails(
|
||||
@ -3259,7 +3298,13 @@ void DrawGeneratedMaterialDetails(
|
||||
return;
|
||||
}
|
||||
|
||||
MetaCoreMaterialAssetDocument materialAsset = *loadedMaterial;
|
||||
auto& materialDrafts = MetaCoreGetMaterialEditorDrafts();
|
||||
const auto draftIterator = materialDrafts.find(selectedMaterial.AssetGuid);
|
||||
MetaCoreMaterialAssetDocument materialAsset =
|
||||
draftIterator != materialDrafts.end() ? draftIterator->second : *loadedMaterial;
|
||||
if (!materialAsset.AssetGuid.IsValid()) {
|
||||
materialAsset.AssetGuid = selectedMaterial.AssetGuid;
|
||||
}
|
||||
ImGui::Separator();
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
if (assetDatabaseService == nullptr) {
|
||||
@ -3276,7 +3321,7 @@ void DrawGeneratedMaterialDetails(
|
||||
DrawReferencedObjectList(editorContext, references, "SelectedMaterialRefs");
|
||||
}
|
||||
|
||||
const bool modified = MetaCoreDrawMaterialVisualEditor(
|
||||
const MetaCoreMaterialEditorChange materialChange = MetaCoreDrawMaterialVisualEditor(
|
||||
editorContext,
|
||||
*assetDatabaseService,
|
||||
materialAsset,
|
||||
@ -3285,13 +3330,20 @@ void DrawGeneratedMaterialDetails(
|
||||
"GeneratedMaterial"
|
||||
);
|
||||
|
||||
if (modified) {
|
||||
if (materialChange.PreviewModified) {
|
||||
materialDrafts[materialAsset.AssetGuid] = materialAsset;
|
||||
MetaCoreApplyGeneratedMaterialDraftPreviewToScene(
|
||||
editorContext,
|
||||
*assetEditingService,
|
||||
*resolvedMaterial,
|
||||
materialAsset
|
||||
);
|
||||
}
|
||||
|
||||
if (materialChange.CommitModified) {
|
||||
if (assetEditingService->SaveMaterialAsset(materialAsset.AssetGuid, materialAsset)) {
|
||||
if (const auto modelDocument = assetEditingService->LoadModelAsset(resolvedMaterial->SourceAsset.Guid);
|
||||
modelDocument.has_value()) {
|
||||
MetaCoreApplyMaterialPreviewToScene(editorContext, *modelDocument);
|
||||
}
|
||||
ImGui::TextColored(ImVec4(0.32F, 0.85F, 0.42F, 1.0F), "已自动保存材质资源");
|
||||
materialDrafts.erase(materialAsset.AssetGuid);
|
||||
ImGui::TextColored(ImVec4(0.32F, 0.85F, 0.42F, 1.0F), "已保存材质资源");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.32F, 0.28F, 1.0F), "材质资源保存失败");
|
||||
}
|
||||
@ -4425,14 +4477,17 @@ void DrawMaterialAssetDetails(
|
||||
ImGui::TextColored(ImVec4(1.0F, 0.4F, 0.4F, 1.0F), "加载材质失败。");
|
||||
return;
|
||||
}
|
||||
MetaCoreMaterialAssetDocument materialDoc = *materialDocOpt;
|
||||
auto& materialDrafts = MetaCoreGetMaterialEditorDrafts();
|
||||
const auto draftIterator = materialDrafts.find(selectedAsset.Guid);
|
||||
MetaCoreMaterialAssetDocument materialDoc =
|
||||
draftIterator != materialDrafts.end() ? draftIterator->second : *materialDocOpt;
|
||||
if (!materialDoc.AssetGuid.IsValid()) {
|
||||
materialDoc.AssetGuid = selectedAsset.Guid;
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F));
|
||||
if (ImGui::BeginChild("MaterialInspector", ImVec2(0, 0), true, 0)) {
|
||||
const bool changed = MetaCoreDrawMaterialVisualEditor(
|
||||
const MetaCoreMaterialEditorChange materialChange = MetaCoreDrawMaterialVisualEditor(
|
||||
editorContext,
|
||||
assetDatabaseService,
|
||||
materialDoc,
|
||||
@ -4440,10 +4495,14 @@ void DrawMaterialAssetDetails(
|
||||
assetRecord->RelativePath,
|
||||
"StandaloneMaterial"
|
||||
);
|
||||
if (changed) {
|
||||
if (materialChange.PreviewModified) {
|
||||
materialDrafts[selectedAsset.Guid] = materialDoc;
|
||||
MetaCoreApplyStandaloneMaterialPreviewToScene(editorContext, assetDatabaseService, materialDoc);
|
||||
}
|
||||
if (materialChange.CommitModified) {
|
||||
if (assetEditingService->SaveMaterialAsset(selectedAsset.Guid, materialDoc)) {
|
||||
MetaCoreApplyStandaloneMaterialPreviewToScene(editorContext, assetDatabaseService, materialDoc);
|
||||
ImGui::TextColored(ImVec4(0.32F, 0.85F, 0.42F, 1.0F), "已自动保存材质资源");
|
||||
materialDrafts.erase(selectedAsset.Guid);
|
||||
ImGui::TextColored(ImVec4(0.32F, 0.85F, 0.42F, 1.0F), "已保存材质资源");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.32F, 0.28F, 1.0F), "材质资源保存失败");
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "MetaCoreEditor/MetaCoreBuiltinModules.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
||||
#include "MetaCoreFoundation/MetaCoreProject.h"
|
||||
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
|
||||
#include "MetaCoreEditorCameraController.h"
|
||||
|
||||
@ -96,6 +97,50 @@ constexpr ImVec4 GInfernuxGhost{0.0F, 0.0F, 0.0F, 0.0F};
|
||||
constexpr ImVec4 GInfernuxGhostHovered{1.0F, 1.0F, 1.0F, 0.06F};
|
||||
constexpr ImVec4 GInfernuxGhostActive{1.0F, 1.0F, 1.0F, 0.10F};
|
||||
|
||||
struct MetaCoreEditorStartupArguments {
|
||||
std::optional<std::filesystem::path> ProjectRoot{};
|
||||
std::optional<std::filesystem::path> ScenePath{};
|
||||
};
|
||||
|
||||
[[nodiscard]] MetaCoreEditorStartupArguments MetaCoreParseEditorStartupArguments(int argc, char* argv[]) {
|
||||
MetaCoreEditorStartupArguments arguments;
|
||||
for (int index = 1; index < argc; ++index) {
|
||||
if (argv[index] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string value = argv[index];
|
||||
if (value == "--project") {
|
||||
if (index + 1 < argc && argv[index + 1] != nullptr) {
|
||||
arguments.ProjectRoot = std::filesystem::path(argv[++index]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (value == "--scene") {
|
||||
if (index + 1 < argc && argv[index + 1] != nullptr) {
|
||||
arguments.ScenePath = std::filesystem::path(argv[++index]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!value.empty() && value[0] == '-') {
|
||||
continue;
|
||||
}
|
||||
if (!arguments.ScenePath.has_value()) {
|
||||
arguments.ScenePath = std::filesystem::path(value);
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
void MetaCoreSetProjectRootEnvironment(const std::filesystem::path& projectRoot) {
|
||||
#if defined(_WIN32)
|
||||
_putenv_s("METACORE_PROJECT_PATH", projectRoot.string().c_str());
|
||||
#else
|
||||
setenv("METACORE_PROJECT_PATH", projectRoot.string().c_str(), 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MetaCorePushFlatButtonStyle(const ImVec4& base) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, base);
|
||||
ImGui::PushStyleColor(
|
||||
@ -1132,6 +1177,17 @@ bool MetaCoreEditorApp::Initialize(int argc, char* argv[]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
const MetaCoreEditorStartupArguments startupArguments = MetaCoreParseEditorStartupArguments(argc, argv);
|
||||
if (startupArguments.ProjectRoot.has_value()) {
|
||||
const auto resolvedProjectRoot = MetaCoreResolveProjectRootCandidate(*startupArguments.ProjectRoot);
|
||||
if (!resolvedProjectRoot.has_value()) {
|
||||
MetaCoreTraceStartup("metacore.app: --project path is not a MetaCore project");
|
||||
return false;
|
||||
}
|
||||
MetaCoreSetProjectRootEnvironment(*resolvedProjectRoot);
|
||||
MetaCoreTraceStartup(("metacore.app: --project=" + resolvedProjectRoot->string()).c_str());
|
||||
}
|
||||
|
||||
MetaCoreTraceStartup("metacore.app: initialize window");
|
||||
if (!Window_.Initialize(1600, 900, "MetaCore Editor")) {
|
||||
MetaCoreTraceStartup("metacore.app: initialize window failed");
|
||||
@ -1188,8 +1244,8 @@ bool MetaCoreEditorApp::Initialize(int argc, char* argv[]) {
|
||||
if (const auto scenePersistenceService = ModuleRegistry_.ResolveService<MetaCoreIScenePersistenceService>();
|
||||
scenePersistenceService != nullptr) {
|
||||
// 尝试从命令行参数直接加载场景(支持双击打开关联的场景文件)
|
||||
if (argc > 1 && argv[1] != nullptr) {
|
||||
const std::filesystem::path inputPath(argv[1]);
|
||||
if (startupArguments.ScenePath.has_value()) {
|
||||
const std::filesystem::path inputPath = *startupArguments.ScenePath;
|
||||
const auto assetDatabaseService = ModuleRegistry_.ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
if (assetDatabaseService != nullptr && assetDatabaseService->HasProject() && std::filesystem::is_regular_file(inputPath)) {
|
||||
const std::filesystem::path projectRoot = assetDatabaseService->GetProjectDescriptor().RootPath;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <system_error>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
@ -75,12 +76,105 @@ namespace {
|
||||
return escaped;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::filesystem::path MetaCoreNormalizeProjectRootForCreation(
|
||||
const std::filesystem::path& projectRoot
|
||||
) {
|
||||
if (projectRoot.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
std::filesystem::path absoluteRoot = projectRoot.is_absolute()
|
||||
? projectRoot
|
||||
: std::filesystem::absolute(projectRoot, errorCode);
|
||||
if (errorCode) {
|
||||
absoluteRoot = projectRoot;
|
||||
}
|
||||
|
||||
const std::filesystem::path parentPath = absoluteRoot.parent_path();
|
||||
if (!parentPath.empty() && std::filesystem::exists(parentPath, errorCode)) {
|
||||
errorCode.clear();
|
||||
const std::filesystem::path canonicalParent = std::filesystem::weakly_canonical(parentPath, errorCode);
|
||||
if (!errorCode && !canonicalParent.empty()) {
|
||||
return (canonicalParent / absoluteRoot.filename()).lexically_normal();
|
||||
}
|
||||
}
|
||||
|
||||
return absoluteRoot.lexically_normal();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::filesystem::path MetaCoreGetProjectFilePath(const std::filesystem::path& projectRoot) {
|
||||
return projectRoot / "MetaCore.project.json";
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> MetaCoreResolveProjectRootCandidate(
|
||||
const std::filesystem::path& candidatePath
|
||||
) {
|
||||
if (candidatePath.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::filesystem::path projectRoot = candidatePath;
|
||||
if (projectRoot.filename() == "MetaCore.project.json") {
|
||||
projectRoot = projectRoot.parent_path();
|
||||
}
|
||||
|
||||
const std::filesystem::path projectFilePath = MetaCoreGetProjectFilePath(projectRoot);
|
||||
if (!std::filesystem::exists(projectFilePath)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
const std::filesystem::path canonicalRoot = std::filesystem::weakly_canonical(projectRoot, errorCode);
|
||||
if (!errorCode && !canonicalRoot.empty()) {
|
||||
return canonicalRoot;
|
||||
}
|
||||
|
||||
return projectRoot.lexically_normal();
|
||||
}
|
||||
|
||||
bool MetaCoreCreateProjectSkeleton(
|
||||
const std::filesystem::path& projectRoot,
|
||||
std::string_view projectName
|
||||
) {
|
||||
const std::filesystem::path normalizedRoot = MetaCoreNormalizeProjectRootForCreation(projectRoot);
|
||||
if (normalizedRoot.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
const std::array<std::filesystem::path, 10> directories = {
|
||||
normalizedRoot / "Assets",
|
||||
normalizedRoot / "Scenes",
|
||||
normalizedRoot / "Runtime",
|
||||
normalizedRoot / "Ui",
|
||||
normalizedRoot / "Library",
|
||||
normalizedRoot / "Build",
|
||||
normalizedRoot / "Assets" / "Models",
|
||||
normalizedRoot / "Assets" / "Materials",
|
||||
normalizedRoot / "Assets" / "Textures",
|
||||
normalizedRoot / "Assets" / "Prefabs"
|
||||
};
|
||||
|
||||
for (const auto& directory : directories) {
|
||||
errorCode.clear();
|
||||
std::filesystem::create_directories(directory, errorCode);
|
||||
if (errorCode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MetaCoreProjectFileDocument document;
|
||||
if (!projectName.empty()) {
|
||||
document.Name = std::string(projectName);
|
||||
}
|
||||
document.StartupScenePath = std::filesystem::path("Scenes") / "Main.mcscene.json";
|
||||
|
||||
return MetaCoreWriteProjectFile(MetaCoreGetProjectFilePath(normalizedRoot), document);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> MetaCoreReadProjectRootFromEnvironment() {
|
||||
#if defined(_MSC_VER)
|
||||
char* projectPathRaw = nullptr;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace MetaCore {
|
||||
@ -19,6 +20,15 @@ struct MetaCoreProjectFileDocument {
|
||||
|
||||
[[nodiscard]] std::filesystem::path MetaCoreGetProjectFilePath(const std::filesystem::path& projectRoot);
|
||||
|
||||
[[nodiscard]] std::optional<std::filesystem::path> MetaCoreResolveProjectRootCandidate(
|
||||
const std::filesystem::path& candidatePath
|
||||
);
|
||||
|
||||
[[nodiscard]] bool MetaCoreCreateProjectSkeleton(
|
||||
const std::filesystem::path& projectRoot,
|
||||
std::string_view projectName
|
||||
);
|
||||
|
||||
[[nodiscard]] std::optional<std::filesystem::path> MetaCoreReadProjectRootFromEnvironment();
|
||||
|
||||
[[nodiscard]] std::optional<std::filesystem::path> MetaCoreDiscoverProjectRoot(
|
||||
|
||||
@ -122,6 +122,45 @@ void MetaCoreFilamentDebugLog(const std::string& message) {
|
||||
std::cerr << "[MetaCoreDebug][Filament] " << message << std::endl;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreMaterialDebugLogEnabled() {
|
||||
const char* value = std::getenv("METACORE_DEBUG_MATERIAL");
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return std::strcmp(value, "1") == 0 ||
|
||||
std::strcmp(value, "true") == 0 ||
|
||||
std::strcmp(value, "TRUE") == 0;
|
||||
}
|
||||
|
||||
void MetaCoreFilamentMaterialDebugLog(const std::string& message) {
|
||||
if (MetaCoreMaterialDebugLogEnabled()) {
|
||||
MetaCoreFilamentDebugLog(message);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MetaCoreDebugVec3String(const glm::vec3& value) {
|
||||
std::ostringstream stream;
|
||||
stream << "(" << value.x << "," << value.y << "," << value.z << ")";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MetaCoreDebugGuidListString(const std::vector<MetaCoreAssetGuid>& guids) {
|
||||
if (guids.empty()) {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "[";
|
||||
for (std::size_t index = 0; index < guids.size(); ++index) {
|
||||
if (index != 0) {
|
||||
stream << ",";
|
||||
}
|
||||
stream << (guids[index].IsValid() ? guids[index].ToString() : std::string("<none>"));
|
||||
}
|
||||
stream << "]";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCorePreserveGltfBlendAlphaMode() {
|
||||
const char* preserveBlend = std::getenv("METACORE_GLTF_PRESERVE_BLEND_ALPHA");
|
||||
if (preserveBlend == nullptr) {
|
||||
@ -908,6 +947,7 @@ public:
|
||||
|
||||
LastSyncSnapshot_ = snapshot;
|
||||
ObjectWorldMatrices_ = LastSyncSnapshot_.WorldMatrices;
|
||||
const bool materialDebugLogging = MetaCoreMaterialDebugLogEnabled();
|
||||
|
||||
// 构建物体ID到网格快照的快速映射,方便后续查询可见性及材质属性
|
||||
std::unordered_map<MetaCoreId, const MetaCoreRenderSyncRenderable*> renderableMap;
|
||||
@ -972,6 +1012,40 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
if (materialDebugLogging &&
|
||||
(!HasLoggedSnapshotDebug_ ||
|
||||
LastDebugSnapshotRevision_ != snapshot.SceneRevision ||
|
||||
LastDebugRenderableCount_ != snapshot.Renderables.size())) {
|
||||
std::ostringstream summary;
|
||||
summary
|
||||
<< "Sync snapshot frame=" << snapshot.FrameId
|
||||
<< " revision=" << snapshot.SceneRevision
|
||||
<< " renderables=" << snapshot.Renderables.size()
|
||||
<< " activeHosts=" << activeHostRootIds.size()
|
||||
<< " loadedAssets=" << LoadedAssets_.size()
|
||||
<< " mappings=" << ObjectToFilamentEntity_.size();
|
||||
MetaCoreFilamentMaterialDebugLog(summary.str());
|
||||
|
||||
for (const auto& renderable : snapshot.Renderables) {
|
||||
std::ostringstream renderableStream;
|
||||
renderableStream
|
||||
<< "Sync renderable objectId=" << renderable.ObjectId
|
||||
<< " name=\"" << renderable.Name << "\""
|
||||
<< " hostRootId=" << renderable.HostRootId
|
||||
<< " host=\"" << renderable.HostRootName << "\""
|
||||
<< " modelNode=" << renderable.ModelNodeIndex
|
||||
<< " materialGuids=" << MetaCoreDebugGuidListString(renderable.MaterialAssetGuids)
|
||||
<< " baseColor=" << MetaCoreDebugVec3String(renderable.BaseColor)
|
||||
<< " metallic=" << renderable.Metallic
|
||||
<< " roughness=" << renderable.Roughness
|
||||
<< " source=\"" << renderable.SourceModelPath << "\"";
|
||||
MetaCoreFilamentMaterialDebugLog(renderableStream.str());
|
||||
}
|
||||
|
||||
HasLoggedSnapshotDebug_ = true;
|
||||
LastDebugSnapshotRevision_ = snapshot.SceneRevision;
|
||||
LastDebugRenderableCount_ = snapshot.Renderables.size();
|
||||
}
|
||||
|
||||
// 2. 检查所有已加载的资产,若其 Root ID 不在 activeHostRootIds 中,则说明已被删除,执行清理
|
||||
for (auto it = LoadedAssets_.begin(); it != LoadedAssets_.end(); ) {
|
||||
@ -1440,23 +1514,29 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const auto setFloatParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, float value) {
|
||||
const auto setFloatParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, float value) -> bool {
|
||||
const filament::Material* material = materialInstance.getMaterial();
|
||||
if (material != nullptr && material->hasParameter(name)) {
|
||||
materialInstance.setParameter(name, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const auto setFloat3ParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, filament::math::float3 value) {
|
||||
const auto setFloat3ParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, filament::math::float3 value) -> bool {
|
||||
const filament::Material* material = materialInstance.getMaterial();
|
||||
if (material != nullptr && material->hasParameter(name)) {
|
||||
materialInstance.setParameter(name, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const auto setFloat4ParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, filament::math::float4 value) {
|
||||
const auto setFloat4ParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, filament::math::float4 value) -> bool {
|
||||
const filament::Material* material = materialInstance.getMaterial();
|
||||
if (material != nullptr && material->hasParameter(name)) {
|
||||
materialInstance.setParameter(name, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const auto setTextureParameterIfPresent = [](
|
||||
filament::MaterialInstance& materialInstance,
|
||||
@ -1487,9 +1567,32 @@ public:
|
||||
for (const auto& renderable : snapshot.Renderables) {
|
||||
auto itEntity = ObjectToFilamentEntity_.find(renderable.ObjectId);
|
||||
if (itEntity == ObjectToFilamentEntity_.end()) {
|
||||
if (materialDebugLogging) {
|
||||
const std::string signature = "missing|" + std::to_string(snapshot.SceneRevision);
|
||||
if (LastMaterialSyncDebugByObject_[renderable.ObjectId] != signature) {
|
||||
LastMaterialSyncDebugByObject_[renderable.ObjectId] = signature;
|
||||
MetaCoreFilamentMaterialDebugLog(
|
||||
"Material sync skipped mapping missing objectId=" + std::to_string(renderable.ObjectId) +
|
||||
" name=\"" + renderable.Name + "\"" +
|
||||
" hostRootId=" + std::to_string(renderable.HostRootId) +
|
||||
" materialGuids=" + MetaCoreDebugGuidListString(renderable.MaterialAssetGuids)
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto materialTargetCacheIterator = materialTargetCache.find(itEntity->second.second);
|
||||
utils::Entity materialRootEntity = itEntity->second.second;
|
||||
const utils::Entity originalMappedEntity = materialRootEntity;
|
||||
const bool originalMappedEntityBelongsToAsset =
|
||||
itEntity->second.first != nullptr &&
|
||||
EntityBelongsToAsset(itEntity->second.first, originalMappedEntity);
|
||||
if (itEntity->second.first != nullptr &&
|
||||
renderable.ObjectId == renderable.HostRootId &&
|
||||
!originalMappedEntityBelongsToAsset) {
|
||||
materialRootEntity = itEntity->second.first->getRoot();
|
||||
}
|
||||
|
||||
const auto materialTargetCacheIterator = materialTargetCache.find(materialRootEntity);
|
||||
if (materialTargetCacheIterator == materialTargetCache.end()) {
|
||||
std::vector<utils::Entity> materialTargetEntities;
|
||||
if (itEntity->second.first != nullptr) {
|
||||
@ -1501,18 +1604,53 @@ public:
|
||||
).first;
|
||||
}
|
||||
CollectRenderableDescendantsFromChildMap(
|
||||
itEntity->second.second,
|
||||
materialRootEntity,
|
||||
childMapIterator->second,
|
||||
materialTargetEntities
|
||||
);
|
||||
if (materialTargetEntities.empty() && renderable.ObjectId == renderable.HostRootId) {
|
||||
CollectAllRenderableAssetEntities(itEntity->second.first, materialTargetEntities);
|
||||
if (materialDebugLogging && !materialTargetEntities.empty()) {
|
||||
MetaCoreFilamentMaterialDebugLog(
|
||||
"Material sync fallback all asset renderables objectId=" + std::to_string(renderable.ObjectId) +
|
||||
" hostRootId=" + std::to_string(renderable.HostRootId) +
|
||||
" count=" + std::to_string(materialTargetEntities.size())
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CollectRenderableDescendants(itEntity->second.first, itEntity->second.second, materialTargetEntities);
|
||||
CollectRenderableDescendants(itEntity->second.first, materialRootEntity, materialTargetEntities);
|
||||
}
|
||||
materialTargetCache.emplace(itEntity->second.second, std::move(materialTargetEntities));
|
||||
materialTargetCache.emplace(materialRootEntity, std::move(materialTargetEntities));
|
||||
}
|
||||
|
||||
const std::vector<utils::Entity>& materialTargetEntities = materialTargetCache.find(itEntity->second.second)->second;
|
||||
const std::vector<utils::Entity>& materialTargetEntities = materialTargetCache.find(materialRootEntity)->second;
|
||||
if (materialTargetEntities.empty()) {
|
||||
if (materialDebugLogging) {
|
||||
std::ostringstream signatureStream;
|
||||
signatureStream
|
||||
<< "empty|" << snapshot.SceneRevision
|
||||
<< "|" << renderable.ObjectId
|
||||
<< "|" << materialRootEntity.getId()
|
||||
<< "|" << MetaCoreDebugGuidListString(renderable.MaterialAssetGuids)
|
||||
<< "|" << MetaCoreDebugVec3String(renderable.BaseColor)
|
||||
<< "|" << renderable.Metallic
|
||||
<< "|" << renderable.Roughness;
|
||||
const std::string signature = signatureStream.str();
|
||||
if (LastMaterialSyncDebugByObject_[renderable.ObjectId] != signature) {
|
||||
LastMaterialSyncDebugByObject_[renderable.ObjectId] = signature;
|
||||
MetaCoreFilamentMaterialDebugLog(
|
||||
"Material sync target empty objectId=" + std::to_string(renderable.ObjectId) +
|
||||
" name=\"" + renderable.Name + "\"" +
|
||||
" hostRootId=" + std::to_string(renderable.HostRootId) +
|
||||
" mappedEntity=" + std::to_string(originalMappedEntity.getId()) +
|
||||
" mappedBelongsToAsset=" + (originalMappedEntityBelongsToAsset ? std::string("true") : std::string("false")) +
|
||||
" materialRootEntity=" + std::to_string(materialRootEntity.getId()) +
|
||||
" materialGuids=" + MetaCoreDebugGuidListString(renderable.MaterialAssetGuids) +
|
||||
" baseColor=" + MetaCoreDebugVec3String(renderable.BaseColor)
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1542,6 +1680,14 @@ public:
|
||||
false
|
||||
);
|
||||
|
||||
std::size_t primitiveCount = 0;
|
||||
std::size_t materialInstanceCount = 0;
|
||||
std::size_t baseColorWrites = 0;
|
||||
std::size_t metallicWrites = 0;
|
||||
std::size_t roughnessWrites = 0;
|
||||
std::size_t emissiveWrites = 0;
|
||||
std::size_t alphaCutoffWrites = 0;
|
||||
std::size_t textureWrites = 0;
|
||||
for (utils::Entity materialTargetEntity : materialTargetEntities) {
|
||||
const auto instance = rm.getInstance(materialTargetEntity);
|
||||
if (!instance) {
|
||||
@ -1551,18 +1697,26 @@ public:
|
||||
// 遍历所有 primitive,获取材质实例,并设置材质预览属性。
|
||||
const size_t primCount = rm.getPrimitiveCount(instance);
|
||||
for (size_t primIndex = 0; primIndex < primCount; ++primIndex) {
|
||||
++primitiveCount;
|
||||
filament::MaterialInstance* matInst = rm.getMaterialInstanceAt(instance, primIndex);
|
||||
if (matInst) {
|
||||
++materialInstanceCount;
|
||||
filament::math::float4 baseColorVec{
|
||||
renderable.BaseColor.x,
|
||||
renderable.BaseColor.y,
|
||||
renderable.BaseColor.z,
|
||||
1.0f
|
||||
};
|
||||
setFloat4ParameterIfPresent(*matInst, "baseColorFactor", baseColorVec);
|
||||
setFloatParameterIfPresent(*matInst, "metallicFactor", renderable.Metallic);
|
||||
setFloatParameterIfPresent(*matInst, "roughnessFactor", renderable.Roughness);
|
||||
setFloat3ParameterIfPresent(
|
||||
if (setFloat4ParameterIfPresent(*matInst, "baseColorFactor", baseColorVec)) {
|
||||
++baseColorWrites;
|
||||
}
|
||||
if (setFloatParameterIfPresent(*matInst, "metallicFactor", renderable.Metallic)) {
|
||||
++metallicWrites;
|
||||
}
|
||||
if (setFloatParameterIfPresent(*matInst, "roughnessFactor", renderable.Roughness)) {
|
||||
++roughnessWrites;
|
||||
}
|
||||
if (setFloat3ParameterIfPresent(
|
||||
*matInst,
|
||||
"emissiveFactor",
|
||||
filament::math::float3{
|
||||
@ -1570,23 +1724,91 @@ public:
|
||||
renderable.EmissiveColor.y,
|
||||
renderable.EmissiveColor.z
|
||||
}
|
||||
);
|
||||
)) {
|
||||
++emissiveWrites;
|
||||
}
|
||||
if (renderable.AlphaMode == MetaCoreMeshAlphaMode::Mask ||
|
||||
renderable.AlphaMode == MetaCoreMeshAlphaMode::Blend) {
|
||||
setFloatParameterIfPresent(*matInst, "alphaCutoff", renderable.AlphaCutoff);
|
||||
}
|
||||
setTextureParameterIfPresent(*matInst, "baseColorMap", baseColorTexture, materialSampler);
|
||||
setTextureParameterIfPresent(*matInst, "metallicRoughnessMap", metallicRoughnessTexture, materialSampler);
|
||||
setTextureParameterIfPresent(*matInst, "normalMap", normalTexture, materialSampler);
|
||||
setTextureParameterIfPresent(*matInst, "emissiveMap", emissiveTexture, materialSampler);
|
||||
if (!setTextureParameterIfPresent(*matInst, "occlusionMap", aoTexture, materialSampler)) {
|
||||
if (!setTextureParameterIfPresent(*matInst, "aoMap", aoTexture, materialSampler)) {
|
||||
setTextureParameterIfPresent(*matInst, "ambientOcclusionMap", aoTexture, materialSampler);
|
||||
if (setFloatParameterIfPresent(*matInst, "alphaCutoff", renderable.AlphaCutoff)) {
|
||||
++alphaCutoffWrites;
|
||||
}
|
||||
}
|
||||
if (setTextureParameterIfPresent(*matInst, "baseColorMap", baseColorTexture, materialSampler)) {
|
||||
++textureWrites;
|
||||
}
|
||||
if (setTextureParameterIfPresent(*matInst, "metallicRoughnessMap", metallicRoughnessTexture, materialSampler)) {
|
||||
++textureWrites;
|
||||
}
|
||||
if (setTextureParameterIfPresent(*matInst, "normalMap", normalTexture, materialSampler)) {
|
||||
++textureWrites;
|
||||
}
|
||||
if (setTextureParameterIfPresent(*matInst, "emissiveMap", emissiveTexture, materialSampler)) {
|
||||
++textureWrites;
|
||||
}
|
||||
if (!setTextureParameterIfPresent(*matInst, "occlusionMap", aoTexture, materialSampler)) {
|
||||
if (!setTextureParameterIfPresent(*matInst, "aoMap", aoTexture, materialSampler)) {
|
||||
if (setTextureParameterIfPresent(*matInst, "ambientOcclusionMap", aoTexture, materialSampler)) {
|
||||
++textureWrites;
|
||||
}
|
||||
} else {
|
||||
++textureWrites;
|
||||
}
|
||||
} else {
|
||||
++textureWrites;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (materialDebugLogging) {
|
||||
std::ostringstream signatureStream;
|
||||
signatureStream
|
||||
<< "sync|" << snapshot.SceneRevision
|
||||
<< "|" << renderable.ObjectId
|
||||
<< "|" << renderable.HostRootId
|
||||
<< "|" << materialRootEntity.getId()
|
||||
<< "|" << MetaCoreDebugGuidListString(renderable.MaterialAssetGuids)
|
||||
<< "|" << MetaCoreDebugVec3String(renderable.BaseColor)
|
||||
<< "|" << renderable.Metallic
|
||||
<< "|" << renderable.Roughness
|
||||
<< "|" << materialTargetEntities.size()
|
||||
<< "|" << primitiveCount
|
||||
<< "|" << materialInstanceCount
|
||||
<< "|" << baseColorWrites
|
||||
<< "|" << metallicWrites
|
||||
<< "|" << roughnessWrites
|
||||
<< "|" << textureWrites;
|
||||
const std::string signature = signatureStream.str();
|
||||
if (LastMaterialSyncDebugByObject_[renderable.ObjectId] != signature) {
|
||||
LastMaterialSyncDebugByObject_[renderable.ObjectId] = signature;
|
||||
std::ostringstream stream;
|
||||
stream
|
||||
<< "Material sync objectId=" << renderable.ObjectId
|
||||
<< " name=\"" << renderable.Name << "\""
|
||||
<< " hostRootId=" << renderable.HostRootId
|
||||
<< " mappedEntity=" << originalMappedEntity.getId()
|
||||
<< " mappedBelongsToAsset=" << (originalMappedEntityBelongsToAsset ? "true" : "false")
|
||||
<< " materialRootEntity=" << materialRootEntity.getId()
|
||||
<< " targets=" << materialTargetEntities.size()
|
||||
<< " primitives=" << primitiveCount
|
||||
<< " matInstances=" << materialInstanceCount
|
||||
<< " writes={baseColor:" << baseColorWrites
|
||||
<< ", metallic:" << metallicWrites
|
||||
<< ", roughness:" << roughnessWrites
|
||||
<< ", emissive:" << emissiveWrites
|
||||
<< ", alphaCutoff:" << alphaCutoffWrites
|
||||
<< ", textures:" << textureWrites << "}"
|
||||
<< " materialGuids=" << MetaCoreDebugGuidListString(renderable.MaterialAssetGuids)
|
||||
<< " baseColor=" << MetaCoreDebugVec3String(renderable.BaseColor)
|
||||
<< " metallic=" << renderable.Metallic
|
||||
<< " roughness=" << renderable.Roughness
|
||||
<< " textures={baseColor:\"" << renderable.BaseColorTexturePath
|
||||
<< "\", mr:\"" << renderable.MetallicRoughnessTexturePath
|
||||
<< "\", normal:\"" << renderable.NormalTexturePath
|
||||
<< "\"}";
|
||||
MetaCoreFilamentMaterialDebugLog(stream.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1898,6 +2120,46 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool EntityBelongsToAsset(filament::gltfio::FilamentAsset* asset, utils::Entity entity) const {
|
||||
if (asset == nullptr || entity.isNull()) {
|
||||
return false;
|
||||
}
|
||||
if (entity == asset->getRoot()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const utils::Entity* entities = asset->getEntities();
|
||||
const size_t entityCount = asset->getEntityCount();
|
||||
for (size_t i = 0; i < entityCount; ++i) {
|
||||
if (entities[i] == entity) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollectAllRenderableAssetEntities(
|
||||
filament::gltfio::FilamentAsset* asset,
|
||||
std::vector<utils::Entity>& outEntities
|
||||
) const {
|
||||
if (asset == nullptr || Engine_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& rm = Engine_->getRenderableManager();
|
||||
const utils::Entity* entities = asset->getEntities();
|
||||
const size_t entityCount = asset->getEntityCount();
|
||||
for (size_t i = 0; i < entityCount; ++i) {
|
||||
const utils::Entity entity = entities[i];
|
||||
if (entity.isNull() || !rm.getInstance(entity)) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(outEntities.begin(), outEntities.end(), entity) == outEntities.end()) {
|
||||
outEntities.push_back(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断当前实体是否为过渡节点(即无名且无 Mesh 渲染组件的节点)
|
||||
bool IsTransitionNode(utils::Entity entity, filament::gltfio::FilamentAsset* asset) const {
|
||||
if (!entity) return false;
|
||||
@ -2242,6 +2504,10 @@ private:
|
||||
MetaCoreSceneRenderSync RenderSync_{};
|
||||
MetaCoreSceneRenderSyncSnapshot LastSyncSnapshot_{};
|
||||
MetaCoreSceneView CurrentSceneView_{};
|
||||
bool HasLoggedSnapshotDebug_ = false;
|
||||
std::uint64_t LastDebugSnapshotRevision_ = 0;
|
||||
std::size_t LastDebugRenderableCount_ = 0;
|
||||
std::unordered_map<MetaCoreId, std::string> LastMaterialSyncDebugByObject_{};
|
||||
std::unordered_map<MetaCoreId, glm::mat4> ObjectWorldMatrices_{};
|
||||
std::unordered_map<MetaCoreId, std::pair<filament::gltfio::FilamentAsset*, utils::Entity>> ObjectToFilamentEntity_;
|
||||
std::unordered_map<MetaCoreId, filament::gltfio::FilamentAsset*> LoadedAssets_;
|
||||
|
||||
@ -180,6 +180,7 @@ MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const Met
|
||||
hostRoot.GetId(),
|
||||
hostRoot.GetName(),
|
||||
hostRoot.HasComponent<MetaCoreModelRootTag>(),
|
||||
meshRenderer.MaterialAssetGuids,
|
||||
meshRenderer.BaseColor,
|
||||
meshRenderer.Metallic,
|
||||
meshRenderer.Roughness,
|
||||
|
||||
@ -35,6 +35,7 @@ struct MetaCoreRenderSyncRenderable {
|
||||
MetaCoreId HostRootId = 0;
|
||||
std::string HostRootName{};
|
||||
bool HostRootHasModelRootTag = false;
|
||||
std::vector<MetaCoreAssetGuid> MaterialAssetGuids{};
|
||||
glm::vec3 BaseColor{1.0F, 1.0F, 1.0F};
|
||||
float Metallic = 0.0F;
|
||||
float Roughness = 1.0F;
|
||||
|
||||
@ -474,9 +474,17 @@ MetaCoreScene MetaCoreCreateDefaultScene() {
|
||||
mainCamera.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F);
|
||||
|
||||
MetaCoreGameObject directionalLight = scene.CreateGameObject("Directional Light");
|
||||
directionalLight.AddComponent<MetaCoreLightComponent>();
|
||||
MetaCoreLightComponent& keyLight = directionalLight.AddComponent<MetaCoreLightComponent>();
|
||||
keyLight.Color = glm::vec3(1.0F, 0.96F, 0.9F);
|
||||
keyLight.Intensity = 1.25F;
|
||||
directionalLight.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F);
|
||||
|
||||
MetaCoreGameObject fillLight = scene.CreateGameObject("Fill Light");
|
||||
MetaCoreLightComponent& fillLightComponent = fillLight.AddComponent<MetaCoreLightComponent>();
|
||||
fillLightComponent.Color = glm::vec3(0.72F, 0.82F, 1.0F);
|
||||
fillLightComponent.Intensity = 0.18F;
|
||||
fillLight.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-20.0F, -135.0F, 0.0F);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,16 @@ void MetaCoreExpect(bool condition, const char* message) {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t MetaCoreCountSceneLights(const MetaCore::MetaCoreScene& scene) {
|
||||
std::size_t lightCount = 0;
|
||||
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
|
||||
if (object.HasComponent<MetaCore::MetaCoreLightComponent>()) {
|
||||
++lightCount;
|
||||
}
|
||||
}
|
||||
return lightCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::filesystem::path MetaCoreTestRepositoryRoot() {
|
||||
return std::filesystem::path(__FILE__).parent_path().parent_path();
|
||||
}
|
||||
@ -436,6 +446,54 @@ void MetaCoreTestProjectFileRoundTripAndDiscovery() {
|
||||
std::filesystem::remove_all(tempProjectRoot);
|
||||
}
|
||||
|
||||
void MetaCoreTestProjectSkeletonHelpers() {
|
||||
const std::filesystem::path tempProjectRoot =
|
||||
std::filesystem::temp_directory_path() / "MetaCoreProjectSkeletonHelpers";
|
||||
const std::filesystem::path createdProjectRoot = tempProjectRoot / "CreatedBySkeleton";
|
||||
std::filesystem::remove_all(tempProjectRoot);
|
||||
|
||||
MetaCoreExpect(
|
||||
MetaCore::MetaCoreCreateProjectSkeleton(createdProjectRoot, "CreatedBySkeleton"),
|
||||
"项目骨架 helper 应能创建项目"
|
||||
);
|
||||
MetaCoreExpect(std::filesystem::exists(createdProjectRoot / "MetaCore.project.json"), "项目骨架应包含项目文件");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets"), "项目骨架应包含 Assets");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Scenes"), "项目骨架应包含 Scenes");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Runtime"), "项目骨架应包含 Runtime");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Ui"), "项目骨架应包含 Ui");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Library"), "项目骨架应包含 Library");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Build"), "项目骨架应包含 Build");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Models"), "项目骨架应包含 Models");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Materials"), "项目骨架应包含 Materials");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Textures"), "项目骨架应包含 Textures");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Prefabs"), "项目骨架应包含 Prefabs");
|
||||
|
||||
const auto projectDocument = MetaCore::MetaCoreReadProjectFile(createdProjectRoot / "MetaCore.project.json");
|
||||
MetaCoreExpect(projectDocument.has_value(), "项目骨架项目文件应能读取");
|
||||
MetaCoreExpect(projectDocument->Name == "CreatedBySkeleton", "项目骨架项目名称应正确");
|
||||
MetaCoreExpect(
|
||||
projectDocument->StartupScenePath == std::filesystem::path("Scenes") / "Main.mcscene.json",
|
||||
"项目骨架默认启动场景路径应正确"
|
||||
);
|
||||
|
||||
const auto resolvedFromRoot = MetaCore::MetaCoreResolveProjectRootCandidate(createdProjectRoot);
|
||||
MetaCoreExpect(resolvedFromRoot.has_value(), "项目根路径应可解析");
|
||||
MetaCoreExpect(
|
||||
std::filesystem::equivalent(*resolvedFromRoot, createdProjectRoot),
|
||||
"项目根路径解析结果应正确"
|
||||
);
|
||||
|
||||
const auto resolvedFromProjectFile =
|
||||
MetaCore::MetaCoreResolveProjectRootCandidate(createdProjectRoot / "MetaCore.project.json");
|
||||
MetaCoreExpect(resolvedFromProjectFile.has_value(), "项目文件路径应可解析");
|
||||
MetaCoreExpect(
|
||||
std::filesystem::equivalent(*resolvedFromProjectFile, createdProjectRoot),
|
||||
"项目文件路径解析结果应正确"
|
||||
);
|
||||
|
||||
std::filesystem::remove_all(tempProjectRoot);
|
||||
}
|
||||
|
||||
void MetaCoreTestAssetDatabaseCreateAndOpenProject() {
|
||||
const std::filesystem::path tempProjectRoot =
|
||||
std::filesystem::temp_directory_path() / "MetaCoreCreateOpenProject";
|
||||
@ -454,6 +512,10 @@ void MetaCoreTestAssetDatabaseCreateAndOpenProject() {
|
||||
MetaCoreExpect(assetDatabase->CreateProject(createdProjectRoot, "CreatedProject"), "应能创建项目");
|
||||
MetaCoreExpect(assetDatabase->HasProject(), "创建后应存在当前项目");
|
||||
MetaCoreExpect(std::filesystem::exists(createdProjectRoot / "MetaCore.project.json"), "创建后应存在项目文件");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Models"), "创建后应存在模型目录");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Materials"), "创建后应存在材质目录");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Textures"), "创建后应存在贴图目录");
|
||||
MetaCoreExpect(std::filesystem::is_directory(createdProjectRoot / "Assets" / "Prefabs"), "创建后应存在 Prefabs 目录");
|
||||
MetaCoreExpect(assetDatabase->GetProjectDescriptor().Name == "CreatedProject", "创建后项目名称应正确");
|
||||
MetaCoreExpect(assetDatabase->GetProjectDescriptor().RootPath == createdProjectRoot, "创建后项目根路径应正确");
|
||||
|
||||
@ -1547,6 +1609,7 @@ void MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson() {
|
||||
MetaCore::MetaCoreRenderDevice renderDevice;
|
||||
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
|
||||
MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
|
||||
const std::size_t defaultSceneObjectCount = scene.GetGameObjects().size();
|
||||
MetaCore::MetaCoreLogService logService;
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
|
||||
@ -1596,7 +1659,7 @@ void MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson() {
|
||||
MetaCoreExpect(scene.GetGameObjects().empty(), "清空快照后场景应为空");
|
||||
|
||||
MetaCoreExpect(scenePersistenceService->LoadScene(editorContext, scenePath), "应能重新加载 JSON 场景");
|
||||
MetaCoreExpect(scene.GetGameObjects().size() == 2, "重新加载后对象数量应恢复");
|
||||
MetaCoreExpect(scene.GetGameObjects().size() == defaultSceneObjectCount, "重新加载后对象数量应恢复");
|
||||
MetaCoreExpect(editorContext.GetSelectedObjectId() != 0, "重新加载后应恢复选中对象");
|
||||
|
||||
bool foundRenamedLight = false;
|
||||
@ -2621,6 +2684,7 @@ void MetaCoreTestBootstrapStartupSceneCreation() {
|
||||
MetaCore::MetaCoreRenderDevice renderDevice;
|
||||
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
|
||||
MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
|
||||
const std::size_t defaultSceneObjectCount = scene.GetGameObjects().size();
|
||||
MetaCore::MetaCoreLogService logService;
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
|
||||
@ -2650,7 +2714,7 @@ void MetaCoreTestBootstrapStartupSceneCreation() {
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / bootstrapScenePath), "应生成 Main.mcscene.json");
|
||||
MetaCoreExpect(scenePersistenceService->LoadStartupScene(editorContext), "设置后应能加载 startup scene");
|
||||
MetaCoreExpect(scenePersistenceService->GetCurrentScenePath() == bootstrapScenePath, "startup scene 路径应正确");
|
||||
MetaCoreExpect(scene.GetGameObjects().size() == 2, "bootstrap scene 应保存默认场景内容");
|
||||
MetaCoreExpect(scene.GetGameObjects().size() == defaultSceneObjectCount, "bootstrap scene 应保存默认场景内容");
|
||||
|
||||
coreServicesModule->Shutdown(moduleRegistry);
|
||||
moduleRegistry.ShutdownServices();
|
||||
@ -4482,7 +4546,7 @@ int main() {
|
||||
std::cout << "[RUN] MetaCoreCreateDefaultScene..." << std::endl;
|
||||
MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
|
||||
std::cout << "[RUN] MetaCoreExpect default objects count..." << std::endl;
|
||||
MetaCoreExpect(scene.GetGameObjects().size() == 2, "默认场景应包含两个对象");
|
||||
MetaCoreExpect(scene.GetGameObjects().size() >= 3, "默认场景应包含相机和基础光照对象");
|
||||
|
||||
bool hasCamera = false;
|
||||
bool hasLight = false;
|
||||
@ -4493,6 +4557,7 @@ int main() {
|
||||
|
||||
MetaCoreExpect(hasCamera, "默认场景应包含摄像机");
|
||||
MetaCoreExpect(hasLight, "默认场景应包含光源");
|
||||
MetaCoreExpect(MetaCoreCountSceneLights(scene) >= 2, "默认场景应包含主光和补光");
|
||||
|
||||
std::cout << "[RUN] MetaCoreDummyPanelProvider test..." << std::endl;
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
@ -4525,6 +4590,8 @@ int main() {
|
||||
MetaCoreTestLegacyBinarySceneStartupLoadCompatibility();
|
||||
std::cout << "[RUN] MetaCoreTestProjectFileRoundTripAndDiscovery..." << std::endl;
|
||||
MetaCoreTestProjectFileRoundTripAndDiscovery();
|
||||
std::cout << "[RUN] MetaCoreTestProjectSkeletonHelpers..." << std::endl;
|
||||
MetaCoreTestProjectSkeletonHelpers();
|
||||
std::cout << "[RUN] MetaCoreTestAssetDatabaseCreateAndOpenProject..." << std::endl;
|
||||
MetaCoreTestAssetDatabaseCreateAndOpenProject();
|
||||
std::cout << "[RUN] MetaCoreTestAssetDatabaseRenamePathUpdatesProjectDescriptor..." << std::endl;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user