Split mesh artifacts into material and texture artifacts
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user