Add VolumeField NanoVDB asset pipeline
This commit is contained in:
@@ -24,6 +24,7 @@ TEST(Resources_Types, ResourceType_EnumValues) {
|
||||
EXPECT_EQ(static_cast<uint8_t>(ResourceType::UIView), 13);
|
||||
EXPECT_EQ(static_cast<uint8_t>(ResourceType::UITheme), 14);
|
||||
EXPECT_EQ(static_cast<uint8_t>(ResourceType::UISchema), 15);
|
||||
EXPECT_EQ(static_cast<uint8_t>(ResourceType::VolumeField), 16);
|
||||
}
|
||||
|
||||
TEST(Resources_Types, GetResourceTypeName) {
|
||||
@@ -33,6 +34,7 @@ TEST(Resources_Types, GetResourceTypeName) {
|
||||
EXPECT_STREQ(GetResourceTypeName(ResourceType::UIView), "UIView");
|
||||
EXPECT_STREQ(GetResourceTypeName(ResourceType::UITheme), "UITheme");
|
||||
EXPECT_STREQ(GetResourceTypeName(ResourceType::UISchema), "UISchema");
|
||||
EXPECT_STREQ(GetResourceTypeName(ResourceType::VolumeField), "VolumeField");
|
||||
EXPECT_STREQ(GetResourceTypeName(ResourceType::Unknown), "Unknown");
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ TEST(Resources_Types, GetResourceType_TemplateSpecializations) {
|
||||
EXPECT_EQ(GetResourceType<UIView>(), ResourceType::UIView);
|
||||
EXPECT_EQ(GetResourceType<UITheme>(), ResourceType::UITheme);
|
||||
EXPECT_EQ(GetResourceType<UISchema>(), ResourceType::UISchema);
|
||||
EXPECT_EQ(GetResourceType<VolumeField>(), ResourceType::VolumeField);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -8,3 +8,4 @@ add_subdirectory(Material)
|
||||
add_subdirectory(Shader)
|
||||
add_subdirectory(AudioClip)
|
||||
add_subdirectory(UI)
|
||||
add_subdirectory(Volume)
|
||||
|
||||
35
tests/Resources/Volume/CMakeLists.txt
Normal file
35
tests/Resources/Volume/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
# ============================================================
|
||||
# Volume Tests
|
||||
# ============================================================
|
||||
|
||||
set(VOLUME_TEST_SOURCES
|
||||
test_volume_field.cpp
|
||||
test_volume_field_loader.cpp
|
||||
)
|
||||
|
||||
add_executable(volume_tests ${VOLUME_TEST_SOURCES})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties(volume_tests PROPERTIES
|
||||
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(volume_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(volume_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/tests/Fixtures
|
||||
)
|
||||
|
||||
target_compile_definitions(volume_tests PRIVATE
|
||||
XCENGINE_TEST_FIXTURES_DIR="${CMAKE_SOURCE_DIR}/tests/Fixtures"
|
||||
)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(volume_tests)
|
||||
42
tests/Resources/Volume/test_volume_field.cpp
Normal file
42
tests/Resources/Volume/test_volume_field.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Resources/Volume/VolumeField.h>
|
||||
|
||||
using namespace XCEngine::Resources;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(VolumeField, CreatePreservesPayloadAndMetadata) {
|
||||
const unsigned char payload[] = { 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
VolumeField volumeField;
|
||||
IResource::ConstructParams params;
|
||||
params.name = "cloud.nvdb";
|
||||
params.path = "Assets/cloud.nvdb";
|
||||
params.guid = ResourceGUID::Generate(params.path);
|
||||
volumeField.Initialize(params);
|
||||
|
||||
XCEngine::Math::Bounds bounds;
|
||||
bounds.SetMinMax(
|
||||
XCEngine::Math::Vector3(-1.0f, -2.0f, -3.0f),
|
||||
XCEngine::Math::Vector3(4.0f, 5.0f, 6.0f));
|
||||
|
||||
ASSERT_TRUE(volumeField.Create(
|
||||
VolumeStorageKind::NanoVDB,
|
||||
payload,
|
||||
sizeof(payload),
|
||||
bounds,
|
||||
XCEngine::Math::Vector3(0.5f, 0.25f, 0.125f)));
|
||||
|
||||
EXPECT_TRUE(volumeField.IsValid());
|
||||
EXPECT_EQ(volumeField.GetType(), ResourceType::VolumeField);
|
||||
EXPECT_EQ(volumeField.GetStorageKind(), VolumeStorageKind::NanoVDB);
|
||||
EXPECT_EQ(volumeField.GetPayloadSize(), sizeof(payload));
|
||||
EXPECT_EQ(volumeField.GetBounds().GetMin(), XCEngine::Math::Vector3(-1.0f, -2.0f, -3.0f));
|
||||
EXPECT_EQ(volumeField.GetBounds().GetMax(), XCEngine::Math::Vector3(4.0f, 5.0f, 6.0f));
|
||||
EXPECT_EQ(volumeField.GetVoxelSize(), XCEngine::Math::Vector3(0.5f, 0.25f, 0.125f));
|
||||
EXPECT_EQ(static_cast<const unsigned char*>(volumeField.GetPayloadData())[0], 1u);
|
||||
EXPECT_GT(volumeField.GetMemorySize(), sizeof(VolumeField));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
155
tests/Resources/Volume/test_volume_field_loader.cpp
Normal file
155
tests/Resources/Volume/test_volume_field_loader.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
||||
#include <XCEngine/Core/Asset/AssetRef.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeField.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
using namespace XCEngine::Resources;
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<unsigned char> MakeTestNanoVDBPayload() {
|
||||
return {
|
||||
0x4E, 0x56, 0x44, 0x42,
|
||||
0x10, 0x20, 0x30, 0x40,
|
||||
0x01, 0x03, 0x05, 0x07,
|
||||
0xAA, 0xBB, 0xCC, 0xDD
|
||||
};
|
||||
}
|
||||
|
||||
void WriteBinaryFile(const std::filesystem::path& path, const std::vector<unsigned char>& bytes) {
|
||||
std::ofstream output(path, std::ios::binary | std::ios::trunc);
|
||||
output.write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
|
||||
}
|
||||
|
||||
TEST(VolumeFieldLoader, GetResourceType) {
|
||||
VolumeFieldLoader loader;
|
||||
EXPECT_EQ(loader.GetResourceType(), ResourceType::VolumeField);
|
||||
}
|
||||
|
||||
TEST(VolumeFieldLoader, CanLoad) {
|
||||
VolumeFieldLoader loader;
|
||||
EXPECT_TRUE(loader.CanLoad("cloud.nvdb"));
|
||||
EXPECT_TRUE(loader.CanLoad("cloud.xcvol"));
|
||||
EXPECT_FALSE(loader.CanLoad("cloud.txt"));
|
||||
}
|
||||
|
||||
TEST(VolumeFieldLoader, LoadInvalidPath) {
|
||||
VolumeFieldLoader loader;
|
||||
LoadResult result = loader.Load("invalid/path/cloud.nvdb");
|
||||
EXPECT_FALSE(result);
|
||||
}
|
||||
|
||||
TEST(VolumeFieldLoader, LoadSourceNanoVDBBlob) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path volumePath = fs::temp_directory_path() / "xc_volume_loader_source_test.nvdb";
|
||||
const std::vector<unsigned char> bytes = MakeTestNanoVDBPayload();
|
||||
WriteBinaryFile(volumePath, bytes);
|
||||
|
||||
VolumeFieldLoader loader;
|
||||
LoadResult result = loader.Load(volumePath.string().c_str());
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_NE(result.resource, nullptr);
|
||||
|
||||
auto* volumeField = static_cast<VolumeField*>(result.resource);
|
||||
EXPECT_EQ(volumeField->GetStorageKind(), VolumeStorageKind::NanoVDB);
|
||||
EXPECT_EQ(volumeField->GetPayloadSize(), bytes.size());
|
||||
EXPECT_EQ(static_cast<const unsigned char*>(volumeField->GetPayloadData())[0], bytes[0]);
|
||||
|
||||
delete volumeField;
|
||||
fs::remove(volumePath);
|
||||
}
|
||||
|
||||
TEST(VolumeFieldLoader, AssetDatabaseCreatesVolumeArtifactAndReusesItWithoutReimport) {
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const fs::path projectRoot = fs::temp_directory_path() / "xc_volume_library_cache_test";
|
||||
const fs::path assetsDir = projectRoot / "Assets";
|
||||
const fs::path volumePath = assetsDir / "cloud.nvdb";
|
||||
|
||||
fs::remove_all(projectRoot);
|
||||
fs::create_directories(assetsDir);
|
||||
WriteBinaryFile(volumePath, MakeTestNanoVDBPayload());
|
||||
|
||||
AssetDatabase database;
|
||||
database.Initialize(projectRoot.string().c_str());
|
||||
|
||||
AssetDatabase::ResolvedAsset firstResolve;
|
||||
ASSERT_TRUE(database.EnsureArtifact("Assets/cloud.nvdb", ResourceType::VolumeField, firstResolve));
|
||||
ASSERT_TRUE(firstResolve.exists);
|
||||
ASSERT_TRUE(firstResolve.artifactReady);
|
||||
EXPECT_TRUE(fs::exists(firstResolve.artifactMainPath.CStr()));
|
||||
EXPECT_EQ(fs::path(firstResolve.artifactMainPath.CStr()).extension().generic_string(), ".xcvol");
|
||||
|
||||
VolumeFieldLoader loader;
|
||||
LoadResult artifactLoad = loader.Load(firstResolve.artifactMainPath);
|
||||
ASSERT_TRUE(artifactLoad);
|
||||
ASSERT_NE(artifactLoad.resource, nullptr);
|
||||
auto* artifactVolume = static_cast<VolumeField*>(artifactLoad.resource);
|
||||
EXPECT_EQ(artifactVolume->GetStorageKind(), VolumeStorageKind::NanoVDB);
|
||||
EXPECT_EQ(artifactVolume->GetPayloadSize(), MakeTestNanoVDBPayload().size());
|
||||
delete artifactVolume;
|
||||
|
||||
const auto originalArtifactWriteTime = fs::last_write_time(firstResolve.artifactMainPath.CStr());
|
||||
std::this_thread::sleep_for(50ms);
|
||||
|
||||
AssetDatabase::ResolvedAsset secondResolve;
|
||||
ASSERT_TRUE(database.EnsureArtifact("Assets/cloud.nvdb", ResourceType::VolumeField, secondResolve));
|
||||
EXPECT_EQ(firstResolve.artifactMainPath, secondResolve.artifactMainPath);
|
||||
EXPECT_EQ(originalArtifactWriteTime, fs::last_write_time(secondResolve.artifactMainPath.CStr()));
|
||||
|
||||
database.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
|
||||
TEST(VolumeFieldLoader, ResourceManagerLoadsVolumeByAssetRefFromProjectAssets) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
ResourceManager& manager = ResourceManager::Get();
|
||||
manager.Initialize();
|
||||
|
||||
const fs::path projectRoot = fs::temp_directory_path() / "xc_volume_asset_ref_test";
|
||||
const fs::path assetsDir = projectRoot / "Assets";
|
||||
const fs::path volumePath = assetsDir / "cloud.nvdb";
|
||||
const std::vector<unsigned char> bytes = MakeTestNanoVDBPayload();
|
||||
|
||||
fs::remove_all(projectRoot);
|
||||
fs::create_directories(assetsDir);
|
||||
WriteBinaryFile(volumePath, bytes);
|
||||
|
||||
manager.SetResourceRoot(projectRoot.string().c_str());
|
||||
|
||||
{
|
||||
const auto firstHandle = manager.Load<VolumeField>("Assets/cloud.nvdb");
|
||||
ASSERT_TRUE(firstHandle.IsValid());
|
||||
EXPECT_EQ(firstHandle->GetStorageKind(), VolumeStorageKind::NanoVDB);
|
||||
EXPECT_EQ(firstHandle->GetPayloadSize(), bytes.size());
|
||||
|
||||
AssetRef assetRef;
|
||||
ASSERT_TRUE(manager.TryGetAssetRef("Assets/cloud.nvdb", ResourceType::VolumeField, assetRef));
|
||||
EXPECT_TRUE(assetRef.IsValid());
|
||||
|
||||
manager.UnloadAll();
|
||||
|
||||
const auto secondHandle = manager.Load<VolumeField>(assetRef);
|
||||
ASSERT_TRUE(secondHandle.IsValid());
|
||||
EXPECT_EQ(secondHandle->GetStorageKind(), VolumeStorageKind::NanoVDB);
|
||||
EXPECT_EQ(secondHandle->GetPayloadSize(), bytes.size());
|
||||
}
|
||||
|
||||
manager.SetResourceRoot("");
|
||||
manager.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user