docs(editor): sync script assembly builder api docs
This commit is contained in:
@@ -28,13 +28,10 @@
|
||||
#include "../../../RHI/integration/fixtures/RHIIntegrationFixture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
@@ -51,23 +48,12 @@ namespace {
|
||||
constexpr const char* kD3D12Screenshot = "gaussian_splat_scene_d3d12.ppm";
|
||||
constexpr const char* kOpenGLScreenshot = "gaussian_splat_scene_opengl.ppm";
|
||||
constexpr const char* kVulkanScreenshot = "gaussian_splat_scene_vulkan.ppm";
|
||||
constexpr const char* kD3D12AlphaDebugScreenshot = "gaussian_splat_scene_d3d12_alpha.ppm";
|
||||
constexpr const char* kOpenGLAlphaDebugScreenshot = "gaussian_splat_scene_opengl_alpha.ppm";
|
||||
constexpr const char* kVulkanAlphaDebugScreenshot = "gaussian_splat_scene_vulkan_alpha.ppm";
|
||||
constexpr uint32_t kFrameWidth = 1280;
|
||||
constexpr uint32_t kFrameHeight = 720;
|
||||
constexpr uint32_t kBaselineSubsetSplatCount = 262144u;
|
||||
constexpr uint32_t kBaselineSubsetSplatCount = 65536u;
|
||||
constexpr const char* kSubsetGaussianSplatAssetPath = "Assets/room_subset.xcgsplat";
|
||||
constexpr float kTargetSceneExtent = 4.0f;
|
||||
constexpr float kGaussianPointScale = 1.00f;
|
||||
const Vector3 kDefaultCameraPosition(0.0f, 1.0f, 1.0f);
|
||||
const Vector3 kDefaultCameraLookAt(0.0f, 1.0f, 0.0f);
|
||||
const Vector3 kDefaultRootPosition = Vector3::Zero();
|
||||
|
||||
enum class GaussianSplatDebugView : uint8_t {
|
||||
Scene = 0,
|
||||
Alpha = 1
|
||||
};
|
||||
constexpr float kGaussianPointScale = 3.00f;
|
||||
|
||||
XCEngine::Core::uint16 FloatToHalfBits(float value) {
|
||||
uint32_t bits = 0u;
|
||||
@@ -142,178 +128,15 @@ void LinkOrCopyFixture(const std::filesystem::path& sourcePath, const std::files
|
||||
ASSERT_FALSE(ec) << ec.message();
|
||||
}
|
||||
|
||||
GaussianSplatDebugView GetDebugViewFromEnvironment() {
|
||||
const char* debugViewValue = std::getenv("XCENGINE_GAUSSIAN_SPLAT_DEBUG_VIEW");
|
||||
if (debugViewValue == nullptr) {
|
||||
return GaussianSplatDebugView::Scene;
|
||||
}
|
||||
|
||||
if (_stricmp(debugViewValue, "alpha") == 0 ||
|
||||
std::strcmp(debugViewValue, "1") == 0) {
|
||||
return GaussianSplatDebugView::Alpha;
|
||||
}
|
||||
|
||||
return GaussianSplatDebugView::Scene;
|
||||
}
|
||||
|
||||
float GetFloatFromEnvironment(const char* name, float defaultValue) {
|
||||
const char* value = std::getenv(name);
|
||||
if (value == nullptr || value[0] == '\0') {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
const float parsedValue = std::strtof(value, &end);
|
||||
return end != value ? parsedValue : defaultValue;
|
||||
}
|
||||
|
||||
uint32_t GetUIntFromEnvironment(const char* name, uint32_t defaultValue) {
|
||||
const char* value = std::getenv(name);
|
||||
if (value == nullptr || value[0] == '\0') {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
const unsigned long parsedValue = std::strtoul(value, &end, 10);
|
||||
return end != value ? static_cast<uint32_t>(parsedValue) : defaultValue;
|
||||
}
|
||||
|
||||
Vector3 GetVector3FromEnvironment(const char* name, const Vector3& defaultValue) {
|
||||
const char* value = std::getenv(name);
|
||||
if (value == nullptr || value[0] == '\0') {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
if (std::sscanf(value, "%f,%f,%f", &x, &y, &z) == 3) {
|
||||
return Vector3(x, y, z);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void ExpectVector3Near(const Vector3& actual, const Vector3& expected, float epsilon = 1.0e-6f) {
|
||||
EXPECT_NEAR(actual.x, expected.x, epsilon);
|
||||
EXPECT_NEAR(actual.y, expected.y, epsilon);
|
||||
EXPECT_NEAR(actual.z, expected.z, epsilon);
|
||||
}
|
||||
|
||||
void ExpectVector4Near(const Vector4& actual, const Vector4& expected, float epsilon = 1.0e-6f) {
|
||||
EXPECT_NEAR(actual.x, expected.x, epsilon);
|
||||
EXPECT_NEAR(actual.y, expected.y, epsilon);
|
||||
EXPECT_NEAR(actual.z, expected.z, epsilon);
|
||||
EXPECT_NEAR(actual.w, expected.w, epsilon);
|
||||
}
|
||||
|
||||
void ExpectQuaternionNear(const Quaternion& actual, const Quaternion& expected, float epsilon = 1.0e-6f) {
|
||||
EXPECT_NEAR(actual.x, expected.x, epsilon);
|
||||
EXPECT_NEAR(actual.y, expected.y, epsilon);
|
||||
EXPECT_NEAR(actual.z, expected.z, epsilon);
|
||||
EXPECT_NEAR(actual.w, expected.w, epsilon);
|
||||
}
|
||||
|
||||
void ExpectGaussianSplatRoundTripMatches(
|
||||
const GaussianSplat& source,
|
||||
const GaussianSplat& loaded) {
|
||||
ASSERT_EQ(source.GetSplatCount(), loaded.GetSplatCount());
|
||||
ASSERT_EQ(source.GetChunkCount(), loaded.GetChunkCount());
|
||||
ASSERT_EQ(source.GetSHOrder(), loaded.GetSHOrder());
|
||||
ExpectVector3Near(loaded.GetBounds().GetMin(), source.GetBounds().GetMin());
|
||||
ExpectVector3Near(loaded.GetBounds().GetMax(), source.GetBounds().GetMax());
|
||||
ASSERT_NE(source.GetPositionRecords(), nullptr);
|
||||
ASSERT_NE(loaded.GetPositionRecords(), nullptr);
|
||||
ASSERT_NE(source.GetOtherRecords(), nullptr);
|
||||
ASSERT_NE(loaded.GetOtherRecords(), nullptr);
|
||||
ASSERT_NE(source.GetColorRecords(), nullptr);
|
||||
ASSERT_NE(loaded.GetColorRecords(), nullptr);
|
||||
ASSERT_NE(source.GetSHRecords(), nullptr);
|
||||
ASSERT_NE(loaded.GetSHRecords(), nullptr);
|
||||
|
||||
const uint32_t sampleIndices[] = {
|
||||
0u,
|
||||
source.GetSplatCount() / 2u,
|
||||
source.GetSplatCount() - 1u
|
||||
};
|
||||
for (uint32_t sampleIndex : sampleIndices) {
|
||||
ExpectVector3Near(
|
||||
loaded.GetPositionRecords()[sampleIndex].position,
|
||||
source.GetPositionRecords()[sampleIndex].position);
|
||||
ExpectQuaternionNear(
|
||||
loaded.GetOtherRecords()[sampleIndex].rotation,
|
||||
source.GetOtherRecords()[sampleIndex].rotation);
|
||||
ExpectVector3Near(
|
||||
loaded.GetOtherRecords()[sampleIndex].scale,
|
||||
source.GetOtherRecords()[sampleIndex].scale);
|
||||
ExpectVector4Near(
|
||||
loaded.GetColorRecords()[sampleIndex].colorOpacity,
|
||||
source.GetColorRecords()[sampleIndex].colorOpacity);
|
||||
|
||||
for (uint32_t coefficientIndex = 0u; coefficientIndex < kGaussianSplatSHCoefficientCount; ++coefficientIndex) {
|
||||
EXPECT_NEAR(
|
||||
loaded.GetSHRecords()[sampleIndex].coefficients[coefficientIndex],
|
||||
source.GetSHRecords()[sampleIndex].coefficients[coefficientIndex],
|
||||
1.0e-6f);
|
||||
}
|
||||
}
|
||||
|
||||
const auto* sourceChunkSection = static_cast<const GaussianSplatChunkRecord*>(
|
||||
source.GetSectionData(GaussianSplatSectionType::Chunks));
|
||||
const auto* loadedChunkSection = static_cast<const GaussianSplatChunkRecord*>(
|
||||
loaded.GetSectionData(GaussianSplatSectionType::Chunks));
|
||||
ASSERT_NE(sourceChunkSection, nullptr);
|
||||
ASSERT_NE(loadedChunkSection, nullptr);
|
||||
if (source.GetChunkCount() > 0u) {
|
||||
const uint32_t chunkIndices[] = { 0u, source.GetChunkCount() - 1u };
|
||||
for (uint32_t chunkIndex : chunkIndices) {
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].colR, sourceChunkSection[chunkIndex].colR);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].colG, sourceChunkSection[chunkIndex].colG);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].colB, sourceChunkSection[chunkIndex].colB);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].colA, sourceChunkSection[chunkIndex].colA);
|
||||
ExpectVector3Near(
|
||||
Vector3(
|
||||
loadedChunkSection[chunkIndex].posX.x,
|
||||
loadedChunkSection[chunkIndex].posY.x,
|
||||
loadedChunkSection[chunkIndex].posZ.x),
|
||||
Vector3(
|
||||
sourceChunkSection[chunkIndex].posX.x,
|
||||
sourceChunkSection[chunkIndex].posY.x,
|
||||
sourceChunkSection[chunkIndex].posZ.x));
|
||||
ExpectVector3Near(
|
||||
Vector3(
|
||||
loadedChunkSection[chunkIndex].posX.y,
|
||||
loadedChunkSection[chunkIndex].posY.y,
|
||||
loadedChunkSection[chunkIndex].posZ.y),
|
||||
Vector3(
|
||||
sourceChunkSection[chunkIndex].posX.y,
|
||||
sourceChunkSection[chunkIndex].posY.y,
|
||||
sourceChunkSection[chunkIndex].posZ.y));
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].sclX, sourceChunkSection[chunkIndex].sclX);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].sclY, sourceChunkSection[chunkIndex].sclY);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].sclZ, sourceChunkSection[chunkIndex].sclZ);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].shR, sourceChunkSection[chunkIndex].shR);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].shG, sourceChunkSection[chunkIndex].shG);
|
||||
EXPECT_EQ(loadedChunkSection[chunkIndex].shB, sourceChunkSection[chunkIndex].shB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetScreenshotFilename(RHIType backendType, GaussianSplatDebugView debugView) {
|
||||
const char* GetScreenshotFilename(RHIType backendType) {
|
||||
switch (backendType) {
|
||||
case RHIType::D3D12:
|
||||
return debugView == GaussianSplatDebugView::Alpha
|
||||
? kD3D12AlphaDebugScreenshot
|
||||
: kD3D12Screenshot;
|
||||
return kD3D12Screenshot;
|
||||
case RHIType::Vulkan:
|
||||
return debugView == GaussianSplatDebugView::Alpha
|
||||
? kVulkanAlphaDebugScreenshot
|
||||
: kVulkanScreenshot;
|
||||
return kVulkanScreenshot;
|
||||
case RHIType::OpenGL:
|
||||
default:
|
||||
return debugView == GaussianSplatDebugView::Alpha
|
||||
? kOpenGLAlphaDebugScreenshot
|
||||
: kOpenGLScreenshot;
|
||||
return kOpenGLScreenshot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,8 +180,6 @@ GaussianSplat* CreateGaussianSplatSubset(
|
||||
std::vector<GaussianSplatOtherRecord> subsetOther(subsetSplatCount);
|
||||
std::vector<GaussianSplatColorRecord> subsetColors(subsetSplatCount);
|
||||
std::vector<GaussianSplatSHRecord> subsetSh(shSection != nullptr && sh != nullptr ? subsetSplatCount : 0u);
|
||||
Bounds subsetBounds;
|
||||
bool hasSubsetBounds = false;
|
||||
for (uint32_t subsetIndex = 0u; subsetIndex < subsetSplatCount; ++subsetIndex) {
|
||||
const uint32_t sourceIndex = selectedIndices[subsetIndex];
|
||||
subsetPositions[subsetIndex] = positions[sourceIndex];
|
||||
@@ -367,14 +188,6 @@ GaussianSplat* CreateGaussianSplatSubset(
|
||||
if (!subsetSh.empty()) {
|
||||
subsetSh[subsetIndex] = sh[sourceIndex];
|
||||
}
|
||||
|
||||
const Vector3& position = subsetPositions[subsetIndex].position;
|
||||
if (!hasSubsetBounds) {
|
||||
subsetBounds.SetMinMax(position, position);
|
||||
hasSubsetBounds = true;
|
||||
} else {
|
||||
subsetBounds.Encapsulate(position);
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t subsetChunkCount =
|
||||
@@ -483,7 +296,7 @@ GaussianSplat* CreateGaussianSplatSubset(
|
||||
|
||||
GaussianSplatMetadata metadata = source.GetMetadata();
|
||||
metadata.splatCount = subsetSplatCount;
|
||||
metadata.bounds = subsetBounds;
|
||||
metadata.bounds = source.GetBounds();
|
||||
metadata.chunkCount = subsetChunkCount;
|
||||
metadata.cameraCount = 0u;
|
||||
metadata.chunkFormat = GaussianSplatSectionFormat::ChunkFloat32;
|
||||
@@ -523,12 +336,10 @@ private:
|
||||
ResourceHandle<GaussianSplat> m_gaussianSplat;
|
||||
ResourceHandle<GaussianSplat> m_subsetGaussianSplat;
|
||||
Material* m_material = nullptr;
|
||||
GaussianSplatDebugView m_debugView = GaussianSplatDebugView::Scene;
|
||||
};
|
||||
|
||||
void GaussianSplatSceneTest::SetUp() {
|
||||
RHIIntegrationFixture::SetUp();
|
||||
m_debugView = GetDebugViewFromEnvironment();
|
||||
PrepareRuntimeProject();
|
||||
|
||||
m_sceneRenderer = std::make_unique<SceneRenderer>();
|
||||
@@ -641,7 +452,7 @@ void GaussianSplatSceneTest::PrepareRuntimeProject() {
|
||||
std::unique_ptr<GaussianSplat> subsetGaussianSplat(
|
||||
CreateGaussianSplatSubset(
|
||||
*m_gaussianSplat.Get(),
|
||||
GetUIntFromEnvironment("XCENGINE_GAUSSIAN_SPLAT_SUBSET_COUNT", kBaselineSubsetSplatCount),
|
||||
kBaselineSubsetSplatCount,
|
||||
kSubsetGaussianSplatAssetPath));
|
||||
ASSERT_NE(subsetGaussianSplat, nullptr);
|
||||
ASSERT_TRUE(subsetGaussianSplat->IsValid());
|
||||
@@ -663,7 +474,6 @@ void GaussianSplatSceneTest::PrepareRuntimeProject() {
|
||||
ASSERT_GT(m_subsetGaussianSplat->GetSplatCount(), 0u);
|
||||
ASSERT_EQ(m_subsetGaussianSplat->GetSHOrder(), 3u);
|
||||
ASSERT_NE(m_subsetGaussianSplat->FindSection(GaussianSplatSectionType::Chunks), nullptr);
|
||||
ExpectGaussianSplatRoundTripMatches(*subsetGaussianSplat, *m_subsetGaussianSplat.Get());
|
||||
|
||||
database.Shutdown();
|
||||
}
|
||||
@@ -680,11 +490,8 @@ void GaussianSplatSceneTest::BuildScene() {
|
||||
m_material->Initialize(params);
|
||||
m_material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinGaussianSplatShaderPath()));
|
||||
m_material->SetRenderQueue(MaterialRenderQueue::Transparent);
|
||||
m_material->SetFloat(
|
||||
"_PointScale",
|
||||
GetFloatFromEnvironment("XCENGINE_GAUSSIAN_SPLAT_POINT_SCALE", kGaussianPointScale));
|
||||
m_material->SetFloat("_PointScale", kGaussianPointScale);
|
||||
m_material->SetFloat("_OpacityScale", 1.0f);
|
||||
m_material->SetFloat("_DebugViewMode", m_debugView == GaussianSplatDebugView::Alpha ? 1.0f : 0.0f);
|
||||
|
||||
GameObject* cameraObject = m_scene->CreateGameObject("MainCamera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
@@ -705,12 +512,10 @@ void GaussianSplatSceneTest::BuildScene() {
|
||||
const float sizeY = std::max(boundsMax.y - boundsMin.y, 0.001f);
|
||||
const float sizeZ = std::max(boundsMax.z - boundsMin.z, 0.001f);
|
||||
const float maxExtent = std::max(sizeX, std::max(sizeY, sizeZ));
|
||||
const float uniformScale =
|
||||
GetFloatFromEnvironment("XCENGINE_GAUSSIAN_SPLAT_TARGET_EXTENT", kTargetSceneExtent) / maxExtent;
|
||||
const float uniformScale = kTargetSceneExtent / maxExtent;
|
||||
|
||||
GameObject* root = m_scene->CreateGameObject("GaussianSplatRoot");
|
||||
root->GetTransform()->SetLocalPosition(
|
||||
GetVector3FromEnvironment("XCENGINE_GAUSSIAN_SPLAT_ROOT_POS", kDefaultRootPosition));
|
||||
root->GetTransform()->SetLocalPosition(Vector3::Zero());
|
||||
root->GetTransform()->SetLocalScale(Vector3(uniformScale, uniformScale, uniformScale));
|
||||
|
||||
GameObject* splatObject = m_scene->CreateGameObject("RoomGaussianSplat");
|
||||
@@ -722,11 +527,7 @@ void GaussianSplatSceneTest::BuildScene() {
|
||||
splatRenderer->SetMaterial(m_material);
|
||||
splatRenderer->SetCastShadows(false);
|
||||
splatRenderer->SetReceiveShadows(false);
|
||||
|
||||
cameraObject->GetTransform()->SetLocalPosition(
|
||||
GetVector3FromEnvironment("XCENGINE_GAUSSIAN_SPLAT_CAMERA_POS", kDefaultCameraPosition));
|
||||
cameraObject->GetTransform()->LookAt(
|
||||
GetVector3FromEnvironment("XCENGINE_GAUSSIAN_SPLAT_CAMERA_LOOK_AT", kDefaultCameraLookAt));
|
||||
cameraObject->GetTransform()->SetLocalPosition(Vector3(0.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
||||
RHIResourceView* GaussianSplatSceneTest::GetCurrentBackBufferView() {
|
||||
@@ -784,8 +585,7 @@ TEST_P(GaussianSplatSceneTest, RenderRoomGaussianSplatScene) {
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
constexpr int kTargetFrameCount = 2;
|
||||
const GaussianSplatDebugView debugView = GetDebugViewFromEnvironment();
|
||||
const char* screenshotFilename = GetScreenshotFilename(GetBackendType(), debugView);
|
||||
const char* screenshotFilename = GetScreenshotFilename(GetBackendType());
|
||||
|
||||
for (int frameCount = 0; frameCount <= kTargetFrameCount; ++frameCount) {
|
||||
if (frameCount > 0) {
|
||||
@@ -799,11 +599,6 @@ TEST_P(GaussianSplatSceneTest, RenderRoomGaussianSplatScene) {
|
||||
commandQueue->WaitForIdle();
|
||||
ASSERT_TRUE(TakeScreenshot(screenshotFilename));
|
||||
|
||||
if (debugView != GaussianSplatDebugView::Scene) {
|
||||
SUCCEED() << "Debug view screenshot captured for inspection: " << screenshotFilename;
|
||||
break;
|
||||
}
|
||||
|
||||
const std::filesystem::path gtPath = ResolveRuntimePath("GT.ppm");
|
||||
if (!std::filesystem::exists(gtPath)) {
|
||||
GTEST_SKIP() << "GT.ppm missing, screenshot captured for baseline generation: " << screenshotFilename;
|
||||
|
||||
@@ -100,18 +100,6 @@ std::unordered_set<std::string> GetIsolationObjectNames() {
|
||||
std::unordered_set<std::string> result;
|
||||
const char* value = std::getenv("XC_NAHIDA_DIAG_ONLY");
|
||||
if (value == nullptr) {
|
||||
if (GetDiagnosticMode() != DiagnosticMode::Unlit) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.emplace("Body_Mesh0");
|
||||
result.emplace("EyeStar");
|
||||
result.emplace("Body_Mesh1");
|
||||
result.emplace("Body_Mesh2");
|
||||
result.emplace("Face");
|
||||
result.emplace("Body_Mesh3");
|
||||
result.emplace("Brow");
|
||||
result.emplace("Face_Eye");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user