材质更新
This commit is contained in:
parent
97b704428a
commit
0df9b93ce2
26
SandboxProject/Assets/Materials/Material 1.mcmaterial.json
Normal file
26
SandboxProject/Assets/Materials/Material 1.mcmaterial.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "fc3fd465-4f39-44af-907a-4fc0494c2502",
|
||||
"BaseColor": [
|
||||
1.0,
|
||||
0.8078431487083435,
|
||||
0.5843137502670288
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
"DoubleSided": false,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTexture": "",
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTexture": "",
|
||||
"Name": "Material 1",
|
||||
"NormalTexture": "",
|
||||
"Roughness": 0.5690000057220459,
|
||||
"ShaderModel": 0,
|
||||
"StableImportKey": ""
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "material",
|
||||
"guid": "fc3fd465-4f39-44af-907a-4fc0494c2502",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material 1.mcmaterial.json",
|
||||
"source_hash": 1388934689574133717,
|
||||
"source_path": "Assets/Materials/Material 1.mcmaterial.json"
|
||||
}
|
||||
26
SandboxProject/Assets/Materials/Material 2.mcmaterial.json
Normal file
26
SandboxProject/Assets/Materials/Material 2.mcmaterial.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "579d1aa2-baae-4b56-9b05-86f9d2e9a2ff",
|
||||
"BaseColor": [
|
||||
0.7137255072593689,
|
||||
0.95686274766922,
|
||||
0.7647058963775635
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
"DoubleSided": false,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTexture": "",
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTexture": "",
|
||||
"Name": "Material 2",
|
||||
"NormalTexture": "",
|
||||
"Roughness": 0.4129999876022339,
|
||||
"ShaderModel": 0,
|
||||
"StableImportKey": ""
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "material",
|
||||
"guid": "579d1aa2-baae-4b56-9b05-86f9d2e9a2ff",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material 2.mcmaterial.json",
|
||||
"source_hash": 12206391660553642631,
|
||||
"source_path": "Assets/Materials/Material 2.mcmaterial.json"
|
||||
}
|
||||
26
SandboxProject/Assets/Materials/Material 3.mcmaterial.json
Normal file
26
SandboxProject/Assets/Materials/Material 3.mcmaterial.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "fbe8b648-5368-4af6-b822-d2573ed5f194",
|
||||
"BaseColor": [
|
||||
0.545098066329956,
|
||||
0.30588236451148987,
|
||||
0.5647059082984924
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
"DoubleSided": false,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.27843138575553894,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTexture": "",
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTexture": "",
|
||||
"Name": "Material 3",
|
||||
"NormalTexture": "",
|
||||
"Roughness": 0.781000018119812,
|
||||
"ShaderModel": 0,
|
||||
"StableImportKey": ""
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "material",
|
||||
"guid": "fbe8b648-5368-4af6-b822-d2573ed5f194",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material 3.mcmaterial.json",
|
||||
"source_hash": 7449433069631146521,
|
||||
"source_path": "Assets/Materials/Material 3.mcmaterial.json"
|
||||
}
|
||||
26
SandboxProject/Assets/Materials/Material 4.mcmaterial.json
Normal file
26
SandboxProject/Assets/Materials/Material 4.mcmaterial.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "a23b9298-b98c-47ac-a2ad-3c9dac65c5af",
|
||||
"BaseColor": [
|
||||
0.7647058963775635,
|
||||
0.8392156958580017,
|
||||
0.7882353067398071
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
"DoubleSided": false,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTexture": "",
|
||||
"Metallic": 0.6890000104904175,
|
||||
"MetallicRoughnessTexture": "",
|
||||
"Name": "Material 4",
|
||||
"NormalTexture": "",
|
||||
"Roughness": 0.5450000166893005,
|
||||
"ShaderModel": 0,
|
||||
"StableImportKey": ""
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "material",
|
||||
"guid": "a23b9298-b98c-47ac-a2ad-3c9dac65c5af",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material 4.mcmaterial.json",
|
||||
"source_hash": 12594992031322235982,
|
||||
"source_path": "Assets/Materials/Material 4.mcmaterial.json"
|
||||
}
|
||||
26
SandboxProject/Assets/Materials/Material 5.mcmaterial.json
Normal file
26
SandboxProject/Assets/Materials/Material 5.mcmaterial.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "39f65f8e-cdac-43fb-9762-e6c544547a71",
|
||||
"BaseColor": [
|
||||
0.8705882430076599,
|
||||
0.7921568751335144,
|
||||
0.8627451062202454
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
"DoubleSided": false,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTexture": "",
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTexture": "",
|
||||
"Name": "Material 5",
|
||||
"NormalTexture": "",
|
||||
"Roughness": 1.0,
|
||||
"ShaderModel": 0,
|
||||
"StableImportKey": ""
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "material",
|
||||
"guid": "39f65f8e-cdac-43fb-9762-e6c544547a71",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material 5.mcmaterial.json",
|
||||
"source_hash": 10101379247114018828,
|
||||
"source_path": "Assets/Materials/Material 5.mcmaterial.json"
|
||||
}
|
||||
26
SandboxProject/Assets/Materials/Material.mcmaterial.json
Normal file
26
SandboxProject/Assets/Materials/Material.mcmaterial.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"AlphaCutoff": 0.4129999876022339,
|
||||
"AlphaMode": 0,
|
||||
"AoTexture": "",
|
||||
"AssetGuid": "5edbdbc8-4a0d-4991-82a7-782de9ed4e3a",
|
||||
"BaseColor": [
|
||||
0.9999995231628418,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"BaseColorTexture": "",
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.12941177189350128,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTexture": "",
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTexture": "",
|
||||
"Name": "Material",
|
||||
"NormalTexture": "",
|
||||
"Roughness": 1.0,
|
||||
"ShaderModel": 0,
|
||||
"StableImportKey": ""
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"asset_type": "material",
|
||||
"guid": "5edbdbc8-4a0d-4991-82a7-782de9ed4e3a",
|
||||
"importer_id": "MaterialImporter",
|
||||
"package_path": "Assets/Materials/Material.mcmaterial.json",
|
||||
"source_hash": 7326866736359587430,
|
||||
"source_path": "Assets/Materials/Material.mcmaterial.json"
|
||||
}
|
||||
@ -869,12 +869,33 @@ template <typename TGeneratedAsset, typename TLabelBuilder>
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<MetaCoreGeneratedAssetChoice> MetaCoreCollectMaterialAssetChoices(MetaCoreEditorContext& editorContext) {
|
||||
return MetaCoreCollectGeneratedAssetChoices<MetaCoreMaterialAssetDocument>(
|
||||
std::vector<MetaCoreGeneratedAssetChoice> choices;
|
||||
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
if (assetDatabaseService != nullptr && assetDatabaseService->HasProject()) {
|
||||
for (const MetaCoreAssetRecord& record : assetDatabaseService->GetAssetRegistry()) {
|
||||
if (record.Type != "material" || !record.Guid.IsValid()) {
|
||||
continue;
|
||||
}
|
||||
choices.push_back(MetaCoreGeneratedAssetChoice{
|
||||
record.Guid,
|
||||
record.RelativePath.filename().string() + " / Material"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
auto generatedChoices = MetaCoreCollectGeneratedAssetChoices<MetaCoreMaterialAssetDocument>(
|
||||
editorContext,
|
||||
[](const MetaCoreAssetRecord& record, const MetaCoreMaterialAssetDocument& asset) {
|
||||
return record.RelativePath.filename().string() + " / Material / " + asset.Name;
|
||||
}
|
||||
);
|
||||
choices.insert(choices.end(), generatedChoices.begin(), generatedChoices.end());
|
||||
|
||||
std::sort(choices.begin(), choices.end(), [](const MetaCoreGeneratedAssetChoice& lhs, const MetaCoreGeneratedAssetChoice& rhs) {
|
||||
return lhs.Label < rhs.Label;
|
||||
});
|
||||
return choices;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<MetaCoreGeneratedAssetChoice> MetaCoreCollectTextureAssetChoices(MetaCoreEditorContext& editorContext) {
|
||||
@ -1047,73 +1068,249 @@ void MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(
|
||||
assignTextureReference(materialAsset.AoTexture, meshRenderer.AoTextureGuid, meshRenderer.AoTexturePath);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MetaCoreNormalizeModelRelativePath(std::string path) {
|
||||
if (path.empty()) {
|
||||
return {};
|
||||
}
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
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
|
||||
) {
|
||||
MetaCoreGameObject current = object;
|
||||
while (current) {
|
||||
if (current.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
const auto& meshRenderer = current.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
if (!meshRenderer.SourceModelPath.empty()) {
|
||||
return MetaCoreNormalizeModelRelativePath(meshRenderer.SourceModelPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (current.HasComponent<MetaCoreModelRootTag>()) {
|
||||
const auto& tag = current.GetComponent<MetaCoreModelRootTag>();
|
||||
if (!tag.SourceModelPath.empty()) {
|
||||
return MetaCoreNormalizeModelRelativePath(tag.SourceModelPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (current.GetParentId() == 0) {
|
||||
break;
|
||||
}
|
||||
current = scene.FindGameObject(current.GetParentId());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::size_t> MetaCoreFindGeneratedMaterialIndexByGuid(
|
||||
const MetaCoreModelAssetDocument& document,
|
||||
const MetaCoreAssetGuid& materialGuid
|
||||
) {
|
||||
if (!materialGuid.IsValid()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < document.GeneratedMaterialAssets.size(); ++index) {
|
||||
if (document.GeneratedMaterialAssets[index].AssetGuid == materialGuid) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::size_t> MetaCoreResolveFirstGeneratedMaterialIndexForMeshRenderer(
|
||||
const MetaCoreModelAssetDocument& document,
|
||||
const MetaCoreMeshRendererComponent& meshRenderer
|
||||
) {
|
||||
if (meshRenderer.ModelNodeIndex < 0 ||
|
||||
static_cast<std::size_t>(meshRenderer.ModelNodeIndex) >= document.Nodes.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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 std::nullopt;
|
||||
}
|
||||
|
||||
const auto& materialSlots = document.Meshes[static_cast<std::size_t>(node.MeshIndex)].MaterialSlots;
|
||||
for (const std::int32_t materialIndex : materialSlots) {
|
||||
if (materialIndex >= 0 && static_cast<std::size_t>(materialIndex) < document.GeneratedMaterialAssets.size()) {
|
||||
return static_cast<std::size_t>(materialIndex);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void MetaCoreRepairLegacyModelMaterialBinding(
|
||||
MetaCoreMeshRendererComponent& meshRenderer,
|
||||
const MetaCoreModelAssetDocument& document,
|
||||
const MetaCoreAssetGuid& modelGuid
|
||||
) {
|
||||
if (meshRenderer.SourceModelPath.empty()) {
|
||||
meshRenderer.SourceModelPath = document.SourcePath.generic_string();
|
||||
}
|
||||
if (!meshRenderer.SourceModelAssetGuid.IsValid() && modelGuid.IsValid()) {
|
||||
meshRenderer.SourceModelAssetGuid = modelGuid;
|
||||
}
|
||||
if (!MetaCoreMaterialGuidListIsEmpty(meshRenderer.MaterialAssetGuids)) {
|
||||
return;
|
||||
}
|
||||
if (meshRenderer.ModelNodeIndex < 0 ||
|
||||
static_cast<std::size_t>(meshRenderer.ModelNodeIndex) >= document.Nodes.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const auto& materialSlots = document.Meshes[static_cast<std::size_t>(node.MeshIndex)].MaterialSlots;
|
||||
meshRenderer.MaterialAssetGuids.clear();
|
||||
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);
|
||||
} else {
|
||||
meshRenderer.MaterialAssetGuids.push_back(MetaCoreAssetGuid{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreApplyGeneratedMaterialPreviewToScene(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreModelAssetDocument& document
|
||||
) {
|
||||
std::unordered_map<MetaCoreAssetGuid, std::string, MetaCoreAssetGuidHasher> texturePaths;
|
||||
for (const MetaCoreTextureAssetDocument& textureAsset : document.GeneratedTextureAssets) {
|
||||
if (textureAsset.AssetGuid.IsValid()) {
|
||||
texturePaths[textureAsset.AssetGuid] = textureAsset.SourcePath.generic_string();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string documentSourcePath = MetaCoreNormalizeModelRelativePath(document.SourcePath.generic_string());
|
||||
const MetaCoreAssetGuid modelGuid = documentSourcePath.empty()
|
||||
? MetaCoreAssetGuid{}
|
||||
: MetaCoreAssetRegistry::Get().ResolvePathToGuid(documentSourcePath);
|
||||
bool changed = false;
|
||||
for (MetaCoreGameObject& sceneObject : editorContext.GetScene().GetGameObjects()) {
|
||||
if (!sceneObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const MetaCoreAssetGuid& materialGuid : sceneObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids) {
|
||||
const auto materialIterator = std::find_if(
|
||||
document.GeneratedMaterialAssets.begin(),
|
||||
document.GeneratedMaterialAssets.end(),
|
||||
[&](const MetaCoreMaterialAssetDocument& materialAsset) {
|
||||
return materialAsset.AssetGuid == materialGuid;
|
||||
}
|
||||
);
|
||||
if (materialIterator == document.GeneratedMaterialAssets.end()) {
|
||||
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;
|
||||
}
|
||||
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor = materialIterator->BaseColor;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().Metallic = materialIterator->Metallic;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().Roughness = materialIterator->Roughness;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().DoubleSided = materialIterator->DoubleSided;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().EmissiveColor = materialIterator->EmissiveColor;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().AlphaCutoff = materialIterator->AlphaCutoff;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().AlphaMode =
|
||||
materialIterator->AlphaMode == MetaCoreMaterialAlphaMode::Mask
|
||||
? MetaCoreMeshAlphaMode::Mask
|
||||
: (materialIterator->AlphaMode == MetaCoreMaterialAlphaMode::Blend
|
||||
? MetaCoreMeshAlphaMode::Blend
|
||||
: MetaCoreMeshAlphaMode::Opaque);
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColorTextureGuid = materialIterator->BaseColorTexture;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColorTexturePath =
|
||||
materialIterator->BaseColorTexture.IsValid() && texturePaths.contains(materialIterator->BaseColorTexture)
|
||||
? texturePaths[materialIterator->BaseColorTexture]
|
||||
: std::string{};
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().MetallicRoughnessTextureGuid = materialIterator->MetallicRoughnessTexture;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().MetallicRoughnessTexturePath =
|
||||
materialIterator->MetallicRoughnessTexture.IsValid() && texturePaths.contains(materialIterator->MetallicRoughnessTexture)
|
||||
? texturePaths[materialIterator->MetallicRoughnessTexture]
|
||||
: std::string{};
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().NormalTextureGuid = materialIterator->NormalTexture;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().NormalTexturePath =
|
||||
materialIterator->NormalTexture.IsValid() && texturePaths.contains(materialIterator->NormalTexture)
|
||||
? texturePaths[materialIterator->NormalTexture]
|
||||
: std::string{};
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().EmissiveTextureGuid = materialIterator->EmissiveTexture;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().EmissiveTexturePath =
|
||||
materialIterator->EmissiveTexture.IsValid() && texturePaths.contains(materialIterator->EmissiveTexture)
|
||||
? texturePaths[materialIterator->EmissiveTexture]
|
||||
: std::string{};
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().AoTextureGuid = materialIterator->AoTexture;
|
||||
sceneObject.GetComponent<MetaCoreMeshRendererComponent>().AoTexturePath =
|
||||
materialIterator->AoTexture.IsValid() && texturePaths.contains(materialIterator->AoTexture)
|
||||
? texturePaths[materialIterator->AoTexture]
|
||||
: std::string{};
|
||||
break;
|
||||
}
|
||||
|
||||
const bool sameModelByGuid =
|
||||
modelGuid.IsValid() &&
|
||||
meshRenderer.SourceModelAssetGuid.IsValid() &&
|
||||
meshRenderer.SourceModelAssetGuid == modelGuid;
|
||||
const std::string sceneObjectModelPath = MetaCoreResolveSceneObjectModelPath(editorContext.GetScene(), sceneObject);
|
||||
const bool sameModelByPath = !documentSourcePath.empty() && sceneObjectModelPath == documentSourcePath;
|
||||
const bool sameModel = sameModelByGuid || sameModelByPath;
|
||||
|
||||
if (!materialIndex.has_value() && sameModel && MetaCoreMaterialGuidListIsEmpty(meshRenderer.MaterialAssetGuids)) {
|
||||
materialIndex = MetaCoreResolveFirstGeneratedMaterialIndexForMeshRenderer(document, meshRenderer);
|
||||
}
|
||||
|
||||
if (!materialIndex.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sameModel) {
|
||||
MetaCoreRepairLegacyModelMaterialBinding(meshRenderer, document, modelGuid);
|
||||
}
|
||||
MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(meshRenderer, document, *materialIndex);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreSyncMaterialDocumentPreviewToMeshRenderer(
|
||||
MetaCoreIAssetDatabaseService* assetDatabaseService,
|
||||
MetaCoreIAssetEditingService* assetEditingService,
|
||||
MetaCoreMeshRendererComponent& meshRenderer,
|
||||
const MetaCoreMaterialAssetDocument& materialAsset
|
||||
) {
|
||||
const auto texturePathForGuid = [&](const MetaCoreAssetGuid& textureGuid) -> std::string {
|
||||
if (!textureGuid.IsValid()) {
|
||||
return {};
|
||||
}
|
||||
if (assetDatabaseService != nullptr) {
|
||||
if (const auto textureRecord = assetDatabaseService->FindAssetByGuid(textureGuid); textureRecord.has_value()) {
|
||||
return textureRecord->RelativePath.generic_string();
|
||||
}
|
||||
}
|
||||
if (assetEditingService != nullptr) {
|
||||
if (const auto textureAsset = assetEditingService->LoadTextureAsset(textureGuid);
|
||||
textureAsset.has_value() && !textureAsset->SourcePath.empty()) {
|
||||
return textureAsset->SourcePath.generic_string();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
meshRenderer.BaseColor = materialAsset.BaseColor;
|
||||
meshRenderer.DoubleSided = materialAsset.DoubleSided;
|
||||
meshRenderer.Metallic = materialAsset.Metallic;
|
||||
meshRenderer.Roughness = materialAsset.Roughness;
|
||||
meshRenderer.AlphaCutoff = materialAsset.AlphaCutoff;
|
||||
meshRenderer.AlphaMode =
|
||||
materialAsset.AlphaMode == MetaCoreMaterialAlphaMode::Mask
|
||||
? MetaCoreMeshAlphaMode::Mask
|
||||
: (materialAsset.AlphaMode == MetaCoreMaterialAlphaMode::Blend
|
||||
? MetaCoreMeshAlphaMode::Blend
|
||||
: MetaCoreMeshAlphaMode::Opaque);
|
||||
meshRenderer.EmissiveColor = materialAsset.EmissiveColor;
|
||||
|
||||
meshRenderer.BaseColorTextureGuid = materialAsset.BaseColorTexture;
|
||||
meshRenderer.BaseColorTexturePath = texturePathForGuid(materialAsset.BaseColorTexture);
|
||||
meshRenderer.MetallicRoughnessTextureGuid = materialAsset.MetallicRoughnessTexture;
|
||||
meshRenderer.MetallicRoughnessTexturePath = texturePathForGuid(materialAsset.MetallicRoughnessTexture);
|
||||
meshRenderer.NormalTextureGuid = materialAsset.NormalTexture;
|
||||
meshRenderer.NormalTexturePath = texturePathForGuid(materialAsset.NormalTexture);
|
||||
meshRenderer.EmissiveTextureGuid = materialAsset.EmissiveTexture;
|
||||
meshRenderer.EmissiveTexturePath = texturePathForGuid(materialAsset.EmissiveTexture);
|
||||
meshRenderer.AoTextureGuid = materialAsset.AoTexture;
|
||||
meshRenderer.AoTexturePath = texturePathForGuid(materialAsset.AoTexture);
|
||||
}
|
||||
|
||||
void MetaCoreApplyMaterialPreviewToSceneUsers(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreAssetGuid& materialGuid,
|
||||
const MetaCoreMaterialAssetDocument& materialAsset
|
||||
) {
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
bool changed = false;
|
||||
for (MetaCoreGameObject& sceneObject : editorContext.GetScene().GetGameObjects()) {
|
||||
if (!sceneObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
continue;
|
||||
}
|
||||
auto& meshRenderer = sceneObject.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
if (std::find(meshRenderer.MaterialAssetGuids.begin(), meshRenderer.MaterialAssetGuids.end(), materialGuid) == meshRenderer.MaterialAssetGuids.end()) {
|
||||
continue;
|
||||
}
|
||||
MetaCoreSyncMaterialDocumentPreviewToMeshRenderer(
|
||||
assetDatabaseService.get(),
|
||||
assetEditingService.get(),
|
||||
meshRenderer,
|
||||
materialAsset
|
||||
);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1159,17 +1356,20 @@ void MetaCoreForEachSelectedGameObject(MetaCoreEditorContext& editorContext, TAc
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto resolvedMaterial = assetEditingService->ResolveGeneratedAsset(materialGuid);
|
||||
if (!resolvedMaterial.has_value() || resolvedMaterial->GeneratedKind != "material") {
|
||||
auto materialAsset = assetEditingService->LoadMaterialAsset(materialGuid);
|
||||
if (!materialAsset.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto modelDocument = assetEditingService->LoadModelAsset(resolvedMaterial->SourceAsset.Guid);
|
||||
if (!modelDocument.has_value() || resolvedMaterial->GeneratedIndex >= modelDocument->GeneratedMaterialAssets.size()) {
|
||||
return false;
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
const auto resolvedMaterial = assetEditingService->ResolveGeneratedAsset(materialGuid);
|
||||
std::optional<MetaCoreModelAssetDocument> modelDocument;
|
||||
if (resolvedMaterial.has_value() && resolvedMaterial->GeneratedKind == "material") {
|
||||
modelDocument = assetEditingService->LoadModelAsset(resolvedMaterial->SourceAsset.Guid);
|
||||
}
|
||||
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "拖拽材质到材质槽", false);
|
||||
bool changed = false;
|
||||
MetaCoreForEachSelectedGameObject(editorContext, [&](MetaCoreGameObject& selectedObject) {
|
||||
if (!selectedObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
return;
|
||||
@ -1178,13 +1378,29 @@ void MetaCoreForEachSelectedGameObject(MetaCoreEditorContext& editorContext, TAc
|
||||
selectedObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids.resize(materialSlotIndex + 1);
|
||||
}
|
||||
selectedObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids[materialSlotIndex] = materialGuid;
|
||||
MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(
|
||||
selectedObject.GetComponent<MetaCoreMeshRendererComponent>(),
|
||||
*modelDocument,
|
||||
resolvedMaterial->GeneratedIndex
|
||||
);
|
||||
if (resolvedMaterial.has_value() &&
|
||||
resolvedMaterial->GeneratedKind == "material" &&
|
||||
modelDocument.has_value() &&
|
||||
resolvedMaterial->GeneratedIndex < modelDocument->GeneratedMaterialAssets.size()) {
|
||||
MetaCoreSyncGeneratedMaterialPreviewToMeshRenderer(
|
||||
selectedObject.GetComponent<MetaCoreMeshRendererComponent>(),
|
||||
*modelDocument,
|
||||
resolvedMaterial->GeneratedIndex
|
||||
);
|
||||
} else {
|
||||
MetaCoreSyncMaterialDocumentPreviewToMeshRenderer(
|
||||
assetDatabaseService.get(),
|
||||
assetEditingService.get(),
|
||||
selectedObject.GetComponent<MetaCoreMeshRendererComponent>(),
|
||||
*materialAsset
|
||||
);
|
||||
}
|
||||
changed = true;
|
||||
});
|
||||
return true;
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreReapplyMaterialResourceToSelectedObjects(
|
||||
@ -1218,6 +1434,57 @@ void MetaCoreClearMaterialSlotOnSelectedObjects(
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::filesystem::path MetaCoreBuildUniqueMaterialPath(MetaCoreIAssetDatabaseService& assetDatabaseService) {
|
||||
if (!assetDatabaseService.HasProject()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::filesystem::path relativeDirectory = std::filesystem::path("Assets") / "Materials";
|
||||
for (int suffix = 0; suffix < 1000; ++suffix) {
|
||||
const std::string name = suffix == 0 ? "Material" : ("Material " + std::to_string(suffix));
|
||||
const std::filesystem::path relativePath = relativeDirectory / (name + ".mcmaterial.json");
|
||||
if (!std::filesystem::exists(assetDatabaseService.GetProjectDescriptor().RootPath / relativePath)) {
|
||||
return relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
return relativeDirectory / "Material 1000.mcmaterial.json";
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreCreateAndBindMaterialToSelectedObjects(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::size_t materialSlotIndex
|
||||
) {
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
if (assetDatabaseService == nullptr || !assetDatabaseService->HasProject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path materialPath = MetaCoreBuildUniqueMaterialPath(*assetDatabaseService);
|
||||
if (materialPath.empty() || !assetDatabaseService->CreateMaterialAsset(materialPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto materialRecord = assetDatabaseService->FindAssetByRelativePath(materialPath);
|
||||
if (!materialRecord.has_value() || !materialRecord->Guid.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MetaCoreApplyDraggedMaterialToSelectedObjects(editorContext, materialSlotIndex, materialRecord->Guid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
editorContext.SelectAsset(MetaCoreSelectedAssetState{
|
||||
materialRecord->Guid,
|
||||
materialRecord->RelativePath,
|
||||
materialRecord->Type,
|
||||
materialRecord->StorageKind
|
||||
});
|
||||
editorContext.ClearSelectedAssetSubId();
|
||||
editorContext.ClearSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MetaCoreAssetGuid> MetaCoreResolveDefaultMaterialForMeshRendererSlot(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreMeshRendererComponent& meshRenderer,
|
||||
@ -2308,6 +2575,31 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::Button("添加材质槽")) {
|
||||
const std::size_t newSlotIndex = meshRenderer.MaterialAssetGuids.size();
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "添加材质槽", false);
|
||||
MetaCoreForEachSelectedGameObject(editorContext, [&](MetaCoreGameObject& selectedObject) {
|
||||
if (selectedObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
selectedObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids.resize(newSlotIndex + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
ImGui::SameLine();
|
||||
std::size_t createSlotIndex = meshRenderer.MaterialAssetGuids.size();
|
||||
for (std::size_t materialIndex = 0; materialIndex < meshRenderer.MaterialAssetGuids.size(); ++materialIndex) {
|
||||
if (!meshRenderer.MaterialAssetGuids[materialIndex].IsValid()) {
|
||||
createSlotIndex = materialIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("新建材质并绑定")) {
|
||||
if (MetaCoreCreateAndBindMaterialToSelectedObjects(editorContext, createSlotIndex)) {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已新建材质并绑定到材质槽");
|
||||
} else {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Warning, "Material", "新建材质并绑定失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 槽位列表循环
|
||||
const auto materialChoices = MetaCoreCollectMaterialAssetChoices(editorContext);
|
||||
for (std::size_t materialIndex = 0; materialIndex < meshRenderer.MaterialAssetGuids.size(); ++materialIndex) {
|
||||
@ -2412,6 +2704,14 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
if (ImGui::Selectable("<None>", !materialGuid.IsValid())) {
|
||||
materialGuid = MetaCoreAssetGuid{};
|
||||
}
|
||||
if (ImGui::Selectable("新建材质并绑定", false)) {
|
||||
if (MetaCoreCreateAndBindMaterialToSelectedObjects(editorContext, materialIndex)) {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已新建材质并绑定到材质槽");
|
||||
} else {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Warning, "Material", "新建材质并绑定失败");
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
for (const auto& choice : materialChoices) {
|
||||
bool isSelected = (choice.AssetGuid == materialGuid);
|
||||
if (ImGui::Selectable(choice.Label.c_str(), isSelected)) {
|
||||
@ -2436,6 +2736,13 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
materialGuid = MetaCoreAssetGuid{};
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已清空材质槽位");
|
||||
}
|
||||
if (ImGui::MenuItem("新建材质并绑定")) {
|
||||
if (MetaCoreCreateAndBindMaterialToSelectedObjects(editorContext, materialIndex)) {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已新建材质并绑定到材质槽");
|
||||
} else {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Warning, "Material", "新建材质并绑定失败");
|
||||
}
|
||||
}
|
||||
if (ImGui::MenuItem("恢复模型默认材质 (Restore Default)")) {
|
||||
if (MetaCoreRestoreDefaultMaterialSlotOnSelectedObjects(editorContext, materialIndex)) {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已恢复模型默认材质槽位");
|
||||
@ -2480,6 +2787,21 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
});
|
||||
editorContext.SetSelectedAssetSubId("material:" + materialGuid.ToString());
|
||||
editorContext.ClearSelection();
|
||||
} else {
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
const auto materialRecord = assetDatabaseService != nullptr
|
||||
? assetDatabaseService->FindAssetByGuid(materialGuid)
|
||||
: std::nullopt;
|
||||
if (materialRecord.has_value()) {
|
||||
editorContext.SelectAsset(MetaCoreSelectedAssetState{
|
||||
materialRecord->Guid,
|
||||
materialRecord->RelativePath,
|
||||
materialRecord->Type,
|
||||
materialRecord->StorageKind
|
||||
});
|
||||
editorContext.ClearSelectedAssetSubId();
|
||||
editorContext.ClearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2720,10 +3042,20 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
drawList->AddCircleFilled(highlightCenter, 0.8F, IM_COL32(255, 255, 255, 255));
|
||||
|
||||
if (isPanelOpen) {
|
||||
if (!resolvedMaterial.has_value() ||
|
||||
resolvedMaterial->GeneratedKind != "material" ||
|
||||
!materialDocument.has_value()) {
|
||||
ImGui::TextDisabled("未找到对应材质资源");
|
||||
if (!materialDocument.has_value()) {
|
||||
ImGui::TextColored(ImVec4(0.95F, 0.38F, 0.32F, 1.0F), "未找到对应材质资源");
|
||||
ImGui::TextDisabled("可以清空槽位,或新建一个材质并绑定到当前槽位。");
|
||||
if (ImGui::Button(("新建材质并绑定##MissingMaterial_" + std::to_string(materialIndex)).c_str())) {
|
||||
if (MetaCoreCreateAndBindMaterialToSelectedObjects(editorContext, materialIndex)) {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已新建材质并绑定到材质槽");
|
||||
} else {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Warning, "Material", "新建材质并绑定失败");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(("清空槽位##MissingMaterial_" + std::to_string(materialIndex)).c_str())) {
|
||||
MetaCoreClearMaterialSlotOnSelectedObjects(editorContext, materialIndex);
|
||||
}
|
||||
} else {
|
||||
MetaCoreMaterialAssetDocument materialAsset = *materialDocument;
|
||||
bool modified = false;
|
||||
@ -2791,9 +3123,13 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
|
||||
|
||||
if (modified) {
|
||||
if (assetEditingService->SaveMaterialAsset(materialGuid, materialAsset)) {
|
||||
if (const auto modelDocument = assetEditingService->LoadModelAsset(resolvedMaterial->SourceAsset.Guid);
|
||||
modelDocument.has_value()) {
|
||||
MetaCoreApplyGeneratedMaterialPreviewToScene(editorContext, *modelDocument);
|
||||
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);
|
||||
}
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Material", "已保存材质资源修改");
|
||||
} else {
|
||||
@ -5609,7 +5945,7 @@ bool MetaCoreBuiltinAssetEditingService::SaveMaterialAsset(
|
||||
}
|
||||
}
|
||||
|
||||
if (PackageService_ != nullptr && assetRecord.has_value()) {
|
||||
if (PackageService_ != nullptr) {
|
||||
const auto resolved = MetaCoreResolveGeneratedAssetDocument(
|
||||
*AssetDatabaseService_,
|
||||
*PackageService_,
|
||||
@ -5621,13 +5957,17 @@ bool MetaCoreBuiltinAssetEditingService::SaveMaterialAsset(
|
||||
MetaCoreModelAssetDocument editableDocument = resolved->Document;
|
||||
editableDocument.GeneratedMaterialAssets[resolved->GeneratedIndex] = document;
|
||||
editableDocument.GeneratedMaterialAssets[resolved->GeneratedIndex].AssetGuid = materialGuid;
|
||||
return MetaCoreWriteImportedGltfAssetDocument(
|
||||
const bool saved = MetaCoreWriteImportedGltfAssetDocument(
|
||||
*AssetDatabaseService_,
|
||||
*PackageService_,
|
||||
*ReflectionRegistry_,
|
||||
resolved->AssetRecord,
|
||||
editableDocument
|
||||
);
|
||||
if (saved) {
|
||||
ModelCache_[resolved->AssetRecord.Guid] = editableDocument;
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1274,89 +1274,177 @@ void MetaCoreApplyMaterialPreviewToScene(
|
||||
return;
|
||||
}
|
||||
|
||||
const auto normalizeModelPath = [](std::string path) {
|
||||
if (path.empty()) {
|
||||
return std::string{};
|
||||
}
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
return std::filesystem::path(path).lexically_normal().generic_string();
|
||||
};
|
||||
const auto materialGuidListIsEmpty = [](const std::vector<MetaCoreAssetGuid>& materialGuids) {
|
||||
return std::all_of(materialGuids.begin(), materialGuids.end(), [](const MetaCoreAssetGuid& guid) {
|
||||
return !guid.IsValid();
|
||||
});
|
||||
};
|
||||
const auto findMaterialIndexByGuid = [&](const MetaCoreAssetGuid& materialGuid) -> std::optional<std::size_t> {
|
||||
if (!materialGuid.IsValid()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
for (std::size_t index = 0; index < document.GeneratedMaterialAssets.size(); ++index) {
|
||||
if (document.GeneratedMaterialAssets[index].AssetGuid == materialGuid) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
const auto resolveFirstMaterialIndexForMeshRenderer = [&](const MetaCoreMeshRendererComponent& meshRenderer) -> std::optional<std::size_t> {
|
||||
if (meshRenderer.ModelNodeIndex < 0 ||
|
||||
static_cast<std::size_t>(meshRenderer.ModelNodeIndex) >= document.Nodes.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
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 std::nullopt;
|
||||
}
|
||||
for (const std::int32_t materialIndex : document.Meshes[static_cast<std::size_t>(node.MeshIndex)].MaterialSlots) {
|
||||
if (materialIndex >= 0 && static_cast<std::size_t>(materialIndex) < document.GeneratedMaterialAssets.size()) {
|
||||
return static_cast<std::size_t>(materialIndex);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
const auto resolveSceneObjectModelPath = [&](MetaCoreGameObject object) {
|
||||
MetaCoreGameObject current = object;
|
||||
while (current) {
|
||||
if (current.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
const auto& meshRenderer = current.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
if (!meshRenderer.SourceModelPath.empty()) {
|
||||
return normalizeModelPath(meshRenderer.SourceModelPath);
|
||||
}
|
||||
}
|
||||
if (current.HasComponent<MetaCoreModelRootTag>()) {
|
||||
const auto& tag = current.GetComponent<MetaCoreModelRootTag>();
|
||||
if (!tag.SourceModelPath.empty()) {
|
||||
return normalizeModelPath(tag.SourceModelPath);
|
||||
}
|
||||
}
|
||||
if (current.GetParentId() == 0) {
|
||||
break;
|
||||
}
|
||||
current = editorContext.GetScene().FindGameObject(current.GetParentId());
|
||||
}
|
||||
return std::string{};
|
||||
};
|
||||
const auto repairLegacyBinding = [&](MetaCoreMeshRendererComponent& meshRenderer, const MetaCoreAssetGuid& modelGuid) {
|
||||
if (meshRenderer.SourceModelPath.empty()) {
|
||||
meshRenderer.SourceModelPath = document.SourcePath.generic_string();
|
||||
}
|
||||
if (!meshRenderer.SourceModelAssetGuid.IsValid() && modelGuid.IsValid()) {
|
||||
meshRenderer.SourceModelAssetGuid = modelGuid;
|
||||
}
|
||||
if (!materialGuidListIsEmpty(meshRenderer.MaterialAssetGuids) ||
|
||||
meshRenderer.ModelNodeIndex < 0 ||
|
||||
static_cast<std::size_t>(meshRenderer.ModelNodeIndex) >= document.Nodes.size()) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
meshRenderer.MaterialAssetGuids.clear();
|
||||
for (const std::int32_t materialIndex : document.Meshes[static_cast<std::size_t>(node.MeshIndex)].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);
|
||||
} else {
|
||||
meshRenderer.MaterialAssetGuids.push_back(MetaCoreAssetGuid{});
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto applyMaterialIndex = [&](MetaCoreMeshRendererComponent& meshRenderer, std::size_t materialIndex) {
|
||||
if (materialIndex >= document.GeneratedMaterialAssets.size()) {
|
||||
return;
|
||||
}
|
||||
const MetaCoreMaterialAssetDocument& materialDocument = document.GeneratedMaterialAssets[materialIndex];
|
||||
const MetaCoreAssetGuid materialGuid = materialDocument.AssetGuid;
|
||||
meshRenderer.BaseColor = materialDocument.BaseColor;
|
||||
meshRenderer.BaseColorTextureGuid = materialDocument.BaseColorTexture;
|
||||
meshRenderer.BaseColorTexturePath =
|
||||
materialDocument.BaseColorTexture.IsValid() && texturePaths.contains(materialDocument.BaseColorTexture)
|
||||
? texturePaths[materialDocument.BaseColorTexture]
|
||||
: std::string{};
|
||||
meshRenderer.MetallicRoughnessTextureGuid = materialDocument.MetallicRoughnessTexture;
|
||||
meshRenderer.MetallicRoughnessTexturePath =
|
||||
materialDocument.MetallicRoughnessTexture.IsValid() && texturePaths.contains(materialDocument.MetallicRoughnessTexture)
|
||||
? texturePaths[materialDocument.MetallicRoughnessTexture]
|
||||
: std::string{};
|
||||
meshRenderer.DoubleSided = doubleSidedFlags.contains(materialGuid) ? doubleSidedFlags[materialGuid] : true;
|
||||
meshRenderer.Metallic = metallicValues.contains(materialGuid) ? metallicValues[materialGuid] : 0.0F;
|
||||
meshRenderer.Roughness = roughnessValues.contains(materialGuid) ? roughnessValues[materialGuid] : 1.0F;
|
||||
meshRenderer.EmissiveColor = emissiveColors.contains(materialGuid) ? emissiveColors[materialGuid] : glm::vec3(0.0F, 0.0F, 0.0F);
|
||||
const MetaCoreMaterialAlphaMode alphaMode = alphaModes.contains(materialGuid) ? alphaModes[materialGuid] : MetaCoreMaterialAlphaMode::Opaque;
|
||||
meshRenderer.AlphaMode =
|
||||
alphaMode == MetaCoreMaterialAlphaMode::Mask
|
||||
? MetaCoreMeshAlphaMode::Mask
|
||||
: (alphaMode == MetaCoreMaterialAlphaMode::Blend ? MetaCoreMeshAlphaMode::Blend : MetaCoreMeshAlphaMode::Opaque);
|
||||
meshRenderer.AlphaCutoff = alphaCutoffs.contains(materialGuid) ? alphaCutoffs[materialGuid] : 0.5F;
|
||||
meshRenderer.NormalTextureGuid = materialDocument.NormalTexture;
|
||||
meshRenderer.NormalTexturePath =
|
||||
materialDocument.NormalTexture.IsValid() && texturePaths.contains(materialDocument.NormalTexture)
|
||||
? texturePaths[materialDocument.NormalTexture]
|
||||
: std::string{};
|
||||
meshRenderer.EmissiveTextureGuid = materialDocument.EmissiveTexture;
|
||||
meshRenderer.EmissiveTexturePath =
|
||||
materialDocument.EmissiveTexture.IsValid() && texturePaths.contains(materialDocument.EmissiveTexture)
|
||||
? texturePaths[materialDocument.EmissiveTexture]
|
||||
: std::string{};
|
||||
meshRenderer.AoTextureGuid = materialDocument.AoTexture;
|
||||
meshRenderer.AoTexturePath =
|
||||
materialDocument.AoTexture.IsValid() && texturePaths.contains(materialDocument.AoTexture)
|
||||
? texturePaths[materialDocument.AoTexture]
|
||||
: std::string{};
|
||||
};
|
||||
|
||||
const std::string documentSourcePath = normalizeModelPath(document.SourcePath.generic_string());
|
||||
const MetaCoreAssetGuid modelGuid = documentSourcePath.empty()
|
||||
? MetaCoreAssetGuid{}
|
||||
: MetaCoreAssetRegistry::Get().ResolvePathToGuid(documentSourcePath);
|
||||
bool changed = false;
|
||||
for (MetaCoreGameObject& gameObject : editorContext.GetScene().GetGameObjects()) {
|
||||
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const MetaCoreAssetGuid& materialGuid : gameObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids) {
|
||||
const auto iterator = materialBaseColors.find(materialGuid);
|
||||
if (iterator != materialBaseColors.end()) {
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor = iterator->second;
|
||||
const auto materialDocumentIterator = std::find_if(
|
||||
document.GeneratedMaterialAssets.begin(),
|
||||
document.GeneratedMaterialAssets.end(),
|
||||
[&](const MetaCoreMaterialAssetDocument& materialAsset) {
|
||||
return materialAsset.AssetGuid == materialGuid;
|
||||
}
|
||||
);
|
||||
if (materialDocumentIterator != document.GeneratedMaterialAssets.end() &&
|
||||
materialDocumentIterator->BaseColorTexture.IsValid()) {
|
||||
const auto textureIterator = texturePaths.find(materialDocumentIterator->BaseColorTexture);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColorTexturePath =
|
||||
textureIterator != texturePaths.end() ? textureIterator->second : std::string{};
|
||||
} else {
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColorTexturePath.clear();
|
||||
}
|
||||
if (materialDocumentIterator != document.GeneratedMaterialAssets.end() &&
|
||||
materialDocumentIterator->MetallicRoughnessTexture.IsValid()) {
|
||||
const auto textureIterator = texturePaths.find(materialDocumentIterator->MetallicRoughnessTexture);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().MetallicRoughnessTexturePath =
|
||||
textureIterator != texturePaths.end() ? textureIterator->second : std::string{};
|
||||
} else {
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().MetallicRoughnessTexturePath.clear();
|
||||
}
|
||||
const auto doubleSidedIterator = doubleSidedFlags.find(materialGuid);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().DoubleSided =
|
||||
doubleSidedIterator != doubleSidedFlags.end() ? doubleSidedIterator->second : true;
|
||||
const auto metallicIterator = metallicValues.find(materialGuid);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().Metallic =
|
||||
metallicIterator != metallicValues.end() ? metallicIterator->second : 0.0F;
|
||||
const auto roughnessIterator = roughnessValues.find(materialGuid);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().Roughness =
|
||||
roughnessIterator != roughnessValues.end() ? roughnessIterator->second : 1.0F;
|
||||
const auto emissiveColorIterator = emissiveColors.find(materialGuid);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().EmissiveColor =
|
||||
emissiveColorIterator != emissiveColors.end() ? emissiveColorIterator->second : glm::vec3(0.0F, 0.0F, 0.0F);
|
||||
const auto alphaModeIterator = alphaModes.find(materialGuid);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().AlphaMode =
|
||||
alphaModeIterator != alphaModes.end()
|
||||
? (alphaModeIterator->second == MetaCoreMaterialAlphaMode::Mask
|
||||
? MetaCoreMeshAlphaMode::Mask
|
||||
: (alphaModeIterator->second == MetaCoreMaterialAlphaMode::Blend
|
||||
? MetaCoreMeshAlphaMode::Blend
|
||||
: MetaCoreMeshAlphaMode::Opaque))
|
||||
: MetaCoreMeshAlphaMode::Opaque;
|
||||
const auto alphaCutoffIterator = alphaCutoffs.find(materialGuid);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().AlphaCutoff =
|
||||
alphaCutoffIterator != alphaCutoffs.end() ? alphaCutoffIterator->second : 0.5F;
|
||||
if (materialDocumentIterator != document.GeneratedMaterialAssets.end() &&
|
||||
materialDocumentIterator->NormalTexture.IsValid()) {
|
||||
const auto textureIterator = texturePaths.find(materialDocumentIterator->NormalTexture);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().NormalTexturePath =
|
||||
textureIterator != texturePaths.end() ? textureIterator->second : std::string{};
|
||||
} else {
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().NormalTexturePath.clear();
|
||||
}
|
||||
if (materialDocumentIterator != document.GeneratedMaterialAssets.end() &&
|
||||
materialDocumentIterator->EmissiveTexture.IsValid()) {
|
||||
const auto textureIterator = texturePaths.find(materialDocumentIterator->EmissiveTexture);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().EmissiveTexturePath =
|
||||
textureIterator != texturePaths.end() ? textureIterator->second : std::string{};
|
||||
} else {
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().EmissiveTexturePath.clear();
|
||||
}
|
||||
if (materialDocumentIterator != document.GeneratedMaterialAssets.end() &&
|
||||
materialDocumentIterator->AoTexture.IsValid()) {
|
||||
const auto textureIterator = texturePaths.find(materialDocumentIterator->AoTexture);
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().AoTexturePath =
|
||||
textureIterator != texturePaths.end() ? textureIterator->second : std::string{};
|
||||
} else {
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().AoTexturePath.clear();
|
||||
}
|
||||
auto& meshRenderer = gameObject.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
std::optional<std::size_t> materialIndex;
|
||||
for (const MetaCoreAssetGuid& materialGuid : meshRenderer.MaterialAssetGuids) {
|
||||
materialIndex = findMaterialIndexByGuid(materialGuid);
|
||||
if (materialIndex.has_value()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const bool sameModelByGuid =
|
||||
modelGuid.IsValid() &&
|
||||
meshRenderer.SourceModelAssetGuid.IsValid() &&
|
||||
meshRenderer.SourceModelAssetGuid == modelGuid;
|
||||
const bool sameModelByPath = !documentSourcePath.empty() && resolveSceneObjectModelPath(gameObject) == documentSourcePath;
|
||||
const bool sameModel = sameModelByGuid || sameModelByPath;
|
||||
if (!materialIndex.has_value() && sameModel && materialGuidListIsEmpty(meshRenderer.MaterialAssetGuids)) {
|
||||
materialIndex = resolveFirstMaterialIndexForMeshRenderer(meshRenderer);
|
||||
}
|
||||
if (!materialIndex.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sameModel) {
|
||||
repairLegacyBinding(meshRenderer, modelGuid);
|
||||
}
|
||||
applyMaterialIndex(meshRenderer, *materialIndex);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2686,6 +2774,472 @@ void DrawReferencedObjectList(MetaCoreEditorContext& editorContext, const std::v
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* MetaCoreMaterialShaderModelLabel(MetaCoreMaterialShaderModel shaderModel) {
|
||||
switch (shaderModel) {
|
||||
case MetaCoreMaterialShaderModel::PbrMetalRough:
|
||||
return "PBR 金属粗糙度";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* MetaCoreMaterialAlphaModeLabel(MetaCoreMaterialAlphaMode alphaMode) {
|
||||
switch (alphaMode) {
|
||||
case MetaCoreMaterialAlphaMode::Opaque:
|
||||
return "不透明";
|
||||
case MetaCoreMaterialAlphaMode::Mask:
|
||||
return "镂空裁剪";
|
||||
case MetaCoreMaterialAlphaMode::Blend:
|
||||
return "透明混合";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] ImU32 MetaCoreMaterialColorToU32(const glm::vec3& color, int alpha = 255) {
|
||||
const auto toByte = [](float value) {
|
||||
return static_cast<int>(std::clamp(value, 0.0F, 1.0F) * 255.0F + 0.5F);
|
||||
};
|
||||
return IM_COL32(toByte(color.r), toByte(color.g), toByte(color.b), std::clamp(alpha, 0, 255));
|
||||
}
|
||||
|
||||
void MetaCoreApplyMaterialDocumentToMeshRenderer(
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService,
|
||||
const MetaCoreIAssetEditingService* assetEditingService,
|
||||
MetaCoreMeshRendererComponent& meshRenderer,
|
||||
const MetaCoreMaterialAssetDocument& materialDocument
|
||||
) {
|
||||
const auto texturePathForGuid = [&](const MetaCoreAssetGuid& textureGuid) -> std::string {
|
||||
if (!textureGuid.IsValid()) {
|
||||
return {};
|
||||
}
|
||||
const auto textureRecord = assetDatabaseService.FindAssetByGuid(textureGuid);
|
||||
if (textureRecord.has_value()) {
|
||||
return textureRecord->RelativePath.generic_string();
|
||||
}
|
||||
if (assetEditingService != nullptr) {
|
||||
const auto textureDocument = assetEditingService->LoadTextureAsset(textureGuid);
|
||||
if (textureDocument.has_value() && !textureDocument->SourcePath.empty()) {
|
||||
return textureDocument->SourcePath.generic_string();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
meshRenderer.BaseColor = materialDocument.BaseColor;
|
||||
meshRenderer.Metallic = materialDocument.Metallic;
|
||||
meshRenderer.Roughness = materialDocument.Roughness;
|
||||
meshRenderer.DoubleSided = materialDocument.DoubleSided;
|
||||
meshRenderer.EmissiveColor = materialDocument.EmissiveColor;
|
||||
meshRenderer.AlphaCutoff = materialDocument.AlphaCutoff;
|
||||
meshRenderer.AlphaMode =
|
||||
materialDocument.AlphaMode == MetaCoreMaterialAlphaMode::Mask
|
||||
? MetaCoreMeshAlphaMode::Mask
|
||||
: (materialDocument.AlphaMode == MetaCoreMaterialAlphaMode::Blend
|
||||
? MetaCoreMeshAlphaMode::Blend
|
||||
: MetaCoreMeshAlphaMode::Opaque);
|
||||
|
||||
meshRenderer.BaseColorTextureGuid = materialDocument.BaseColorTexture;
|
||||
meshRenderer.BaseColorTexturePath = texturePathForGuid(materialDocument.BaseColorTexture);
|
||||
meshRenderer.NormalTextureGuid = materialDocument.NormalTexture;
|
||||
meshRenderer.NormalTexturePath = texturePathForGuid(materialDocument.NormalTexture);
|
||||
meshRenderer.MetallicRoughnessTextureGuid = materialDocument.MetallicRoughnessTexture;
|
||||
meshRenderer.MetallicRoughnessTexturePath = texturePathForGuid(materialDocument.MetallicRoughnessTexture);
|
||||
meshRenderer.EmissiveTextureGuid = materialDocument.EmissiveTexture;
|
||||
meshRenderer.EmissiveTexturePath = texturePathForGuid(materialDocument.EmissiveTexture);
|
||||
meshRenderer.AoTextureGuid = materialDocument.AoTexture;
|
||||
meshRenderer.AoTexturePath = texturePathForGuid(materialDocument.AoTexture);
|
||||
}
|
||||
|
||||
void MetaCoreApplyStandaloneMaterialPreviewToScene(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService,
|
||||
const MetaCoreMaterialAssetDocument& materialDocument
|
||||
) {
|
||||
if (!materialDocument.AssetGuid.IsValid()) {
|
||||
return;
|
||||
}
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
bool changed = false;
|
||||
for (MetaCoreGameObject& gameObject : editorContext.GetScene().GetGameObjects()) {
|
||||
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
continue;
|
||||
}
|
||||
const auto& materialGuids = gameObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids;
|
||||
if (std::find(materialGuids.begin(), materialGuids.end(), materialDocument.AssetGuid) == materialGuids.end()) {
|
||||
continue;
|
||||
}
|
||||
MetaCoreApplyMaterialDocumentToMeshRenderer(
|
||||
assetDatabaseService,
|
||||
assetEditingService.get(),
|
||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>(),
|
||||
materialDocument
|
||||
);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
editorContext.GetScene().IncrementRevision();
|
||||
}
|
||||
}
|
||||
|
||||
struct MetaCoreMaterialTextureChoice {
|
||||
MetaCoreAssetGuid AssetGuid{};
|
||||
std::string Label{};
|
||||
};
|
||||
|
||||
struct MetaCoreMaterialTextureBindingState {
|
||||
std::string DisplayName = "无贴图";
|
||||
std::string DetailText{};
|
||||
bool Missing = false;
|
||||
std::optional<MetaCoreAssetRecord> LocateAsset{};
|
||||
std::string LocateSubId{};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<MetaCoreMaterialTextureChoice> MetaCoreCollectMaterialTextureChoices(
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService
|
||||
) {
|
||||
std::vector<MetaCoreMaterialTextureChoice> choices;
|
||||
choices.reserve(assetDatabaseService.GetAssetRegistry().size());
|
||||
for (const MetaCoreAssetRecord& record : assetDatabaseService.GetAssetRegistry()) {
|
||||
if (record.Type != "texture" || !record.Guid.IsValid()) {
|
||||
continue;
|
||||
}
|
||||
std::string label = record.RelativePath.filename().string();
|
||||
if (!record.RelativePath.parent_path().empty()) {
|
||||
label += " | " + record.RelativePath.parent_path().generic_string();
|
||||
}
|
||||
choices.push_back(MetaCoreMaterialTextureChoice{record.Guid, std::move(label)});
|
||||
}
|
||||
std::sort(choices.begin(), choices.end(), [](const auto& lhs, const auto& rhs) {
|
||||
return lhs.Label < rhs.Label;
|
||||
});
|
||||
return choices;
|
||||
}
|
||||
|
||||
[[nodiscard]] MetaCoreMaterialTextureBindingState MetaCoreResolveMaterialTextureBinding(
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService,
|
||||
const MetaCoreIAssetEditingService* assetEditingService,
|
||||
const MetaCoreAssetGuid& textureGuid
|
||||
) {
|
||||
MetaCoreMaterialTextureBindingState state;
|
||||
if (!textureGuid.IsValid()) {
|
||||
return state;
|
||||
}
|
||||
if (const auto record = assetDatabaseService.FindAssetByGuid(textureGuid); record.has_value()) {
|
||||
state.DisplayName = record->RelativePath.filename().string();
|
||||
state.DetailText = record->RelativePath.generic_string();
|
||||
state.LocateAsset = *record;
|
||||
return state;
|
||||
}
|
||||
|
||||
if (assetEditingService != nullptr) {
|
||||
const auto resolved = assetEditingService->ResolveGeneratedAsset(textureGuid);
|
||||
const auto textureDocument = assetEditingService->LoadTextureAsset(textureGuid);
|
||||
if (resolved.has_value() && resolved->GeneratedKind == "texture" && textureDocument.has_value()) {
|
||||
state.DisplayName = !textureDocument->Name.empty()
|
||||
? textureDocument->Name
|
||||
: (!textureDocument->SourcePath.empty() ? textureDocument->SourcePath.filename().string() : std::string("生成贴图"));
|
||||
state.DetailText = "生成贴图: " + resolved->SourceAsset.RelativePath.generic_string();
|
||||
if (!textureDocument->SourcePath.empty()) {
|
||||
state.DetailText += " / " + textureDocument->SourcePath.generic_string();
|
||||
}
|
||||
state.LocateAsset = resolved->SourceAsset;
|
||||
state.LocateSubId = MetaCoreBuildGeneratedResourceSubId("texture", textureGuid);
|
||||
return state;
|
||||
}
|
||||
if (textureDocument.has_value()) {
|
||||
state.DisplayName = !textureDocument->Name.empty()
|
||||
? textureDocument->Name
|
||||
: (!textureDocument->SourcePath.empty() ? textureDocument->SourcePath.filename().string() : std::string("贴图资源"));
|
||||
state.DetailText = textureDocument->SourcePath.generic_string();
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
state.DisplayName = "贴图缺失";
|
||||
state.Missing = true;
|
||||
return state;
|
||||
}
|
||||
|
||||
void MetaCoreDrawMaterialPreviewCard(const MetaCoreMaterialAssetDocument& materialDocument) {
|
||||
const float cardSize = std::min(std::max(ImGui::GetContentRegionAvail().x, 180.0F), 260.0F);
|
||||
const ImVec2 canvasMin = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 canvasSize(cardSize, cardSize + 72.0F);
|
||||
const ImVec2 canvasMax(canvasMin.x + canvasSize.x, canvasMin.y + canvasSize.y);
|
||||
ImGui::InvisibleButton("##MaterialPreviewCard", canvasSize);
|
||||
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
drawList->AddRectFilled(canvasMin, canvasMax, IM_COL32(28, 31, 38, 255), 12.0F);
|
||||
drawList->AddRect(canvasMin, canvasMax, IM_COL32(255, 255, 255, 28), 12.0F);
|
||||
|
||||
const ImVec2 previewMin(canvasMin.x + 14.0F, canvasMin.y + 14.0F);
|
||||
const ImVec2 previewMax(canvasMax.x - 14.0F, canvasMin.y + cardSize - 6.0F);
|
||||
const float checker = 14.0F;
|
||||
for (float y = previewMin.y; y < previewMax.y; y += checker) {
|
||||
for (float x = previewMin.x; x < previewMax.x; x += checker) {
|
||||
const bool even = (static_cast<int>((x - previewMin.x) / checker) + static_cast<int>((y - previewMin.y) / checker)) % 2 == 0;
|
||||
drawList->AddRectFilled(
|
||||
ImVec2(x, y),
|
||||
ImVec2(std::min(x + checker, previewMax.x), std::min(y + checker, previewMax.y)),
|
||||
even ? IM_COL32(55, 58, 66, 255) : IM_COL32(39, 42, 50, 255)
|
||||
);
|
||||
}
|
||||
}
|
||||
drawList->AddRectFilled(previewMin, previewMax, MetaCoreMaterialColorToU32(materialDocument.BaseColor, 238), 10.0F);
|
||||
|
||||
const ImVec2 sphereCenter(
|
||||
(previewMin.x + previewMax.x) * 0.5F,
|
||||
(previewMin.y + previewMax.y) * 0.5F
|
||||
);
|
||||
const float radius = std::min(previewMax.x - previewMin.x, previewMax.y - previewMin.y) * 0.31F;
|
||||
drawList->AddCircleFilled(sphereCenter, radius, MetaCoreMaterialColorToU32(materialDocument.BaseColor), 48);
|
||||
drawList->AddCircleFilled(
|
||||
ImVec2(sphereCenter.x + radius * 0.20F, sphereCenter.y + radius * 0.22F),
|
||||
radius * 0.78F,
|
||||
IM_COL32(0, 0, 0, static_cast<int>(50.0F + materialDocument.Roughness * 80.0F)),
|
||||
48
|
||||
);
|
||||
const float highlightRadius = std::max(3.0F, radius * (0.24F - materialDocument.Roughness * 0.14F));
|
||||
const int highlightAlpha = static_cast<int>(95.0F + materialDocument.Metallic * 115.0F + (1.0F - materialDocument.Roughness) * 40.0F);
|
||||
drawList->AddCircleFilled(
|
||||
ImVec2(sphereCenter.x - radius * 0.34F, sphereCenter.y - radius * 0.36F),
|
||||
highlightRadius,
|
||||
IM_COL32(255, 255, 255, std::clamp(highlightAlpha, 80, 245)),
|
||||
24
|
||||
);
|
||||
const float emissiveStrength = std::max({materialDocument.EmissiveColor.r, materialDocument.EmissiveColor.g, materialDocument.EmissiveColor.b});
|
||||
if (emissiveStrength > 0.001F) {
|
||||
drawList->AddCircle(
|
||||
sphereCenter,
|
||||
radius + 5.0F,
|
||||
MetaCoreMaterialColorToU32(materialDocument.EmissiveColor, 180),
|
||||
48,
|
||||
3.0F
|
||||
);
|
||||
}
|
||||
drawList->AddRect(previewMin, previewMax, IM_COL32(255, 255, 255, 42), 10.0F);
|
||||
|
||||
const float infoY = previewMax.y + 12.0F;
|
||||
drawList->AddText(ImVec2(canvasMin.x + 16.0F, infoY), IM_COL32(225, 230, 238, 255), "材质预览");
|
||||
const std::string info =
|
||||
"金属 " + std::to_string(static_cast<int>(materialDocument.Metallic * 100.0F + 0.5F)) +
|
||||
"% 粗糙 " + std::to_string(static_cast<int>(materialDocument.Roughness * 100.0F + 0.5F)) + "%";
|
||||
drawList->AddText(ImVec2(canvasMin.x + 16.0F, infoY + ImGui::GetTextLineHeight() + 4.0F), IM_COL32(151, 160, 174, 255), info.c_str());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreDrawMaterialTextureSlot(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService,
|
||||
const MetaCoreIAssetEditingService* assetEditingService,
|
||||
const char* label,
|
||||
const char* id,
|
||||
MetaCoreAssetGuid& textureGuid,
|
||||
const std::vector<MetaCoreMaterialTextureChoice>& textureChoices
|
||||
) {
|
||||
bool changed = false;
|
||||
ImGui::PushID(id);
|
||||
if (ImGui::BeginTable("TextureSlot", 2, ImGuiTableFlags_SizingStretchProp)) {
|
||||
ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthFixed, 118.0F);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(label);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
const MetaCoreMaterialTextureBindingState binding =
|
||||
MetaCoreResolveMaterialTextureBinding(assetDatabaseService, assetEditingService, textureGuid);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, binding.Missing ? ImVec4(0.34F, 0.13F, 0.12F, 1.0F) : ImVec4(0.12F, 0.13F, 0.16F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, binding.Missing ? ImVec4(0.44F, 0.18F, 0.16F, 1.0F) : ImVec4(0.18F, 0.20F, 0.25F, 1.0F));
|
||||
const bool openPicker =
|
||||
ImGui::Button((binding.DisplayName + "##TexturePickerButton").c_str(), ImVec2(std::max(ImGui::GetContentRegionAvail().x - 98.0F, 80.0F), 0.0F));
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(MetaCoreProjectAssetDragDropPayloadType);
|
||||
payload != nullptr && payload->DataSize == sizeof(MetaCoreProjectAssetDragDropPayload)) {
|
||||
const auto* assetPayload = static_cast<const MetaCoreProjectAssetDragDropPayload*>(payload->Data);
|
||||
if (assetPayload != nullptr &&
|
||||
!assetPayload->IsDirectory &&
|
||||
assetPayload->AssetGuid.IsValid() &&
|
||||
std::strcmp(assetPayload->AssetType, "texture") == 0) {
|
||||
textureGuid = assetPayload->AssetGuid;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
if (openPicker) {
|
||||
ImGui::OpenPopup("TexturePickerPopup");
|
||||
}
|
||||
if (ImGui::BeginPopup("TexturePickerPopup")) {
|
||||
if (ImGui::Selectable("无贴图", !textureGuid.IsValid())) {
|
||||
textureGuid = MetaCoreAssetGuid{};
|
||||
changed = true;
|
||||
}
|
||||
for (const auto& choice : textureChoices) {
|
||||
const bool selected = choice.AssetGuid == textureGuid;
|
||||
if (ImGui::Selectable(choice.Label.c_str(), selected)) {
|
||||
textureGuid = choice.AssetGuid;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("清空")) {
|
||||
if (textureGuid.IsValid()) {
|
||||
textureGuid = MetaCoreAssetGuid{};
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
const bool canLocate = binding.LocateAsset.has_value();
|
||||
ImGui::BeginDisabled(!canLocate);
|
||||
if (ImGui::SmallButton("定位")) {
|
||||
if (binding.LocateAsset.has_value()) {
|
||||
const MetaCoreAssetRecord& textureRecord = *binding.LocateAsset;
|
||||
editorContext.SelectAsset(MetaCoreSelectedAssetState{
|
||||
textureRecord.Guid,
|
||||
textureRecord.RelativePath,
|
||||
textureRecord.Type,
|
||||
textureRecord.StorageKind
|
||||
});
|
||||
if (binding.LocateSubId.empty()) {
|
||||
editorContext.ClearSelectedAssetSubId();
|
||||
} else {
|
||||
editorContext.SetSelectedAssetSubId(binding.LocateSubId);
|
||||
}
|
||||
editorContext.ClearSelection();
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (binding.Missing) {
|
||||
ImGui::TextColored(ImVec4(0.95F, 0.38F, 0.32F, 1.0F), "贴图缺失: %s", textureGuid.ToString().c_str());
|
||||
} else if (!binding.DetailText.empty()) {
|
||||
ImGui::TextDisabled("%s", binding.DetailText.c_str());
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopID();
|
||||
return changed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreDrawMaterialVisualEditor(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService,
|
||||
MetaCoreMaterialAssetDocument& materialDocument,
|
||||
const char* sourceLabel,
|
||||
const std::filesystem::path& displayPath,
|
||||
std::string_view idSuffix
|
||||
) {
|
||||
bool changed = false;
|
||||
const std::string suffix(idSuffix);
|
||||
const auto textureChoices = MetaCoreCollectMaterialTextureChoices(assetDatabaseService);
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
|
||||
ImGui::PushID(suffix.c_str());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0F, 4.0F));
|
||||
ImGui::TextUnformatted(materialDocument.Name.empty() ? "<未命名材质>" : materialDocument.Name.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("%s", sourceLabel);
|
||||
ImGui::TextDisabled("GUID: %s", materialDocument.AssetGuid.ToString().c_str());
|
||||
if (!displayPath.empty()) {
|
||||
ImGui::TextDisabled("路径: %s", displayPath.generic_string().c_str());
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
const float availableWidth = ImGui::GetContentRegionAvail().x;
|
||||
const bool useTwoColumns = availableWidth >= 520.0F;
|
||||
if (useTwoColumns) {
|
||||
ImGui::BeginTable("MaterialVisualLayout", 2, ImGuiTableFlags_SizingStretchProp);
|
||||
ImGui::TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch, 0.62F);
|
||||
ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthStretch, 0.38F);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("基础参数", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
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())) {
|
||||
materialDocument.Name = nameBuffer.data();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const char* shaderItems[] = {"PBR 金属粗糙度"};
|
||||
int shaderIndex = 0;
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Combo("着色模型", &shaderIndex, shaderItems, IM_ARRAYSIZE(shaderItems));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
float baseColor[3] = {materialDocument.BaseColor.r, materialDocument.BaseColor.g, materialDocument.BaseColor.b};
|
||||
if (ImGui::ColorEdit3("基础颜色", baseColor)) {
|
||||
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;
|
||||
}
|
||||
float emissiveColor[3] = {materialDocument.EmissiveColor.r, materialDocument.EmissiveColor.g, materialDocument.EmissiveColor.b};
|
||||
if (ImGui::ColorEdit3("发光颜色", emissiveColor)) {
|
||||
materialDocument.EmissiveColor = {emissiveColor[0], emissiveColor[1], emissiveColor[2]};
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("透明与渲染选项", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
const char* alphaItems[] = {"不透明", "镂空裁剪", "透明混合"};
|
||||
int alphaIndex = static_cast<int>(materialDocument.AlphaMode);
|
||||
if (ImGui::Combo("透明模式", &alphaIndex, alphaItems, IM_ARRAYSIZE(alphaItems))) {
|
||||
materialDocument.AlphaMode = static_cast<MetaCoreMaterialAlphaMode>(std::clamp(alphaIndex, 0, 2));
|
||||
changed = true;
|
||||
}
|
||||
if (materialDocument.AlphaMode == MetaCoreMaterialAlphaMode::Mask) {
|
||||
if (ImGui::SliderFloat("裁剪阈值", &materialDocument.AlphaCutoff, 0.0F, 1.0F, "%.3f")) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (ImGui::Checkbox("双面渲染", &materialDocument.DoubleSided)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("高级信息")) {
|
||||
ImGui::TextDisabled("着色模型: %s", MetaCoreMaterialShaderModelLabel(materialDocument.ShaderModel));
|
||||
ImGui::TextDisabled("透明模式: %s", MetaCoreMaterialAlphaModeLabel(materialDocument.AlphaMode));
|
||||
ImGui::TextDisabled("StableImportKey: %s", materialDocument.StableImportKey.empty() ? "<无>" : materialDocument.StableImportKey.c_str());
|
||||
}
|
||||
|
||||
if (useTwoColumns) {
|
||||
ImGui::TableNextColumn();
|
||||
MetaCoreDrawMaterialPreviewCard(materialDocument);
|
||||
ImGui::EndTable();
|
||||
} else {
|
||||
ImGui::Separator();
|
||||
MetaCoreDrawMaterialPreviewCard(materialDocument);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopID();
|
||||
return changed;
|
||||
}
|
||||
|
||||
void DrawGeneratedMaterialDetails(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreMaterialAssetDocument& selectedMaterial
|
||||
@ -2707,84 +3261,29 @@ void DrawGeneratedMaterialDetails(
|
||||
|
||||
MetaCoreMaterialAssetDocument materialAsset = *loadedMaterial;
|
||||
ImGui::Separator();
|
||||
ImGui::TextDisabled("MATERIAL RESOURCE");
|
||||
ImGui::Text("Name: %s", materialAsset.Name.empty() ? "<Unnamed>" : materialAsset.Name.c_str());
|
||||
ImGui::TextDisabled("Guid: %s", materialAsset.AssetGuid.ToString().c_str());
|
||||
const auto assetDatabaseService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
if (assetDatabaseService == nullptr) {
|
||||
ImGui::TextDisabled("资产数据库不可用。");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto references = FindSceneObjectsUsingMaterial(editorContext, materialAsset.AssetGuid);
|
||||
ImGui::Text("Scene References: %d", static_cast<int>(references.size()));
|
||||
ImGui::TextDisabled("场景引用: %d", static_cast<int>(references.size()));
|
||||
if (!references.empty()) {
|
||||
if (ImGui::Button("Highlight Scene Objects##SelectedMaterial")) {
|
||||
if (ImGui::Button("高亮引用对象##SelectedMaterial")) {
|
||||
editorContext.SetSelection(references, references.front());
|
||||
}
|
||||
DrawReferencedObjectList(editorContext, references, "SelectedMaterialRefs");
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
float baseColor[3] = {materialAsset.BaseColor.x, materialAsset.BaseColor.y, materialAsset.BaseColor.z};
|
||||
if (ImGui::ColorEdit3("Base Color##SelectedMaterial", baseColor)) {
|
||||
materialAsset.BaseColor = {baseColor[0], baseColor[1], baseColor[2]};
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::SliderFloat("Metallic##SelectedMaterial", &materialAsset.Metallic, 0.0F, 1.0F)) {
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::SliderFloat("Roughness##SelectedMaterial", &materialAsset.Roughness, 0.0F, 1.0F)) {
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::Checkbox("Double Sided##SelectedMaterial", &materialAsset.DoubleSided)) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
const char* alphaModeItems[] = {"Opaque", "Mask", "Blend"};
|
||||
int alphaModeIndex = static_cast<int>(materialAsset.AlphaMode);
|
||||
if (ImGui::Combo("Alpha Mode##SelectedMaterial", &alphaModeIndex, alphaModeItems, IM_ARRAYSIZE(alphaModeItems))) {
|
||||
materialAsset.AlphaMode = static_cast<MetaCoreMaterialAlphaMode>(alphaModeIndex);
|
||||
modified = true;
|
||||
}
|
||||
if (materialAsset.AlphaMode == MetaCoreMaterialAlphaMode::Mask) {
|
||||
if (ImGui::SliderFloat("Alpha Cutoff##SelectedMaterial", &materialAsset.AlphaCutoff, 0.0F, 1.0F)) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
const auto textureChoices = MetaCoreCollectTextureAssetChoices(editorContext);
|
||||
MetaCoreAssetGuid baseColorTexture = materialAsset.BaseColorTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo("Base Color Texture##SelectedMaterial", textureChoices, baseColorTexture)) {
|
||||
materialAsset.BaseColorTexture = baseColorTexture;
|
||||
modified = true;
|
||||
}
|
||||
DrawMaterialTextureSlotSummary(editorContext, *assetEditingService, "BaseColor", materialAsset.BaseColorTexture);
|
||||
MetaCoreAssetGuid normalTexture = materialAsset.NormalTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo("Normal Texture##SelectedMaterial", textureChoices, normalTexture)) {
|
||||
materialAsset.NormalTexture = normalTexture;
|
||||
modified = true;
|
||||
}
|
||||
DrawMaterialTextureSlotSummary(editorContext, *assetEditingService, "Normal", materialAsset.NormalTexture);
|
||||
MetaCoreAssetGuid metalRoughTexture = materialAsset.MetallicRoughnessTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo("MetalRough Texture##SelectedMaterial", textureChoices, metalRoughTexture)) {
|
||||
materialAsset.MetallicRoughnessTexture = metalRoughTexture;
|
||||
modified = true;
|
||||
}
|
||||
DrawMaterialTextureSlotSummary(editorContext, *assetEditingService, "MetalRough", materialAsset.MetallicRoughnessTexture);
|
||||
MetaCoreAssetGuid emissiveTexture = materialAsset.EmissiveTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo("Emissive Texture##SelectedMaterial", textureChoices, emissiveTexture)) {
|
||||
materialAsset.EmissiveTexture = emissiveTexture;
|
||||
modified = true;
|
||||
}
|
||||
DrawMaterialTextureSlotSummary(editorContext, *assetEditingService, "Emissive", materialAsset.EmissiveTexture);
|
||||
MetaCoreAssetGuid aoTexture = materialAsset.AoTexture;
|
||||
if (MetaCoreDrawGeneratedAssetCombo("AO Texture##SelectedMaterial", textureChoices, aoTexture)) {
|
||||
materialAsset.AoTexture = aoTexture;
|
||||
modified = true;
|
||||
}
|
||||
DrawMaterialTextureSlotSummary(editorContext, *assetEditingService, "AO", materialAsset.AoTexture);
|
||||
|
||||
float emissiveColor[3] = {materialAsset.EmissiveColor.x, materialAsset.EmissiveColor.y, materialAsset.EmissiveColor.z};
|
||||
if (ImGui::ColorEdit3("Emissive Color##SelectedMaterial", emissiveColor)) {
|
||||
materialAsset.EmissiveColor = {emissiveColor[0], emissiveColor[1], emissiveColor[2]};
|
||||
modified = true;
|
||||
}
|
||||
const bool modified = MetaCoreDrawMaterialVisualEditor(
|
||||
editorContext,
|
||||
*assetDatabaseService,
|
||||
materialAsset,
|
||||
"模型生成材质",
|
||||
resolvedMaterial->SourceAsset.RelativePath,
|
||||
"GeneratedMaterial"
|
||||
);
|
||||
|
||||
if (modified) {
|
||||
if (assetEditingService->SaveMaterialAsset(materialAsset.AssetGuid, materialAsset)) {
|
||||
@ -2792,7 +3291,7 @@ void DrawGeneratedMaterialDetails(
|
||||
modelDocument.has_value()) {
|
||||
MetaCoreApplyMaterialPreviewToScene(editorContext, *modelDocument);
|
||||
}
|
||||
ImGui::TextColored(ImVec4(0.32F, 0.85F, 0.42F, 1.0F), "已保存材质资源修改");
|
||||
ImGui::TextColored(ImVec4(0.32F, 0.85F, 0.42F, 1.0F), "已自动保存材质资源");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.32F, 0.28F, 1.0F), "材质资源保存失败");
|
||||
}
|
||||
@ -3912,107 +4411,41 @@ void DrawMaterialAssetDetails(
|
||||
MetaCoreIAssetDatabaseService& assetDatabaseService,
|
||||
MetaCoreIReflectionRegistry& reflectionRegistry
|
||||
) {
|
||||
(void)reflectionRegistry;
|
||||
const MetaCoreSelectedAssetState& selectedAsset = editorContext.GetSelectedAsset();
|
||||
const auto assetRecord = assetDatabaseService.FindAssetByGuid(selectedAsset.Guid);
|
||||
if (!assetRecord.has_value()) return;
|
||||
|
||||
const std::filesystem::path absolutePath = assetDatabaseService.GetProjectDescriptor().RootPath / assetRecord->RelativePath;
|
||||
|
||||
auto materialDocOpt = MetaCoreSceneSerializer::LoadMaterialFromJson(absolutePath, reflectionRegistry.GetTypeRegistry());
|
||||
if (!materialDocOpt.has_value()) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "加载材质失败!");
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
if (assetEditingService == nullptr) {
|
||||
ImGui::TextColored(ImVec4(1.0F, 0.4F, 0.4F, 1.0F), "材质编辑服务不可用。");
|
||||
return;
|
||||
}
|
||||
MetaCoreMaterialAssetDocument materialDoc = std::move(*materialDocOpt);
|
||||
auto materialDocOpt = assetEditingService->LoadMaterialAsset(selectedAsset.Guid);
|
||||
if (!materialDocOpt.has_value()) {
|
||||
ImGui::TextColored(ImVec4(1.0F, 0.4F, 0.4F, 1.0F), "加载材质失败。");
|
||||
return;
|
||||
}
|
||||
MetaCoreMaterialAssetDocument materialDoc = *materialDocOpt;
|
||||
if (!materialDoc.AssetGuid.IsValid()) {
|
||||
materialDoc.AssetGuid = selectedAsset.Guid;
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 4));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F));
|
||||
if (ImGui::BeginChild("MaterialInspector", ImVec2(0, 0), true, 0)) {
|
||||
ImGui::TextDisabled("MATERIAL ASSET");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
bool changed = false;
|
||||
|
||||
float color[3] = { materialDoc.BaseColor.r, materialDoc.BaseColor.g, materialDoc.BaseColor.b };
|
||||
if (ImGui::ColorEdit3("基础颜色 (Base Color)", color)) {
|
||||
materialDoc.BaseColor = glm::vec3(color[0], color[1], color[2]);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
float metallic = materialDoc.Metallic;
|
||||
if (ImGui::SliderFloat("金属度 (Metallic)", &metallic, 0.0f, 1.0f)) {
|
||||
materialDoc.Metallic = metallic;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
float roughness = materialDoc.Roughness;
|
||||
if (ImGui::SliderFloat("粗糙度 (Roughness)", &roughness, 0.0f, 1.0f)) {
|
||||
materialDoc.Roughness = roughness;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Text("贴图绑定 (Textures)");
|
||||
|
||||
auto drawTextureCombo = [&](const char* label, MetaCoreAssetGuid& currentGuid) {
|
||||
std::string previewName = "None";
|
||||
if (currentGuid.IsValid()) {
|
||||
const auto record = assetDatabaseService.FindAssetByGuid(currentGuid);
|
||||
if (record.has_value()) {
|
||||
previewName = record->RelativePath.filename().string();
|
||||
} else {
|
||||
previewName = "Missing (" + currentGuid.ToString() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo(label, previewName.c_str())) {
|
||||
if (ImGui::Selectable("None", !currentGuid.IsValid())) {
|
||||
if (currentGuid.IsValid()) {
|
||||
currentGuid = MetaCoreAssetGuid{};
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& a : assetDatabaseService.GetAssetRegistry()) {
|
||||
if (a.Type == "texture") {
|
||||
const bool selected = (currentGuid == a.Guid);
|
||||
const std::string name = a.RelativePath.filename().string();
|
||||
if (ImGui::Selectable(name.c_str(), selected)) {
|
||||
if (currentGuid != a.Guid) {
|
||||
currentGuid = a.Guid;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
};
|
||||
|
||||
drawTextureCombo("基础颜色贴图 (Base Color Texture)", materialDoc.BaseColorTexture);
|
||||
drawTextureCombo("法线贴图 (Normal Texture)", materialDoc.NormalTexture);
|
||||
drawTextureCombo("金属粗糙度贴图 (Metallic Roughness)", materialDoc.MetallicRoughnessTexture);
|
||||
drawTextureCombo("遮蔽贴图 (Occlusion/Ao Texture)", materialDoc.AoTexture);
|
||||
|
||||
const bool changed = MetaCoreDrawMaterialVisualEditor(
|
||||
editorContext,
|
||||
assetDatabaseService,
|
||||
materialDoc,
|
||||
"独立材质",
|
||||
assetRecord->RelativePath,
|
||||
"StandaloneMaterial"
|
||||
);
|
||||
if (changed) {
|
||||
if (MetaCoreSceneSerializer::SaveMaterialToJson(absolutePath, materialDoc, reflectionRegistry.GetTypeRegistry())) {
|
||||
const std::filesystem::path metaPath = assetDatabaseService.GetProjectDescriptor().RootPath / MetaCoreBuildMetaPath(assetRecord->RelativePath);
|
||||
const std::uint64_t newHash = MetaCoreHashFile(absolutePath).value_or(0);
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists(metaPath, ec)) {
|
||||
try {
|
||||
std::ifstream input(metaPath);
|
||||
nlohmann::json json;
|
||||
input >> json;
|
||||
input.close();
|
||||
json["source_hash"] = newHash;
|
||||
std::ofstream output(metaPath);
|
||||
output << json.dump(4);
|
||||
output.close();
|
||||
} catch (...) {}
|
||||
}
|
||||
assetDatabaseService.Refresh();
|
||||
if (assetEditingService->SaveMaterialAsset(selectedAsset.Guid, materialDoc)) {
|
||||
MetaCoreApplyStandaloneMaterialPreviewToScene(editorContext, assetDatabaseService, materialDoc);
|
||||
ImGui::TextColored(ImVec4(0.32F, 0.85F, 0.42F, 1.0F), "已自动保存材质资源");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.32F, 0.28F, 1.0F), "材质资源保存失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
||||
#include "MetaCoreRender/MetaCoreImGuiHelper.h"
|
||||
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
|
||||
#include "stb/stb_image.h"
|
||||
#include <imgui.h>
|
||||
#include "MetaCoreFoundation/MetaCoreAssetRegistry.h"
|
||||
#include "MetaCoreFoundation/MetaCoreRuntimeAssetRegistry.h"
|
||||
@ -21,7 +22,9 @@
|
||||
#include <filament/RenderTarget.h>
|
||||
#include <filament/LightManager.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
@ -306,6 +309,7 @@ public:
|
||||
}
|
||||
}
|
||||
AssetDuplicatedMaterials_.clear();
|
||||
DestroyMaterialTextureCache();
|
||||
|
||||
for (auto& [id, entity] : SceneLightEntities_) {
|
||||
if (Scene_) {
|
||||
@ -391,9 +395,121 @@ public:
|
||||
}
|
||||
|
||||
void SetProjectRootPath(const std::filesystem::path& projectRootPath) {
|
||||
if (ProjectRootPath_ != projectRootPath) {
|
||||
DestroyMaterialTextureCache();
|
||||
}
|
||||
ProjectRootPath_ = projectRootPath;
|
||||
}
|
||||
|
||||
void DestroyMaterialTextureCache() {
|
||||
if (Engine_) {
|
||||
Engine_->flushAndWait();
|
||||
for (auto& [_, texture] : MaterialTextureCache_) {
|
||||
if (texture != nullptr) {
|
||||
Engine_->destroy(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
MaterialTextureCache_.clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::filesystem::path ResolveMaterialTexturePath(
|
||||
const std::string& texturePath,
|
||||
const std::string& sourceModelPath
|
||||
) const {
|
||||
if (texturePath.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::filesystem::path rawPath(texturePath);
|
||||
if (rawPath.is_absolute()) {
|
||||
return std::filesystem::exists(rawPath) ? rawPath : std::filesystem::path{};
|
||||
}
|
||||
|
||||
const std::filesystem::path projectRelativePath = ProjectRootPath_ / rawPath;
|
||||
if (std::filesystem::exists(projectRelativePath)) {
|
||||
return projectRelativePath;
|
||||
}
|
||||
|
||||
if (!sourceModelPath.empty()) {
|
||||
const std::filesystem::path modelRelativePath(sourceModelPath);
|
||||
const std::filesystem::path modelDirectory = modelRelativePath.parent_path();
|
||||
if (!modelDirectory.empty()) {
|
||||
const std::filesystem::path modelRelativeTexturePath = ProjectRootPath_ / modelDirectory / rawPath;
|
||||
if (std::filesystem::exists(modelRelativeTexturePath)) {
|
||||
return modelRelativeTexturePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::filesystem::path assetsRelativePath = ProjectRootPath_ / "Assets" / rawPath;
|
||||
if (std::filesystem::exists(assetsRelativePath)) {
|
||||
return assetsRelativePath;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] filament::Texture* LoadMaterialTexture(
|
||||
const std::string& texturePath,
|
||||
const std::string& sourceModelPath,
|
||||
bool srgb
|
||||
) {
|
||||
if (Engine_ == nullptr || texturePath.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::filesystem::path absolutePath = ResolveMaterialTexturePath(texturePath, sourceModelPath);
|
||||
if (absolutePath.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string cacheKey = absolutePath.lexically_normal().generic_string() + (srgb ? "|srgb" : "|linear");
|
||||
if (const auto cacheIterator = MaterialTextureCache_.find(cacheKey);
|
||||
cacheIterator != MaterialTextureCache_.end()) {
|
||||
return cacheIterator->second;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (pixels != nullptr) {
|
||||
stbi_image_free(pixels);
|
||||
}
|
||||
MetaCoreFilamentDebugLog("Material texture load failed path=\"" + absolutePath.generic_string() + "\"");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::size_t byteSize = static_cast<std::size_t>(width) * static_cast<std::size_t>(height) * 4U;
|
||||
filament::Texture::PixelBufferDescriptor pixelBuffer(
|
||||
pixels,
|
||||
byteSize,
|
||||
filament::Texture::Format::RGBA,
|
||||
filament::Texture::Type::UBYTE,
|
||||
[](void* buffer, size_t, void*) {
|
||||
stbi_image_free(buffer);
|
||||
}
|
||||
);
|
||||
|
||||
filament::Texture* texture = filament::Texture::Builder()
|
||||
.width(static_cast<std::uint32_t>(width))
|
||||
.height(static_cast<std::uint32_t>(height))
|
||||
.levels(1)
|
||||
.sampler(filament::Texture::Sampler::SAMPLER_2D)
|
||||
.format(srgb ? filament::Texture::InternalFormat::SRGB8_A8 : filament::Texture::InternalFormat::RGBA8)
|
||||
.build(*Engine_);
|
||||
|
||||
if (texture == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
texture->setImage(*Engine_, 0, std::move(pixelBuffer));
|
||||
MaterialTextureCache_[cacheKey] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
void ParseModelHierarchyToEcs(MetaCoreScene& scene, MetaCoreGameObject& parentObject, filament::gltfio::FilamentAsset* asset) {
|
||||
if (!asset) return;
|
||||
|
||||
@ -1015,36 +1131,124 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 同步所有存活网格的材质属性,例如 BaseColor
|
||||
const auto setFloatParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, float value) {
|
||||
const filament::Material* material = materialInstance.getMaterial();
|
||||
if (material != nullptr && material->hasParameter(name)) {
|
||||
materialInstance.setParameter(name, value);
|
||||
}
|
||||
};
|
||||
const auto setFloat3ParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, filament::math::float3 value) {
|
||||
const filament::Material* material = materialInstance.getMaterial();
|
||||
if (material != nullptr && material->hasParameter(name)) {
|
||||
materialInstance.setParameter(name, value);
|
||||
}
|
||||
};
|
||||
const auto setFloat4ParameterIfPresent = [](filament::MaterialInstance& materialInstance, const char* name, filament::math::float4 value) {
|
||||
const filament::Material* material = materialInstance.getMaterial();
|
||||
if (material != nullptr && material->hasParameter(name)) {
|
||||
materialInstance.setParameter(name, value);
|
||||
}
|
||||
};
|
||||
const auto setTextureParameterIfPresent = [](
|
||||
filament::MaterialInstance& materialInstance,
|
||||
const char* name,
|
||||
filament::Texture* texture,
|
||||
const filament::TextureSampler& sampler
|
||||
) -> bool {
|
||||
const filament::Material* material = materialInstance.getMaterial();
|
||||
if (material == nullptr || !material->hasParameter(name)) {
|
||||
return false;
|
||||
}
|
||||
materialInstance.setParameter(name, texture, sampler);
|
||||
return true;
|
||||
};
|
||||
|
||||
// 5. 同步所有存活网格的材质预览属性。MaterialInstance 参数必须在渲染前更新。
|
||||
auto& rm = Engine_->getRenderableManager();
|
||||
const filament::TextureSampler materialSampler(
|
||||
filament::TextureSampler::MinFilter::LINEAR,
|
||||
filament::TextureSampler::MagFilter::LINEAR,
|
||||
filament::TextureSampler::WrapMode::REPEAT
|
||||
);
|
||||
for (const auto& renderable : snapshot.Renderables) {
|
||||
auto itEntity = ObjectToFilamentEntity_.find(renderable.ObjectId);
|
||||
if (itEntity == ObjectToFilamentEntity_.end()) {
|
||||
continue;
|
||||
}
|
||||
utils::Entity entity = itEntity->second.second;
|
||||
if (!entity) {
|
||||
std::vector<utils::Entity> materialTargetEntities;
|
||||
CollectRenderableDescendants(itEntity->second.first, itEntity->second.second, materialTargetEntities);
|
||||
if (materialTargetEntities.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto instance = rm.getInstance(entity);
|
||||
if (!instance) {
|
||||
continue;
|
||||
}
|
||||
filament::Texture* baseColorTexture = LoadMaterialTexture(
|
||||
renderable.BaseColorTexturePath,
|
||||
renderable.SourceModelPath,
|
||||
true
|
||||
);
|
||||
filament::Texture* metallicRoughnessTexture = LoadMaterialTexture(
|
||||
renderable.MetallicRoughnessTexturePath,
|
||||
renderable.SourceModelPath,
|
||||
false
|
||||
);
|
||||
filament::Texture* normalTexture = LoadMaterialTexture(
|
||||
renderable.NormalTexturePath,
|
||||
renderable.SourceModelPath,
|
||||
false
|
||||
);
|
||||
filament::Texture* emissiveTexture = LoadMaterialTexture(
|
||||
renderable.EmissiveTexturePath,
|
||||
renderable.SourceModelPath,
|
||||
true
|
||||
);
|
||||
filament::Texture* aoTexture = LoadMaterialTexture(
|
||||
renderable.AoTexturePath,
|
||||
renderable.SourceModelPath,
|
||||
false
|
||||
);
|
||||
|
||||
// 遍历所有 primitive,获取材质实例,并设置其 baseColorFactor 属性
|
||||
size_t primCount = rm.getPrimitiveCount(instance);
|
||||
for (size_t primIndex = 0; primIndex < primCount; ++primIndex) {
|
||||
filament::MaterialInstance* matInst = rm.getMaterialInstanceAt(instance, primIndex);
|
||||
if (matInst) {
|
||||
// BaseColor 包含 RGB,以 float4 传递,Alpha 设为 1.0
|
||||
filament::math::float4 baseColorVec{
|
||||
renderable.BaseColor.x,
|
||||
renderable.BaseColor.y,
|
||||
renderable.BaseColor.z,
|
||||
1.0f
|
||||
};
|
||||
matInst->setParameter("baseColorFactor", baseColorVec);
|
||||
for (utils::Entity materialTargetEntity : materialTargetEntities) {
|
||||
const auto instance = rm.getInstance(materialTargetEntity);
|
||||
if (!instance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 遍历所有 primitive,获取材质实例,并设置材质预览属性。
|
||||
const size_t primCount = rm.getPrimitiveCount(instance);
|
||||
for (size_t primIndex = 0; primIndex < primCount; ++primIndex) {
|
||||
filament::MaterialInstance* matInst = rm.getMaterialInstanceAt(instance, primIndex);
|
||||
if (matInst) {
|
||||
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(
|
||||
*matInst,
|
||||
"emissiveFactor",
|
||||
filament::math::float3{
|
||||
renderable.EmissiveColor.x,
|
||||
renderable.EmissiveColor.y,
|
||||
renderable.EmissiveColor.z
|
||||
}
|
||||
);
|
||||
if (renderable.AlphaMode == MetaCoreMeshAlphaMode::Mask) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1420,6 +1624,60 @@ private:
|
||||
return parentToChildren;
|
||||
}
|
||||
|
||||
void CollectRenderableDescendants(
|
||||
filament::gltfio::FilamentAsset* asset,
|
||||
utils::Entity rootEntity,
|
||||
std::vector<utils::Entity>& outEntities
|
||||
) const {
|
||||
if (!Engine_ || rootEntity.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& rm = Engine_->getRenderableManager();
|
||||
auto& tm = Engine_->getTransformManager();
|
||||
const auto addRenderable = [&](utils::Entity entity) {
|
||||
if (entity.isNull() || !rm.getInstance(entity)) {
|
||||
return;
|
||||
}
|
||||
if (std::find(outEntities.begin(), outEntities.end(), entity) == outEntities.end()) {
|
||||
outEntities.push_back(entity);
|
||||
}
|
||||
};
|
||||
|
||||
addRenderable(rootEntity);
|
||||
if (asset == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const utils::Entity* entities = asset->getEntities();
|
||||
const size_t entityCount = asset->getEntityCount();
|
||||
for (size_t i = 0; i < entityCount; ++i) {
|
||||
utils::Entity candidate = entities[i];
|
||||
if (candidate.isNull() || candidate == rootEntity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
utils::Entity current = candidate;
|
||||
bool isDescendant = false;
|
||||
while (!current.isNull()) {
|
||||
if (current == rootEntity) {
|
||||
isDescendant = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto transformInstance = tm.getInstance(current);
|
||||
if (!transformInstance) {
|
||||
break;
|
||||
}
|
||||
current = tm.getParent(transformInstance);
|
||||
}
|
||||
|
||||
if (isDescendant) {
|
||||
addRenderable(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static filament::math::float3 ToFilamentFloat3(const glm::vec3& value) {
|
||||
return filament::math::float3{ value.x, value.y, value.z };
|
||||
}
|
||||
@ -1565,6 +1823,7 @@ private:
|
||||
std::unordered_map<MetaCoreId, MetaCoreAssetGuid> HostRootSourceModelGuids_;
|
||||
std::unordered_set<MetaCoreAssetGuid, MetaCoreAssetGuidHasher> InvalidatedModelGuids_;
|
||||
std::unordered_map<MetaCoreId, std::vector<filament::MaterialInstance*>> AssetDuplicatedMaterials_;
|
||||
std::unordered_map<std::string, filament::Texture*> MaterialTextureCache_;
|
||||
std::unordered_map<MetaCoreId, utils::Entity> SceneLightEntities_;
|
||||
std::unordered_set<utils::Entity, utils::Entity::Hasher> EntitiesInScene_;
|
||||
|
||||
|
||||
@ -180,7 +180,18 @@ MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const Met
|
||||
hostRoot.GetId(),
|
||||
hostRoot.GetName(),
|
||||
hostRoot.HasComponent<MetaCoreModelRootTag>(),
|
||||
meshRenderer.BaseColor
|
||||
meshRenderer.BaseColor,
|
||||
meshRenderer.Metallic,
|
||||
meshRenderer.Roughness,
|
||||
meshRenderer.EmissiveColor,
|
||||
meshRenderer.AlphaMode,
|
||||
meshRenderer.AlphaCutoff,
|
||||
meshRenderer.DoubleSided,
|
||||
meshRenderer.BaseColorTexturePath,
|
||||
meshRenderer.MetallicRoughnessTexturePath,
|
||||
meshRenderer.NormalTexturePath,
|
||||
meshRenderer.EmissiveTexturePath,
|
||||
meshRenderer.AoTexturePath
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,17 @@ struct MetaCoreRenderSyncRenderable {
|
||||
std::string HostRootName{};
|
||||
bool HostRootHasModelRootTag = false;
|
||||
glm::vec3 BaseColor{1.0F, 1.0F, 1.0F};
|
||||
float Metallic = 0.0F;
|
||||
float Roughness = 1.0F;
|
||||
glm::vec3 EmissiveColor{0.0F, 0.0F, 0.0F};
|
||||
MetaCoreMeshAlphaMode AlphaMode = MetaCoreMeshAlphaMode::Opaque;
|
||||
float AlphaCutoff = 0.5F;
|
||||
bool DoubleSided = true;
|
||||
std::string BaseColorTexturePath{};
|
||||
std::string MetallicRoughnessTexturePath{};
|
||||
std::string NormalTexturePath{};
|
||||
std::string EmissiveTexturePath{};
|
||||
std::string AoTexturePath{};
|
||||
};
|
||||
|
||||
struct MetaCoreRenderSyncCamera {
|
||||
|
||||
@ -652,6 +652,44 @@ void MetaCoreTestProjectAssetDatabaseOperations() {
|
||||
MetaCoreExpect(materialRecord.has_value(), "材质应进入资产数据库");
|
||||
MetaCoreExpect(materialRecord->Guid.IsValid(), "材质 GUID 应有效");
|
||||
MetaCoreExpect(materialRecord->Type == "material", "材质资产类型应正确");
|
||||
const auto assetEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetEditingService>();
|
||||
MetaCoreExpect(assetEditingService != nullptr, "应解析到 AssetEditingService");
|
||||
const std::filesystem::path materialMetaPath = tempProjectRoot / (materialPath.generic_string() + ".mcmeta");
|
||||
nlohmann::json oldMaterialMeta;
|
||||
{
|
||||
std::ifstream input(materialMetaPath);
|
||||
input >> oldMaterialMeta;
|
||||
}
|
||||
const std::uint64_t oldMaterialHash = oldMaterialMeta.value("source_hash", 0ULL);
|
||||
auto editableMaterial = assetEditingService->LoadMaterialAsset(materialRecord->Guid);
|
||||
MetaCoreExpect(editableMaterial.has_value(), "AssetEditingService 应能加载独立材质");
|
||||
editableMaterial->Name = "PanelMaterialEdited";
|
||||
editableMaterial->BaseColor = glm::vec3(0.12F, 0.34F, 0.56F);
|
||||
editableMaterial->Metallic = 0.42F;
|
||||
editableMaterial->Roughness = 0.68F;
|
||||
editableMaterial->DoubleSided = true;
|
||||
editableMaterial->AlphaMode = MetaCore::MetaCoreMaterialAlphaMode::Mask;
|
||||
editableMaterial->AlphaCutoff = 0.31F;
|
||||
editableMaterial->EmissiveColor = glm::vec3(0.03F, 0.04F, 0.05F);
|
||||
MetaCoreExpect(assetEditingService->SaveMaterialAsset(materialRecord->Guid, *editableMaterial), "AssetEditingService 应能保存独立材质");
|
||||
const auto reloadedMaterial = assetEditingService->LoadMaterialAsset(materialRecord->Guid);
|
||||
MetaCoreExpect(reloadedMaterial.has_value(), "保存后应能重新加载独立材质");
|
||||
MetaCoreExpect(reloadedMaterial->Name == "PanelMaterialEdited", "材质名称应 round-trip");
|
||||
MetaCoreExpectVec3Near(reloadedMaterial->BaseColor, glm::vec3(0.12F, 0.34F, 0.56F), "材质 BaseColor 应 round-trip");
|
||||
MetaCoreExpect(std::abs(reloadedMaterial->Metallic - 0.42F) < 0.0001F, "材质 Metallic 应 round-trip");
|
||||
MetaCoreExpect(std::abs(reloadedMaterial->Roughness - 0.68F) < 0.0001F, "材质 Roughness 应 round-trip");
|
||||
MetaCoreExpect(reloadedMaterial->DoubleSided, "材质 DoubleSided 应 round-trip");
|
||||
MetaCoreExpect(reloadedMaterial->AlphaMode == MetaCore::MetaCoreMaterialAlphaMode::Mask, "材质 AlphaMode 应 round-trip");
|
||||
MetaCoreExpect(std::abs(reloadedMaterial->AlphaCutoff - 0.31F) < 0.0001F, "材质 AlphaCutoff 应 round-trip");
|
||||
nlohmann::json newMaterialMeta;
|
||||
{
|
||||
std::ifstream input(materialMetaPath);
|
||||
input >> newMaterialMeta;
|
||||
}
|
||||
const std::uint64_t newMaterialHash = newMaterialMeta.value("source_hash", 0ULL);
|
||||
MetaCoreExpect(newMaterialHash != 0ULL, "保存材质后 mcmeta source_hash 应有效");
|
||||
MetaCoreExpect(newMaterialHash != oldMaterialHash, "保存材质后 mcmeta source_hash 应更新");
|
||||
MetaCoreExpect(newMaterialHash == MetaCore::MetaCoreHashFile(tempProjectRoot / materialPath).value_or(0), "mcmeta source_hash 应匹配材质文件 hash");
|
||||
|
||||
const std::filesystem::path scenePath = std::filesystem::path("Scenes") / "PanelScene.mcscene.json";
|
||||
MetaCoreExpect(assetDatabase->CreateEmptySceneAsset(scenePath, true), "应能创建空场景资源");
|
||||
@ -1268,6 +1306,17 @@ void MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot() {
|
||||
rootMesh.MeshSource = MetaCore::MetaCoreMeshSourceKind::Asset;
|
||||
rootMesh.SourceModelPath = "Assets/Models/SyncRoot.glb";
|
||||
rootMesh.BaseColor = glm::vec3(0.5F, 0.6F, 0.7F);
|
||||
rootMesh.Metallic = 0.35F;
|
||||
rootMesh.Roughness = 0.45F;
|
||||
rootMesh.EmissiveColor = glm::vec3(0.05F, 0.06F, 0.07F);
|
||||
rootMesh.AlphaMode = MetaCore::MetaCoreMeshAlphaMode::Mask;
|
||||
rootMesh.AlphaCutoff = 0.42F;
|
||||
rootMesh.DoubleSided = false;
|
||||
rootMesh.BaseColorTexturePath = "Assets/Textures/SyncBaseColor.png";
|
||||
rootMesh.MetallicRoughnessTexturePath = "Assets/Textures/SyncMetallicRoughness.png";
|
||||
rootMesh.NormalTexturePath = "Assets/Textures/SyncNormal.png";
|
||||
rootMesh.EmissiveTexturePath = "Assets/Textures/SyncEmissive.png";
|
||||
rootMesh.AoTexturePath = "Assets/Textures/SyncAo.png";
|
||||
|
||||
MetaCore::MetaCoreGameObject child = scene.CreateGameObject("RenderChild", root.GetId());
|
||||
child.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(4.0F, 5.0F, 6.0F);
|
||||
@ -1300,6 +1349,17 @@ void MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot() {
|
||||
if (renderable.ObjectId == root.GetId()) {
|
||||
MetaCoreExpect(renderable.Visible == true, "RenderSync 根对象 Visible 应为 true");
|
||||
MetaCoreExpectVec3Near(renderable.BaseColor, glm::vec3(0.5F, 0.6F, 0.7F), "RenderSync 根对象 BaseColor 应正确");
|
||||
MetaCoreExpect(std::abs(renderable.Metallic - 0.35F) < 0.0001F, "RenderSync 根对象 Metallic 应正确");
|
||||
MetaCoreExpect(std::abs(renderable.Roughness - 0.45F) < 0.0001F, "RenderSync 根对象 Roughness 应正确");
|
||||
MetaCoreExpectVec3Near(renderable.EmissiveColor, glm::vec3(0.05F, 0.06F, 0.07F), "RenderSync 根对象 EmissiveColor 应正确");
|
||||
MetaCoreExpect(renderable.AlphaMode == MetaCore::MetaCoreMeshAlphaMode::Mask, "RenderSync 根对象 AlphaMode 应正确");
|
||||
MetaCoreExpect(std::abs(renderable.AlphaCutoff - 0.42F) < 0.0001F, "RenderSync 根对象 AlphaCutoff 应正确");
|
||||
MetaCoreExpect(renderable.DoubleSided == false, "RenderSync 根对象 DoubleSided 应正确");
|
||||
MetaCoreExpect(renderable.BaseColorTexturePath == "Assets/Textures/SyncBaseColor.png", "RenderSync 根对象 BaseColor 贴图路径应正确");
|
||||
MetaCoreExpect(renderable.MetallicRoughnessTexturePath == "Assets/Textures/SyncMetallicRoughness.png", "RenderSync 根对象金属粗糙贴图路径应正确");
|
||||
MetaCoreExpect(renderable.NormalTexturePath == "Assets/Textures/SyncNormal.png", "RenderSync 根对象法线贴图路径应正确");
|
||||
MetaCoreExpect(renderable.EmissiveTexturePath == "Assets/Textures/SyncEmissive.png", "RenderSync 根对象自发光贴图路径应正确");
|
||||
MetaCoreExpect(renderable.AoTexturePath == "Assets/Textures/SyncAo.png", "RenderSync 根对象 AO 贴图路径应正确");
|
||||
} else if (renderable.ObjectId == child.GetId()) {
|
||||
MetaCoreExpect(renderable.Visible == false, "RenderSync 子对象 Visible 应为 false");
|
||||
MetaCoreExpectVec3Near(renderable.BaseColor, glm::vec3(0.1F, 0.2F, 0.3F), "RenderSync 子对象 BaseColor 应正确");
|
||||
@ -1903,10 +1963,12 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
|
||||
const auto sceneEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreISceneEditingService>();
|
||||
const auto packageService = moduleRegistry.ResolveService<MetaCore::MetaCoreIPackageService>();
|
||||
const auto reflectionRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIReflectionRegistry>();
|
||||
const auto assetEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetEditingService>();
|
||||
MetaCoreExpect(assetDatabase != nullptr, "应解析到 AssetDatabaseService");
|
||||
MetaCoreExpect(sceneEditingService != nullptr, "应解析到 SceneEditingService");
|
||||
MetaCoreExpect(packageService != nullptr, "应解析到 PackageService");
|
||||
MetaCoreExpect(reflectionRegistry != nullptr, "应解析到 ReflectionRegistry");
|
||||
MetaCoreExpect(assetEditingService != nullptr, "应解析到 AssetEditingService");
|
||||
|
||||
const auto modelRecord = assetDatabase->FindAssetByRelativePath(std::filesystem::path("Assets") / "Assembly.gltf");
|
||||
MetaCoreExpect(modelRecord.has_value(), "应能找到导入后的多节点模型资源");
|
||||
@ -1985,6 +2047,36 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
|
||||
packageService->WritePackage(tempProjectRoot / modelRecord->PackagePath, std::move(package)),
|
||||
"应能写入多节点模型资源包"
|
||||
);
|
||||
MetaCoreExpect(assetDatabase->Refresh(), "写入模型包后应能刷新资产数据库");
|
||||
|
||||
{
|
||||
MetaCore::MetaCoreMaterialAssetDocument generatedMaterial = materialAsset;
|
||||
const MetaCore::MetaCoreAssetGuid generatedMaterialGuid = generatedMaterial.AssetGuid;
|
||||
generatedMaterial.Name = "GeneratedMaterialEdited";
|
||||
generatedMaterial.BaseColor = glm::vec3(0.8F, 0.2F, 0.1F);
|
||||
generatedMaterial.Metallic = 0.77F;
|
||||
generatedMaterial.Roughness = 0.22F;
|
||||
generatedMaterial.DoubleSided = true;
|
||||
MetaCoreExpect(assetEditingService->SaveMaterialAsset(generatedMaterialGuid, generatedMaterial), "应能保存模型生成材质");
|
||||
const auto reloadedGeneratedMaterial = assetEditingService->LoadMaterialAsset(generatedMaterialGuid);
|
||||
MetaCoreExpect(reloadedGeneratedMaterial.has_value(), "保存后应能重新加载模型生成材质");
|
||||
MetaCoreExpect(reloadedGeneratedMaterial->AssetGuid == generatedMaterialGuid, "保存模型生成材质应保持 GUID 不变");
|
||||
MetaCoreExpect(reloadedGeneratedMaterial->Name == "GeneratedMaterialEdited", "模型生成材质名称应写回");
|
||||
MetaCoreExpectVec3Near(reloadedGeneratedMaterial->BaseColor, glm::vec3(0.8F, 0.2F, 0.1F), "模型生成材质 BaseColor 应写回");
|
||||
MetaCoreExpect(std::abs(reloadedGeneratedMaterial->Metallic - 0.77F) < 0.0001F, "模型生成材质 Metallic 应写回");
|
||||
MetaCoreExpect(std::abs(reloadedGeneratedMaterial->Roughness - 0.22F) < 0.0001F, "模型生成材质 Roughness 应写回");
|
||||
const auto reloadedModelAfterMaterialEdit = assetEditingService->LoadModelAsset(modelRecord->Guid);
|
||||
MetaCoreExpect(reloadedModelAfterMaterialEdit.has_value(), "保存生成材质后应能重新加载源模型");
|
||||
const auto generatedMaterialIterator = std::find_if(
|
||||
reloadedModelAfterMaterialEdit->GeneratedMaterialAssets.begin(),
|
||||
reloadedModelAfterMaterialEdit->GeneratedMaterialAssets.end(),
|
||||
[&](const MetaCore::MetaCoreMaterialAssetDocument& material) {
|
||||
return material.AssetGuid == generatedMaterialGuid;
|
||||
}
|
||||
);
|
||||
MetaCoreExpect(generatedMaterialIterator != reloadedModelAfterMaterialEdit->GeneratedMaterialAssets.end(), "源模型中应保留已编辑的生成材质");
|
||||
MetaCoreExpect(generatedMaterialIterator->Name == "GeneratedMaterialEdited", "源模型生成材质文档应更新");
|
||||
}
|
||||
|
||||
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
|
||||
MetaCoreExpect(instantiatedRootId.has_value(), "应能将多节点模型实例化到场景");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user