Files
UnrealEngine/Engine/Source/ThirdParty/Windows/glTF-Toolkit/glTF-Toolkit.Test/GLTFLODUtilsTests.cpp
2025-05-18 13:04:45 +08:00

327 lines
14 KiB
C++

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
#include "pch.h"
#include <CppUnitTest.h>
#include "GLTFSDK/IStreamWriter.h"
#include "GLTFSDK/Constants.h"
#include "GLTFSDK/Serialize.h"
#include "GLTFSDK/Deserialize.h"
#include "GLTFSDK/GLBResourceReader.h"
#include "GLTFSDK/GLTFResourceWriter.h"
#include "GLTFSDK/RapidJsonUtils.h"
#include "GLTFSDK/ExtensionsKHR.h"
#include "GLTFLODUtils.h"
#include "Helpers/WStringUtils.h"
#include "Helpers/TestUtils.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace Microsoft::glTF;
using namespace Microsoft::glTF::Toolkit;
namespace Microsoft::glTF::Toolkit::Test
{
TEST_CLASS(GLTFLODUtilsTests)
{
static void CheckGLTFLODNodeCountAgainstOriginal(Document& doc, Document& docWLod, size_t lodCount)
{
// All elements in the lod'd doc should be double the original
Assert::IsTrue(doc.buffers.Size() * lodCount == docWLod.buffers.Size());
Assert::IsTrue(doc.accessors.Size() * lodCount == docWLod.accessors.Size());
Assert::IsTrue(doc.bufferViews.Size() * lodCount == docWLod.bufferViews.Size());
Assert::IsTrue(doc.materials.Size() * lodCount == docWLod.materials.Size());
Assert::IsTrue(doc.images.Size() * lodCount == docWLod.images.Size());
Assert::IsTrue(doc.meshes.Size() * lodCount == docWLod.meshes.Size());
Assert::IsTrue(doc.nodes.Size() * lodCount == docWLod.nodes.Size());
Assert::IsTrue(doc.textures.Size() * lodCount == docWLod.textures.Size());
Assert::IsTrue(doc.samplers.Size() * lodCount == docWLod.samplers.Size());
// Scene count should be untouched
Assert::IsTrue(doc.scenes.Size() == docWLod.scenes.Size());
}
static void CheckGLTFLODCount(const char * gltfDocPath, uint32_t expectedNumberOfLods)
{
auto input = TestUtils::ReadLocalAsset(TestUtils::GetAbsolutePath(gltfDocPath));
auto readwriter = std::make_shared<StreamMock>();
try
{
GLTFResourceReader resourceReader(readwriter);
auto inputJson = std::string(std::istreambuf_iterator<char>(*input), std::istreambuf_iterator<char>());
auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer());
auto lods = GLTFLODUtils::ParseDocumentNodeLODs(doc);
Assert::IsTrue(GLTFLODUtils::NumberOfNodeLODLevels(doc, lods) == expectedNumberOfLods);
}
catch (std::exception ex)
{
std::stringstream ss;
ss << "Received exception was unexpected. Got: " << ex.what();
Assert::Fail(WStringUtils::ToWString(ss).c_str());
}
}
static std::shared_ptr<Document> ImportGLTF(const std::shared_ptr<IStreamReader>& streamReader, const std::shared_ptr<std::istream>& stream)
{
GLTFResourceReader resourceReader(streamReader);
auto json = std::string(std::istreambuf_iterator<char>(*stream), std::istreambuf_iterator<char>());
auto doc = Deserialize(json, KHR::GetKHRExtensionDeserializer());
return std::make_shared<Document>(doc);
}
const char* c_cubeAsset3DJson = "Resources\\gltf\\cubeAsset3D.gltf";
const char* c_cubeWithLODJson = "Resources\\gltf\\cubeWithLOD.gltf";
TEST_METHOD(GLTFLODUtils_NodeLodCount)
{
CheckGLTFLODCount(c_cubeAsset3DJson, 0);
}
TEST_METHOD(GLTFLODUtils_NodeLodCount_DocWithLODs)
{
CheckGLTFLODCount(c_cubeWithLODJson, 1);
}
TEST_METHOD(GLTFLODUtils_GLTFNodeLODMerge)
{
auto input = TestUtils::ReadLocalAsset(TestUtils::GetAbsolutePath(c_cubeAsset3DJson));
auto readwriter = std::make_shared<StreamMock>();
try
{
// Deserialize input json
GLTFResourceReader resourceReader(readwriter);
auto inputJson = std::string(std::istreambuf_iterator<char>(*input), std::istreambuf_iterator<char>());
auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer());
std::vector<Document> docs;
docs.push_back(doc);
docs.push_back(doc);
auto newlodgltfDoc = GLTFLODUtils::MergeDocumentsAsLODs(docs);
// Serialize Document back to json
auto outputJson = Serialize(newlodgltfDoc, KHR::GetKHRExtensionSerializer());
CheckGLTFLODNodeCountAgainstOriginal(doc, newlodgltfDoc, 2);
// Check Node Lods are correctly stored and labelled in the document
auto nodes = newlodgltfDoc.nodes.Elements();
auto lods = GLTFLODUtils::ParseDocumentNodeLODs(newlodgltfDoc);
bool validLodExtension = false;
bool containsLOD1RootNode = false;
bool containsLOD1PolyNode = false;
for (auto node : nodes)
{
if (node.name == "root" && (std::find(lods[node.id]->begin(), lods[node.id]->end(), "3") != lods[node.id]->end()))
{
validLodExtension = true;
}
if (node.name == "root_lod1")
{
containsLOD1RootNode = true;
}
if (node.name == "polygon_lod1")
{
containsLOD1PolyNode = true;
}
}
Assert::IsTrue(validLodExtension);
Assert::IsTrue(containsLOD1RootNode);
Assert::IsTrue(containsLOD1PolyNode);
}
catch (std::exception ex)
{
std::stringstream ss;
ss << "Received exception was unexpected. Got: " << ex.what();
Assert::Fail(WStringUtils::ToWString(ss).c_str());
}
}
TEST_METHOD(GLTFLODUTils_GLTFNodeLODMergeMultiple)
{
auto input = TestUtils::ReadLocalAsset(TestUtils::GetAbsolutePath(c_cubeAsset3DJson));
auto readwriter = std::make_shared<StreamMock>();
try
{
// Deserialize input json
GLTFResourceReader resourceReader(readwriter);
auto inputJson = std::string(std::istreambuf_iterator<char>(*input), std::istreambuf_iterator<char>());
auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer());
std::vector<Document> docs;
docs.push_back(doc);
docs.push_back(doc);
docs.push_back(doc);
auto newlodgltfDoc = GLTFLODUtils::MergeDocumentsAsLODs(docs);
CheckGLTFLODNodeCountAgainstOriginal(doc, newlodgltfDoc, 3);
// Check Node Lods are correctly stored and labelled in the document
auto nodes = newlodgltfDoc.nodes.Elements();
auto lods = GLTFLODUtils::ParseDocumentNodeLODs(newlodgltfDoc);
bool validLodExtension = false;
bool containsLOD1RootNode = false;
bool containsLOD1PolyNode = false;
bool containsLOD2RootNode = false;
bool containsLOD2PolyNode = false;
for (auto node : nodes)
{
if (node.name == "root" &&
(std::find(lods[node.id]->begin(), lods[node.id]->end(), "3") != lods[node.id]->end()) &&
(std::find(lods[node.id]->begin(), lods[node.id]->end(), "5") != lods[node.id]->end())
)
{
validLodExtension = true;
}
if (node.name == "root_lod1") containsLOD1RootNode = true;
if (node.name == "polygon_lod1") containsLOD1PolyNode = true;
if (node.name == "root_lod2") containsLOD2RootNode = true;
if (node.name == "polygon_lod2") containsLOD2PolyNode = true;
}
Assert::IsTrue(validLodExtension);
Assert::IsTrue(containsLOD1RootNode);
Assert::IsTrue(containsLOD1PolyNode);
Assert::IsTrue(containsLOD2RootNode);
Assert::IsTrue(containsLOD2PolyNode);
// Serialize Document back to json
auto outputJson = Serialize(newlodgltfDoc, KHR::GetKHRExtensionSerializer());
}
catch (std::exception ex)
{
std::stringstream ss;
ss << "Received exception was unexpected. Got: " << ex.what();
Assert::Fail(WStringUtils::ToWString(ss).c_str());
}
}
TEST_METHOD(GLTFLODUtils_GLTFNodeLODMergeScreenCoverage)
{
auto input = TestUtils::ReadLocalAsset(TestUtils::GetAbsolutePath(c_cubeAsset3DJson));
auto readwriter = std::make_shared<StreamMock>();
try
{
// Deserialize input json
GLTFResourceReader resourceReader(readwriter);
auto inputJson = std::string(std::istreambuf_iterator<char>(*input), std::istreambuf_iterator<char>());
auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer());
std::vector<Document> docs;
docs.push_back(doc);
docs.push_back(doc);
docs.push_back(doc);
std::vector<double> screenCoverages{ 0.5, 0.2, 0.01 };
auto newlodgltfDoc = GLTFLODUtils::MergeDocumentsAsLODs(docs, screenCoverages);
CheckGLTFLODNodeCountAgainstOriginal(doc, newlodgltfDoc, 3);
// Check Node Lods have correct screen coverage values
auto nodes = newlodgltfDoc.nodes.Elements();
bool rootNodeContainsCoverage = false;
for (auto node : nodes)
{
if (node.name == "root" && !node.extras.empty())
{
auto extrasJson = RapidJsonUtils::CreateDocumentFromString(node.extras);
Assert::IsTrue(extrasJson.IsObject());
Assert::IsTrue(extrasJson["MSFT_screencoverage"].IsArray());
Assert::IsTrue(extrasJson["MSFT_screencoverage"].GetArray().Size() == 3);
rootNodeContainsCoverage = true;
}
}
Assert::IsTrue(rootNodeContainsCoverage);
// Serialize Document back to json
auto outputJson = Serialize(newlodgltfDoc, KHR::GetKHRExtensionSerializer());
}
catch (std::exception ex)
{
std::stringstream ss;
ss << "Received exception was unexpected. Got: " << ex.what();
Assert::Fail(WStringUtils::ToWString(ss).c_str());
}
}
TEST_METHOD(GLTFLODUtils_DeserialiseNodeLODExtension)
{
auto input = TestUtils::ReadLocalAsset(TestUtils::GetAbsolutePath(c_cubeWithLODJson));
auto readwriter = std::make_shared<StreamMock>();
try
{
auto gltfDoc = ImportGLTF(readwriter, input);
auto nodes = gltfDoc->nodes.Elements();
Assert::IsTrue(nodes.size() == 4);
auto lods = GLTFLODUtils::ParseDocumentNodeLODs(*gltfDoc);
bool validLodExtension = false;
for (auto node : nodes)
{
if (node.name == "root" && (std::find(lods[node.id]->begin(), lods[node.id]->end(), "3") != lods[node.id]->end()))
{
validLodExtension = true;
break;
}
}
Assert::IsTrue(validLodExtension);
}
catch (std::exception ex)
{
std::stringstream ss;
ss << "Received exception was unexpected. Got: " << ex.what();
Assert::Fail(WStringUtils::ToWString(ss).c_str());
}
}
TEST_METHOD(GLTFLODUtils_DeserializeSerializeLoopNodeLODExtension)
{
auto input = TestUtils::ReadLocalAsset(TestUtils::GetAbsolutePath(c_cubeWithLODJson));
auto readwriter = std::make_shared<StreamMock>();
try
{
// Deserialize input json
GLTFResourceReader resourceReader(readwriter);
auto inputJson = std::string(std::istreambuf_iterator<char>(*input), std::istreambuf_iterator<char>());
auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer());
// Serialize Document back to json
auto outputJson = Serialize(doc, KHR::GetKHRExtensionSerializer());
auto outputDoc = Deserialize(outputJson, KHR::GetKHRExtensionDeserializer());
// Compare input and output GLTFDocuments
Assert::AreNotSame(doc == outputDoc, true, L"Input gltf and output gltf are not equal");
// Specifically ensure Node LODs are preserved through de/serialization loop
auto nodes = outputDoc.nodes.Elements();
Assert::IsTrue(nodes.size() == 4);
auto lods = GLTFLODUtils::ParseDocumentNodeLODs(outputDoc);
bool validLodExtension = false;
for (auto node : nodes)
{
if (node.name == "root" && (std::find(lods[node.id]->begin(), lods[node.id]->end(), "3") != lods[node.id]->end()))
{
validLodExtension = true;
break;
}
}
Assert::IsTrue(validLodExtension);
}
catch (std::exception ex)
{
std::stringstream ss;
ss << "Received exception was unexpected. Got: " << ex.what();
Assert::Fail(WStringUtils::ToWString(ss).c_str());
}
}
};
}