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

@@ -1,6 +1,7 @@
#include <XCEngine/Resources/Mesh/MeshLoader.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Texture/Texture.h>
@@ -148,6 +149,68 @@ Core::uint32 FindEmbeddedTextureIndex(const aiScene& scene, const aiTexture& emb
return 0;
}
Containers::String NormalizePathString(const std::filesystem::path& path) {
return Containers::String(path.lexically_normal().generic_string().c_str());
}
Containers::String ResolveArtifactDependencyPath(const Containers::String& dependencyPath,
const Containers::String& ownerArtifactPath) {
if (dependencyPath.Empty()) {
return dependencyPath;
}
std::filesystem::path dependencyFsPath(dependencyPath.CStr());
if (dependencyFsPath.is_absolute() && std::filesystem::exists(dependencyFsPath)) {
return NormalizePathString(dependencyFsPath);
}
if (std::filesystem::exists(dependencyFsPath)) {
return NormalizePathString(dependencyFsPath);
}
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
if (!resourceRoot.Empty()) {
const std::filesystem::path projectRelativeCandidate =
std::filesystem::path(resourceRoot.CStr()) / dependencyFsPath;
if (std::filesystem::exists(projectRelativeCandidate)) {
return NormalizePathString(projectRelativeCandidate);
}
}
const std::filesystem::path ownerArtifactFsPath(ownerArtifactPath.CStr());
if (!ownerArtifactFsPath.is_absolute()) {
return dependencyPath;
}
const std::filesystem::path ownerRelativeCandidate =
ownerArtifactFsPath.parent_path() / dependencyFsPath;
if (std::filesystem::exists(ownerRelativeCandidate)) {
return NormalizePathString(ownerRelativeCandidate);
}
std::filesystem::path current = ownerArtifactFsPath.parent_path();
while (!current.empty()) {
if (current.filename() == "Library") {
const std::filesystem::path projectRoot = current.parent_path();
if (!projectRoot.empty()) {
const std::filesystem::path projectRelativeCandidate = projectRoot / dependencyFsPath;
if (std::filesystem::exists(projectRelativeCandidate)) {
return NormalizePathString(projectRelativeCandidate);
}
}
break;
}
const std::filesystem::path parent = current.parent_path();
if (parent == current) {
break;
}
current = parent;
}
return dependencyPath;
}
Texture* CreateRawTexture(const Containers::String& texturePath,
TextureFormat format,
Core::uint32 width,
@@ -570,7 +633,7 @@ LoadResult LoadMeshArtifact(const Containers::String& path) {
}
const std::string magic(header.magic, header.magic + 7);
if (magic != "XCMESH1") {
if (magic != "XCMESH2") {
return LoadResult(Containers::String("Invalid mesh artifact magic: ") + path);
}
@@ -628,93 +691,31 @@ LoadResult LoadMeshArtifact(const Containers::String& path) {
bounds.SetMinMax(header.boundsMin, header.boundsMax);
mesh->SetBounds(bounds);
std::vector<Containers::String> textureFiles;
textureFiles.reserve(header.textureCount);
for (Core::uint32 textureIndex = 0; textureIndex < header.textureCount; ++textureIndex) {
textureFiles.push_back(ReadBinaryString(input));
}
std::unordered_map<std::string, Texture*> loadedTextures;
TextureLoader textureLoader;
const std::filesystem::path artifactDirectory = std::filesystem::path(path.CStr()).parent_path();
MaterialLoader materialLoader;
for (Core::uint32 materialIndex = 0; materialIndex < header.materialCount; ++materialIndex) {
Core::uint32 materialPresent = 0;
input.read(reinterpret_cast<char*>(&materialPresent), sizeof(materialPresent));
const Containers::String materialArtifactPath = ReadBinaryString(input);
if (!input) {
return LoadResult(Containers::String("Failed to read mesh material flag: ") + path);
return LoadResult(Containers::String("Failed to read mesh material artifact path: ") + path);
}
if (materialPresent == 0) {
if (materialArtifactPath.Empty()) {
mesh->AddMaterial(nullptr);
continue;
}
auto* material = new Material();
material->m_name = ReadBinaryString(input);
material->m_path = ReadBinaryString(input);
material->m_guid = ResourceGUID::Generate(material->m_path);
material->m_isValid = true;
material->SetShaderPass(ReadBinaryString(input));
MaterialArtifactHeader materialHeader;
input.read(reinterpret_cast<char*>(&materialHeader), sizeof(materialHeader));
if (!input) {
delete material;
return LoadResult(Containers::String("Failed to read material artifact header: ") + path);
}
material->SetRenderQueue(materialHeader.renderQueue);
material->SetRenderState(materialHeader.renderState);
for (Core::uint32 tagIndex = 0; tagIndex < materialHeader.tagCount; ++tagIndex) {
material->SetTag(ReadBinaryString(input), ReadBinaryString(input));
}
for (Core::uint32 propertyIndex = 0; propertyIndex < materialHeader.propertyCount; ++propertyIndex) {
MaterialProperty property;
property.name = ReadBinaryString(input);
MaterialPropertyArtifact propertyArtifact;
input.read(reinterpret_cast<char*>(&propertyArtifact), sizeof(propertyArtifact));
if (!input) {
delete material;
return LoadResult(Containers::String("Failed to read material property: ") + path);
}
property.type = static_cast<MaterialPropertyType>(propertyArtifact.propertyType);
property.value = propertyArtifact.value;
ApplyMaterialProperty(*material, property);
}
for (Core::uint32 bindingIndex = 0; bindingIndex < materialHeader.textureBindingCount; ++bindingIndex) {
const Containers::String bindingName = ReadBinaryString(input);
const Containers::String textureFile = ReadBinaryString(input);
if (textureFile.Empty()) {
continue;
}
const std::string textureKey(textureFile.CStr());
Texture* texture = nullptr;
auto textureIt = loadedTextures.find(textureKey);
if (textureIt != loadedTextures.end()) {
texture = textureIt->second;
} else {
const Containers::String texturePath =
Containers::String((artifactDirectory / textureFile.CStr()).lexically_normal().string().c_str());
LoadResult textureResult = textureLoader.Load(texturePath);
if (textureResult && textureResult.resource != nullptr) {
texture = static_cast<Texture*>(textureResult.resource);
loadedTextures.emplace(textureKey, texture);
mesh->AddTexture(texture);
}
}
if (texture != nullptr) {
material->SetTexture(bindingName, ResourceHandle<Texture>(texture));
}
const Containers::String resolvedMaterialArtifactPath =
ResolveArtifactDependencyPath(materialArtifactPath, path);
LoadResult materialResult = materialLoader.Load(resolvedMaterialArtifactPath);
if (!materialResult || materialResult.resource == nullptr) {
return LoadResult(
Containers::String("Failed to load mesh material artifact: ") +
resolvedMaterialArtifactPath +
" for " +
path);
}
auto* material = static_cast<Material*>(materialResult.resource);
material->RecalculateMemorySize();
mesh->AddMaterial(material);
}