Fix NanoVDB volume loading and rendering
This commit is contained in:
626381
tests/Rendering/integration/volume_scene/GT.ppm
Normal file
626381
tests/Rendering/integration/volume_scene/GT.ppm
Normal file
File diff suppressed because it is too large
Load Diff
269
tests/Rendering/integration/volume_scene/main.cpp
Normal file
269
tests/Rendering/integration/volume_scene/main.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "../RenderingIntegrationImageAssert.h"
|
||||
#include "../RenderingIntegrationMain.h"
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Components/VolumeRendererComponent.h>
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Core/Math/Vector2.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Rendering/RenderContext.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
#include <XCEngine/Rendering/Execution/SceneRenderer.h>
|
||||
#include <XCEngine/Rendering/Extraction/RenderSceneExtractor.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeField.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
|
||||
#include <XCEngine/RHI/RHITexture.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include "../../../RHI/integration/fixtures/RHIIntegrationFixture.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace XCEngine::Components;
|
||||
using namespace XCEngine::Math;
|
||||
using namespace XCEngine::Rendering;
|
||||
using namespace XCEngine::Resources;
|
||||
using namespace XCEngine::RHI;
|
||||
using namespace XCEngine::RHI::Integration;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kD3D12Screenshot = "volume_scene_d3d12.ppm";
|
||||
constexpr uint32_t kFrameWidth = 1280;
|
||||
constexpr uint32_t kFrameHeight = 720;
|
||||
constexpr const char* kVolumeFixtureRelativePath = "Res/Volumes/cloud.nvdb";
|
||||
|
||||
Material* CreateVolumetricMaterial() {
|
||||
auto* material = new Material();
|
||||
IResource::ConstructParams params = {};
|
||||
params.name = "VolumeMaterial";
|
||||
params.path = "Tests/Rendering/VolumeScene/Volume.material";
|
||||
params.guid = ResourceGUID::Generate(params.path);
|
||||
material->Initialize(params);
|
||||
material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinVolumetricShaderPath()));
|
||||
material->SetRenderQueue(MaterialRenderQueue::Transparent);
|
||||
material->SetFloat4("_Tint", Vector4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
material->SetFloat("_DensityScale", 0.2f);
|
||||
material->SetFloat("_StepSize", 1.0f);
|
||||
material->SetFloat("_MaxSteps", 2000.0f);
|
||||
material->SetFloat("_AmbientStrength", 0.005f);
|
||||
material->SetFloat4("_LightDirection", Vector4(0.5f, 0.8f, 0.3f, 0.0f));
|
||||
material->SetFloat("_LightSamples", 8.0f);
|
||||
return material;
|
||||
}
|
||||
|
||||
const char* GetScreenshotFilename(RHIType backendType) {
|
||||
switch (backendType) {
|
||||
case RHIType::D3D12:
|
||||
default:
|
||||
return kD3D12Screenshot;
|
||||
}
|
||||
}
|
||||
|
||||
class VolumeSceneTest : public RHIIntegrationFixture {
|
||||
protected:
|
||||
void SetUp() override;
|
||||
void TearDown() override;
|
||||
void RenderFrame() override;
|
||||
|
||||
private:
|
||||
RHIResourceView* GetCurrentBackBufferView();
|
||||
|
||||
std::unique_ptr<Scene> mScene;
|
||||
std::unique_ptr<SceneRenderer> mSceneRenderer;
|
||||
std::vector<RHIResourceView*> mBackBufferViews;
|
||||
RHITexture* mDepthTexture = nullptr;
|
||||
RHIResourceView* mDepthView = nullptr;
|
||||
Material* mVolumeMaterial = nullptr;
|
||||
VolumeField* mVolumeField = nullptr;
|
||||
};
|
||||
|
||||
void VolumeSceneTest::SetUp() {
|
||||
RHIIntegrationFixture::SetUp();
|
||||
|
||||
mSceneRenderer = std::make_unique<SceneRenderer>();
|
||||
mScene = std::make_unique<Scene>("VolumeScene");
|
||||
|
||||
mVolumeMaterial = CreateVolumetricMaterial();
|
||||
ASSERT_NE(mVolumeMaterial, nullptr);
|
||||
|
||||
const std::filesystem::path fixturePath =
|
||||
RenderingIntegrationTestUtils::ResolveRuntimePath(kVolumeFixtureRelativePath);
|
||||
ASSERT_TRUE(std::filesystem::exists(fixturePath)) << fixturePath.string();
|
||||
|
||||
VolumeFieldLoader volumeFieldLoader;
|
||||
LoadResult volumeResult = volumeFieldLoader.Load(fixturePath.string().c_str());
|
||||
ASSERT_TRUE(volumeResult);
|
||||
ASSERT_NE(volumeResult.resource, nullptr);
|
||||
mVolumeField = static_cast<VolumeField*>(volumeResult.resource);
|
||||
|
||||
GameObject* cameraObject = mScene->CreateGameObject("MainCamera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
camera->SetFieldOfView(45.0f);
|
||||
camera->SetNearClipPlane(0.1f);
|
||||
camera->SetFarClipPlane(5000.0f);
|
||||
camera->SetClearColor(XCEngine::Math::Color(0.03f, 0.04f, 0.06f, 1.0f));
|
||||
cameraObject->GetTransform()->SetLocalPosition(Vector3(-10.0f, 300.0f, -1200.0f));
|
||||
cameraObject->GetTransform()->LookAt(Vector3(-10.0f, 73.0f, 0.0f));
|
||||
|
||||
GameObject* volumeObject = mScene->CreateGameObject("CloudVolume");
|
||||
auto* volumeRenderer = volumeObject->AddComponent<VolumeRendererComponent>();
|
||||
volumeRenderer->SetVolumeField(mVolumeField);
|
||||
volumeRenderer->SetMaterial(mVolumeMaterial);
|
||||
volumeRenderer->SetCastShadows(false);
|
||||
volumeRenderer->SetReceiveShadows(false);
|
||||
|
||||
TextureDesc depthDesc = {};
|
||||
depthDesc.width = kFrameWidth;
|
||||
depthDesc.height = kFrameHeight;
|
||||
depthDesc.depth = 1;
|
||||
depthDesc.mipLevels = 1;
|
||||
depthDesc.arraySize = 1;
|
||||
depthDesc.format = static_cast<uint32_t>(Format::D24_UNorm_S8_UInt);
|
||||
depthDesc.textureType = static_cast<uint32_t>(XCEngine::RHI::TextureType::Texture2D);
|
||||
depthDesc.sampleCount = 1;
|
||||
depthDesc.sampleQuality = 0;
|
||||
depthDesc.flags = 0;
|
||||
mDepthTexture = GetDevice()->CreateTexture(depthDesc);
|
||||
ASSERT_NE(mDepthTexture, nullptr);
|
||||
|
||||
ResourceViewDesc depthViewDesc = {};
|
||||
depthViewDesc.format = static_cast<uint32_t>(Format::D24_UNorm_S8_UInt);
|
||||
depthViewDesc.dimension = ResourceViewDimension::Texture2D;
|
||||
depthViewDesc.mipLevel = 0;
|
||||
mDepthView = GetDevice()->CreateDepthStencilView(mDepthTexture, depthViewDesc);
|
||||
ASSERT_NE(mDepthView, nullptr);
|
||||
|
||||
mBackBufferViews.resize(2, nullptr);
|
||||
|
||||
RenderSceneExtractor extractor;
|
||||
const RenderSceneData sceneData = extractor.Extract(*mScene, nullptr, kFrameWidth, kFrameHeight);
|
||||
ASSERT_EQ(sceneData.visibleItems.size(), 0u);
|
||||
ASSERT_EQ(sceneData.visibleVolumes.size(), 1u);
|
||||
}
|
||||
|
||||
void VolumeSceneTest::TearDown() {
|
||||
mSceneRenderer.reset();
|
||||
|
||||
if (mDepthView != nullptr) {
|
||||
mDepthView->Shutdown();
|
||||
delete mDepthView;
|
||||
mDepthView = nullptr;
|
||||
}
|
||||
|
||||
if (mDepthTexture != nullptr) {
|
||||
mDepthTexture->Shutdown();
|
||||
delete mDepthTexture;
|
||||
mDepthTexture = nullptr;
|
||||
}
|
||||
|
||||
for (RHIResourceView*& backBufferView : mBackBufferViews) {
|
||||
if (backBufferView != nullptr) {
|
||||
backBufferView->Shutdown();
|
||||
delete backBufferView;
|
||||
backBufferView = nullptr;
|
||||
}
|
||||
}
|
||||
mBackBufferViews.clear();
|
||||
|
||||
mScene.reset();
|
||||
|
||||
delete mVolumeField;
|
||||
mVolumeField = nullptr;
|
||||
delete mVolumeMaterial;
|
||||
mVolumeMaterial = nullptr;
|
||||
|
||||
RHIIntegrationFixture::TearDown();
|
||||
}
|
||||
|
||||
RHIResourceView* VolumeSceneTest::GetCurrentBackBufferView() {
|
||||
const int backBufferIndex = GetCurrentBackBufferIndex();
|
||||
if (backBufferIndex < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(backBufferIndex) >= mBackBufferViews.size()) {
|
||||
mBackBufferViews.resize(static_cast<size_t>(backBufferIndex) + 1, nullptr);
|
||||
}
|
||||
|
||||
if (mBackBufferViews[backBufferIndex] == nullptr) {
|
||||
ResourceViewDesc viewDesc = {};
|
||||
viewDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
viewDesc.dimension = ResourceViewDimension::Texture2D;
|
||||
viewDesc.mipLevel = 0;
|
||||
mBackBufferViews[backBufferIndex] = GetDevice()->CreateRenderTargetView(GetCurrentBackBuffer(), viewDesc);
|
||||
}
|
||||
|
||||
return mBackBufferViews[backBufferIndex];
|
||||
}
|
||||
|
||||
void VolumeSceneTest::RenderFrame() {
|
||||
ASSERT_NE(mScene, nullptr);
|
||||
ASSERT_NE(mSceneRenderer, nullptr);
|
||||
|
||||
RHICommandList* commandList = GetCommandList();
|
||||
ASSERT_NE(commandList, nullptr);
|
||||
|
||||
commandList->Reset();
|
||||
|
||||
RenderSurface surface(kFrameWidth, kFrameHeight);
|
||||
surface.SetColorAttachment(GetCurrentBackBufferView());
|
||||
surface.SetDepthAttachment(mDepthView);
|
||||
|
||||
RenderContext renderContext = {};
|
||||
renderContext.device = GetDevice();
|
||||
renderContext.commandList = commandList;
|
||||
renderContext.commandQueue = GetCommandQueue();
|
||||
renderContext.backendType = GetBackendType();
|
||||
|
||||
ASSERT_TRUE(mSceneRenderer->Render(*mScene, nullptr, renderContext, surface));
|
||||
|
||||
commandList->Close();
|
||||
void* commandLists[] = { commandList };
|
||||
GetCommandQueue()->ExecuteCommandLists(1, commandLists);
|
||||
}
|
||||
|
||||
TEST_P(VolumeSceneTest, RenderNanoVdbVolumeScene) {
|
||||
RHICommandQueue* commandQueue = GetCommandQueue();
|
||||
RHISwapChain* swapChain = GetSwapChain();
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
const int targetFrameCount = 30;
|
||||
const char* screenshotFilename = GetScreenshotFilename(GetBackendType());
|
||||
|
||||
for (int frameCount = 0; frameCount <= targetFrameCount; ++frameCount) {
|
||||
if (frameCount > 0) {
|
||||
commandQueue->WaitForPreviousFrame();
|
||||
}
|
||||
|
||||
BeginRender();
|
||||
RenderFrame();
|
||||
|
||||
if (frameCount >= targetFrameCount) {
|
||||
commandQueue->WaitForIdle();
|
||||
ASSERT_TRUE(TakeScreenshot(screenshotFilename));
|
||||
ASSERT_TRUE(CompareWithGoldenTemplate(screenshotFilename, "GT.ppm", 0.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
swapChain->Present(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(D3D12, VolumeSceneTest, ::testing::Values(RHIType::D3D12));
|
||||
|
||||
GTEST_API_ int main(int argc, char** argv) {
|
||||
return RunRenderingIntegrationTestMain(argc, argv);
|
||||
}
|
||||
@@ -26,7 +26,10 @@ TEST(VolumeField, CreatePreservesPayloadAndMetadata) {
|
||||
payload,
|
||||
sizeof(payload),
|
||||
bounds,
|
||||
XCEngine::Math::Vector3(0.5f, 0.25f, 0.125f)));
|
||||
XCEngine::Math::Vector3(0.5f, 0.25f, 0.125f),
|
||||
VolumeIndexBounds{ -4, -5, -6, 7, 8, 9 },
|
||||
1u,
|
||||
2u));
|
||||
|
||||
EXPECT_TRUE(volumeField.IsValid());
|
||||
EXPECT_EQ(volumeField.GetType(), ResourceType::VolumeField);
|
||||
@@ -35,6 +38,9 @@ TEST(VolumeField, CreatePreservesPayloadAndMetadata) {
|
||||
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(volumeField.GetIndexBounds(), (VolumeIndexBounds{ -4, -5, -6, 7, 8, 9 }));
|
||||
EXPECT_EQ(volumeField.GetGridType(), 1u);
|
||||
EXPECT_EQ(volumeField.GetGridClass(), 2u);
|
||||
EXPECT_EQ(static_cast<const unsigned char*>(volumeField.GetPayloadData())[0], 1u);
|
||||
EXPECT_GT(volumeField.GetMemorySize(), sizeof(VolumeField));
|
||||
}
|
||||
|
||||
@@ -3,33 +3,100 @@
|
||||
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
||||
#include <XCEngine/Core/Asset/AssetRef.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Math/Bounds.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeField.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
|
||||
|
||||
#if defined(XCENGINE_HAS_NANOVDB)
|
||||
#include <nanovdb/GridHandle.h>
|
||||
#include <nanovdb/HostBuffer.h>
|
||||
#include <nanovdb/io/IO.h>
|
||||
#endif
|
||||
|
||||
#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
|
||||
};
|
||||
struct GeneratedNanoVDBVolume {
|
||||
size_t payloadSize = 0u;
|
||||
XCEngine::Math::Bounds bounds;
|
||||
XCEngine::Math::Vector3 voxelSize = XCEngine::Math::Vector3::Zero();
|
||||
VolumeIndexBounds indexBounds = {};
|
||||
XCEngine::Core::uint32 gridType = 0u;
|
||||
XCEngine::Core::uint32 gridClass = 0u;
|
||||
};
|
||||
|
||||
std::filesystem::path GetCloudVolumePath() {
|
||||
return std::filesystem::path(XCENGINE_TEST_CLOUD_NVDB_PATH);
|
||||
}
|
||||
|
||||
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()));
|
||||
void ExpectVector3Near(
|
||||
const XCEngine::Math::Vector3& actual,
|
||||
const XCEngine::Math::Vector3& expected,
|
||||
float epsilon = 1e-4f) {
|
||||
EXPECT_NEAR(actual.x, expected.x, epsilon);
|
||||
EXPECT_NEAR(actual.y, expected.y, epsilon);
|
||||
EXPECT_NEAR(actual.z, expected.z, epsilon);
|
||||
}
|
||||
|
||||
void ExpectIndexBoundsEq(
|
||||
const VolumeIndexBounds& actual,
|
||||
const VolumeIndexBounds& expected) {
|
||||
EXPECT_EQ(actual.minX, expected.minX);
|
||||
EXPECT_EQ(actual.minY, expected.minY);
|
||||
EXPECT_EQ(actual.minZ, expected.minZ);
|
||||
EXPECT_EQ(actual.maxX, expected.maxX);
|
||||
EXPECT_EQ(actual.maxY, expected.maxY);
|
||||
EXPECT_EQ(actual.maxZ, expected.maxZ);
|
||||
}
|
||||
|
||||
#if defined(XCENGINE_HAS_NANOVDB)
|
||||
|
||||
XCEngine::Math::Vector3 ToVector3(const nanovdb::Vec3d& value) {
|
||||
return XCEngine::Math::Vector3(
|
||||
static_cast<float>(value[0]),
|
||||
static_cast<float>(value[1]),
|
||||
static_cast<float>(value[2]));
|
||||
}
|
||||
|
||||
VolumeIndexBounds ToIndexBounds(const nanovdb::CoordBBox& value) {
|
||||
VolumeIndexBounds bounds = {};
|
||||
bounds.minX = value.min()[0];
|
||||
bounds.minY = value.min()[1];
|
||||
bounds.minZ = value.min()[2];
|
||||
bounds.maxX = value.max()[0];
|
||||
bounds.maxY = value.max()[1];
|
||||
bounds.maxZ = value.max()[2];
|
||||
return bounds;
|
||||
}
|
||||
|
||||
GeneratedNanoVDBVolume ReadTestNanoVDBFileMetadata(const std::filesystem::path& path) {
|
||||
nanovdb::GridHandle<nanovdb::HostBuffer> handle =
|
||||
nanovdb::io::readGrid<nanovdb::HostBuffer>(path.string());
|
||||
|
||||
GeneratedNanoVDBVolume generated;
|
||||
generated.payloadSize = static_cast<size_t>(handle.bufferSize());
|
||||
|
||||
const nanovdb::GridMetaData* metadata = handle.gridMetaData();
|
||||
if (metadata != nullptr) {
|
||||
generated.bounds.SetMinMax(
|
||||
ToVector3(metadata->worldBBox().min()),
|
||||
ToVector3(metadata->worldBBox().max()));
|
||||
generated.voxelSize = ToVector3(metadata->voxelSize());
|
||||
generated.indexBounds = ToIndexBounds(metadata->indexBBox());
|
||||
generated.gridType = static_cast<XCEngine::Core::uint32>(metadata->gridType());
|
||||
generated.gridClass = static_cast<XCEngine::Core::uint32>(metadata->gridClass());
|
||||
}
|
||||
|
||||
return generated;
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(VolumeFieldLoader, GetResourceType) {
|
||||
VolumeFieldLoader loader;
|
||||
EXPECT_EQ(loader.GetResourceType(), ResourceType::VolumeField);
|
||||
@@ -48,12 +115,14 @@ TEST(VolumeFieldLoader, LoadInvalidPath) {
|
||||
EXPECT_FALSE(result);
|
||||
}
|
||||
|
||||
TEST(VolumeFieldLoader, LoadSourceNanoVDBBlob) {
|
||||
#if defined(XCENGINE_HAS_NANOVDB)
|
||||
TEST(VolumeFieldLoader, LoadSourceNanoVDBGridPayload) {
|
||||
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);
|
||||
const fs::path volumePath = GetCloudVolumePath();
|
||||
ASSERT_TRUE(fs::exists(volumePath));
|
||||
|
||||
const GeneratedNanoVDBVolume generated = ReadTestNanoVDBFileMetadata(volumePath);
|
||||
|
||||
VolumeFieldLoader loader;
|
||||
LoadResult result = loader.Load(volumePath.string().c_str());
|
||||
@@ -62,13 +131,19 @@ TEST(VolumeFieldLoader, LoadSourceNanoVDBBlob) {
|
||||
|
||||
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]);
|
||||
EXPECT_EQ(volumeField->GetPayloadSize(), generated.payloadSize);
|
||||
ExpectVector3Near(volumeField->GetBounds().GetMin(), generated.bounds.GetMin());
|
||||
ExpectVector3Near(volumeField->GetBounds().GetMax(), generated.bounds.GetMax());
|
||||
ExpectVector3Near(volumeField->GetVoxelSize(), generated.voxelSize);
|
||||
ExpectIndexBoundsEq(volumeField->GetIndexBounds(), generated.indexBounds);
|
||||
EXPECT_EQ(volumeField->GetGridType(), generated.gridType);
|
||||
EXPECT_EQ(volumeField->GetGridClass(), generated.gridClass);
|
||||
|
||||
delete volumeField;
|
||||
fs::remove(volumePath);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(XCENGINE_HAS_NANOVDB)
|
||||
TEST(VolumeFieldLoader, AssetDatabaseCreatesVolumeArtifactAndReusesItWithoutReimport) {
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::chrono_literals;
|
||||
@@ -76,10 +151,13 @@ TEST(VolumeFieldLoader, AssetDatabaseCreatesVolumeArtifactAndReusesItWithoutReim
|
||||
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";
|
||||
const fs::path fixturePath = GetCloudVolumePath();
|
||||
|
||||
fs::remove_all(projectRoot);
|
||||
fs::create_directories(assetsDir);
|
||||
WriteBinaryFile(volumePath, MakeTestNanoVDBPayload());
|
||||
ASSERT_TRUE(fs::exists(fixturePath));
|
||||
fs::copy_file(fixturePath, volumePath, fs::copy_options::overwrite_existing);
|
||||
const GeneratedNanoVDBVolume generated = ReadTestNanoVDBFileMetadata(fixturePath);
|
||||
|
||||
AssetDatabase database;
|
||||
database.Initialize(projectRoot.string().c_str());
|
||||
@@ -97,7 +175,13 @@ TEST(VolumeFieldLoader, AssetDatabaseCreatesVolumeArtifactAndReusesItWithoutReim
|
||||
ASSERT_NE(artifactLoad.resource, nullptr);
|
||||
auto* artifactVolume = static_cast<VolumeField*>(artifactLoad.resource);
|
||||
EXPECT_EQ(artifactVolume->GetStorageKind(), VolumeStorageKind::NanoVDB);
|
||||
EXPECT_EQ(artifactVolume->GetPayloadSize(), MakeTestNanoVDBPayload().size());
|
||||
EXPECT_EQ(artifactVolume->GetPayloadSize(), generated.payloadSize);
|
||||
ExpectVector3Near(artifactVolume->GetBounds().GetMin(), generated.bounds.GetMin());
|
||||
ExpectVector3Near(artifactVolume->GetBounds().GetMax(), generated.bounds.GetMax());
|
||||
ExpectVector3Near(artifactVolume->GetVoxelSize(), generated.voxelSize);
|
||||
ExpectIndexBoundsEq(artifactVolume->GetIndexBounds(), generated.indexBounds);
|
||||
EXPECT_EQ(artifactVolume->GetGridType(), generated.gridType);
|
||||
EXPECT_EQ(artifactVolume->GetGridClass(), generated.gridClass);
|
||||
delete artifactVolume;
|
||||
|
||||
const auto originalArtifactWriteTime = fs::last_write_time(firstResolve.artifactMainPath.CStr());
|
||||
@@ -111,7 +195,13 @@ TEST(VolumeFieldLoader, AssetDatabaseCreatesVolumeArtifactAndReusesItWithoutReim
|
||||
database.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
#else
|
||||
TEST(VolumeFieldLoader, AssetDatabaseCreatesVolumeArtifactAndReusesItWithoutReimport) {
|
||||
GTEST_SKIP() << "NanoVDB headers are unavailable in this build";
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(XCENGINE_HAS_NANOVDB)
|
||||
TEST(VolumeFieldLoader, ResourceManagerLoadsVolumeByAssetRefFromProjectAssets) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -121,11 +211,13 @@ TEST(VolumeFieldLoader, ResourceManagerLoadsVolumeByAssetRefFromProjectAssets) {
|
||||
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();
|
||||
const fs::path fixturePath = GetCloudVolumePath();
|
||||
|
||||
fs::remove_all(projectRoot);
|
||||
fs::create_directories(assetsDir);
|
||||
WriteBinaryFile(volumePath, bytes);
|
||||
ASSERT_TRUE(fs::exists(fixturePath));
|
||||
fs::copy_file(fixturePath, volumePath, fs::copy_options::overwrite_existing);
|
||||
const GeneratedNanoVDBVolume generated = ReadTestNanoVDBFileMetadata(fixturePath);
|
||||
|
||||
manager.SetResourceRoot(projectRoot.string().c_str());
|
||||
|
||||
@@ -133,7 +225,13 @@ TEST(VolumeFieldLoader, ResourceManagerLoadsVolumeByAssetRefFromProjectAssets) {
|
||||
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());
|
||||
EXPECT_EQ(firstHandle->GetPayloadSize(), generated.payloadSize);
|
||||
ExpectVector3Near(firstHandle->GetBounds().GetMin(), generated.bounds.GetMin());
|
||||
ExpectVector3Near(firstHandle->GetBounds().GetMax(), generated.bounds.GetMax());
|
||||
ExpectVector3Near(firstHandle->GetVoxelSize(), generated.voxelSize);
|
||||
ExpectIndexBoundsEq(firstHandle->GetIndexBounds(), generated.indexBounds);
|
||||
EXPECT_EQ(firstHandle->GetGridType(), generated.gridType);
|
||||
EXPECT_EQ(firstHandle->GetGridClass(), generated.gridClass);
|
||||
|
||||
AssetRef assetRef;
|
||||
ASSERT_TRUE(manager.TryGetAssetRef("Assets/cloud.nvdb", ResourceType::VolumeField, assetRef));
|
||||
@@ -144,12 +242,23 @@ TEST(VolumeFieldLoader, ResourceManagerLoadsVolumeByAssetRefFromProjectAssets) {
|
||||
const auto secondHandle = manager.Load<VolumeField>(assetRef);
|
||||
ASSERT_TRUE(secondHandle.IsValid());
|
||||
EXPECT_EQ(secondHandle->GetStorageKind(), VolumeStorageKind::NanoVDB);
|
||||
EXPECT_EQ(secondHandle->GetPayloadSize(), bytes.size());
|
||||
EXPECT_EQ(secondHandle->GetPayloadSize(), generated.payloadSize);
|
||||
ExpectVector3Near(secondHandle->GetBounds().GetMin(), generated.bounds.GetMin());
|
||||
ExpectVector3Near(secondHandle->GetBounds().GetMax(), generated.bounds.GetMax());
|
||||
ExpectVector3Near(secondHandle->GetVoxelSize(), generated.voxelSize);
|
||||
ExpectIndexBoundsEq(secondHandle->GetIndexBounds(), generated.indexBounds);
|
||||
EXPECT_EQ(secondHandle->GetGridType(), generated.gridType);
|
||||
EXPECT_EQ(secondHandle->GetGridClass(), generated.gridClass);
|
||||
}
|
||||
|
||||
manager.SetResourceRoot("");
|
||||
manager.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
#else
|
||||
TEST(VolumeFieldLoader, ResourceManagerLoadsVolumeByAssetRefFromProjectAssets) {
|
||||
GTEST_SKIP() << "NanoVDB headers are unavailable in this build";
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user