Add VolumeField NanoVDB asset pipeline

This commit is contained in:
2026-04-08 19:45:53 +08:00
parent 6bf9203eec
commit c6815fa809
16 changed files with 608 additions and 1 deletions

View File

@@ -0,0 +1,43 @@
#include <XCEngine/Resources/Volume/VolumeField.h>
#include <cstring>
namespace XCEngine {
namespace Resources {
VolumeField::VolumeField() = default;
VolumeField::~VolumeField() = default;
void VolumeField::Release() {
delete this;
}
bool VolumeField::Create(VolumeStorageKind storageKind,
const void* payload,
size_t payloadSize,
const Math::Bounds& bounds,
const Math::Vector3& voxelSize) {
if (payload == nullptr || payloadSize == 0) {
return false;
}
m_storageKind = storageKind;
m_bounds = bounds;
m_voxelSize = voxelSize;
m_payload.Resize(payloadSize);
std::memcpy(m_payload.Data(), payload, payloadSize);
m_isValid = true;
UpdateMemorySize();
return true;
}
void VolumeField::UpdateMemorySize() {
m_memorySize = sizeof(VolumeField) +
m_name.Length() +
m_path.Length() +
m_payload.Size();
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,144 @@
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Resources/Volume/VolumeField.h>
#include <cstring>
#include <filesystem>
#include <fstream>
namespace XCEngine {
namespace Resources {
namespace {
Containers::String GetResourceNameFromPath(const Containers::String& path) {
const std::filesystem::path filePath(path.CStr());
const std::string fileName = filePath.filename().string();
if (!fileName.empty()) {
return Containers::String(fileName.c_str());
}
return path;
}
LoadResult CreateVolumeFieldResource(const Containers::String& path,
VolumeStorageKind storageKind,
const Math::Bounds& bounds,
const Math::Vector3& voxelSize,
const void* payload,
size_t payloadSize) {
auto* volumeField = new VolumeField();
IResource::ConstructParams params;
params.name = GetResourceNameFromPath(path);
params.path = path;
params.guid = ResourceGUID::Generate(path);
params.memorySize = payloadSize;
volumeField->Initialize(params);
if (!volumeField->Create(storageKind, payload, payloadSize, bounds, voxelSize)) {
delete volumeField;
return LoadResult(Containers::String("Failed to create volume field resource: ") + path);
}
return LoadResult(volumeField);
}
LoadResult LoadVolumeFieldArtifact(const Containers::String& path) {
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 volume artifact: ") + path);
}
VolumeFieldArtifactHeader header;
input.read(reinterpret_cast<char*>(&header), sizeof(header));
if (!input) {
return LoadResult(Containers::String("Failed to parse volume artifact header: ") + path);
}
const bool validHeader =
std::memcmp(header.magic, "XCVOL01", 7) == 0 &&
header.schemaVersion == kVolumeFieldArtifactSchemaVersion &&
header.payloadSize > 0;
if (!validHeader) {
return LoadResult(Containers::String("Invalid volume artifact header: ") + path);
}
Containers::Array<Core::uint8> payload;
payload.Resize(static_cast<size_t>(header.payloadSize));
input.read(reinterpret_cast<char*>(payload.Data()), static_cast<std::streamsize>(header.payloadSize));
if (!input) {
return LoadResult(Containers::String("Failed to read volume artifact payload: ") + path);
}
Math::Bounds bounds;
bounds.SetMinMax(header.boundsMin, header.boundsMax);
return CreateVolumeFieldResource(path,
static_cast<VolumeStorageKind>(header.storageKind),
bounds,
header.voxelSize,
payload.Data(),
payload.Size());
}
} // namespace
VolumeFieldLoader::VolumeFieldLoader() = default;
VolumeFieldLoader::~VolumeFieldLoader() = default;
Containers::Array<Containers::String> VolumeFieldLoader::GetSupportedExtensions() const {
Containers::Array<Containers::String> extensions;
extensions.PushBack("nvdb");
extensions.PushBack("xcvol");
return extensions;
}
bool VolumeFieldLoader::CanLoad(const Containers::String& path) const {
const Containers::String ext = GetExtension(path).ToLower();
return ext == "nvdb" || ext == "xcvol";
}
LoadResult VolumeFieldLoader::Load(const Containers::String& path, const ImportSettings* settings) {
(void)settings;
if (!CanLoad(path)) {
return LoadResult(Containers::String("Unsupported volume format: ") + GetExtension(path).ToLower());
}
const Containers::String ext = GetExtension(path).ToLower();
if (ext == "xcvol") {
return LoadVolumeFieldArtifact(path);
}
Containers::Array<Core::uint8> payload = ReadFileData(path);
if (payload.Empty()) {
return LoadResult(Containers::String("Failed to read file: ") + path);
}
return CreateVolumeFieldResource(path,
VolumeStorageKind::NanoVDB,
Math::Bounds(),
Math::Vector3::Zero(),
payload.Data(),
payload.Size());
}
ImportSettings* VolumeFieldLoader::GetDefaultSettings() const {
return nullptr;
}
REGISTER_RESOURCE_LOADER(VolumeFieldLoader);
} // namespace Resources
} // namespace XCEngine