Split mesh artifacts into material and texture artifacts
This commit is contained in:
@@ -52,6 +52,15 @@ void RemoveTextureBindingByName(
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureTextureProperty(Containers::HashMap<Containers::String, MaterialProperty>& properties,
|
||||
const Containers::String& name) {
|
||||
MaterialProperty prop;
|
||||
prop.name = name;
|
||||
prop.type = MaterialPropertyType::Texture;
|
||||
prop.refCount = 1;
|
||||
properties.Insert(name, prop);
|
||||
}
|
||||
|
||||
void WritePackedMaterialProperty(Core::uint8* destination, const MaterialProperty& property) {
|
||||
std::memset(destination, 0, kMaterialConstantSlotSize);
|
||||
|
||||
@@ -271,15 +280,13 @@ void Material::SetBool(const Containers::String& name, bool value) {
|
||||
}
|
||||
|
||||
void Material::SetTexture(const Containers::String& name, const ResourceHandle<Texture>& texture) {
|
||||
MaterialProperty prop;
|
||||
prop.name = name;
|
||||
prop.type = MaterialPropertyType::Texture;
|
||||
prop.refCount = 1;
|
||||
m_properties.Insert(name, prop);
|
||||
EnsureTextureProperty(m_properties, name);
|
||||
|
||||
for (auto& binding : m_textureBindings) {
|
||||
if (binding.name == name) {
|
||||
binding.texture = texture;
|
||||
binding.texturePath = texture.Get() != nullptr ? texture->GetPath() : Containers::String();
|
||||
binding.pendingLoad.reset();
|
||||
MarkChanged(false);
|
||||
return;
|
||||
}
|
||||
@@ -289,6 +296,33 @@ void Material::SetTexture(const Containers::String& name, const ResourceHandle<T
|
||||
binding.name = name;
|
||||
binding.slot = static_cast<Core::uint32>(m_textureBindings.Size());
|
||||
binding.texture = texture;
|
||||
binding.texturePath = texture.Get() != nullptr ? texture->GetPath() : Containers::String();
|
||||
m_textureBindings.PushBack(binding);
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetTexturePath(const Containers::String& name, const Containers::String& texturePath) {
|
||||
if (texturePath.Empty()) {
|
||||
RemoveProperty(name);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureTextureProperty(m_properties, name);
|
||||
|
||||
for (auto& binding : m_textureBindings) {
|
||||
if (binding.name == name) {
|
||||
binding.texture.Reset();
|
||||
binding.texturePath = texturePath;
|
||||
binding.pendingLoad.reset();
|
||||
MarkChanged(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTextureBinding binding;
|
||||
binding.name = name;
|
||||
binding.slot = static_cast<Core::uint32>(m_textureBindings.Size());
|
||||
binding.texturePath = texturePath;
|
||||
m_textureBindings.PushBack(binding);
|
||||
MarkChanged(false);
|
||||
}
|
||||
@@ -343,8 +377,16 @@ bool Material::GetBool(const Containers::String& name) const {
|
||||
}
|
||||
|
||||
ResourceHandle<Texture> Material::GetTexture(const Containers::String& name) const {
|
||||
for (const auto& binding : m_textureBindings) {
|
||||
Material* material = const_cast<Material*>(this);
|
||||
material->ResolvePendingTextureBindings();
|
||||
for (Core::uint32 bindingIndex = 0; bindingIndex < material->m_textureBindings.Size(); ++bindingIndex) {
|
||||
MaterialTextureBinding& binding = material->m_textureBindings[bindingIndex];
|
||||
if (binding.name == name) {
|
||||
if (binding.texture.Get() == nullptr &&
|
||||
binding.pendingLoad == nullptr &&
|
||||
!binding.texturePath.Empty()) {
|
||||
material->BeginAsyncTextureLoad(bindingIndex);
|
||||
}
|
||||
return binding.texture;
|
||||
}
|
||||
}
|
||||
@@ -355,8 +397,24 @@ Containers::String Material::GetTextureBindingName(Core::uint32 index) const {
|
||||
return index < m_textureBindings.Size() ? m_textureBindings[index].name : Containers::String();
|
||||
}
|
||||
|
||||
Containers::String Material::GetTextureBindingPath(Core::uint32 index) const {
|
||||
return index < m_textureBindings.Size() ? m_textureBindings[index].texturePath : Containers::String();
|
||||
}
|
||||
|
||||
ResourceHandle<Texture> Material::GetTextureBindingTexture(Core::uint32 index) const {
|
||||
return index < m_textureBindings.Size() ? m_textureBindings[index].texture : ResourceHandle<Texture>();
|
||||
Material* material = const_cast<Material*>(this);
|
||||
material->ResolvePendingTextureBinding(index);
|
||||
if (index < material->m_textureBindings.Size()) {
|
||||
MaterialTextureBinding& binding = material->m_textureBindings[index];
|
||||
if (binding.texture.Get() == nullptr &&
|
||||
binding.pendingLoad == nullptr &&
|
||||
!binding.texturePath.Empty()) {
|
||||
material->BeginAsyncTextureLoad(index);
|
||||
}
|
||||
return binding.texture;
|
||||
}
|
||||
|
||||
return ResourceHandle<Texture>();
|
||||
}
|
||||
|
||||
std::vector<MaterialProperty> Material::GetProperties() const {
|
||||
@@ -404,6 +462,59 @@ void Material::RecalculateMemorySize() {
|
||||
UpdateMemorySize();
|
||||
}
|
||||
|
||||
void Material::BeginAsyncTextureLoad(Core::uint32 index) {
|
||||
if (index >= m_textureBindings.Size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialTextureBinding& binding = m_textureBindings[index];
|
||||
if (binding.texture.Get() != nullptr || binding.texturePath.Empty() || binding.pendingLoad != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
binding.pendingLoad = std::make_shared<PendingTextureLoadState>();
|
||||
std::weak_ptr<PendingTextureLoadState> weakState = binding.pendingLoad;
|
||||
const Containers::String texturePath = binding.texturePath;
|
||||
ResourceManager::Get().LoadAsync(
|
||||
texturePath,
|
||||
ResourceType::Texture,
|
||||
[weakState](LoadResult result) {
|
||||
if (std::shared_ptr<PendingTextureLoadState> state = weakState.lock()) {
|
||||
state->resource = result.resource;
|
||||
state->errorMessage = result.errorMessage;
|
||||
state->completed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Material::ResolvePendingTextureBinding(Core::uint32 index) {
|
||||
if (index >= m_textureBindings.Size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialTextureBinding& binding = m_textureBindings[index];
|
||||
if (!binding.pendingLoad || !binding.pendingLoad->completed) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<PendingTextureLoadState> completedLoad = std::move(binding.pendingLoad);
|
||||
binding.pendingLoad.reset();
|
||||
if (completedLoad->resource == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
binding.texture = ResourceHandle<Texture>(static_cast<Texture*>(completedLoad->resource));
|
||||
if (binding.texture.Get() != nullptr) {
|
||||
binding.texturePath = binding.texture->GetPath();
|
||||
}
|
||||
}
|
||||
|
||||
void Material::ResolvePendingTextureBindings() {
|
||||
for (Core::uint32 bindingIndex = 0; bindingIndex < m_textureBindings.Size(); ++bindingIndex) {
|
||||
ResolvePendingTextureBinding(bindingIndex);
|
||||
}
|
||||
}
|
||||
|
||||
bool Material::HasProperty(const Containers::String& name) const {
|
||||
return m_properties.Contains(name);
|
||||
}
|
||||
@@ -457,6 +568,7 @@ void Material::UpdateMemorySize() {
|
||||
|
||||
for (const auto& binding : m_textureBindings) {
|
||||
m_memorySize += binding.name.Length();
|
||||
m_memorySize += binding.texturePath.Length();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -18,6 +21,107 @@ std::string ToStdString(const Containers::Array<Core::uint8>& data) {
|
||||
return std::string(reinterpret_cast<const char*>(data.Data()), data.Size());
|
||||
}
|
||||
|
||||
Containers::Array<Core::uint8> ReadMaterialArtifactFileData(const Containers::String& path) {
|
||||
Containers::Array<Core::uint8> data;
|
||||
|
||||
auto tryRead = [&data](const std::filesystem::path& filePath, bool& opened) {
|
||||
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
opened = false;
|
||||
return;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
const std::streamsize size = file.tellg();
|
||||
if (size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
data.Resize(static_cast<size_t>(size));
|
||||
if (!file.read(reinterpret_cast<char*>(data.Data()), size)) {
|
||||
data.Clear();
|
||||
}
|
||||
};
|
||||
|
||||
bool opened = false;
|
||||
const std::filesystem::path inputPath(path.CStr());
|
||||
tryRead(inputPath, opened);
|
||||
if (opened || path.Empty() || inputPath.is_absolute()) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
|
||||
if (resourceRoot.Empty()) {
|
||||
return data;
|
||||
}
|
||||
|
||||
tryRead(std::filesystem::path(resourceRoot.CStr()) / inputPath, opened);
|
||||
return data;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t SkipWhitespace(const std::string& text, size_t pos) {
|
||||
while (pos < text.size() && std::isspace(static_cast<unsigned char>(text[pos])) != 0) {
|
||||
++pos;
|
||||
@@ -551,6 +655,181 @@ bool MaterialFileExists(const Containers::String& path) {
|
||||
return std::filesystem::exists(std::filesystem::path(resourceRoot.CStr()) / inputPath);
|
||||
}
|
||||
|
||||
ResourceHandle<Shader> LoadShaderHandle(const Containers::String& shaderPath);
|
||||
|
||||
template<typename T>
|
||||
bool ReadMaterialArtifactValue(const Containers::Array<Core::uint8>& data, size_t& offset, T& outValue) {
|
||||
if (offset + sizeof(T) > data.Size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(&outValue, data.Data() + offset, sizeof(T));
|
||||
offset += sizeof(T);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadMaterialArtifactString(const Containers::Array<Core::uint8>& data,
|
||||
size_t& offset,
|
||||
Containers::String& outValue) {
|
||||
Core::uint32 length = 0;
|
||||
if (!ReadMaterialArtifactValue(data, offset, length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
outValue.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (offset + length > data.Size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outValue = Containers::String(
|
||||
std::string(reinterpret_cast<const char*>(data.Data() + offset), length).c_str());
|
||||
offset += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApplyMaterialProperty(Material& material, const MaterialProperty& property) {
|
||||
switch (property.type) {
|
||||
case MaterialPropertyType::Float:
|
||||
material.SetFloat(property.name, property.value.floatValue[0]);
|
||||
break;
|
||||
case MaterialPropertyType::Float2:
|
||||
material.SetFloat2(
|
||||
property.name,
|
||||
Math::Vector2(property.value.floatValue[0], property.value.floatValue[1]));
|
||||
break;
|
||||
case MaterialPropertyType::Float3:
|
||||
material.SetFloat3(
|
||||
property.name,
|
||||
Math::Vector3(
|
||||
property.value.floatValue[0],
|
||||
property.value.floatValue[1],
|
||||
property.value.floatValue[2]));
|
||||
break;
|
||||
case MaterialPropertyType::Float4:
|
||||
material.SetFloat4(
|
||||
property.name,
|
||||
Math::Vector4(
|
||||
property.value.floatValue[0],
|
||||
property.value.floatValue[1],
|
||||
property.value.floatValue[2],
|
||||
property.value.floatValue[3]));
|
||||
break;
|
||||
case MaterialPropertyType::Int:
|
||||
material.SetInt(property.name, property.value.intValue[0]);
|
||||
break;
|
||||
case MaterialPropertyType::Bool:
|
||||
material.SetBool(property.name, property.value.boolValue);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LoadResult LoadMaterialArtifact(const Containers::String& path) {
|
||||
const Containers::Array<Core::uint8> data = ReadMaterialArtifactFileData(path);
|
||||
if (data.Empty()) {
|
||||
return LoadResult("Failed to read material artifact: " + path);
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
MaterialArtifactFileHeader fileHeader;
|
||||
if (!ReadMaterialArtifactValue(data, offset, fileHeader)) {
|
||||
return LoadResult("Failed to parse material artifact header: " + path);
|
||||
}
|
||||
|
||||
const std::string magic(fileHeader.magic, fileHeader.magic + 7);
|
||||
if (magic != "XCMAT01") {
|
||||
return LoadResult("Invalid material artifact magic: " + path);
|
||||
}
|
||||
|
||||
auto material = std::make_unique<Material>();
|
||||
material->m_path = path;
|
||||
material->m_name = path;
|
||||
material->m_guid = ResourceGUID::Generate(path);
|
||||
|
||||
Containers::String materialName;
|
||||
Containers::String materialSourcePath;
|
||||
Containers::String shaderPath;
|
||||
Containers::String shaderPass;
|
||||
if (!ReadMaterialArtifactString(data, offset, materialName) ||
|
||||
!ReadMaterialArtifactString(data, offset, materialSourcePath) ||
|
||||
!ReadMaterialArtifactString(data, offset, shaderPath) ||
|
||||
!ReadMaterialArtifactString(data, offset, shaderPass)) {
|
||||
return LoadResult("Failed to parse material artifact strings: " + path);
|
||||
}
|
||||
|
||||
material->m_name = materialName.Empty() ? path : materialName;
|
||||
if (!materialSourcePath.Empty()) {
|
||||
material->m_path = materialSourcePath;
|
||||
material->m_guid = ResourceGUID::Generate(materialSourcePath);
|
||||
}
|
||||
|
||||
if (!shaderPath.Empty()) {
|
||||
const ResourceHandle<Shader> shaderHandle = LoadShaderHandle(shaderPath);
|
||||
if (shaderHandle.IsValid()) {
|
||||
material->SetShader(shaderHandle);
|
||||
}
|
||||
}
|
||||
if (!shaderPass.Empty()) {
|
||||
material->SetShaderPass(shaderPass);
|
||||
}
|
||||
|
||||
MaterialArtifactHeader header;
|
||||
if (!ReadMaterialArtifactValue(data, offset, header)) {
|
||||
return LoadResult("Failed to parse material artifact body: " + path);
|
||||
}
|
||||
|
||||
material->SetRenderQueue(header.renderQueue);
|
||||
material->SetRenderState(header.renderState);
|
||||
|
||||
for (Core::uint32 tagIndex = 0; tagIndex < header.tagCount; ++tagIndex) {
|
||||
Containers::String tagName;
|
||||
Containers::String tagValue;
|
||||
if (!ReadMaterialArtifactString(data, offset, tagName) ||
|
||||
!ReadMaterialArtifactString(data, offset, tagValue)) {
|
||||
return LoadResult("Failed to read material artifact tags: " + path);
|
||||
}
|
||||
|
||||
material->SetTag(tagName, tagValue);
|
||||
}
|
||||
|
||||
for (Core::uint32 propertyIndex = 0; propertyIndex < header.propertyCount; ++propertyIndex) {
|
||||
Containers::String propertyName;
|
||||
MaterialPropertyArtifact propertyArtifact;
|
||||
if (!ReadMaterialArtifactString(data, offset, propertyName) ||
|
||||
!ReadMaterialArtifactValue(data, offset, propertyArtifact)) {
|
||||
return LoadResult("Failed to read material artifact properties: " + path);
|
||||
}
|
||||
|
||||
MaterialProperty property;
|
||||
property.name = propertyName;
|
||||
property.type = static_cast<MaterialPropertyType>(propertyArtifact.propertyType);
|
||||
property.value = propertyArtifact.value;
|
||||
ApplyMaterialProperty(*material, property);
|
||||
}
|
||||
|
||||
for (Core::uint32 bindingIndex = 0; bindingIndex < header.textureBindingCount; ++bindingIndex) {
|
||||
Containers::String bindingName;
|
||||
Containers::String texturePath;
|
||||
if (!ReadMaterialArtifactString(data, offset, bindingName) ||
|
||||
!ReadMaterialArtifactString(data, offset, texturePath)) {
|
||||
return LoadResult("Failed to read material artifact texture bindings: " + path);
|
||||
}
|
||||
|
||||
if (!texturePath.Empty()) {
|
||||
material->SetTexturePath(bindingName, ResolveArtifactDependencyPath(texturePath, path));
|
||||
}
|
||||
}
|
||||
|
||||
material->m_isValid = true;
|
||||
material->RecalculateMemorySize();
|
||||
return LoadResult(material.release());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MaterialLoader::MaterialLoader() = default;
|
||||
@@ -562,6 +841,7 @@ Containers::Array<Containers::String> MaterialLoader::GetSupportedExtensions() c
|
||||
extensions.PushBack("mat");
|
||||
extensions.PushBack("material");
|
||||
extensions.PushBack("json");
|
||||
extensions.PushBack("xcmat");
|
||||
return extensions;
|
||||
}
|
||||
|
||||
@@ -570,8 +850,8 @@ bool MaterialLoader::CanLoad(const Containers::String& path) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Containers::String ext = GetExtension(path);
|
||||
return ext == "mat" || ext == "material" || ext == "json";
|
||||
Containers::String ext = GetExtension(path).ToLower();
|
||||
return ext == "mat" || ext == "material" || ext == "json" || ext == "xcmat";
|
||||
}
|
||||
|
||||
LoadResult MaterialLoader::Load(const Containers::String& path, const ImportSettings* settings) {
|
||||
@@ -581,6 +861,11 @@ LoadResult MaterialLoader::Load(const Containers::String& path, const ImportSett
|
||||
return CreateBuiltinMaterialResource(path);
|
||||
}
|
||||
|
||||
const Containers::String ext = GetExtension(path).ToLower();
|
||||
if (ext == "xcmat") {
|
||||
return LoadMaterialArtifact(path);
|
||||
}
|
||||
|
||||
Containers::Array<Core::uint8> data = ReadFileData(path);
|
||||
Material* material = new Material();
|
||||
material->m_path = path;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,15 @@ LoadResult CreateTextureResource(const Containers::String& path,
|
||||
}
|
||||
|
||||
LoadResult LoadTextureArtifact(const Containers::String& path) {
|
||||
std::ifstream input(path.CStr(), std::ios::binary);
|
||||
std::filesystem::path resolvedPath(path.CStr());
|
||||
if (!resolvedPath.is_absolute() && !std::filesystem::exists(resolvedPath)) {
|
||||
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
|
||||
if (!resourceRoot.Empty()) {
|
||||
resolvedPath = std::filesystem::path(resourceRoot.CStr()) / resolvedPath;
|
||||
}
|
||||
}
|
||||
|
||||
std::ifstream input(resolvedPath, std::ios::binary);
|
||||
if (!input.is_open()) {
|
||||
return LoadResult(Containers::String("Failed to read texture artifact: ") + path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user