1315 lines
52 KiB
C++
1315 lines
52 KiB
C++
#include "ConvertTileToGltf.h"
|
|
|
|
#include <Cesium3DTilesContent/GltfConverterResult.h>
|
|
#include <CesiumGltf/Accessor.h>
|
|
#include <CesiumGltf/Buffer.h>
|
|
#include <CesiumGltf/BufferView.h>
|
|
#include <CesiumGltf/ExtensionCesiumRTC.h>
|
|
#include <CesiumGltf/ExtensionExtMeshFeatures.h>
|
|
#include <CesiumGltf/ExtensionKhrMaterialsUnlit.h>
|
|
#include <CesiumGltf/ExtensionModelExtStructuralMetadata.h>
|
|
#include <CesiumGltf/FeatureId.h>
|
|
#include <CesiumGltf/Material.h>
|
|
#include <CesiumGltf/MaterialPBRMetallicRoughness.h>
|
|
#include <CesiumGltf/Mesh.h>
|
|
#include <CesiumGltf/MeshPrimitive.h>
|
|
#include <CesiumGltf/Model.h>
|
|
#include <CesiumGltf/Node.h>
|
|
#include <CesiumUtility/Math.h>
|
|
|
|
#include <doctest/doctest.h>
|
|
#include <glm/ext/vector_double3.hpp>
|
|
#include <glm/ext/vector_double4.hpp>
|
|
#include <glm/ext/vector_float3.hpp>
|
|
#include <glm/ext/vector_float4.hpp>
|
|
#include <glm/ext/vector_uint2_sized.hpp>
|
|
#include <glm/ext/vector_uint3_sized.hpp>
|
|
#include <glm/ext/vector_uint4_sized.hpp>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace doctest;
|
|
using namespace CesiumGltf;
|
|
using namespace Cesium3DTilesContent;
|
|
using namespace CesiumUtility;
|
|
|
|
template <typename Type>
|
|
static void checkBufferContents(
|
|
const std::vector<std::byte>& buffer,
|
|
const std::vector<Type>& expected,
|
|
[[maybe_unused]] const double epsilon = Math::Epsilon6) {
|
|
REQUIRE(buffer.size() == expected.size() * sizeof(Type));
|
|
const int32_t byteStride = sizeof(Type);
|
|
if constexpr (std::is_same_v<Type, glm::vec3>) {
|
|
for (size_t i = 0; i < expected.size(); ++i) {
|
|
const glm::vec3& value =
|
|
*reinterpret_cast<const glm::vec3*>(buffer.data() + i * byteStride);
|
|
const glm::vec3& expectedValue = expected[i];
|
|
REQUIRE(Math::equalsEpsilon(
|
|
static_cast<glm::dvec3>(value),
|
|
static_cast<glm::dvec3>(expectedValue),
|
|
epsilon));
|
|
}
|
|
} else if constexpr (std::is_same_v<Type, glm::vec4>) {
|
|
for (size_t i = 0; i < expected.size(); ++i) {
|
|
const glm::vec4& value =
|
|
*reinterpret_cast<const glm::vec4*>(buffer.data() + i * byteStride);
|
|
const glm::vec4& expectedValue = expected[i];
|
|
REQUIRE(Math::equalsEpsilon(
|
|
static_cast<glm::dvec4>(value),
|
|
static_cast<glm::dvec4>(expectedValue),
|
|
epsilon));
|
|
}
|
|
} else if constexpr (std::is_floating_point_v<Type>) {
|
|
for (size_t i = 0; i < expected.size(); ++i) {
|
|
const Type& value =
|
|
*reinterpret_cast<const Type*>(buffer.data() + i * byteStride);
|
|
const Type& expectedValue = expected[i];
|
|
REQUIRE(value == Approx(expectedValue));
|
|
}
|
|
} else if constexpr (
|
|
std::is_integral_v<Type> || std::is_same_v<Type, glm::u8vec2> ||
|
|
std::is_same_v<Type, glm::u8vec3> || std::is_same_v<Type, glm::u8vec4>) {
|
|
for (size_t i = 0; i < expected.size(); ++i) {
|
|
const Type& value =
|
|
*reinterpret_cast<const Type*>(buffer.data() + i * byteStride);
|
|
const Type& expectedValue = expected[i];
|
|
REQUIRE(value == expectedValue);
|
|
}
|
|
} else {
|
|
FAIL("Buffer check has not been implemented for the given type.");
|
|
}
|
|
}
|
|
|
|
template <typename Type>
|
|
static void checkAttribute(
|
|
const Model& gltf,
|
|
const MeshPrimitive& primitive,
|
|
const std::string& attributeSemantic,
|
|
const uint32_t expectedCount) {
|
|
const auto& attributes = primitive.attributes;
|
|
REQUIRE(attributes.find(attributeSemantic) != attributes.end());
|
|
|
|
const int32_t accessorId = attributes.at(attributeSemantic);
|
|
REQUIRE(accessorId >= 0);
|
|
const uint32_t accessorIdUint = static_cast<uint32_t>(accessorId);
|
|
const Accessor& accessor = gltf.accessors[accessorIdUint];
|
|
|
|
int32_t expectedComponentType = -1;
|
|
std::string expectedType;
|
|
|
|
if constexpr (std::is_same_v<Type, glm::vec3>) {
|
|
expectedComponentType = Accessor::ComponentType::FLOAT;
|
|
expectedType = Accessor::Type::VEC3;
|
|
} else if constexpr (std::is_same_v<Type, glm::vec4>) {
|
|
expectedComponentType = Accessor::ComponentType::FLOAT;
|
|
expectedType = Accessor::Type::VEC4;
|
|
} else if constexpr (std::is_same_v<Type, glm::u8vec3>) {
|
|
expectedComponentType = Accessor::ComponentType::UNSIGNED_BYTE;
|
|
expectedType = Accessor::Type::VEC3;
|
|
} else if constexpr (std::is_same_v<Type, glm::u8vec4>) {
|
|
expectedComponentType = Accessor::ComponentType::UNSIGNED_BYTE;
|
|
expectedType = Accessor::Type::VEC4;
|
|
} else if constexpr (std::is_same_v<Type, uint8_t>) {
|
|
expectedComponentType = Accessor::ComponentType::UNSIGNED_BYTE;
|
|
expectedType = Accessor::Type::SCALAR;
|
|
} else {
|
|
FAIL("Accessor check has not been implemented for the given type.");
|
|
}
|
|
|
|
CHECK(accessor.byteOffset == 0);
|
|
CHECK(accessor.componentType == expectedComponentType);
|
|
CHECK(accessor.count == expectedCount);
|
|
CHECK(accessor.type == expectedType);
|
|
|
|
const int64_t expectedByteLength =
|
|
static_cast<int64_t>(expectedCount * sizeof(Type));
|
|
|
|
const int32_t bufferViewId = accessor.bufferView;
|
|
REQUIRE(bufferViewId >= 0);
|
|
const uint32_t bufferViewIdUint = static_cast<uint32_t>(bufferViewId);
|
|
const BufferView& bufferView = gltf.bufferViews[bufferViewIdUint];
|
|
CHECK(bufferView.byteLength == expectedByteLength);
|
|
CHECK(bufferView.byteOffset == 0);
|
|
|
|
const int32_t bufferId = bufferView.buffer;
|
|
REQUIRE(bufferId >= 0);
|
|
const uint32_t bufferIdUint = static_cast<uint32_t>(bufferId);
|
|
const Buffer& buffer = gltf.buffers[static_cast<uint32_t>(bufferIdUint)];
|
|
CHECK(buffer.byteLength == expectedByteLength);
|
|
CHECK(static_cast<int64_t>(buffer.cesium.data.size()) == buffer.byteLength);
|
|
}
|
|
|
|
TEST_CASE("Converts simple point cloud to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudPositionsOnly.pnts";
|
|
const int32_t pointsLength = 8;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.asset.version == "2.0");
|
|
|
|
// Check for single mesh node
|
|
REQUIRE(gltf.nodes.size() == 1);
|
|
Node& node = gltf.nodes[0];
|
|
// clang-format off
|
|
std::vector<double> expectedMatrix = {
|
|
1.0, 0.0, 0.0, 0.0,
|
|
0.0, 0.0, -1.0, 0.0,
|
|
0.0, 1.0, 0.0, 0.0,
|
|
0.0, 0.0, 0.0, 1.0};
|
|
// clang-format on
|
|
CHECK(node.matrix == expectedMatrix);
|
|
CHECK(node.mesh == 0);
|
|
|
|
// Check for a default scene referencing the node
|
|
CHECK(gltf.scene == 0);
|
|
REQUIRE(gltf.scenes.size() == 1);
|
|
REQUIRE(gltf.scenes[0].nodes.size() == 1);
|
|
CHECK(gltf.scenes[0].nodes[0] == 0);
|
|
|
|
// Check for single mesh primitive
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
CHECK(primitive.material == 0);
|
|
|
|
// Check for single material
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(material.pbrMetallicRoughness);
|
|
CHECK(material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == 1);
|
|
REQUIRE(attributes.find("POSITION") != attributes.end());
|
|
CHECK(attributes.at("POSITION") == 0);
|
|
|
|
// Check for single accessor
|
|
REQUIRE(gltf.accessors.size() == 1);
|
|
Accessor& accessor = gltf.accessors[0];
|
|
CHECK(accessor.bufferView == 0);
|
|
CHECK(accessor.byteOffset == 0);
|
|
CHECK(accessor.componentType == Accessor::ComponentType::FLOAT);
|
|
CHECK(accessor.count == pointsLength);
|
|
CHECK(accessor.type == Accessor::Type::VEC3);
|
|
|
|
const glm::vec3 expectedMin(-3.2968313, -4.0330467, -3.5223078);
|
|
CHECK(accessor.min[0] == Approx(expectedMin.x));
|
|
CHECK(accessor.min[1] == Approx(expectedMin.y));
|
|
CHECK(accessor.min[2] == Approx(expectedMin.z));
|
|
|
|
const glm::vec3 expectedMax(3.2968313, 4.0330467, 3.5223078);
|
|
CHECK(accessor.max[0] == Approx(expectedMax.x));
|
|
CHECK(accessor.max[1] == Approx(expectedMax.y));
|
|
CHECK(accessor.max[2] == Approx(expectedMax.z));
|
|
|
|
// Check for single bufferView
|
|
REQUIRE(gltf.bufferViews.size() == 1);
|
|
BufferView& bufferView = gltf.bufferViews[0];
|
|
CHECK(bufferView.buffer == 0);
|
|
CHECK(bufferView.byteLength == pointsLength * sizeof(glm::vec3));
|
|
CHECK(bufferView.byteOffset == 0);
|
|
|
|
// Check for single buffer
|
|
REQUIRE(gltf.buffers.size() == 1);
|
|
Buffer& buffer = gltf.buffers[0];
|
|
CHECK(buffer.byteLength == pointsLength * sizeof(glm::vec3));
|
|
CHECK(static_cast<int64_t>(buffer.cesium.data.size()) == buffer.byteLength);
|
|
|
|
const std::vector<glm::vec3> expectedPositions = {
|
|
glm::vec3(-2.4975082, -0.3252686, -3.5223078),
|
|
glm::vec3(2.3456699, 0.9171584, -3.5223078),
|
|
glm::vec3(-3.2968313, 2.7906193, 0.3055275),
|
|
glm::vec3(1.5463469, 4.03304672, 0.3055275),
|
|
glm::vec3(-1.5463469, -4.03304672, -0.3055275),
|
|
glm::vec3(3.2968313, -2.7906193, -0.3055275),
|
|
glm::vec3(-2.3456699, -0.9171584, 3.5223078),
|
|
glm::vec3(2.4975082, 0.3252686, 3.5223078)};
|
|
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expectedPositions);
|
|
|
|
// Check for RTC extension
|
|
REQUIRE(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
const auto& rtcExtension =
|
|
result.model->getExtension<CesiumGltf::ExtensionCesiumRTC>();
|
|
const glm::vec3 expectedRtcCenter(
|
|
1215012.8828876,
|
|
-4736313.0511995,
|
|
4081605.2212604);
|
|
CHECK(rtcExtension->center[0] == Approx(expectedRtcCenter.x));
|
|
CHECK(rtcExtension->center[1] == Approx(expectedRtcCenter.y));
|
|
CHECK(rtcExtension->center[2] == Approx(expectedRtcCenter.z));
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with RGBA to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudRGBA.pnts";
|
|
const int32_t pointsLength = 8;
|
|
const int32_t expectedAttributeCount = 2;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(material.alphaMode == Material::AlphaMode::BLEND);
|
|
CHECK(material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
REQUIRE(gltf.accessors.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.bufferViews.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.buffers.size() == expectedAttributeCount);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == expectedAttributeCount);
|
|
|
|
// Check that position and color attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec4>(gltf, primitive, "COLOR_0", pointsLength);
|
|
|
|
// Check color attribute more thoroughly
|
|
uint32_t colorAccessorId = static_cast<uint32_t>(attributes.at("COLOR_0"));
|
|
Accessor& colorAccessor = gltf.accessors[colorAccessorId];
|
|
CHECK(!colorAccessor.normalized);
|
|
|
|
uint32_t colorBufferViewId = static_cast<uint32_t>(colorAccessor.bufferView);
|
|
BufferView& colorBufferView = gltf.bufferViews[colorBufferViewId];
|
|
|
|
uint32_t colorBufferId = static_cast<uint32_t>(colorBufferView.buffer);
|
|
Buffer& colorBuffer = gltf.buffers[colorBufferId];
|
|
|
|
const std::vector<glm::vec4> expectedColors = {
|
|
glm::vec4(0.263174f, 0.315762f, 0.476177f, 0.423529f),
|
|
glm::vec4(0.325036f, 0.708297f, 0.259027f, 0.423529f),
|
|
glm::vec4(0.151058f, 0.353740f, 0.378676f, 0.192156f),
|
|
glm::vec4(0.160443f, 0.067724f, 0.774227f, 0.027450f),
|
|
glm::vec4(0.915750f, 0.056374f, 0.119264f, 0.239215f),
|
|
glm::vec4(0.592438f, 0.632042f, 0.242796f, 0.239215f),
|
|
glm::vec4(0.284452f, 0.127529f, 0.843369f, 0.419607f),
|
|
glm::vec4(0.002932f, 0.091518f, 0.004559f, 0.321568f)};
|
|
|
|
checkBufferContents<glm::vec4>(colorBuffer.cesium.data, expectedColors);
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with RGB to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudRGB.pnts";
|
|
const int32_t pointsLength = 8;
|
|
const int32_t expectedAttributeCount = 2;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(material.alphaMode == Material::AlphaMode::OPAQUE);
|
|
CHECK(material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
REQUIRE(gltf.accessors.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.bufferViews.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.buffers.size() == expectedAttributeCount);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == expectedAttributeCount);
|
|
|
|
// Check that position and color attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
|
|
// Check color attribute more thoroughly
|
|
uint32_t colorAccessorId = static_cast<uint32_t>(attributes.at("COLOR_0"));
|
|
Accessor& colorAccessor = gltf.accessors[colorAccessorId];
|
|
CHECK(!colorAccessor.normalized);
|
|
|
|
uint32_t colorBufferViewId = static_cast<uint32_t>(colorAccessor.bufferView);
|
|
BufferView& colorBufferView = gltf.bufferViews[colorBufferViewId];
|
|
|
|
uint32_t colorBufferId = static_cast<uint32_t>(colorBufferView.buffer);
|
|
Buffer& colorBuffer = gltf.buffers[colorBufferId];
|
|
|
|
const std::vector<glm::vec3> expectedColors = {
|
|
glm::vec3(0.263174f, 0.315762f, 0.476177f),
|
|
glm::vec3(0.325036f, 0.708297f, 0.259027f),
|
|
glm::vec3(0.151058f, 0.353740f, 0.378676f),
|
|
glm::vec3(0.160443f, 0.067724f, 0.774227f),
|
|
glm::vec3(0.915750f, 0.056374f, 0.119264f),
|
|
glm::vec3(0.592438f, 0.632042f, 0.242796f),
|
|
glm::vec3(0.284452f, 0.127529f, 0.843369f),
|
|
glm::vec3(0.002932f, 0.091518f, 0.004559f)};
|
|
|
|
checkBufferContents<glm::vec3>(colorBuffer.cesium.data, expectedColors);
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with RGB565 to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudRGB565.pnts";
|
|
const int32_t pointsLength = 8;
|
|
const int32_t expectedAttributeCount = 2;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(material.alphaMode == Material::AlphaMode::OPAQUE);
|
|
CHECK(material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
REQUIRE(gltf.accessors.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.bufferViews.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.buffers.size() == expectedAttributeCount);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == expectedAttributeCount);
|
|
|
|
// Check that position and color attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
|
|
// Check color attribute more thoroughly
|
|
uint32_t colorAccessorId = static_cast<uint32_t>(attributes.at("COLOR_0"));
|
|
Accessor& colorAccessor = gltf.accessors[colorAccessorId];
|
|
CHECK(!colorAccessor.normalized);
|
|
|
|
uint32_t colorBufferViewId = static_cast<uint32_t>(colorAccessor.bufferView);
|
|
BufferView& colorBufferView = gltf.bufferViews[colorBufferViewId];
|
|
|
|
uint32_t colorBufferId = static_cast<uint32_t>(colorBufferView.buffer);
|
|
Buffer& colorBuffer = gltf.buffers[colorBufferId];
|
|
|
|
const std::vector<glm::vec3> expectedColors = {
|
|
glm::vec3(0.2666808f, 0.3100948f, 0.4702556f),
|
|
glm::vec3(0.3024152f, 0.7123886f, 0.2333824f),
|
|
glm::vec3(0.1478017f, 0.3481712f, 0.3813029f),
|
|
glm::vec3(0.1478017f, 0.0635404f, 0.7379118f),
|
|
glm::vec3(0.8635347f, 0.0560322f, 0.1023452f),
|
|
glm::vec3(0.5694675f, 0.6282104f, 0.2333824f),
|
|
glm::vec3(0.2666808f, 0.1196507f, 0.7993773f),
|
|
glm::vec3(0.0024058f, 0.0891934f, 0.0024058f)};
|
|
|
|
checkBufferContents<glm::vec3>(colorBuffer.cesium.data, expectedColors);
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with CONSTANT_RGBA") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudConstantRGBA.pnts";
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
const int32_t pointsLength = 8;
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
CHECK(primitive.material == 0);
|
|
|
|
CHECK(gltf.buffers.size() == 1);
|
|
CHECK(gltf.bufferViews.size() == 1);
|
|
CHECK(gltf.accessors.size() == 1);
|
|
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
REQUIRE(material.pbrMetallicRoughness);
|
|
MaterialPBRMetallicRoughness& pbrMetallicRoughness =
|
|
material.pbrMetallicRoughness.value();
|
|
const auto& baseColorFactor = pbrMetallicRoughness.baseColorFactor;
|
|
|
|
// Check that CONSTANT_RGBA is stored in the material base color
|
|
const glm::vec4 expectedConstantRGBA =
|
|
glm::vec4(1.0f, 1.0f, 0.0f, 51.0f / 255.0f);
|
|
CHECK(baseColorFactor[0] == Approx(expectedConstantRGBA.x));
|
|
CHECK(baseColorFactor[1] == Approx(expectedConstantRGBA.y));
|
|
CHECK(baseColorFactor[2] == Approx(expectedConstantRGBA.z));
|
|
CHECK(baseColorFactor[3] == Approx(expectedConstantRGBA.w));
|
|
|
|
CHECK(material.alphaMode == Material::AlphaMode::BLEND);
|
|
CHECK(material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with quantized positions to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudQuantized.pnts";
|
|
const int32_t pointsLength = 8;
|
|
const int32_t expectedAttributeCount = 2;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(!gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(!gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(!gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
REQUIRE(gltf.accessors.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.bufferViews.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.buffers.size() == expectedAttributeCount);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == expectedAttributeCount);
|
|
|
|
// Check that position and color attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
|
|
// Check position attribute more thoroughly
|
|
uint32_t positionAccessorId =
|
|
static_cast<uint32_t>(attributes.at("POSITION"));
|
|
Accessor& positionAccessor = gltf.accessors[positionAccessorId];
|
|
CHECK(!positionAccessor.normalized);
|
|
|
|
const glm::vec3 expectedMin(1215009.59, -4736317.08, 4081601.7);
|
|
CHECK(positionAccessor.min[0] == Approx(expectedMin.x));
|
|
CHECK(positionAccessor.min[1] == Approx(expectedMin.y));
|
|
CHECK(positionAccessor.min[2] == Approx(expectedMin.z));
|
|
|
|
const glm::vec3 expectedMax(1215016.18, -4736309.02, 4081608.74);
|
|
CHECK(positionAccessor.max[0] == Approx(expectedMax.x));
|
|
CHECK(positionAccessor.max[1] == Approx(expectedMax.y));
|
|
CHECK(positionAccessor.max[2] == Approx(expectedMax.z));
|
|
|
|
uint32_t positionBufferViewId =
|
|
static_cast<uint32_t>(positionAccessor.bufferView);
|
|
BufferView& positionBufferView = gltf.bufferViews[positionBufferViewId];
|
|
|
|
uint32_t positionBufferId = static_cast<uint32_t>(positionBufferView.buffer);
|
|
Buffer& positionBuffer = gltf.buffers[positionBufferId];
|
|
|
|
const std::vector<glm::vec3> expectedPositions = {
|
|
glm::vec3(1215010.39, -4736313.38, 4081601.7),
|
|
glm::vec3(1215015.23, -4736312.13, 4081601.7),
|
|
glm::vec3(1215009.59, -4736310.26, 4081605.53),
|
|
glm::vec3(1215014.43, -4736309.02, 4081605.53),
|
|
glm::vec3(1215011.34, -4736317.08, 4081604.92),
|
|
glm::vec3(1215016.18, -4736315.84, 4081604.92),
|
|
glm::vec3(1215010.54, -4736313.97, 4081608.74),
|
|
glm::vec3(1215015.38, -4736312.73, 4081608.74)};
|
|
|
|
checkBufferContents<glm::vec3>(positionBuffer.cesium.data, expectedPositions);
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with normals to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudNormals.pnts";
|
|
const int32_t pointsLength = 8;
|
|
const int32_t expectedAttributeCount = 3;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(!material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(!gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
REQUIRE(gltf.accessors.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.bufferViews.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.buffers.size() == expectedAttributeCount);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == expectedAttributeCount);
|
|
|
|
// Check that position, color, and normal attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "NORMAL", pointsLength);
|
|
|
|
// Check normal attribute more thoroughly
|
|
uint32_t normalAccessorId = static_cast<uint32_t>(attributes.at("NORMAL"));
|
|
Accessor& normalAccessor = gltf.accessors[normalAccessorId];
|
|
|
|
uint32_t normalBufferViewId =
|
|
static_cast<uint32_t>(normalAccessor.bufferView);
|
|
BufferView& normalBufferView = gltf.bufferViews[normalBufferViewId];
|
|
|
|
uint32_t normalBufferId = static_cast<uint32_t>(normalBufferView.buffer);
|
|
Buffer& normalBuffer = gltf.buffers[normalBufferId];
|
|
|
|
const std::vector<glm::vec3> expectedNormals = {
|
|
glm::vec3(-0.9854088, 0.1667507, 0.0341110),
|
|
glm::vec3(-0.5957704, 0.5378777, 0.5964436),
|
|
glm::vec3(-0.5666092, -0.7828890, -0.2569800),
|
|
glm::vec3(-0.5804154, -0.7226123, 0.3754320),
|
|
glm::vec3(-0.8535281, -0.1291752, -0.5047805),
|
|
glm::vec3(0.7557975, 0.1243999, 0.6428800),
|
|
glm::vec3(0.1374090, -0.2333731, -0.9626296),
|
|
glm::vec3(-0.0633145, 0.9630424, 0.2618022)};
|
|
|
|
checkBufferContents<glm::vec3>(normalBuffer.cesium.data, expectedNormals);
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with oct-encoded normals to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath =
|
|
testFilePath / "PointCloud" / "pointCloudNormalsOctEncoded.pnts";
|
|
const int32_t pointsLength = 8;
|
|
const int32_t expectedAttributeCount = 3;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(!material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(!gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
REQUIRE(gltf.accessors.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.bufferViews.size() == expectedAttributeCount);
|
|
REQUIRE(gltf.buffers.size() == expectedAttributeCount);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == expectedAttributeCount);
|
|
|
|
// Check that position, color, and normal attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "NORMAL", pointsLength);
|
|
|
|
// Check normal attribute more thoroughly
|
|
uint32_t normalAccessorId = static_cast<uint32_t>(attributes.at("NORMAL"));
|
|
Accessor& normalAccessor = gltf.accessors[normalAccessorId];
|
|
CHECK(!normalAccessor.normalized);
|
|
|
|
uint32_t normalBufferViewId =
|
|
static_cast<uint32_t>(normalAccessor.bufferView);
|
|
BufferView& normalBufferView = gltf.bufferViews[normalBufferViewId];
|
|
|
|
uint32_t normalBufferId = static_cast<uint32_t>(normalBufferView.buffer);
|
|
Buffer& normalBuffer = gltf.buffers[normalBufferId];
|
|
|
|
const std::vector<glm::vec3> expectedNormals = {
|
|
glm::vec3(-0.9856477, 0.1634960, 0.0420418),
|
|
glm::vec3(-0.5901730, 0.5359042, 0.6037402),
|
|
glm::vec3(-0.5674310, -0.7817938, -0.2584963),
|
|
glm::vec3(-0.5861990, -0.7179291, 0.3754308),
|
|
glm::vec3(-0.8519385, -0.1283743, -0.5076620),
|
|
glm::vec3(0.7587127, 0.1254564, 0.6392304),
|
|
glm::vec3(0.1354662, -0.2292506, -0.9638947),
|
|
glm::vec3(-0.0656172, 0.9640687, 0.2574214)};
|
|
|
|
checkBufferContents<glm::vec3>(normalBuffer.cesium.data, expectedNormals);
|
|
}
|
|
|
|
std::set<int32_t>
|
|
getUniqueBufferIds(const std::vector<BufferView>& bufferViews) {
|
|
std::set<int32_t> result;
|
|
for (auto it = bufferViews.begin(); it != bufferViews.end(); it++) {
|
|
result.insert(it->buffer);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with batch IDs to glTF with "
|
|
"EXT_structural_metadata") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudBatched.pnts";
|
|
const int32_t pointsLength = 8;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
// The correctness of the model extension is thoroughly tested in
|
|
// TestUpgradeBatchTableToExtStructuralMetadata
|
|
CHECK(gltf.hasExtension<ExtensionModelExtStructuralMetadata>());
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
auto primitiveExtension = primitive.getExtension<ExtensionExtMeshFeatures>();
|
|
REQUIRE(primitiveExtension);
|
|
REQUIRE(primitiveExtension->featureIds.size() == 1);
|
|
FeatureId& featureId = primitiveExtension->featureIds[0];
|
|
CHECK(featureId.featureCount == 8);
|
|
CHECK(featureId.attribute == 0);
|
|
CHECK(featureId.propertyTable == 0);
|
|
|
|
CHECK(gltf.materials.size() == 1);
|
|
|
|
// The file has three metadata properties:
|
|
// - "name": string scalars in JSON
|
|
// - "dimensions": float vec3s in binary
|
|
// - "id": int scalars in binary
|
|
// There are three accessors (one per primitive attribute)
|
|
// and four additional buffer views:
|
|
// - "name" string data buffer view
|
|
// - "name" string offsets buffer view
|
|
// - "dimensions" buffer view
|
|
// - "id" buffer view
|
|
REQUIRE(gltf.accessors.size() == 3);
|
|
REQUIRE(gltf.bufferViews.size() == 7);
|
|
|
|
// There are also three added buffers:
|
|
// - binary data in the batch table
|
|
// - string data of "name"
|
|
// - string offsets for the data for "name"
|
|
REQUIRE(gltf.buffers.size() == 6);
|
|
std::set<int32_t> bufferSet = getUniqueBufferIds(gltf.bufferViews);
|
|
CHECK(bufferSet.size() == 6);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == 3);
|
|
|
|
// Check that position, normal, and feature ID attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "NORMAL", pointsLength);
|
|
checkAttribute<uint8_t>(gltf, primitive, "_FEATURE_ID_0", pointsLength);
|
|
|
|
// Check feature ID attribute more thoroughly
|
|
uint32_t featureIdAccessorId =
|
|
static_cast<uint32_t>(attributes.at("_FEATURE_ID_0"));
|
|
Accessor& featureIdAccessor = gltf.accessors[featureIdAccessorId];
|
|
|
|
uint32_t featureIdBufferViewId =
|
|
static_cast<uint32_t>(featureIdAccessor.bufferView);
|
|
BufferView& featureIdBufferView = gltf.bufferViews[featureIdBufferViewId];
|
|
|
|
uint32_t featureIdBufferId =
|
|
static_cast<uint32_t>(featureIdBufferView.buffer);
|
|
Buffer& featureIdBuffer = gltf.buffers[featureIdBufferId];
|
|
|
|
const std::vector<uint8_t> expectedFeatureIDs = {5, 5, 6, 6, 7, 0, 3, 1};
|
|
checkBufferContents<uint8_t>(featureIdBuffer.cesium.data, expectedFeatureIDs);
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with per-point properties to glTF with "
|
|
"EXT_structural_metadata") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath =
|
|
testFilePath / "PointCloud" / "pointCloudWithPerPointProperties.pnts";
|
|
const int32_t pointsLength = 8;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
// The correctness of the model extension is thoroughly tested in
|
|
// TestUpgradeBatchTableToExtStructuralMetadata
|
|
CHECK(gltf.hasExtension<ExtensionModelExtStructuralMetadata>());
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
auto primitiveExtension = primitive.getExtension<ExtensionExtMeshFeatures>();
|
|
REQUIRE(primitiveExtension);
|
|
REQUIRE(primitiveExtension->featureIds.size() == 1);
|
|
FeatureId& featureId = primitiveExtension->featureIds[0];
|
|
// Check for implicit feature IDs
|
|
CHECK(featureId.featureCount == pointsLength);
|
|
CHECK(!featureId.attribute);
|
|
CHECK(featureId.propertyTable == 0);
|
|
|
|
CHECK(gltf.materials.size() == 1);
|
|
|
|
// The file has three binary metadata properties:
|
|
// - "temperature": float scalars
|
|
// - "secondaryColor": float vec3s
|
|
// - "id": unsigned short scalars
|
|
// There are two accessors (one per primitive attribute)
|
|
// and three additional buffer views:
|
|
// - temperature buffer view
|
|
// - secondary color buffer view
|
|
// - id buffer view
|
|
REQUIRE(gltf.accessors.size() == 2);
|
|
REQUIRE(gltf.bufferViews.size() == 5);
|
|
|
|
// There is only one added buffer containing all the binary values.
|
|
REQUIRE(gltf.buffers.size() == 3);
|
|
std::set<int32_t> bufferSet = getUniqueBufferIds(gltf.bufferViews);
|
|
CHECK(bufferSet.size() == 3);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == 2);
|
|
REQUIRE(attributes.find("_FEATURE_ID_0") == attributes.end());
|
|
|
|
// Check that position and color attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with Draco compression to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudDraco.pnts";
|
|
const int32_t pointsLength = 8;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
// The correctness of the model extension is thoroughly tested in
|
|
// TestUpgradeBatchTableToExtStructuralMetadata
|
|
CHECK(gltf.hasExtension<ExtensionModelExtStructuralMetadata>());
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
auto primitiveExtension = primitive.getExtension<ExtensionExtMeshFeatures>();
|
|
REQUIRE(primitiveExtension);
|
|
REQUIRE(primitiveExtension->featureIds.size() == 1);
|
|
FeatureId& featureId = primitiveExtension->featureIds[0];
|
|
// Check for implicit feature IDs
|
|
CHECK(featureId.featureCount == pointsLength);
|
|
CHECK(!featureId.attribute);
|
|
CHECK(featureId.propertyTable == 0);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(!material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(!gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
// The file has three binary metadata properties:
|
|
// - "temperature": float scalars
|
|
// - "secondaryColor": float vec3s
|
|
// - "id": unsigned short scalars
|
|
// There are three accessors (one per primitive attribute)
|
|
// and three additional buffer views:
|
|
// - temperature buffer view
|
|
// - secondary color buffer view
|
|
// - id buffer view
|
|
REQUIRE(gltf.accessors.size() == 3);
|
|
REQUIRE(gltf.bufferViews.size() == 6);
|
|
|
|
// There is only one added buffer containing all the binary values.
|
|
REQUIRE(gltf.buffers.size() == 4);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == 3);
|
|
|
|
// Check that position, color, and normal attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "NORMAL", pointsLength);
|
|
|
|
// Check each attribute more thoroughly
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("POSITION"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
|
|
const glm::vec3 expectedMin(-4.9270443f, -3.9144449f, -4.8131480f);
|
|
CHECK(accessor.min[0] == Approx(expectedMin.x));
|
|
CHECK(accessor.min[1] == Approx(expectedMin.y));
|
|
CHECK(accessor.min[2] == Approx(expectedMin.z));
|
|
|
|
const glm::vec3 expectedMax(3.7791683f, 4.8152132f, 3.2142156f);
|
|
CHECK(accessor.max[0] == Approx(expectedMax.x));
|
|
CHECK(accessor.max[1] == Approx(expectedMax.y));
|
|
CHECK(accessor.max[2] == Approx(expectedMax.z));
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
std::vector<glm::vec3> expected = {
|
|
glm::vec3(-4.9270443f, 0.8337686f, 0.1705846f),
|
|
glm::vec3(-2.9789500f, 2.6891474f, 2.9824265f),
|
|
glm::vec3(-2.8329495f, -3.9144449f, -1.2851576f),
|
|
glm::vec3(-2.9022198f, -3.6128526f, 1.8772986f),
|
|
glm::vec3(-4.2673778f, -0.6459517f, -2.5240305f),
|
|
glm::vec3(3.7791683f, 0.6222278f, 3.2142156f),
|
|
glm::vec3(0.6870481f, -1.1670776f, -4.8131480f),
|
|
glm::vec3(-0.3168385f, 4.8152132f, 1.3087492f),
|
|
};
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expected);
|
|
}
|
|
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("COLOR_0"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
CHECK(!accessor.normalized);
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
std::vector<glm::vec3> expected = {
|
|
glm::vec3(0.4761772f, 0.6870308f, 0.3250369f),
|
|
glm::vec3(0.1510580f, 0.3537409f, 0.3786762f),
|
|
glm::vec3(0.7742273f, 0.0016869f, 0.9157501f),
|
|
glm::vec3(0.5924380f, 0.6320426f, 0.2427963f),
|
|
glm::vec3(0.8433697f, 0.6730490f, 0.0029323f),
|
|
glm::vec3(0.0001751f, 0.1087111f, 0.6661169f),
|
|
glm::vec3(0.7299188f, 0.7299188f, 0.9489649f),
|
|
glm::vec3(0.1801442f, 0.2348952f, 0.5795466f),
|
|
};
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expected);
|
|
}
|
|
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("NORMAL"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
// The Draco-decoded normals are slightly different from the values
|
|
// derived by manually decoding the uncompressed oct-encoded normals,
|
|
// hence the less precise comparison.
|
|
std::vector<glm::vec3> expected{
|
|
glm::vec3(-0.9824559f, 0.1803542f, 0.0474616f),
|
|
glm::vec3(-0.5766854f, 0.5427628f, 0.6106081f),
|
|
glm::vec3(-0.5725988f, -0.7802446f, -0.2516918f),
|
|
glm::vec3(-0.5705807f, -0.7345407f, 0.36727036f),
|
|
glm::vec3(-0.8560267f, -0.1281128f, -0.5008047f),
|
|
glm::vec3(0.7647877f, 0.11264316f, 0.63435888f),
|
|
glm::vec3(0.1301889f, -0.23434004f, -0.9633979f),
|
|
glm::vec3(-0.0450783f, 0.9616723f, 0.2704703f),
|
|
};
|
|
checkBufferContents<glm::vec3>(
|
|
buffer.cesium.data,
|
|
expected,
|
|
Math::Epsilon1);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Converts point cloud with partial Draco compression to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudDracoPartial.pnts";
|
|
const int32_t pointsLength = 8;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
CHECK(gltf.hasExtension<CesiumGltf::ExtensionCesiumRTC>());
|
|
CHECK(gltf.isExtensionUsed(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.isExtensionRequired(ExtensionCesiumRTC::ExtensionName));
|
|
CHECK(gltf.hasExtension<ExtensionModelExtStructuralMetadata>());
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
auto primitiveExtension = primitive.getExtension<ExtensionExtMeshFeatures>();
|
|
REQUIRE(primitiveExtension);
|
|
REQUIRE(primitiveExtension->featureIds.size() == 1);
|
|
FeatureId& featureId = primitiveExtension->featureIds[0];
|
|
// Check for implicit feature IDs
|
|
CHECK(featureId.featureCount == pointsLength);
|
|
CHECK(!featureId.attribute);
|
|
CHECK(featureId.propertyTable == 0);
|
|
|
|
REQUIRE(gltf.materials.size() == 1);
|
|
Material& material = gltf.materials[0];
|
|
CHECK(!material.hasExtension<ExtensionKhrMaterialsUnlit>());
|
|
CHECK(!gltf.isExtensionUsed(ExtensionKhrMaterialsUnlit::ExtensionName));
|
|
|
|
// The file has three binary metadata properties:
|
|
// - "temperature": float scalars
|
|
// - "secondaryColor": float vec3s
|
|
// - "id": unsigned short scalars
|
|
// There are three accessors (one per primitive attribute)
|
|
// and three additional buffer views:
|
|
// - temperature buffer view
|
|
// - secondary color buffer view
|
|
// - id buffer view
|
|
REQUIRE(gltf.accessors.size() == 3);
|
|
REQUIRE(gltf.bufferViews.size() == 6);
|
|
|
|
// There is only one added buffer containing all the binary values.
|
|
REQUIRE(gltf.buffers.size() == 4);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == 3);
|
|
|
|
// Check that position, color, and normal attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "NORMAL", pointsLength);
|
|
|
|
// Check each attribute more thoroughly
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("POSITION"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
|
|
const glm::vec3 expectedMin(-4.9270443f, -3.9144449f, -4.8131480f);
|
|
CHECK(accessor.min[0] == Approx(expectedMin.x));
|
|
CHECK(accessor.min[1] == Approx(expectedMin.y));
|
|
CHECK(accessor.min[2] == Approx(expectedMin.z));
|
|
|
|
const glm::vec3 expectedMax(3.7791683f, 4.8152132f, 3.2142156f);
|
|
CHECK(accessor.max[0] == Approx(expectedMax.x));
|
|
CHECK(accessor.max[1] == Approx(expectedMax.y));
|
|
CHECK(accessor.max[2] == Approx(expectedMax.z));
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
std::vector<glm::vec3> expected = {
|
|
glm::vec3(-4.9270443f, 0.8337686f, 0.1705846f),
|
|
glm::vec3(-2.9789500f, 2.6891474f, 2.9824265f),
|
|
glm::vec3(-2.8329495f, -3.9144449f, -1.2851576f),
|
|
glm::vec3(-2.9022198f, -3.6128526f, 1.8772986f),
|
|
glm::vec3(-4.2673778f, -0.6459517f, -2.5240305f),
|
|
glm::vec3(3.7791683f, 0.6222278f, 3.2142156f),
|
|
glm::vec3(0.6870481f, -1.1670776f, -4.8131480f),
|
|
glm::vec3(-0.3168385f, 4.8152132f, 1.3087492f),
|
|
};
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expected);
|
|
}
|
|
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("COLOR_0"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
CHECK(!accessor.normalized);
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
std::vector<glm::vec3> expected = {
|
|
glm::vec3(0.4761772f, 0.6870308f, 0.3250369f),
|
|
glm::vec3(0.1510580f, 0.3537409f, 0.3786762f),
|
|
glm::vec3(0.7742273f, 0.0016869f, 0.9157501f),
|
|
glm::vec3(0.5924380f, 0.6320426f, 0.2427963f),
|
|
glm::vec3(0.8433697f, 0.6730490f, 0.0029323f),
|
|
glm::vec3(0.0001751f, 0.1087111f, 0.6661169f),
|
|
glm::vec3(0.7299188f, 0.7299188f, 0.9489649f),
|
|
glm::vec3(0.1801442f, 0.2348952f, 0.5795466f),
|
|
};
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expected);
|
|
}
|
|
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("NORMAL"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
const std::vector<glm::vec3> expected = {
|
|
glm::vec3(-0.9854088, 0.1667507, 0.0341110),
|
|
glm::vec3(-0.5957704, 0.5378777, 0.5964436),
|
|
glm::vec3(-0.5666092, -0.7828890, -0.2569800),
|
|
glm::vec3(-0.5804154, -0.7226123, 0.3754320),
|
|
glm::vec3(-0.8535281, -0.1291752, -0.5047805),
|
|
glm::vec3(0.7557975, 0.1243999, 0.6428800),
|
|
glm::vec3(0.1374090, -0.2333731, -0.9626296),
|
|
glm::vec3(-0.0633145, 0.9630424, 0.2618022)};
|
|
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expected);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Converts batched point cloud with Draco compression to glTF") {
|
|
std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
testFilePath = testFilePath / "PointCloud" / "pointCloudDracoBatched.pnts";
|
|
const int32_t pointsLength = 8;
|
|
|
|
GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath);
|
|
|
|
REQUIRE(result.model);
|
|
Model& gltf = *result.model;
|
|
|
|
// The correctness of the model extension is thoroughly tested in
|
|
// TestUpgradeBatchTableToExtStructuralMetadata
|
|
CHECK(gltf.hasExtension<ExtensionModelExtStructuralMetadata>());
|
|
|
|
CHECK(gltf.scenes.size() == 1);
|
|
CHECK(gltf.nodes.size() == 1);
|
|
REQUIRE(gltf.meshes.size() == 1);
|
|
Mesh& mesh = gltf.meshes[0];
|
|
REQUIRE(mesh.primitives.size() == 1);
|
|
MeshPrimitive& primitive = mesh.primitives[0];
|
|
CHECK(primitive.mode == MeshPrimitive::Mode::POINTS);
|
|
|
|
auto primitiveExtension = primitive.getExtension<ExtensionExtMeshFeatures>();
|
|
REQUIRE(primitiveExtension);
|
|
REQUIRE(primitiveExtension->featureIds.size() == 1);
|
|
FeatureId& featureId = primitiveExtension->featureIds[0];
|
|
CHECK(featureId.featureCount == 8);
|
|
CHECK(featureId.attribute == 0);
|
|
CHECK(featureId.propertyTable == 0);
|
|
|
|
CHECK(gltf.materials.size() == 1);
|
|
|
|
// The file has three metadata properties:
|
|
// - "name": string scalars in JSON
|
|
// - "dimensions": float vec3s in binary
|
|
// - "id": int scalars in binary
|
|
// There are four accessors (one per primitive attribute)
|
|
// and four additional buffer views:
|
|
// - "name" string data buffer view
|
|
// - "name" string offsets buffer view
|
|
// - "dimensions" buffer view
|
|
// - "id" buffer view
|
|
REQUIRE(gltf.accessors.size() == 4);
|
|
REQUIRE(gltf.bufferViews.size() == 8);
|
|
|
|
// There are also three added buffers:
|
|
// - binary data in the batch table
|
|
// - string data of "name"
|
|
// - string offsets for the data for "name"
|
|
REQUIRE(gltf.buffers.size() == 7);
|
|
std::set<int32_t> bufferSet = getUniqueBufferIds(gltf.bufferViews);
|
|
CHECK(bufferSet.size() == 7);
|
|
|
|
auto attributes = primitive.attributes;
|
|
REQUIRE(attributes.size() == 4);
|
|
|
|
// Check that position, normal, and feature ID attributes are present
|
|
checkAttribute<glm::vec3>(gltf, primitive, "POSITION", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "COLOR_0", pointsLength);
|
|
checkAttribute<glm::vec3>(gltf, primitive, "NORMAL", pointsLength);
|
|
checkAttribute<uint8_t>(gltf, primitive, "_FEATURE_ID_0", pointsLength);
|
|
|
|
// Check each attribute more thoroughly
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("POSITION"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
|
|
const glm::vec3 expectedMin(-4.9270443f, -3.9144449f, -4.8131480f);
|
|
CHECK(accessor.min[0] == Approx(expectedMin.x));
|
|
CHECK(accessor.min[1] == Approx(expectedMin.y));
|
|
CHECK(accessor.min[2] == Approx(expectedMin.z));
|
|
|
|
const glm::vec3 expectedMax(3.7791683f, 4.8152132f, 3.2142156f);
|
|
CHECK(accessor.max[0] == Approx(expectedMax.x));
|
|
CHECK(accessor.max[1] == Approx(expectedMax.y));
|
|
CHECK(accessor.max[2] == Approx(expectedMax.z));
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
std::vector<glm::vec3> expected = {
|
|
glm::vec3(-4.9270443f, 0.8337686f, 0.1705846f),
|
|
glm::vec3(-2.9789500f, 2.6891474f, 2.9824265f),
|
|
glm::vec3(-2.8329495f, -3.9144449f, -1.2851576f),
|
|
glm::vec3(-2.9022198f, -3.6128526f, 1.8772986f),
|
|
glm::vec3(-4.2673778f, -0.6459517f, -2.5240305f),
|
|
glm::vec3(3.7791683f, 0.6222278f, 3.2142156f),
|
|
glm::vec3(0.6870481f, -1.1670776f, -4.8131480f),
|
|
glm::vec3(-0.3168385f, 4.8152132f, 1.3087492f),
|
|
};
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expected);
|
|
}
|
|
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("COLOR_0"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
CHECK(!accessor.normalized);
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
std::vector<glm::vec3> expected = {
|
|
glm::vec3(0.4761772f, 0.6870308f, 0.3250369f),
|
|
glm::vec3(0.1510580f, 0.3537409f, 0.3786762f),
|
|
glm::vec3(0.7742273f, 0.0016869f, 0.9157501f),
|
|
glm::vec3(0.5924380f, 0.6320426f, 0.2427963f),
|
|
glm::vec3(0.8433697f, 0.6730490f, 0.0029323f),
|
|
glm::vec3(0.0001751f, 0.1087111f, 0.6661169f),
|
|
glm::vec3(0.7299188f, 0.7299188f, 0.9489649f),
|
|
glm::vec3(0.1801442f, 0.2348952f, 0.5795466f),
|
|
};
|
|
checkBufferContents<glm::vec3>(buffer.cesium.data, expected);
|
|
}
|
|
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("NORMAL"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
// The Draco-decoded normals are slightly different from the values
|
|
// derived by manually decoding the uncompressed oct-encoded normals,
|
|
// hence the less precise comparison.
|
|
std::vector<glm::vec3> expected{
|
|
glm::vec3(-0.9824559f, 0.1803542f, 0.0474616f),
|
|
glm::vec3(-0.5766854f, 0.5427628f, 0.6106081f),
|
|
glm::vec3(-0.5725988f, -0.7802446f, -0.2516918f),
|
|
glm::vec3(-0.5705807f, -0.7345407f, 0.36727036f),
|
|
glm::vec3(-0.8560267f, -0.1281128f, -0.5008047f),
|
|
glm::vec3(0.7647877f, 0.11264316f, 0.63435888f),
|
|
glm::vec3(0.1301889f, -0.23434004f, -0.9633979f),
|
|
glm::vec3(-0.0450783f, 0.9616723f, 0.2704703f),
|
|
};
|
|
checkBufferContents<glm::vec3>(
|
|
buffer.cesium.data,
|
|
expected,
|
|
Math::Epsilon1);
|
|
}
|
|
|
|
{
|
|
uint32_t accessorId = static_cast<uint32_t>(attributes.at("_FEATURE_ID_0"));
|
|
Accessor& accessor = gltf.accessors[accessorId];
|
|
|
|
uint32_t bufferViewId = static_cast<uint32_t>(accessor.bufferView);
|
|
BufferView& bufferView = gltf.bufferViews[bufferViewId];
|
|
|
|
uint32_t bufferId = static_cast<uint32_t>(bufferView.buffer);
|
|
Buffer& buffer = gltf.buffers[bufferId];
|
|
|
|
const std::vector<uint8_t> expected = {5, 5, 6, 6, 7, 0, 3, 1};
|
|
checkBufferContents<uint8_t>(buffer.cesium.data, expected);
|
|
}
|
|
}
|