Split mesh artifacts into material and texture artifacts

This commit is contained in:
2026-04-02 19:36:16 +08:00
parent b2d0570b1b
commit e30f5d5ffa
12 changed files with 939 additions and 135 deletions

View File

@@ -2,7 +2,9 @@
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Debug/Logger.h>
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include <XCEngine/Resources/Mesh/MeshLoader.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <XCEngine/Resources/Texture/TextureLoader.h>
#include <algorithm>
@@ -39,6 +41,13 @@ Containers::String ToContainersString(const std::string& value) {
return Containers::String(value.c_str());
}
Containers::String NormalizeArtifactPathString(const Containers::String& path) {
if (path.Empty()) {
return Containers::String();
}
return ToContainersString(fs::path(path.CStr()).lexically_normal().generic_string());
}
std::string TrimCopy(const std::string& text) {
const auto begin = std::find_if_not(text.begin(), text.end(), [](unsigned char ch) {
return std::isspace(ch) != 0;
@@ -200,11 +209,45 @@ std::vector<MaterialProperty> GatherMaterialProperties(const Material& material)
return material.GetProperties();
}
void WriteMaterialBlock(std::ofstream& output,
const Material& material,
const std::unordered_map<const Texture*, std::string>& textureFileNames) {
Containers::String ResolveTextureBindingPath(
const Material& material,
Core::uint32 bindingIndex,
const std::unordered_map<const Texture*, Containers::String>& textureArtifactPaths) {
const ResourceHandle<Texture> textureHandle = material.GetTextureBindingTexture(bindingIndex);
const Texture* texture = textureHandle.Get();
if (texture != nullptr) {
const auto textureIt = textureArtifactPaths.find(texture);
if (textureIt != textureArtifactPaths.end()) {
return textureIt->second;
}
if (!texture->GetPath().Empty()) {
return NormalizeArtifactPathString(texture->GetPath());
}
}
return NormalizeArtifactPathString(material.GetTextureBindingPath(bindingIndex));
}
bool WriteMaterialArtifactFile(
const fs::path& artifactPath,
const Material& material,
const std::unordered_map<const Texture*, Containers::String>& textureArtifactPaths = {}) {
std::ofstream output(artifactPath, std::ios::binary | std::ios::trunc);
if (!output.is_open()) {
return false;
}
MaterialArtifactFileHeader fileHeader;
output.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));
WriteString(output, material.GetName());
WriteString(output, material.GetPath());
WriteString(
output,
material.GetShader() != nullptr
? material.GetShader()->GetPath()
: Containers::String());
WriteString(output, material.GetShaderPass());
MaterialArtifactHeader header;
@@ -243,18 +286,17 @@ void WriteMaterialBlock(std::ofstream& output,
for (Core::uint32 bindingIndex = 0; bindingIndex < material.GetTextureBindingCount(); ++bindingIndex) {
const Containers::String bindingName = material.GetTextureBindingName(bindingIndex);
const Texture* texture = material.GetTextureBindingTexture(bindingIndex).Get();
auto fileIt = texture != nullptr ? textureFileNames.find(texture) : textureFileNames.end();
WriteString(output, bindingName);
WriteString(output,
fileIt != textureFileNames.end()
? ToContainersString(fileIt->second)
: Containers::String());
WriteString(output, ResolveTextureBindingPath(material, bindingIndex, textureArtifactPaths));
}
return static_cast<bool>(output);
}
bool WriteMeshArtifactFile(const fs::path& artifactPath, const Mesh& mesh) {
bool WriteMeshArtifactFile(const fs::path& artifactPath,
const Mesh& mesh,
const std::vector<Containers::String>& materialArtifactPaths) {
std::ofstream output(artifactPath, std::ios::binary | std::ios::trunc);
if (!output.is_open()) {
return false;
@@ -267,8 +309,7 @@ bool WriteMeshArtifactFile(const fs::path& artifactPath, const Mesh& mesh) {
header.indexCount = mesh.GetIndexCount();
header.use32BitIndex = mesh.IsUse32BitIndex() ? 1u : 0u;
header.sectionCount = static_cast<Core::uint32>(mesh.GetSections().Size());
header.materialCount = static_cast<Core::uint32>(mesh.GetMaterials().Size());
header.textureCount = static_cast<Core::uint32>(mesh.GetTextures().Size());
header.materialCount = static_cast<Core::uint32>(materialArtifactPaths.size());
header.boundsMin = mesh.GetBounds().GetMin();
header.boundsMax = mesh.GetBounds().GetMax();
header.vertexDataSize = static_cast<Core::uint64>(mesh.GetVertexDataSize());
@@ -286,25 +327,8 @@ bool WriteMeshArtifactFile(const fs::path& artifactPath, const Mesh& mesh) {
output.write(static_cast<const char*>(mesh.GetIndexData()), mesh.GetIndexDataSize());
}
std::unordered_map<const Texture*, std::string> textureFileNames;
for (size_t textureIndex = 0; textureIndex < mesh.GetTextures().Size(); ++textureIndex) {
const Texture* texture = mesh.GetTextures()[textureIndex];
if (texture == nullptr) {
continue;
}
const std::string fileName = "texture_" + std::to_string(textureIndex) + ".xctex";
textureFileNames.emplace(texture, fileName);
WriteString(output, ToContainersString(fileName));
}
for (size_t materialIndex = 0; materialIndex < mesh.GetMaterials().Size(); ++materialIndex) {
const Material* material = mesh.GetMaterials()[materialIndex];
const Core::uint32 materialPresent = material != nullptr ? 1u : 0u;
output.write(reinterpret_cast<const char*>(&materialPresent), sizeof(materialPresent));
if (material != nullptr) {
WriteMaterialBlock(output, *material, textureFileNames);
}
for (const Containers::String& materialArtifactPath : materialArtifactPaths) {
WriteString(output, NormalizeArtifactPathString(materialArtifactPath));
}
return static_cast<bool>(output);
@@ -852,7 +876,8 @@ bool AssetDatabase::ShouldReimport(const SourceAssetRecord& sourceRecord,
return true;
}
return artifactRecord->sourceHash != sourceRecord.sourceHash ||
return artifactRecord->importerVersion != sourceRecord.importerVersion ||
artifactRecord->sourceHash != sourceRecord.sourceHash ||
artifactRecord->metaHash != sourceRecord.metaHash ||
artifactRecord->sourceFileSize != sourceRecord.sourceFileSize ||
artifactRecord->sourceWriteTime != sourceRecord.sourceWriteTime;
@@ -864,6 +889,8 @@ bool AssetDatabase::ImportAsset(const SourceAssetRecord& sourceRecord,
switch (primaryType) {
case ResourceType::Texture:
return ImportTextureAsset(sourceRecord, outRecord);
case ResourceType::Material:
return ImportMaterialAsset(sourceRecord, outRecord);
case ResourceType::Mesh:
return ImportModelAsset(sourceRecord, outRecord);
default:
@@ -1034,6 +1061,53 @@ bool AssetDatabase::ImportTextureAsset(const SourceAssetRecord& sourceRecord,
return true;
}
bool AssetDatabase::ImportMaterialAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord) {
MaterialLoader loader;
const Containers::String absolutePath =
NormalizePathString(fs::path(m_projectRoot.CStr()) / sourceRecord.relativePath.CStr());
LoadResult result = loader.Load(absolutePath);
if (!result || result.resource == nullptr) {
return false;
}
Material* material = static_cast<Material*>(result.resource);
const Containers::String artifactKey = BuildArtifactKey(sourceRecord);
const Containers::String artifactDir = BuildArtifactDirectory(artifactKey);
const Containers::String mainArtifactPath =
NormalizePathString(fs::path(artifactDir.CStr()) / "main.xcmat");
std::error_code ec;
fs::remove_all(fs::path(m_projectRoot.CStr()) / artifactDir.CStr(), ec);
ec.clear();
fs::create_directories(fs::path(m_projectRoot.CStr()) / artifactDir.CStr(), ec);
if (ec) {
delete material;
return false;
}
const bool writeOk =
WriteMaterialArtifactFile(fs::path(m_projectRoot.CStr()) / mainArtifactPath.CStr(), *material);
delete material;
if (!writeOk) {
return false;
}
outRecord.artifactKey = artifactKey;
outRecord.assetGuid = sourceRecord.guid;
outRecord.importerName = sourceRecord.importerName;
outRecord.importerVersion = sourceRecord.importerVersion;
outRecord.resourceType = ResourceType::Material;
outRecord.artifactDirectory = artifactDir;
outRecord.mainArtifactPath = mainArtifactPath;
outRecord.sourceHash = sourceRecord.sourceHash;
outRecord.metaHash = sourceRecord.metaHash;
outRecord.sourceFileSize = sourceRecord.sourceFileSize;
outRecord.sourceWriteTime = sourceRecord.sourceWriteTime;
outRecord.mainLocalID = kMainAssetLocalID;
return true;
}
bool AssetDatabase::ImportModelAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord) {
MeshLoader loader;
@@ -1057,18 +1131,54 @@ bool AssetDatabase::ImportModelAsset(const SourceAssetRecord& sourceRecord,
return false;
}
bool writeOk = WriteMeshArtifactFile(fs::path(m_projectRoot.CStr()) / mainArtifactPath.CStr(), *mesh);
bool writeOk = true;
std::unordered_map<const Texture*, Containers::String> textureArtifactPaths;
for (size_t textureIndex = 0; writeOk && textureIndex < mesh->GetTextures().Size(); ++textureIndex) {
Texture* texture = mesh->GetTextures()[textureIndex];
if (texture == nullptr) {
continue;
}
const fs::path textureArtifactPath =
fs::path(m_projectRoot.CStr()) / artifactDir.CStr() / ("texture_" + std::to_string(textureIndex) + ".xctex");
writeOk = WriteTextureArtifactFile(textureArtifactPath, *texture);
const Containers::String textureArtifactPath =
NormalizePathString(fs::path(artifactDir.CStr()) / ("texture_" + std::to_string(textureIndex) + ".xctex"));
writeOk = WriteTextureArtifactFile(
fs::path(m_projectRoot.CStr()) / textureArtifactPath.CStr(),
*texture);
if (!writeOk) {
break;
}
textureArtifactPaths.emplace(texture, textureArtifactPath);
}
std::vector<Containers::String> materialArtifactPaths;
materialArtifactPaths.reserve(mesh->GetMaterials().Size());
for (size_t materialIndex = 0; writeOk && materialIndex < mesh->GetMaterials().Size(); ++materialIndex) {
Material* material = mesh->GetMaterials()[materialIndex];
if (material == nullptr) {
materialArtifactPaths.emplace_back();
continue;
}
const Containers::String materialArtifactPath =
NormalizePathString(fs::path(artifactDir.CStr()) / ("material_" + std::to_string(materialIndex) + ".xcmat"));
writeOk = WriteMaterialArtifactFile(
fs::path(m_projectRoot.CStr()) / materialArtifactPath.CStr(),
*material,
textureArtifactPaths);
if (!writeOk) {
break;
}
materialArtifactPaths.push_back(materialArtifactPath);
}
writeOk = writeOk &&
WriteMeshArtifactFile(
fs::path(m_projectRoot.CStr()) / mainArtifactPath.CStr(),
*mesh,
materialArtifactPaths);
DestroyImportedMesh(mesh);
if (!writeOk) {
return false;